Skip to content

Commit 659f646

Browse files
committed
Merge branch 'v2' into v2-cleanup
2 parents 777ceac + c38e016 commit 659f646

File tree

13 files changed

+1904
-63
lines changed

13 files changed

+1904
-63
lines changed

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@
3737
"dependencies": {
3838
"@babel/plugin-proposal-class-properties": "^7.1.0",
3939
"@babel/plugin-transform-typescript": "^7.1.0",
40+
"ansi-to-html": "^0.6.6",
4041
"debug": "^3.1.0",
4142
"ember-cli-babel-plugin-helpers": "^1.0.0",
4243
"execa": "^0.9.0",
4344
"fs-extra": "^5.0.0",
4445
"resolve": "^1.5.0",
4546
"rsvp": "^4.8.1",
4647
"semver": "^5.5.1",
48+
"stagehand": "^1.0.0",
4749
"walk-sync": "^0.3.2"
4850
},
4951
"devDependencies": {
@@ -52,8 +54,10 @@
5254
"@types/core-object": "^3.0.0",
5355
"@types/ember": "2.8.13",
5456
"@types/ember-qunit": "^3.0.1",
57+
"@types/express": "^4.16.0",
5558
"@types/node": "^9.6.5",
5659
"@types/qunit": "^2.0.31",
60+
"@types/resolve": "^0.0.8",
5761
"@types/semver": "^5.5.0",
5862
"broccoli-asset-rev": "^2.7.0",
5963
"co": "^4.6.0",
@@ -65,7 +69,7 @@
6569
"ember-cli-eslint": "^4.2.3",
6670
"ember-cli-htmlbars": "^3.0.0",
6771
"ember-cli-htmlbars-inline-precompile": "^1.0.3",
68-
"ember-cli-inject-live-reload": "^1.8.2",
72+
"ember-cli-inject-live-reload": "^2.0.1",
6973
"ember-cli-release": "^0.2.9",
7074
"ember-cli-sri": "^2.1.1",
7175
"ember-cli-typescript-blueprints": "^1.0.0",
@@ -83,6 +87,7 @@
8387
"eslint-plugin-ember": "^5.2.0",
8488
"eslint-plugin-node": "^7.0.1",
8589
"esprima": "^4.0.0",
90+
"got": "^9.2.2",
8691
"in-repo-a": "link:tests/dummy/lib/in-repo-a",
8792
"in-repo-b": "link:tests/dummy/lib/in-repo-b",
8893
"loader.js": "^4.7.0",
@@ -104,6 +109,7 @@
104109
"ember-addon": {
105110
"configPath": "tests/dummy/config",
106111
"before": [
112+
"broccoli-watcher",
107113
"ember-cli-babel"
108114
]
109115
},

ts/addon.ts

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import semver from 'semver';
2+
import { Remote } from 'stagehand';
3+
import { connect } from 'stagehand/lib/adapters/child-process';
24
import { hasPlugin, addPlugin, AddPluginOptions } from 'ember-cli-babel-plugin-helpers';
35
import Addon from 'ember-cli/lib/models/addon';
46
import { addon } from './lib/utilities/ember-cli-entities';
7+
import fork from './lib/utilities/fork';
8+
import TypecheckWorker from './lib/typechecking/worker';
9+
import TypecheckMiddleware from './lib/typechecking/middleware';
510

611
export default addon({
712
name: 'ember-cli-typescript',
@@ -10,6 +15,12 @@ export default addon({
1015
this._super.included.apply(this, arguments);
1116
this._checkDevelopment();
1217
this._checkBabelVersion();
18+
19+
// If we're a direct dependency of the host app, go ahead and start up the
20+
// typecheck worker so we don't wait until the end of the build to check
21+
if (this.parent === this.project) {
22+
this._getTypecheckWorker();
23+
}
1324
},
1425

1526
includedCommands() {
@@ -25,7 +36,30 @@ export default addon({
2536
return `${__dirname}/blueprints`;
2637
},
2738

28-
setupPreprocessorRegistry(type: string) {
39+
serverMiddleware({ app }) {
40+
let workerPromise = this._getTypecheckWorker();
41+
let middleware = new TypecheckMiddleware(this.project, workerPromise);
42+
middleware.register(app);
43+
},
44+
45+
async postBuild() {
46+
// This code makes the fundamental assumption that the TS compiler's fs watcher
47+
// will notice a file change before the full Broccoli build completes. Otherwise
48+
// the `getStatus` call here might report the status of the previous check. In
49+
// practice, though, building takes much longer than the time to trigger the
50+
// compiler's "hey, a file changed" hook, and once the typecheck has begun, the
51+
// `getStatus` call will block until it's complete.
52+
let worker = await this._getTypecheckWorker();
53+
let { failed } = await worker.getStatus();
54+
55+
if (failed) {
56+
// The actual details of the errors will already have been printed
57+
// with nice highlighting and formatting separately.
58+
throw new Error('Typechecking failed');
59+
}
60+
},
61+
62+
setupPreprocessorRegistry(type) {
2963
if (type !== 'parent') return;
3064

3165
// Normally this is the sort of logic that would live in `included()`, but
@@ -52,10 +86,11 @@ export default addon({
5286

5387
_checkBabelVersion() {
5488
let babel = this.parent.addons.find(addon => addon.name === 'ember-cli-babel');
55-
if (!babel || !semver.satisfies(babel.pkg.version, '>=7.0.0-alpha.0 <8')) {
56-
let version = babel ? `version ${babel.pkg.version}` : `no instance of ember-cli-babel`;
89+
let version = babel && babel.pkg.version;
90+
if (!babel || !(semver.gte(version!, '7.1.0') && semver.lt(version!, '8.0.0'))) {
91+
let versionString = babel ? `version ${babel.pkg.version}` : `no instance of ember-cli-babel`;
5792
this.ui.writeWarnLine(
58-
`ember-cli-typescript requires ember-cli-babel@7, but you have ${version} installed; ` +
93+
`ember-cli-typescript requires ember-cli-babel ^7.1.0, but you have ${versionString} installed; ` +
5994
'your TypeScript files may not be transpiled correctly.'
6095
);
6196
}
@@ -95,4 +130,29 @@ export default addon({
95130
extensions.push('ts');
96131
}
97132
},
133+
134+
_typecheckWorker: undefined as Promise<Remote<TypecheckWorker>> | undefined,
135+
136+
_getTypecheckWorker() {
137+
if (!this._typecheckWorker) {
138+
this._typecheckWorker = this._forkTypecheckWorker();
139+
}
140+
141+
return this._typecheckWorker;
142+
},
143+
144+
async _forkTypecheckWorker() {
145+
let childProcess = fork(`${__dirname}/lib/typechecking/worker/launch`);
146+
let worker = await connect<TypecheckWorker>(childProcess);
147+
148+
await worker.onTypecheck(status => {
149+
for (let error of status.errors) {
150+
this.ui.writeLine(error);
151+
}
152+
});
153+
154+
await worker.start(this.project.root);
155+
156+
return worker;
157+
},
98158
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Remote } from 'stagehand';
2+
import { Application, Request, Response, NextFunction } from 'express';
3+
import Project from 'ember-cli/lib/models/project';
4+
import TypecheckWorker from '../worker';
5+
import renderErrorPage from './render-error-page';
6+
7+
export const LIVE_RELOAD_PATH = '/ember-cli-live-reload.js';
8+
9+
export default class TypecheckMiddleware {
10+
constructor(private project: Project, private workerPromise: Promise<Remote<TypecheckWorker>>) {}
11+
12+
public register(app: Application): void {
13+
app.use((...params) => this.handleRequest(...params));
14+
}
15+
16+
private async handleRequest(request: Request, response: Response, next: NextFunction) {
17+
if (!request.accepts('html') || request.path === LIVE_RELOAD_PATH) {
18+
next();
19+
return;
20+
}
21+
22+
let worker = await this.workerPromise;
23+
let { errors, failed } = await worker.getStatus();
24+
25+
if (failed) {
26+
response.type('html');
27+
response.end(renderErrorPage(errors, this.environmentInfo()));
28+
} else {
29+
next();
30+
}
31+
}
32+
33+
private environmentInfo(): string {
34+
let tsVersion = (this.project.require('typescript/package.json') as any).version;
35+
let ectsVersion = require(`${__dirname}/../../../../package`).version;
36+
37+
return `typescript@${tsVersion}, ember-cli-typescript@${ectsVersion}`;
38+
}
39+
}

0 commit comments

Comments
 (0)