Skip to content

Commit 94b6f99

Browse files
committed
refactor(@ngtools/webpack): use ts transforms in refactor
1 parent 35c10df commit 94b6f99

31 files changed

+1257
-138
lines changed

packages/@angular/cli/models/webpack-configs/typescript.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ function _createAotPlugin(wco: WebpackConfigOptions, options: any) {
7070
locale: buildOptions.locale,
7171
replaceExport: appConfig.platform === 'server',
7272
hostReplacementPaths,
73+
sourceMap: buildOptions.sourcemaps,
7374
// If we don't explicitely list excludes, it will default to `['**/*.spec.ts']`.
7475
exclude: []
7576
}, options));

packages/@ngtools/webpack/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ The loader works with the webpack plugin to compile your TypeScript. It's import
3838
* `skipCodeGeneration`. Optional, defaults to false. Disable code generation and do not refactor the code to bootstrap. This replaces `templateUrl: "string"` with `template: require("string")` (and similar for styles) to allow for webpack to properly link the resources.
3939
* `typeChecking`. Optional, defaults to true. Enable type checking through your application. This will slow down compilation, but show syntactic and semantic errors in webpack.
4040
* `exclude`. Optional. Extra files to exclude from TypeScript compilation.
41+
* `sourceMap`. Optional. Include sourcemaps.
4142
* `compilerOptions`. Optional. Override options in `tsconfig.json`.
4243

4344
## Features

