Skip to content

Commit 9f02eef

Browse files
filipesilvahansl
authored andcommitted
fix(@ngtools/webpack): fix module detection with reexports
Fix #7925
1 parent 1abd7b0 commit 9f02eef

File tree

4 files changed

+69
-30
lines changed

4 files changed

+69
-30
lines changed

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

+20-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { transformTypescript } from './ast_helpers';
55
import { exportNgFactory } from './export_ngfactory';
66

77
describe('@ngtools/webpack transformers', () => {
8-
describe('replace_resources', () => {
9-
it('should replace resources', () => {
8+
describe('export_ngfactory', () => {
9+
it('should export the ngfactory', () => {
1010
const input = stripIndent`
1111
export { AppModule } from './app/app.module';
1212
`;
@@ -15,8 +15,24 @@ describe('@ngtools/webpack transformers', () => {
1515
export { AppModule } from './app/app.module';
1616
`;
1717

18-
const transformOpsCb = (sourceFile: ts.SourceFile) =>
19-
exportNgFactory(sourceFile, { path: '/app.module', className: 'AppModule' });
18+
const transformOpsCb = (sourceFile: ts.SourceFile) => exportNgFactory(sourceFile,
19+
{ path: '/project/src/app/app.module', className: 'AppModule' });
20+
const result = transformTypescript(input, transformOpsCb);
21+
22+
expect(oneLine`${result}`).toEqual(oneLine`${output}`);
23+
});
24+
25+
it('should export the ngfactory when there is a barrel file', () => {
26+
const input = stripIndent`
27+
export { AppModule } from './app';
28+
`;
29+
const output = stripIndent`
30+
export { AppModuleNgFactory } from "./app/app.module.ngfactory";
31+
export { AppModule } from './app';
32+
`;
33+
34+
const transformOpsCb = (sourceFile: ts.SourceFile) => exportNgFactory(sourceFile,
35+
{ path: '/project/src/app/app.module', className: 'AppModule' });
2036
const result = transformTypescript(input, transformOpsCb);
2137

2238
expect(oneLine`${result}`).toEqual(oneLine`${output}`);

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ignoreDep typescript
22
import * as ts from 'typescript';
3+
import { relative, dirname } from 'path';
34

45
import { findAstNodes, getFirstNode } from './ast_helpers';
56
import { TransformOperation, AddNodeOperation } from './make_transform';
@@ -19,6 +20,9 @@ export function exportNgFactory(
1920
return [];
2021
}
2122

23+
const relativeEntryModulePath = relative(dirname(sourceFile.fileName), entryModule.path);
24+
const normalizedEntryModulePath = `./${relativeEntryModulePath}`.replace(/\\/g, '/');
25+
2226
// Get the module path from the import.
2327
let modulePath: string;
2428
entryModuleIdentifiers.forEach((entryModuleIdentifier) => {
@@ -37,7 +41,7 @@ export function exportNgFactory(
3741

3842
// Add the transform operations.
3943
const factoryClassName = entryModule.className + 'NgFactory';
40-
const factoryModulePath = modulePath + '.ngfactory';
44+
const factoryModulePath = normalizedEntryModulePath + '.ngfactory';
4145

4246
const namedExports = ts.createNamedExports([ts.createExportSpecifier(undefined,
4347
ts.createIdentifier(factoryClassName))]);

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

+39-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,45 @@ describe('@ngtools/webpack transformers', () => {
3636
`;
3737
// tslint:enable:max-line-length
3838

39-
const transformOpsCb = (sourceFile: ts.SourceFile) =>
40-
replaceBootstrap(sourceFile, { path: '/app.module', className: 'AppModule' });
39+
const transformOpsCb = (sourceFile: ts.SourceFile) => replaceBootstrap(sourceFile,
40+
{ path: '/project/src/app/app.module', className: 'AppModule' });
41+
const result = transformTypescript(input, transformOpsCb);
42+
43+
expect(oneLine`${result}`).toEqual(oneLine`${output}`);
44+
});
45+
46+
it('should replace bootstrap when barrel files are used', () => {
47+
const input = stripIndent`
48+
import { enableProdMode } from '@angular/core';
49+
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
50+
51+
import { AppModule } from './app';
52+
import { environment } from './environments/environment';
53+
54+
if (environment.production) {
55+
enableProdMode();
56+
}
57+
58+
platformBrowserDynamic().bootstrapModule(AppModule);
59+
`;
60+
61+
// tslint:disable:max-line-length
62+
const output = stripIndent`
63+
import { enableProdMode } from '@angular/core';
64+
import { environment } from './environments/environment';
65+
66+
import * as __NgCli_bootstrap_1 from "./app/app.module.ngfactory";
67+
import * as __NgCli_bootstrap_2 from "@angular/platform-browser";
68+
69+
if (environment.production) {
70+
enableProdMode();
71+
}
72+
__NgCli_bootstrap_2.platformBrowser().bootstrapModuleFactory(__NgCli_bootstrap_1.AppModuleNgFactory);
73+
`;
74+
// tslint:enable:max-line-length
75+
76+
const transformOpsCb = (sourceFile: ts.SourceFile) => replaceBootstrap(sourceFile,
77+
{ path: '/project/src/app/app.module', className: 'AppModule' });
4178
const result = transformTypescript(input, transformOpsCb);
4279

4380
expect(oneLine`${result}`).toEqual(oneLine`${output}`);

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

+5-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ignoreDep typescript
22
import * as ts from 'typescript';
3+
import { relative, dirname } from 'path';
34

45
import { findAstNodes } from './ast_helpers';
56
import { insertStarImport } from './insert_import';
@@ -16,7 +17,7 @@ export function replaceBootstrap(
1617
): TransformOperation[] {
1718
const ops: TransformOperation[] = [];
1819

19-
// Find all identifiers using the entry module class name.
20+
// Find all identifiers.
2021
const entryModuleIdentifiers = findAstNodes<ts.Identifier>(null, sourceFile,
2122
ts.SyntaxKind.Identifier, true)
2223
.filter(identifier => identifier.getText() === entryModule.className);
@@ -25,27 +26,8 @@ export function replaceBootstrap(
2526
return [];
2627
}
2728

28-
// Get the module path from the import.
29-
let modulePath: string;
30-
entryModuleIdentifiers.forEach((entryModuleIdentifier) => {
31-
// TODO: only supports `import {A, B, C} from 'modulePath'` atm, add other import support later.
32-
if (entryModuleIdentifier.parent.kind !== ts.SyntaxKind.ImportSpecifier) {
33-
return;
34-
}
35-
36-
const importSpec = entryModuleIdentifier.parent as ts.ImportSpecifier;
37-
const moduleSpecifier = importSpec.parent.parent.parent.moduleSpecifier;
38-
39-
if (moduleSpecifier.kind !== ts.SyntaxKind.StringLiteral) {
40-
return;
41-
}
42-
43-
modulePath = (moduleSpecifier as ts.StringLiteral).text;
44-
});
45-
46-
if (!modulePath) {
47-
return [];
48-
}
29+
const relativeEntryModulePath = relative(dirname(sourceFile.fileName), entryModule.path);
30+
const normalizedEntryModulePath = `./${relativeEntryModulePath}`.replace(/\\/g, '/');
4931

5032
// Find the bootstrap calls.
5133
const removedEntryModuleIdentifiers: ts.Identifier[] = [];
@@ -89,7 +71,7 @@ export function replaceBootstrap(
8971

9072
// Add the transform operations.
9173
const factoryClassName = entryModule.className + 'NgFactory';
92-
const factoryModulePath = modulePath + '.ngfactory';
74+
const factoryModulePath = normalizedEntryModulePath + '.ngfactory';
9375
ops.push(
9476
// Replace the entry module import.
9577
...insertStarImport(sourceFile, idNgFactory, factoryModulePath),

0 commit comments

Comments
 (0)