Skip to content

Commit 7a7e399

Browse files
davidmpazweaverryan
authored andcommitted
Forked typescript type checking
1 parent ad306b9 commit 7a7e399

11 files changed

+231
-34
lines changed

index.js

+17
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,23 @@ module.exports = {
380380
return this;
381381
},
382382

383+
/**
384+
* Call this to enable forked type checking for TypeScript loader
385+
* https://github.com/TypeStrong/ts-loader/blob/v2.3.0/README.md#faster-builds
386+
*
387+
* This is a build optimization API to reduce build times.
388+
*
389+
* @param {function} forkedTypeScriptTypesCheckOptionsCallback
390+
* @return {exports}
391+
*/
392+
enableForkedTypeScriptTypesChecking(forkedTypeScriptTypesCheckOptionsCallback = () => {}) {
393+
webpackConfig.enableForkedTypeScriptTypesChecking(
394+
forkedTypeScriptTypesCheckOptionsCallback
395+
);
396+
397+
return this;
398+
},
399+
383400
/**
384401
* If enabled, the Vue.js loader is enabled.
385402
*

lib/WebpackConfig.js

+13
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class WebpackConfig {
5656
this.loaders = [];
5757
this.useTypeScriptLoader = false;
5858
this.tsConfigurationCallback = function() {};
59+
this.useForkedTypeScriptTypeChecking = false;
60+
this.forkedTypeScriptTypesCheckOptionsCallback = () => {};
5961
}
6062

6163
getContext() {
@@ -238,6 +240,17 @@ class WebpackConfig {
238240
this.tsConfigurationCallback = callback;
239241
}
240242

243+
enableForkedTypeScriptTypesChecking(forkedTypeScriptTypesCheckOptionsCallback = () => {}) {
244+
245+
if (typeof forkedTypeScriptTypesCheckOptionsCallback !== 'function') {
246+
throw new Error('Argument 1 to enableForkedTypeScriptTypesChecking() must be a callback function.');
247+
}
248+
249+
this.useForkedTypeScriptTypeChecking = true;
250+
this.forkedTypeScriptTypesCheckOptionsCallback =
251+
forkedTypeScriptTypesCheckOptionsCallback;
252+
}
253+
241254
enableVueLoader(vueLoaderOptionsCallback = () => {}) {
242255
this.useVueLoader = true;
243256

lib/loader-features.js

+5
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ const loaderFeatures = {
4141
packages: ['typescript', 'ts-loader'],
4242
description: 'process TypeScript files'
4343
},
44+
forkedtypecheck: {
45+
method: 'enableForkedTypeScriptTypesChecking()',
46+
packages: ['typescript', 'ts-loader', 'fork-ts-checker-webpack-plugin'],
47+
description: 'check TypeScript types in a separate process'
48+
},
4449
vue: {
4550
method: 'enableVueLoader()',
4651
// vue is needed so the end-user can do things

lib/loaders/typescript.js

+11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ module.exports = {
3232
[config]
3333
);
3434

35+
// fork-ts-checker-webpack-plugin integration
36+
if (webpackConfig.useForkedTypeScriptTypeChecking) {
37+
loaderFeatures.ensureLoaderPackagesExist('forkedtypecheck');
38+
// force transpileOnly to speed up
39+
config.transpileOnly = true;
40+
41+
// add forked ts types plugin to the stack
42+
const forkedTypesPluginUtil = require('../plugins/forked-ts-types'); // eslint-disable-line
43+
forkedTypesPluginUtil(webpackConfig);
44+
}
45+
3546
// use ts alongside with babel
3647
// @see https://github.com/TypeStrong/ts-loader/blob/master/README.md#babel
3748
let loaders = babelLoader.getLoaders(webpackConfig);

lib/plugins/forked-ts-types.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* This file is part of the Symfony package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); // eslint-disable-line
13+
14+
/**
15+
* @param {WebpackConfig} webpackConfig
16+
* @return {void}
17+
*/
18+
module.exports = function(webpackConfig) {
19+
let config = {};
20+
21+
// allow for ts-loader config to be controlled
22+
webpackConfig.forkedTypeScriptTypesCheckOptionsCallback.apply(
23+
// use config as the this variable
24+
config,
25+
[config]
26+
);
27+
28+
webpackConfig.addPlugin(new ForkTsCheckerWebpackPlugin(config));
29+
};

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"eslint": "^3.19.0",
5656
"eslint-plugin-header": "^1.0.0",
5757
"eslint-plugin-node": "^4.2.2",
58+
"fork-ts-checker-webpack-plugin": "^0.2.7",
5859
"http-server": "^0.9.0",
5960
"less": "^2.7.2",
6061
"less-loader": "^4.0.2",

test/WebpackConfig.js

+19
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,25 @@ describe('WebpackConfig object', () => {
332332
});
333333
});
334334

