Skip to content

Commit 99403d6

Browse files
authored
Improve compilation speed of TypeScript code. (#1154)
* ✨ faster compilation for windows * merged master * 📝 change log * Fixes #1146
1 parent 6c8c864 commit 99403d6

File tree

2 files changed

+69
-18
lines changed

2 files changed

+69
-18
lines changed

gulpfile.js

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const fs = require('fs');
2424
const remapIstanbul = require('remap-istanbul');
2525
const istanbul = require('istanbul');
2626
const glob = require('glob');
27+
const _ = require('lodash');
2728

2829
/**
2930
* Hygiene works by creating cascading subsets of all our files and
@@ -41,7 +42,6 @@ const all = [
4142

4243
const tsFilter = [
4344
'src/**/*.ts',
44-
'src/client/**/*.ts',
4545
];
4646

4747
const indentationFilter = [
@@ -60,7 +60,7 @@ const tslintFilter = [
6060
'!resources/**/*',
6161
'!snippets/**/*',
6262
'!syntaxes/**/*',
63-
'!**/typings/**/*',
63+
'!**/typings/**/*'
6464
];
6565

6666
const copyrightHeader = [
@@ -78,7 +78,7 @@ gulp.task('watch', ['hygiene-modified', 'hygiene-watch']);
7878

7979
gulp.task('debugger-coverage', () => buildDebugAdapterCoverage());
8080

81-
gulp.task('hygiene-watch', () => gulp.watch(tsFilter, debounce(() => run({ mode: 'changes' }), 1000)));
81+
gulp.task('hygiene-watch', () => gulp.watch(tsFilter, debounce(() => run({ mode: 'changes', skipFormatCheck: true, skipIndentationCheck: true, skipCopyrightCheck: true }), 100)));
8282

8383
gulp.task('hygiene-all', () => run({ mode: 'all' }));
8484

@@ -131,20 +131,52 @@ function buildDebugAdapterCoverage() {
131131
* @property {'changes'|'staged'|'all'|'compile'} [mode=] - Mode.
132132
* @property {boolean=} skipIndentationCheck - Skip indentation checks.
133133
* @property {boolean=} skipFormatCheck - Skip format checks.
134+
* @property {boolean=} skipCopyrightCheck - Skip copyright checks.
134135
* @property {boolean=} skipLinter - Skip linter.
135136
*/
136137

138+
const tsProjectMap = {};
139+
/**
140+
*
141+
* @param {hygieneOptions} options
142+
*/
143+
function getTsProject(options) {
144+
const tsOptions = options.mode === 'compile' ? undefined : { strict: true, noImplicitAny: false, noImplicitThis: false };
145+
const mode = tsOptions && tsOptions.mode ? tsOptions.mode : '';
146+
return tsProjectMap[mode] ? tsProjectMap[mode] : tsProjectMap[mode] = ts.createProject('tsconfig.json', tsOptions);
147+
}
148+
149+
let configuration;
150+
let program;
151+
let linter;
152+
/**
153+
*
154+
* @param {hygieneOptions} options
155+
*/
156+
function getLinter(options) {
157+
configuration = configuration ? configuration : tslint.Configuration.findConfiguration(null, '.');
158+
program = program ? program : tslint.Linter.createProgram('./tsconfig.json');
159+
linter = linter ? linter : new tslint.Linter({ formatter: 'json' }, program);
160+
return { linter, configuration };
161+
}
162+
let compilationInProgress = false;
163+
let reRunCompilation = false;
137164
/**
138165
*
139166
* @param {hygieneOptions} options
140167
* @returns {NodeJS.ReadWriteStream}
141168
*/
142169
const hygiene = (options) => {
170+
if (compilationInProgress) {
171+
reRunCompilation = true;
172+
return;
173+
}
174+
const started = new Date().getTime();
175+
compilationInProgress = true;
143176
options = options || {};
144177
let errorCount = 0;
145-
const addedFiles = getAddedFilesSync();
178+
const addedFiles = options.skipCopyrightCheck ? [] : getAddedFilesSync();
146179
console.log(colors.blue('Hygiene started.'));
147-
148180
const copyrights = es.through(function (file) {
149181
if (addedFiles.indexOf(file.path) !== -1 && file.contents.toString('utf8').indexOf(copyrightHeader) !== 0) {
150182
// Use tslint format.
@@ -228,15 +260,16 @@ const hygiene = (options) => {
228260
.filter(reported => reported === true)
229261
.length > 0;
230262
}
231-
const configuration = tslint.Configuration.findConfiguration(null, '.');
232-
const program = tslint.Linter.createProgram('./tsconfig.json');
233-
const linter = new tslint.Linter({ formatter: 'json' }, program);
263+
264+
const { linter, configuration } = getLinter(options);
234265
const tsl = es.through(function (file) {
235266
const contents = file.contents.toString('utf8');
236267
// Don't print anything to the console, we'll do that.
237268
// Yes this is a hack, but tslinter doesn't provide an option to prevent this.
238269
const oldWarn = console.warn;
239270
console.warn = () => { };
271+
linter.failures = [];
272+
linter.fixes = [];
240273
linter.lint(file.relative, contents, configuration.results);
241274
console.warn = oldWarn;
242275
const result = linter.getResult();
@@ -259,8 +292,7 @@ const hygiene = (options) => {
259292
this.emit('data', file);
260293
});
261294

262-
const tsOptions = options.mode === 'compile' ? undefined : { strict: true, noImplicitAny: false, noImplicitThis: false };
263-
const tsProject = ts.createProject('tsconfig.json', tsOptions);
295+
const tsProject = getTsProject(options);
264296

265297
const tsc = function () {
266298
function customReporter() {
@@ -294,8 +326,11 @@ const hygiene = (options) => {
294326
}
295327

296328
result = result
297-
.pipe(filter(tslintFilter))
298-
.pipe(copyrights);
329+
.pipe(filter(tslintFilter));
330+
331+
if (!options.skipCopyrightCheck) {
332+
result = result.pipe(copyrights);
333+
}
299334

300335
if (!options.skipFormatCheck) {
301336
// result = result
@@ -306,7 +341,7 @@ const hygiene = (options) => {
306341
result = result
307342
.pipe(tsl);
308343
}
309-
344+
let totalTime = 0;
310345
result = result
311346
.pipe(tscFilesTracker)
312347
.pipe(sourcemaps.init())
@@ -323,15 +358,22 @@ const hygiene = (options) => {
323358
.pipe(gulp.dest(dest))
324359
.pipe(es.through(null, function () {
325360
if (errorCount > 0) {
326-
const errorMessage = `Hygiene failed with errors 👎 . Check 'gulpfile.js'.`;
361+
const errorMessage = `Hygiene failed with errors 👎 . Check 'gulpfile.js' (completed in ${new Date().getTime() - started}ms).`;
327362
console.error(colors.red(errorMessage));
328363
exitHandler(options);
329364
} else {
330-
console.log(colors.green('Hygiene passed with 0 errors 👍.'));
365+
console.log(colors.green(`Hygiene passed with 0 errors 👍 (completed in ${new Date().getTime() - started}ms).`));
331366
}
332367
// Reset error counter.
333368
errorCount = 0;
334369
reportedLinterFailures = [];
370+
compilationInProgress = false;
371+
if (reRunCompilation) {
372+
reRunCompilation = false;
373+
setTimeout(() => {
374+
hygiene(options);
375+
}, 10);
376+
}
335377
this.emit('end');
336378
}))
337379
.on('error', exitHandler.bind(this, options));
@@ -346,6 +388,7 @@ const hygiene = (options) => {
346388
* @property {string[]=} files - Optional list of files to be modified.
347389
* @property {boolean=} skipIndentationCheck - Skip indentation checks.
348390
* @property {boolean=} skipFormatCheck - Skip format checks.
391+
* @property {boolean=} skipCopyrightCheck - Skip copyright checks.
349392
* @property {boolean=} skipLinter - Skip linter.
350393
* @property {boolean=} watch - Watch mode.
351394
*/
@@ -391,7 +434,15 @@ function getAddedFilesSync() {
391434
return out
392435
.split(/\r?\n/)
393436
.filter(l => !!l)
394-
.filter(l => l.startsWith('A') || l.startsWith('??'))
437+
.filter(l => _.intersection(['A', '?'], l.substring(0, 2).trim().split()).length > 0)
438+
.map(l => path.join(__dirname, l.substring(2).trim()));
439+
}
440+
function getModifiedFilesSync() {
441+
const out = cp.execSync('git status -u -s', { encoding: 'utf8' });
442+
return out
443+
.split(/\r?\n/)
444+
.filter(l => !!l)
445+
.filter(l => _.intersection(['M', 'A', 'R', 'C'], l.substring(0, 2).trim().split()).length > 0)
395446
.map(l => path.join(__dirname, l.substring(2).trim()));
396447
}
397448

@@ -404,8 +455,7 @@ function getFilesToProcess(options) {
404455

405456
// If we need only modified files, then filter the glob.
406457
if (options && options.mode === 'changes') {
407-
return gulp.src(all, gulpSrcOptions)
408-
.pipe(gitmodified(['M', 'A', 'AM', 'D', 'R', 'C', 'U', '??']));
458+
return gulp.src(getModifiedFilesSync(), gulpSrcOptions);
409459
}
410460

411461
if (options && options.mode === 'staged') {

news/3 Code Health/1146.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve compilation speed of TypeScript code.

0 commit comments

Comments
 (0)