Skip to content
This repository was archived by the owner on Dec 1, 2019. It is now read-only.

Commit d3d740b

Browse files
committed
feat(*): speed-up compilation of dynamic requires + total speedup
1 parent d1fe07e commit d3d740b

File tree

4 files changed

+91
-26
lines changed

4 files changed

+91
-26
lines changed

src/deps.ts

+32-5
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ function isIgnoreDependency(absulutePath: string) {
8787
return absulutePath == '%%ignore';
8888
}
8989

90+
let lock: Promise<any>;
91+
9092
export class FileAnalyzer {
9193
dependencies = new DependencyManager();
9294
validFiles = new ValidFilesManager();
@@ -96,9 +98,35 @@ export class FileAnalyzer {
9698
this.state = state;
9799
}
98100

101+
async checkDependenciesLocked(resolver: IResolver, fileName: string): Promise<boolean> {
102+
let isValid = this.validFiles.isFileValid(fileName);
103+
if (isValid) {
104+
return isValid;
105+
}
106+
107+
if (lock) {
108+
return lock
109+
.then(() => {
110+
return this.checkDependenciesLocked(resolver, fileName);
111+
});
112+
}
113+
114+
let resolveLock;
115+
lock = new Promise((res, rej) => { resolveLock = res; });
116+
117+
try {
118+
let checked = await this.checkDependencies(resolver, fileName);
119+
return checked;
120+
} finally {
121+
lock = null;
122+
resolveLock();
123+
}
124+
}
125+
99126
async checkDependencies(resolver: IResolver, fileName: string): Promise<boolean> {
100-
if (this.validFiles.isFileValid(fileName)) {
101-
return false;
127+
let isValid = this.validFiles.isFileValid(fileName);
128+
if (isValid) {
129+
return isValid;
102130
}
103131

104132
this.validFiles.markFileValid(fileName);
@@ -110,7 +138,6 @@ export class FileAnalyzer {
110138
if (!this.state.hasFile(fileName)) {
111139
changed = await this.state.readFileAndUpdate(fileName);
112140
}
113-
114141
await this.checkDependenciesInternal(resolver, fileName);
115142
} catch (err) {
116143
this.validFiles.markFileInvalid(fileName);
@@ -350,8 +377,8 @@ export class DependencyManager {
350377
export class ValidFilesManager {
351378
files: {[fileName: string]: boolean} = {};
352379

353-
isFileValid(fileName: string): boolean {
354-
return !!this.files[fileName];
380+
isFileValid(fileName: string): Promise<boolean> | boolean {
381+
return this.files[fileName];
355382
}
356383

357384
markFileValid(fileName: string) {

src/host.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,26 @@ export class State {
306306
}
307307
}
308308

309+
fastEmit(fileName: string) {
310+
fileName = this.normalizePath(fileName);
311+
312+
let file = this.getFile(fileName);
313+
if (!file) {
314+
throw new Error(`Unknown file ${ fileName }`);
315+
}
316+
317+
let transpileResult = this.ts.transpileModule(file.text, {
318+
compilerOptions: this.options,
319+
reportDiagnostics: false,
320+
fileName
321+
});
322+
323+
return {
324+
text: transpileResult.outputText,
325+
sourceMap: transpileResult.sourceMapText
326+
};
327+
}
328+
309329
updateFile(fileName: string, text: string, checked: boolean = false): boolean {
310330
fileName = this.normalizePath(fileName);
311331
let prevFile = this.files[fileName];
@@ -349,7 +369,8 @@ export class State {
349369
async readFile(fileName: string): Promise<string> {
350370
fileName = this.normalizePath(fileName);
351371
let buf = await this.readFileImpl(fileName);
352-
return buf.toString('utf8');
372+
let string = buf.toString('utf8');
373+
return string;
353374
}
354375

355376
readFileSync(fileName: string): string {

src/index.ts

+27-16
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ async function compiler(webpack: IWebPack, text: string): Promise<void> {
8484
}
8585

8686
try {
87-
let wasChanged = await state.fileAnalyzer.checkDependencies(resolver, fileName);
87+
let wasChanged = await state.fileAnalyzer.checkDependenciesLocked(resolver, fileName);
8888
if (wasChanged || doUpdate) {
89-
state.updateProgram();
89+
instance.shouldUpdateProgram = true;
9090
}
9191

9292
let compiledModule;
@@ -109,26 +109,37 @@ async function compiler(webpack: IWebPack, text: string): Promise<void> {
109109
function transform() {
110110
let resultText;
111111
let resultSourceMap = null;
112-
let output = state.emit(fileName);
113112

114-
let result = helpers.findResultFor(output, fileName);
113+
if (state.options.declaration) {
114+
// can't use fastEmit with declaration generation
115115

116-
if (result.text === undefined) {
117-
throw new Error('No output found for ' + fileName);
118-
}
116+
let output = state.emit(fileName);
117+
let result = helpers.findResultFor(output, fileName);
119118

120-
if (result.declaration) {
121-
webpack.emitFile(
122-
path.relative(process.cwd(), result.declaration.sourceName),
123-
result.declaration.text
124-
);
125-
}
119+
if (result.text === undefined) {
120+
throw new Error('No output found for ' + fileName);
121+
}
122+
123+
if (result.declaration) {
124+
webpack.emitFile(
125+
path.relative(process.cwd(), result.declaration.sourceName),
126+
result.declaration.text
127+
);
128+
}
126129

127-
resultText = result.text;
130+
resultText = result.text;
131+
resultSourceMap = result.sourceMap;
132+
} else {
133+
// Use super-fast emit
134+
135+
let result = state.fastEmit(fileName);
136+
resultText = result.text;
137+
resultSourceMap = result.sourceMap;
138+
}
128139

129140
let sourceFileName = fileName.replace(process.cwd() + '/', '');
130-
if (result.sourceMap) {
131-
resultSourceMap = JSON.parse(result.sourceMap);
141+
if (resultSourceMap) {
142+
resultSourceMap = JSON.parse(resultSourceMap);
132143
resultSourceMap.sources = [ sourceFileName ];
133144
resultSourceMap.file = sourceFileName;
134145
resultSourceMap.sourcesContent = [ text ];

src/instance.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export interface ICompilerInstance {
3535
plugins: LoaderPluginDef[];
3636
initedPlugins: LoaderPlugin[];
3737
externalsInvocation: Promise<any>;
38+
shouldUpdateProgram: boolean;
3839
}
3940

4041
interface ICompiler {
@@ -109,6 +110,7 @@ export function ensureInstance(webpack: IWebPack, options: ICompilerOptions, ins
109110
ensureInstanceStore(webpack._compiler);
110111

111112
let exInstance = resolveInstance(webpack._compiler, instanceName);
113+
112114
if (exInstance) {
113115
return exInstance;
114116
}
@@ -269,7 +271,8 @@ export function ensureInstance(webpack: IWebPack, options: ICompilerOptions, ins
269271
cacheIdentifier,
270272
plugins,
271273
initedPlugins,
272-
externalsInvocation: null
274+
externalsInvocation: null,
275+
shouldUpdateProgram: true
273276
};
274277
}
275278

@@ -298,13 +301,15 @@ function setupWatchRun(compiler, instanceName: string) {
298301
if (EXTENSIONS.test(changedFile)) {
299302
if (state.hasFile(changedFile)) {
300303
await state.readFileAndUpdate(changedFile);
301-
await state.fileAnalyzer.checkDependencies(resolver, changedFile);
304+
await state.fileAnalyzer.checkDependenciesLocked(resolver, changedFile);
302305
}
303306
}
304307
});
305308

306309
await Promise.all(tasks);
307-
state.updateProgram();
310+
if (!state.options.forkChecker) {
311+
state.updateProgram();
312+
}
308313
callback();
309314
} catch (err) {
310315
console.error(err.toString());
@@ -341,10 +346,11 @@ function setupAfterCompile(compiler, instanceName, forkChecker = false) {
341346

342347
runChecker(instance, payload);
343348
} else {
344-
if (!state.program) {
349+
if (!state.program || instance.shouldUpdateProgram) {
345350
// program may be undefined here, if all files
346351
// will be loaded by tsconfig
347352
state.updateProgram();
353+
instance.shouldUpdateProgram = false;
348354
}
349355

350356
let diagnostics = state.ts.getPreEmitDiagnostics(state.program);

0 commit comments

Comments
 (0)