335+
describe('enableForkedTypeScriptTypesChecking', () => {
336+
it('Calling method sets it', () => {
337+
const config = createConfig();
338+
config.enableTypeScriptLoader();
339+
const testCallback = () => {};
340+
config.enableForkedTypeScriptTypesChecking(testCallback);
341+
expect(config.forkedTypeScriptTypesCheckOptionsCallback)
342+
.to.equal(testCallback);
343+
});
344+
345+
it('Calling with non-callback throws an error', () => {
346+
const config = createConfig();
347+
348+
expect(() => {
349+
config.enableForkedTypeScriptTypesChecking('FOO');
350+
}).to.throw('must be a callback function');
351+
});
352+
});
353+
335354
describe('addPlugin', () => {
336355
it('extends the current registered plugins', () => {
337356
const config = createConfig();

test/functional.js

+29-9
Original file line numberDiff line numberDiff line change
@@ -288,13 +288,13 @@ describe('Functional tests using webpack', function() {
288288
'bg.483832e48e67e6a3b7f0ae064eadca51.css',
289289
'manifest.json'
290290
]
291-
);
291+
);
292292

293293
expect(path.join(config.outputPath, 'images')).to.be.a.directory()
294294
.with.files([
295295
'symfony_logo.ea1ca6f7.png'
296296
]
297-
);
297+
);
298298

299299
webpackAssert.assertOutputFileContains(
300300
'bg.483832e48e67e6a3b7f0ae064eadca51.css',
@@ -319,19 +319,19 @@ describe('Functional tests using webpack', function() {
319319
'font.css',
320320
'manifest.json'
321321
]
322-
);
322+
);
323323

324324
expect(path.join(config.outputPath, 'images')).to.be.a.directory()
325325
.with.files([
326326
'symfony_logo.ea1ca6f7.png'
327327
]
328-
);
328+
);
329329

330330
expect(path.join(config.outputPath, 'fonts')).to.be.a.directory()
331331
.with.files([
332332
'Roboto.9896f773.woff2'
333333
]
334-
);
334+
);
335335

336336
webpackAssert.assertOutputFileContains(
337337
'bg.css',
@@ -359,21 +359,21 @@ describe('Functional tests using webpack', function() {
359359
'styles.css',
360360
'manifest.json'
361361
]
362-
);
362+
);
363363

364364
expect(path.join(config.outputPath, 'images')).to.be.a.directory()
365365
.with.files([
366366
'symfony_logo.ea1ca6f7.png',
367367
'symfony_logo.f27119c2.png'
368368
]
369-
);
369+
);
370370

371371
expect(path.join(config.outputPath, 'fonts')).to.be.a.directory()
372372
.with.files([
373373
'Roboto.9896f773.woff2',
374374
'Roboto.3c37aa69.woff2'
375375
]
376-
);
376+
);
377377

378378
webpackAssert.assertOutputFileContains(
379379
'styles.css',
@@ -587,7 +587,7 @@ module.exports = {
587587

588588
fs.writeFileSync(
589589
path.join(appDir, '.babelrc'),
590-
`
590+
`
591591
{
592592
"presets": [
593593
["env", {
@@ -667,6 +667,26 @@ module.exports = {
667667
});
668668
});
669669

670+
it('TypeScript is compiled and type checking is done in a separate process!', (done) => {
671+
this.timeout(8000);
672+
setTimeout(done, 7000);
673+
674+
const config = createWebpackConfig('www/build', 'dev');
675+
config.setPublicPath('/build');
676+
config.addEntry('main', ['./js/render.ts', './js/index.ts']);
677+
config.enableTypeScriptLoader();
678+
// test should fail if `config.tsconfig` is not set up properly
679+
config.enableForkedTypeScriptTypesChecking((config) => {
680+
config.silent = true; // remove to get output on terminal
681+
});
682+
683+
expect(function() {
684+
testSetup.runWebpack(config, (webpackAssert) => {
685+
done();
686+
});
687+
}).to.throw('wrong `tsconfig` path in fork plugin configuration (should be a relative or absolute path)');
688+
});
689+
670690
it('The output directory is cleaned between builds', (done) => {
671691
const config = createWebpackConfig('www/build', 'dev');
672692
config.setPublicPath('/build');

test/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,15 @@ describe('Public API', () => {
186186

187187
});
188188

189+
describe('enableForkedTypeScriptTypesChecking', () => {
190+
191+
it('must return the API object', () => {
192+
const returnedValue = api.enableForkedTypeScriptTypesChecking();
193+
expect(returnedValue).to.equal(api);
194+
});
195+
196+
});
197+
189198
describe('enableVueLoader', () => {
190199

191200
it('must return the API object', () => {

test/plugins/forked-ts-types.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* This file is part of the Symfony package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const expect = require('chai').expect;
13+
const WebpackConfig = require('../../lib/WebpackConfig');
14+
const RuntimeConfig = require('../../lib/config/RuntimeConfig');
15+
const tsLoader = require('../../lib/loaders/typescript');
16+
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
17+
18+
function createConfig() {
19+
const runtimeConfig = new RuntimeConfig();
20+
runtimeConfig.context = __dirname;
21+
runtimeConfig.babelRcFileExists = false;
22+
23+
return new WebpackConfig(runtimeConfig);
24+
}
25+
26+
describe('plugins/forkedtypecheck', () => {
27+
it('getPlugins() basic usage', () => {
28+
const config = createConfig();
29+
config.enableTypeScriptLoader();
30+
config.enableForkedTypeScriptTypesChecking();
31+
32+
expect(config.plugins).to.have.lengthOf(0);
33+
const tsTypeChecker = require('../../lib/plugins/forked-ts-types');
34+
tsTypeChecker(config);
35+
expect(config.plugins).to.have.lengthOf(1);
36+
expect(config.plugins[0]).to.be.an.instanceof(ForkTsCheckerWebpackPlugin);
37+
// after enabling plugin, check typescript loader has right config
38+
const actualLoaders = tsLoader.getLoaders(config);
39+
expect(actualLoaders[1].options.transpileOnly).to.be.true;
40+
});
41+
});

0 commit comments

Comments
 (0)