Skip to content

Commit 5855e4a

Browse files
committed
fix(@ngtools/webpack): analyze the typescript file for additional dependencies
By default, Webpack only add dependencies it can see. Types or imports that are not kept in transpilations are removed, and webpack dont see them. By reading the AST directly we manually add the dependencies to webpack. Fixes #7995.
1 parent fe8e14e commit 5855e4a

File tree

3 files changed

+72
-7
lines changed

3 files changed

+72
-7
lines changed

packages/@ngtools/webpack/src/loader.ts

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,28 @@ function _getResourcesUrls(refactor: TypeScriptFileRefactor): string[] {
431431
}
432432

433433

434+
function _getImports(refactor: TypeScriptFileRefactor,
435+
compilerOptions: ts.CompilerOptions,
436+
host: ts.ModuleResolutionHost,
437+
cache: ts.ModuleResolutionCache): string[] {
438+
const containingFile = refactor.fileName;
439+
440+
return refactor.findAstNodes(null, ts.SyntaxKind.ImportDeclaration, false)
441+
.map((clause: ts.ImportDeclaration) => {
442+
const moduleName = (clause.moduleSpecifier as ts.StringLiteral).text;
443+
const resolved = ts.resolveModuleName(
444+
moduleName, containingFile, compilerOptions, host, cache);
445+
446+
if (resolved.resolvedModule) {
447+
return resolved.resolvedModule.resolvedFileName;
448+
} else {
449+
return null;
450+
}
451+
})
452+
.filter(x => x);
453+
}
454+
455+
434456
/**
435457
* Recursively calls diagnose on the plugins for all the reverse dependencies.
436458
* @private
@@ -577,6 +599,12 @@ export function ngcLoader(this: LoaderContext & { _compilation: any }, source: s
577599
const refactor = new TypeScriptFileRefactor(
578600
sourceFileName, plugin.compilerHost, plugin.program, source);
579601

602+
// Force a few compiler options to make sure we get the result we want.
603+
const compilerOptions: ts.CompilerOptions = Object.assign({}, plugin.compilerOptions, {
604+
inlineSources: true,
605+
inlineSourceMap: false,
606+
sourceRoot: plugin.basePath
607+
});
580608

581609
Promise.resolve()
582610
.then(() => {
@@ -615,6 +643,8 @@ export function ngcLoader(this: LoaderContext & { _compilation: any }, source: s
615643
_getResourcesUrls(refactor).forEach((url: string) => {
616644
this.addDependency(path.resolve(path.dirname(sourceFileName), url));
617645
});
646+
_getImports(refactor, compilerOptions, plugin.compilerHost, plugin.moduleResolutionCache)
647+
.forEach((importString: string) => this.addDependency(importString));
618648
timeEnd(timeLabel + '.ngcLoader.AotPlugin.addDependency');
619649
})
620650
.then(() => {
@@ -642,13 +672,6 @@ export function ngcLoader(this: LoaderContext & { _compilation: any }, source: s
642672
timeEnd(timeLabel + '.ngcLoader.AotPlugin.getDiagnostics');
643673
}
644674

645-
// Force a few compiler options to make sure we get the result we want.
646-
const compilerOptions: ts.CompilerOptions = Object.assign({}, plugin.compilerOptions, {
647-
inlineSources: true,
648-
inlineSourceMap: false,
649-
sourceRoot: plugin.basePath
650-
});
651-
652675
time(timeLabel + '.ngcLoader.AotPlugin.transpile');
653676
const result = refactor.transpile(compilerOptions);
654677
timeEnd(timeLabel + '.ngcLoader.AotPlugin.transpile');

packages/@ngtools/webpack/src/plugin.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class AotPlugin implements Tapable {
5151
private _compilerOptions: ts.CompilerOptions;
5252
private _angularCompilerOptions: any;
5353
private _program: ts.Program;
54+
private _moduleResolutionCache: ts.ModuleResolutionCache;
5455
private _rootFilePath: string[];
5556
private _compilerHost: WebpackCompilerHost;
5657
private _resourceLoader: WebpackResourceLoader;
@@ -97,6 +98,7 @@ export class AotPlugin implements Tapable {
9798
}
9899
get genDir() { return this._genDir; }
99100
get program() { return this._program; }
101+
get moduleResolutionCache() { return this._moduleResolutionCache; }
100102
get skipCodeGeneration() { return this._skipCodeGeneration; }
101103
get replaceExport() { return this._replaceExport; }
102104
get typeCheck() { return this._typeCheck; }
@@ -250,6 +252,12 @@ export class AotPlugin implements Tapable {
250252
this._program = ts.createProgram(
251253
this._rootFilePath, this._compilerOptions, this._compilerHost);
252254

255+
// We use absolute paths everywhere.
256+
this._moduleResolutionCache = ts.createModuleResolutionCache(
257+
this._basePath,
258+
(fileName: string) => this._compilerHost.resolve(fileName),
259+
);
260+
253261
// We enable caching of the filesystem in compilerHost _after_ the program has been created,
254262
// because we don't want SourceFile instances to be cached past this point.
255263
this._compilerHost.enableCaching();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
killAllProcesses,
3+
waitForAnyProcessOutputToMatch,
4+
execAndWaitForOutputToMatch,
5+
} from '../../utils/process';
6+
import { writeFile, prependToFile } from '../../utils/fs';
7+
import {getGlobalVariable} from '../../utils/env';
8+
9+
10+
const successRe = /webpack: Compiled successfully/;
11+
12+
export default async function() {
13+
if (process.platform.startsWith('win')) {
14+
return;
15+
}
16+
// Skip this in ejected tests.
17+
if (getGlobalVariable('argv').eject) {
18+
return;
19+
}
20+
21+
await writeFile('src/app/type.ts', `export type MyType = number;`);
22+
await prependToFile('src/app/app.component.ts', 'import { MyType } from "./type";\n');
23+
24+
try {
25+
await execAndWaitForOutputToMatch('ng', ['serve'], successRe);
26+
27+
await Promise.all([
28+
waitForAnyProcessOutputToMatch(successRe, 20000),
29+
writeFile('src/app/type.ts', `export type MyType = string;`),
30+
]);
31+
} finally {
32+
killAllProcesses();
33+
}
34+
}

0 commit comments

Comments
 (0)