packages/@ngtools/webpack/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"dependencies": {
2828
"loader-utils": "^1.0.2",
2929
"magic-string": "^0.22.3",
30-
"source-map": "^0.5.6"
30+
"source-map": "^0.5.6",
31+
"semver": "^5.3.0"
3132
},
3233
"peerDependencies": {
3334
"enhanced-resolve": "^3.1.0",

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,14 @@ export class WebpackCompilerHost implements ts.CompilerHost {
214214
this._changedFiles = Object.create(null);
215215
this._changedDirs = Object.create(null);
216216
}
217-
getChangedFilePaths(): string[] {
218-
return Object.keys(this._changedFiles);
217+
218+
// TypeScriptFileRefactor will also write .js and .js.map to the compiler host, but we usually
219+
// only care about changed ts files.
220+
getChangedFilePaths(onlyTs = true): string[] {
221+
const files = Object.keys(this._changedFiles);
222+
return onlyTs
223+
? files.filter((f) => f.endsWith('.ts'))
224+
: files;
219225
}
220226

221227
invalidate(fileName: string): void {

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

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import * as fs from 'fs';
22
import {join} from 'path';
33
import * as ts from 'typescript';
44

5-
import {TypeScriptFileRefactor} from './refactor';
5+
import {TypeScriptFileRefactor, getTypeScriptFileRefactor} from './refactor/refactor';
6+
import {ProgramManager} from './program_manager';
67

78

89
function _recursiveSymbolExportLookup(refactor: TypeScriptFileRefactor,
910
symbolName: string,
1011
host: ts.CompilerHost,
11-
program: ts.Program): string | null {
12+
programManager: ProgramManager): string | null {
1213
// Check this file.
1314
const hasSymbol = refactor.findAstNodes(null, ts.SyntaxKind.ClassDeclaration)
1415
.some((cd: ts.ClassDeclaration) => {
@@ -29,15 +30,16 @@ function _recursiveSymbolExportLookup(refactor: TypeScriptFileRefactor,
2930

3031
const modulePath = (decl.moduleSpecifier as ts.StringLiteral).text;
3132
const resolvedModule = ts.resolveModuleName(
32-
modulePath, refactor.fileName, program.getCompilerOptions(), host);
33+
modulePath, refactor.fileName, programManager.program.getCompilerOptions(), host);
3334
if (!resolvedModule.resolvedModule || !resolvedModule.resolvedModule.resolvedFileName) {
3435
return null;
3536
}
3637

3738
const module = resolvedModule.resolvedModule.resolvedFileName;
3839
if (!decl.exportClause) {
39-
const moduleRefactor = new TypeScriptFileRefactor(module, host, program);
40-
const maybeModule = _recursiveSymbolExportLookup(moduleRefactor, symbolName, host, program);
40+
const moduleRefactor = getTypeScriptFileRefactor(module, host, programManager);
41+
const maybeModule = _recursiveSymbolExportLookup(
42+
moduleRefactor, symbolName, host, programManager);
4143
if (maybeModule) {
4244
return maybeModule;
4345
}
@@ -51,17 +53,17 @@ function _recursiveSymbolExportLookup(refactor: TypeScriptFileRefactor,
5153
if (fs.statSync(module).isDirectory()) {
5254
const indexModule = join(module, 'index.ts');
5355
if (fs.existsSync(indexModule)) {
54-
const indexRefactor = new TypeScriptFileRefactor(indexModule, host, program);
56+
const indexRefactor = getTypeScriptFileRefactor(indexModule, host, programManager);
5557
const maybeModule = _recursiveSymbolExportLookup(
56-
indexRefactor, symbolName, host, program);
58+
indexRefactor, symbolName, host, programManager);
5759
if (maybeModule) {
5860
return maybeModule;
5961
}
6062
}
6163
}
6264

6365
// Create the source and verify that the symbol is at least a class.
64-
const source = new TypeScriptFileRefactor(module, host, program);
66+
const source = getTypeScriptFileRefactor(module, host, programManager);
6567
const hasSymbol = source.findAstNodes(null, ts.SyntaxKind.ClassDeclaration)
6668
.some((cd: ts.ClassDeclaration) => {
6769
return cd.name && cd.name.text == symbolName;
@@ -80,7 +82,7 @@ function _recursiveSymbolExportLookup(refactor: TypeScriptFileRefactor,
8082
function _symbolImportLookup(refactor: TypeScriptFileRefactor,
8183
symbolName: string,
8284
host: ts.CompilerHost,
83-
program: ts.Program): string | null {
85+
programManager: ProgramManager): string | null {
8486
// We found the bootstrap variable, now we just need to get where it's imported.
8587
const imports = refactor.findAstNodes(null, ts.SyntaxKind.ImportDeclaration)
8688
.map(node => node as ts.ImportDeclaration);
@@ -95,7 +97,7 @@ function _symbolImportLookup(refactor: TypeScriptFileRefactor,
9597

9698
const resolvedModule = ts.resolveModuleName(
9799
(decl.moduleSpecifier as ts.StringLiteral).text,
98-
refactor.fileName, program.getCompilerOptions(), host);
100+
refactor.fileName, programManager.program.getCompilerOptions(), host);
99101
if (!resolvedModule.resolvedModule || !resolvedModule.resolvedModule.resolvedFileName) {
100102
continue;
101103
}
@@ -112,8 +114,9 @@ function _symbolImportLookup(refactor: TypeScriptFileRefactor,
112114
for (const specifier of binding.elements) {
113115
if (specifier.name.text == symbolName) {
114116
// Create the source and recursively lookup the import.
115-
const source = new TypeScriptFileRefactor(module, host, program);
116-
const maybeModule = _recursiveSymbolExportLookup(source, symbolName, host, program);
117+
const source = getTypeScriptFileRefactor(module, host, programManager);
118+
const maybeModule = _recursiveSymbolExportLookup(
119+
source, symbolName, host, programManager);
117120
if (maybeModule) {
118121
return maybeModule;
119122
}
@@ -127,8 +130,8 @@ function _symbolImportLookup(refactor: TypeScriptFileRefactor,
127130

128131
export function resolveEntryModuleFromMain(mainPath: string,
129132
host: ts.CompilerHost,
130-
program: ts.Program) {
131-
const source = new TypeScriptFileRefactor(mainPath, host, program);
133+
programManager: ProgramManager) {
134+
const source = getTypeScriptFileRefactor(mainPath, host, programManager);
132135

133136
const bootstrap = source.findAstNodes(source.sourceFile, ts.SyntaxKind.CallExpression, true)
134137
.map(node => node as ts.CallExpression)
@@ -148,7 +151,7 @@ export function resolveEntryModuleFromMain(mainPath: string,
148151
+ 'to the plugins options.');
149152
}
150153
const bootstrapSymbolName = bootstrap[0].text;
151-
const module = _symbolImportLookup(source, bootstrapSymbolName, host, program);
154+
const module = _symbolImportLookup(source, bootstrapSymbolName, host, programManager);
152155
if (module) {
153156
return `${module.replace(/\.ts$/, '')}#${bootstrapSymbolName}`;
154157
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {dirname, join} from 'path';
22
import * as ts from 'typescript';
33

4-
import {TypeScriptFileRefactor} from './refactor';
4+
import {getTypeScriptFileRefactor} from './refactor/refactor';
5+
import {ProgramManager} from './program_manager';
56

67

78
function _getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): string {
@@ -21,9 +22,9 @@ export interface LazyRouteMap {
2122

2223

2324
export function findLazyRoutes(filePath: string,
24-
program: ts.Program,
25+
programManager: ProgramManager,
2526
host: ts.CompilerHost): LazyRouteMap {
26-
const refactor = new TypeScriptFileRefactor(filePath, host, program);
27+
const refactor = getTypeScriptFileRefactor(filePath, host, programManager);
2728

2829
return refactor
2930
// Find all object literals in the file.
@@ -50,7 +51,8 @@ export function findLazyRoutes(filePath: string,
5051
? ({
5152
resolvedModule: { resolvedFileName: join(dirname(filePath), moduleName) + '.ts' }
5253
} as any)
53-
: ts.resolveModuleName(moduleName, filePath, program.getCompilerOptions(), host);
54+
: ts.resolveModuleName(
55+
moduleName, filePath, programManager.program.getCompilerOptions(), host);
5456
if (resolvedModuleName.resolvedModule
5557
&& resolvedModuleName.resolvedModule.resolvedFileName
5658
&& host.fileExists(resolvedModuleName.resolvedModule.resolvedFileName)) {

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

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import * as ts from 'typescript';
21
import {removeModuleIdOnlyForTesting} from './loader';
32
import {WebpackCompilerHost} from './compiler_host';
4-
import {TypeScriptFileRefactor} from './refactor';
3+
import {getTypeScriptFileRefactor} from './refactor/refactor';
4+
import {ProgramManager} from './program_manager';
55

66
describe('@ngtools/webpack', () => {
77
describe('loader', () => {
@@ -20,25 +20,28 @@ describe('@ngtools/webpack', () => {
2020
@SomeDecorator({ otherValue4: 4, moduleId: 123 }) class CLS4 {}
2121
`, false);
2222

23-
const program = ts.createProgram(['/file.ts', '/file2.ts'], {}, host);
23+
const programManager = new ProgramManager(['/file.ts', '/file2.ts'], {}, host);
2424

25-
const refactor = new TypeScriptFileRefactor('/file.ts', host, program);
25+
const refactor = getTypeScriptFileRefactor('/file.ts', host, programManager);
2626
removeModuleIdOnlyForTesting(refactor);
27-
expect(refactor.sourceText).not.toMatch(/obj = \{\s+};/);
28-
expect(refactor.sourceText).not.toMatch(/\{\s*otherValue: 1\s*};/);
2927

30-
const refactor2 = new TypeScriptFileRefactor('/file2.ts', host, program);
28+
const outputText = refactor.transpile().outputText;
29+
expect(outputText).not.toMatch(/obj = \{\s+};/);
30+
expect(outputText).not.toMatch(/\{\s*otherValue: 1\s*};/);
31+
32+
const refactor2 = getTypeScriptFileRefactor('/file2.ts', host, programManager);
3133
removeModuleIdOnlyForTesting(refactor2);
32-
expect(refactor2.sourceText).toMatch(/\(\{\s+}\)/);
33-
expect(refactor2.sourceText).toMatch(/\(\{\s*otherValue1: 1\s*}\)/);
34-
expect(refactor2.sourceText).toMatch(/\(\{\s*otherValue2: 2\s*,\s*otherValue3: 3\s*}\)/);
35-
expect(refactor2.sourceText).toMatch(/\(\{\s*otherValue4: 4\s*}\)/);
34+
const outputText2 = refactor2.transpile().outputText;
35+
expect(outputText2).toMatch(/\(\{\s*}\)/);
36+
expect(outputText2).toMatch(/\(\{\s*otherValue1: 1\s*}\)/);
37+
expect(outputText2).toMatch(/\(\{\s*otherValue2: 2\s*,\s*otherValue3: 3\s*}\)/);
38+
expect(outputText2).toMatch(/\(\{\s*otherValue4: 4\s*}\)/);
3639
});
3740

3841
it('should work without a root name', () => {
3942
const host = new WebpackCompilerHost({}, '');
4043
host.writeFile('/file.ts', `
41-
import './file2.ts';
44+
import './file2';
4245
`, false);
4346
host.writeFile('/file2.ts', `
4447
@SomeDecorator({ moduleId: 123 }) class CLS {}
@@ -47,13 +50,14 @@ describe('@ngtools/webpack', () => {
4750
@SomeDecorator({ otherValue4: 4, moduleId: 123 }) class CLS4 {}
4851
`, false);
4952

50-
const program = ts.createProgram(['/file.ts'], {}, host);
51-
const refactor = new TypeScriptFileRefactor('/file2.ts', host, program);
53+
const programManager = new ProgramManager(['/file.ts'], {}, host);
54+
const refactor = getTypeScriptFileRefactor('/file2.ts', host, programManager);
5255
removeModuleIdOnlyForTesting(refactor);
53-
expect(refactor.sourceText).toMatch(/\(\{\s+}\)/);
54-
expect(refactor.sourceText).toMatch(/\(\{\s*otherValue1: 1\s*}\)/);
55-
expect(refactor.sourceText).toMatch(/\(\{\s*otherValue2: 2\s*,\s*otherValue3: 3\s*}\)/);
56-
expect(refactor.sourceText).toMatch(/\(\{\s*otherValue4: 4\s*}\)/);
56+
const outputText = refactor.transpile().outputText;
57+
expect(outputText).toMatch(/\(\{\s*}\)/);
58+
expect(outputText).toMatch(/\(\{\s*otherValue1: 1\s*}\)/);
59+
expect(outputText).toMatch(/\(\{\s*otherValue2: 2\s*,\s*otherValue3: 3\s*}\)/);
60+
expect(outputText).toMatch(/\(\{\s*otherValue4: 4\s*}\)/);
5761
});
5862
});
5963
});

0 commit comments

Comments
 (0)