Skip to content

Commit 2cae5df

Browse files
committed
feature #101 Forked typescript type checking (davidmpaz)
This PR was squashed before being merged into the master branch (closes #101). Discussion ---------- Forked typescript type checking This PR fixes: #63 it should be possible to merge also with #99 directly. Open points: Regarding point .1 in #2 > If this plugin is added. we need to check dependency is added using loader-features.js. This module name is getting name from loaders, we are not adding a loader here but a plugin. Maybe some more general module is needed for checking features? After implementing this I realize that as soon as you require a module which doesn't exist node throws an error, we don't even go through error catch from encore. So possible solutions I see for this pending point are: 1. We don't refactor `loader-features.js` file and we still will get a loud error when trying to use some plugin not installed. From error is clear whats need to be done. 2. We refactor `loader-features.js` to be named more general and we include plugins dependencies also there as intended initially. But this way we need to check for missing packages earlier in `WebpackConfig.js` like I am doing now in this PR. I like either solutions the same, I would like to get feedback on what do you guys think about it? thanks in advance. Commits ------- 7a7e399 Forked typescript type checking
2 parents facd9ff + 7a7e399 commit 2cae5df

11 files changed

+231
-34
lines changed

index.js

+17
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,23 @@ module.exports = {
390390
return this;
391391
},
392392

393+
/**
394+
* Call this to enable forked type checking for TypeScript loader
395+
* https://github.com/TypeStrong/ts-loader/blob/v2.3.0/README.md#faster-builds
396+
*
397+
* This is a build optimization API to reduce build times.
398+
*
399+
* @param {function} forkedTypeScriptTypesCheckOptionsCallback
400+
* @return {exports}
401+
*/
402+
enableForkedTypeScriptTypesChecking(forkedTypeScriptTypesCheckOptionsCallback = () => {}) {
403+
webpackConfig.enableForkedTypeScriptTypesChecking(
404+
forkedTypeScriptTypesCheckOptionsCallback
405+
);
406+
407+
return this;
408+
},
409+
393410
/**
394411
* If enabled, the Vue.js loader is enabled.
395412
*

lib/WebpackConfig.js

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

6264
getContext() {
@@ -245,6 +247,17 @@ class WebpackConfig {
245247
this.tsConfigurationCallback = callback;
246248
}
247249

250+
enableForkedTypeScriptTypesChecking(forkedTypeScriptTypesCheckOptionsCallback = () => {}) {
251+
252+
if (typeof forkedTypeScriptTypesCheckOptionsCallback !== 'function') {
253+
throw new Error('Argument 1 to enableForkedTypeScriptTypesChecking() must be a callback function.');
254+
}
255+
256+
this.useForkedTypeScriptTypeChecking = true;
257+
this.forkedTypeScriptTypesCheckOptionsCallback =
258+
forkedTypeScriptTypesCheckOptionsCallback;
259+
}
260+
248261
enableVueLoader(vueLoaderOptionsCallback = () => {}) {
249262
this.useVueLoader = true;
250263

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
@@ -356,6 +356,25 @@ describe('WebpackConfig object', () => {
356356
});
357357
});
358358

359+
describe('enableForkedTypeScriptTypesChecking', () => {
360+
it('Calling method sets it', () => {
361+
const config = createConfig();
362+
config.enableTypeScriptLoader();
363+
const testCallback = () => {};
364+
config.enableForkedTypeScriptTypesChecking(testCallback);
365+
expect(config.forkedTypeScriptTypesCheckOptionsCallback)
366+
.to.equal(testCallback);
367+
});
368+
369+
it('Calling with non-callback throws an error', () => {
370+
const config = createConfig();
371+
372+
expect(() => {
373+
config.enableForkedTypeScriptTypesChecking('FOO');
374+
}).to.throw('must be a callback function');
375+
});
376+
});
377+
359378
describe('addPlugin', () => {
360379
it('extends the current registered plugins', () => {
361380
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)