Skip to content

Commit 450383c

Browse files
committed
fix(@ngtools/webpack): fix bootstrapping code
There is no link between a new identifier and its import (and apparently TS does not export any API to make one in the type checker). The best way to import a symbol reliably is to use a namespace import (import * as x from "abc"). The new bootstrapping code uses just that.
1 parent 6984cc2 commit 450383c

File tree

3 files changed

+51
-9
lines changed

3 files changed

+51
-9
lines changed

packages/@ngtools/webpack/src/transformers/insert_import.ts

+37
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,43 @@ import { findAstNodes, getFirstNode } from './ast_helpers';
55
import { AddNodeOperation, TransformOperation } from './make_transform';
66

77

8+
export function insertStarImport(
9+
sourceFile: ts.SourceFile,
10+
identifier: ts.Identifier,
11+
modulePath: string,
12+
): TransformOperation[] {
13+
const ops: TransformOperation[] = [];
14+
const allImports = findAstNodes(null, sourceFile, ts.SyntaxKind.ImportDeclaration);
15+
16+
// We don't need to verify if the symbol is already imported, star imports should be unique.
17+
18+
// Create the new import node.
19+
const namespaceImport = ts.createNamespaceImport(identifier);
20+
const importClause = ts.createImportClause(undefined, namespaceImport);
21+
const newImport = ts.createImportDeclaration(undefined, undefined, importClause,
22+
ts.createLiteral(modulePath));
23+
24+
if (allImports.length > 0) {
25+
// Find the last import and insert after.
26+
ops.push(new AddNodeOperation(
27+
sourceFile,
28+
allImports[allImports.length - 1],
29+
undefined,
30+
newImport
31+
));
32+
} else {
33+
// Insert before the first node.
34+
ops.push(new AddNodeOperation(
35+
sourceFile,
36+
getFirstNode(sourceFile),
37+
newImport
38+
));
39+
}
40+
41+
return ops;
42+
}
43+
44+
845
export function insertImport(
946
sourceFile: ts.SourceFile,
1047
symbolName: string,

packages/@ngtools/webpack/src/transformers/replace_bootstrap.spec.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,21 @@ describe('@ngtools/webpack transformers', () => {
2020
2121
platformBrowserDynamic().bootstrapModule(AppModule);
2222
`;
23+
24+
// tslint:disable:max-line-length
2325
const output = stripIndent`
2426
import { enableProdMode } from '@angular/core';
2527
import { environment } from './environments/environment';
2628
27-
import { AppModuleNgFactory } from "./app/app.module.ngfactory";
28-
import { platformBrowser } from "@angular/platform-browser";
29+
import * as __NgCli_bootstrap_1 from "./app/app.module.ngfactory";
30+
import * as __NgCli_bootstrap_2 from "@angular/platform-browser";
2931
3032
if (environment.production) {
3133
enableProdMode();
3234
}
33-
34-
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
35+
__NgCli_bootstrap_2.platformBrowser().bootstrapModuleFactory(__NgCli_bootstrap_1.AppModuleNgFactory);
3536
`;
37+
// tslint:enable:max-line-length
3638

3739
const transformOpsCb = (sourceFile: ts.SourceFile) =>
3840
replaceBootstrap(sourceFile, { path: '/app.module', className: 'AppModule' });

packages/@ngtools/webpack/src/transformers/replace_bootstrap.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import * as ts from 'typescript';
33

44
import { findAstNodes } from './ast_helpers';
5-
import { insertImport } from './insert_import';
5+
import { insertStarImport } from './insert_import';
66
import { removeImport } from './remove_import';
77
import {
88
ReplaceNodeOperation,
@@ -84,18 +84,21 @@ export function replaceBootstrap(
8484

8585
const platformBrowserDynamicIdentifier = innerCallExpr.expression as ts.Identifier;
8686

87+
const idPlatformBrowser = ts.createUniqueName('__NgCli_bootstrap_');
88+
const idNgFactory = ts.createUniqueName('__NgCli_bootstrap_');
89+
8790
// Add the transform operations.
8891
const factoryClassName = entryModule.className + 'NgFactory';
8992
const factoryModulePath = modulePath + '.ngfactory';
9093
ops.push(
9194
// Replace the entry module import.
92-
...insertImport(sourceFile, factoryClassName, factoryModulePath),
95+
...insertStarImport(sourceFile, idNgFactory, factoryModulePath),
9396
new ReplaceNodeOperation(sourceFile, entryModuleIdentifier,
94-
ts.createIdentifier(factoryClassName)),
97+
ts.createPropertyAccess(idNgFactory, ts.createIdentifier(factoryClassName))),
9598
// Replace the platformBrowserDynamic import.
96-
...insertImport(sourceFile, 'platformBrowser', '@angular/platform-browser'),
99+
...insertStarImport(sourceFile, idPlatformBrowser, '@angular/platform-browser'),
97100
new ReplaceNodeOperation(sourceFile, platformBrowserDynamicIdentifier,
98-
ts.createIdentifier('platformBrowser')),
101+
ts.createPropertyAccess(idPlatformBrowser, 'platformBrowser')),
99102
new ReplaceNodeOperation(sourceFile, bootstrapModuleIdentifier,
100103
ts.createIdentifier('bootstrapModuleFactory')),
101104
);

0 commit comments

Comments
 (0)