Skip to content

Commit 12b661e

Browse files
Open editor on clicking compilation error
1 parent 319cae8 commit 12b661e

6 files changed

Lines changed: 749 additions & 444 deletions

File tree

lib/templates/error.html

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,9 @@
10991099
<article class="content">
11001100
<h1 class="error-header">Dang! Looks like a {{errorType}}.</h1>
11011101
{{#if location.file}}
1102-
<h2 class="error-message">{{location.file}}{{#if location.line}}:{{location.line}}{{/if}}</h2>
1102+
<h2 class="error-message" onclick="openEditorForTemplateError()">
1103+
{{location.file}}{{#if location.line}}:{{location.line}}{{/if}}
1104+
</h2>
11031105
{{/if}}
11041106

11051107
{{#if codeFrame}}
@@ -1140,5 +1142,12 @@ <h2>Environment</h2>
11401142
<span>{{versionString}}</span>
11411143
</article>
11421144
</main>
1145+
<script>
1146+
function openEditorForTemplateError() {
1147+
let xhr = new XMLHttpRequest();
1148+
xhr.open('GET', '/ember_open_editor_for_template_compilation_error', true);
1149+
xhr.send();
1150+
}
1151+
</script>
11431152
</body>
11441153
</html>

lib/utils/filename-normaliser.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
const path = require('path');
4+
const findup = require('find-up');
5+
const { existsSync } = require('fs');
6+
7+
const pkg = require(findup.sync('package.json'));
8+
9+
function normaliseFileName(fileName = '') {
10+
// check for invalid file paths
11+
// for example,
12+
// path.basename('myfile') will be myfile, doesn't need normalisation as this is probably
13+
// not a template file
14+
// path.basename('templates/myfile') will not be equal to fileName
15+
if (path.basename(fileName) === fileName) {
16+
return null;
17+
}
18+
19+
// handles module unification layout
20+
// location as per stack : src/ui/components/my-component/template.hbs
21+
// should be normalised to: src/ui/components/my-component/template.hbs
22+
if (fileName.startsWith('src')) {
23+
return fileName;
24+
}
25+
26+
// handle templates inside application with either pod or classic structure
27+
// Classic Structure
28+
// location as per stack : project-name/templates/templateName.hbs
29+
// should be normalised to: app/templates/templateName.hbs
30+
// Pods
31+
// location as per stack : project-name/routeName/templateName.hbs
32+
// should be normalised to: app/routeName/templateName.hbs
33+
if (fileName.startsWith(pkg.name)) {
34+
let fileNameElements = fileName.split(path.sep);
35+
fileNameElements.shift();
36+
37+
return path.join('app', ...fileNameElements);
38+
}
39+
40+
// handle templates inside in-repo engines
41+
// location as per stack : engine-name/templates/cars.hbs
42+
// should be normalised to: lib/engine-name/addon/templates/templateName.hbs
43+
let fileNameElements = fileName.split(path.sep);
44+
fileNameElements = ['lib', fileNameElements.shift(), 'addon', ...fileNameElements];
45+
46+
return path.join(...fileNameElements);
47+
}
48+
49+
function getNormalisedFileName(fileName, line = 1, column = 1) {
50+
let normalisedFileName = normaliseFileName(fileName);
51+
if (existsSync(normalisedFileName)) {
52+
return `${normalisedFileName}:${line}:${column}`;
53+
}
54+
55+
return null;
56+
}
57+
58+
module.exports = { normaliseFileName, getNormalisedFileName };

lib/watcher-middleware.js

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
const url = require('url');
44
const path = require('path');
5+
const launchEditor = require('launch-editor');
56

67
const errorHandler = require('./utils/error-handler');
8+
const { getNormalisedFileName } = require('./utils/filename-normaliser');
79

810
module.exports = function watcherMiddleware(watcher, options) {
911
options = options || {};
@@ -34,10 +36,49 @@ module.exports = function watcherMiddleware(watcher, options) {
3436

3537
next();
3638
}, (buildError) => {
37-
errorHandler(response, {
38-
'buildError': buildError,
39-
'liveReloadPath': options.liveReloadPath
40-
});
39+
if (request.url === '/ember_open_editor_for_template_compilation_error') {
40+
let errorLocation = buildError.broccoliPayload.error.location || {};
41+
let { originalError } = buildError.broccoliPayload;
42+
if (originalError) {
43+
let originalLocation = originalError.loc || originalError.location || {};
44+
errorLocation.line = originalLocation.line || errorLocation.line || 1;
45+
errorLocation.column = originalLocation.column || errorLocation.column || 1;
46+
}
47+
48+
let {
49+
file: fileName,
50+
line,
51+
column
52+
} = errorLocation;
53+
54+
if (fileName) {
55+
let normalisedFileName = getNormalisedFileName(fileName, line, column);
56+
57+
// 1) If the EMBER_CLI_EDITOR is set, it's value will be used as the editor.
58+
// 2) If EMBER_CLI_EDITOR is not set, launchEditor will try to guess which editor is open.
59+
// a) If it succeeds, that guessed editor will be opened.
60+
// b) Else, it will fall back to VISUAL and EDITOR.
61+
// 3) If none of these work, it will display a message asking the user to set EMBER_CLI_EDITOR
62+
let emberCliEditor = process.env.EMBER_CLI_EDITOR;
63+
64+
launchEditor(normalisedFileName, emberCliEditor, () => {
65+
if (!emberCliEditor) {
66+
console.log(
67+
'To set up the editor integration, set the env variable EMBER_CLI_EDITOR ' +
68+
'to an editor of your choice and restart the server.'
69+
);
70+
}
71+
});
72+
}
73+
74+
response.writeHead(200);
75+
response.end();
76+
} else {
77+
errorHandler(response, {
78+
'buildError': buildError,
79+
'liveReloadPath': options.liveReloadPath
80+
});
81+
}
4182
})
4283
.catch((err) => {
4384
console.log(err);

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
"homepage": "https://github.com/ember-cli/broccoli-middleware",
2121
"dependencies": {
2222
"ansi-html": "^0.0.7",
23+
"find-up": "^3.0.0",
2324
"handlebars": "^4.0.4",
2425
"has-ansi": "^3.0.0",
26+
"launch-editor": "^2.2.1",
2527
"mime-types": "^2.1.18"
2628
},
2729
"devDependencies": {

test/filename-normaliser-test.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use strict';
2+
3+
const expect = require('chai').expect;
4+
const path = require('path');
5+
const normaliseFileName = require('./../lib/utils/filename-normaliser').normaliseFileName;
6+
7+
describe('file name normaliser', function() {
8+
describe('handles module unification layout', function() {
9+
it('components are normalised correctly', function() {
10+
const fileName = path.normalize('src/ui/components/my-component/template.hbs');
11+
const normalisedFileName = normaliseFileName(fileName);
12+
13+
expect(normalisedFileName).to.equal(fileName);
14+
});
15+
});
16+
17+
describe('handles pod layout', function() {
18+
it('components are normalised correctly', function() {
19+
const fileName = path.normalize('broccoli-middleware/components/awesome-component/template.hbs');
20+
const expectedFileName = path.normalize('app/components/awesome-component/template.hbs');
21+
const normalisedFileName = normaliseFileName(fileName);
22+
23+
expect(normalisedFileName).to.equal(expectedFileName);
24+
});
25+
26+
it('templates are normalised correctly', function() {
27+
const fileName = path.normalize('broccoli-middleware/awesome-route/template.hbs');
28+
const expectedFileName = path.normalize('app/awesome-route/template.hbs');
29+
const normalisedFileName = normaliseFileName(fileName);
30+
31+
expect(normalisedFileName).to.equal(expectedFileName);
32+
});
33+
34+
describe('in-repo engines are normalised correctly', function() {
35+
it('components are normalised correctly', function() {
36+
const fileName = path.normalize('awesome-engine/components/awesome-component/template.hbs');
37+
const expectedFileName = path.normalize('lib/awesome-engine/addon/components/awesome-component/template.hbs');
38+
const normalisedFileName = normaliseFileName(fileName);
39+
40+
expect(normalisedFileName).to.equal(expectedFileName);
41+
});
42+
43+
it('templates are normalised correctly', function() {
44+
const fileName = path.normalize('awesome-engine/awesome-route/template.hbs');
45+
const expectedFileName = path.normalize('lib/awesome-engine/addon/awesome-route/template.hbs');
46+
const normalisedFileName = normaliseFileName(fileName);
47+
48+
expect(normalisedFileName).to.equal(expectedFileName);
49+
});
50+
});
51+
});
52+
53+
describe('handles classic layout', function() {
54+
it('components are normalised correctly', function() {
55+
const fileName = path.normalize('broccoli-middleware/templates/components/awesome-component.hbs');
56+
const expectedFileName = path.normalize('app/templates/components/awesome-component.hbs');
57+
const normalisedFileName = normaliseFileName(fileName);
58+
59+
expect(normalisedFileName).to.equal(expectedFileName);
60+
});
61+
62+
it('templates are normalised correctly', function() {
63+
const fileName = path.normalize('broccoli-middleware/templates/application.hbs');
64+
const expectedFileName = path.normalize('app/templates/application.hbs');
65+
const normalisedFileName = normaliseFileName(fileName);
66+
67+
expect(normalisedFileName).to.equal(expectedFileName);
68+
});
69+
70+
describe('in-repo engines are normalised correctly', function() {
71+
it('components are normalised correctly', function() {
72+
const fileName = path.normalize('awesome-engine/templates/components/awesome-component.hbs');
73+
const expectedFileName = path.normalize('lib/awesome-engine/addon/templates/components/awesome-component.hbs');
74+
const normalisedFileName = normaliseFileName(fileName);
75+
76+
expect(normalisedFileName).to.equal(expectedFileName);
77+
});
78+
79+
it('templates are normalised correctly', function() {
80+
const fileName = path.normalize('awesome-engine/templates/awesome-route.hbs');
81+
const expectedFileName = path.normalize('lib/awesome-engine/addon/templates/awesome-route.hbs');
82+
const normalisedFileName = normaliseFileName(fileName);
83+
84+
expect(normalisedFileName).to.equal(expectedFileName);
85+
});
86+
});
87+
});
88+
});

0 commit comments

Comments
 (0)