Skip to content

Commit fb0a6af

Browse files
alan-agius4Keen Yee Liau
authored and
Keen Yee Liau
committed
fix(@angular-devkit/build-angular): make app-shell work with Ivy
Fixes #15383
1 parent 9602003 commit fb0a6af

File tree

11 files changed

+86
-69
lines changed

11 files changed

+86
-69
lines changed

packages/angular_devkit/build_angular/src/app-shell/index.ts

+21-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { JsonObject, experimental, join, normalize, resolve, schema } from '@ang
1515
import { NodeJsSyncHost } from '@angular-devkit/core/node';
1616
import * as fs from 'fs';
1717
import * as path from 'path';
18-
import { readTsconfig } from '../angular-cli-files/utilities/read-tsconfig';
1918
import { augmentAppWithServiceWorker } from '../angular-cli-files/utilities/service-worker';
2019
import { BrowserBuilderOutput } from '../browser';
2120
import { Schema as BrowserBuilderSchema } from '../browser/schema';
@@ -42,30 +41,37 @@ async function _renderUniversal(
4241
browserBuilderName,
4342
);
4443

45-
// Determine if browser app was compiled using Ivy.
46-
const { options: compilerOptions } = readTsconfig(browserOptions.tsConfig, context.workspaceRoot);
47-
const ivy = compilerOptions.enableIvy;
48-
4944
// Initialize zone.js
5045
const zonePackage = require.resolve('zone.js', { paths: [root] });
5146
await import(zonePackage);
5247

48+
const {
49+
AppServerModule,
50+
AppServerModuleNgFactory,
51+
renderModule,
52+
renderModuleFactory,
53+
} = await import(serverBundlePath);
54+
55+
let renderModuleFn: (module: unknown, options: {}) => Promise<string>;
56+
let AppServerModuleDef: unknown;
57+
58+
if (renderModuleFactory && AppServerModuleNgFactory) {
59+
renderModuleFn = renderModuleFactory;
60+
AppServerModuleDef = AppServerModuleNgFactory;
61+
} else if (renderModule && AppServerModule) {
62+
renderModuleFn = renderModule;
63+
AppServerModuleDef = AppServerModule;
64+
} else {
65+
throw new Error(`renderModule method and/or AppServerModule were not exported from: ${serverBundlePath}.`);
66+
}
67+
5368
// Load platform server module renderer
54-
const platformServerPackage = require.resolve('@angular/platform-server', { paths: [root] });
5569
const renderOpts = {
5670
document: indexHtml,
5771
url: options.route,
5872
};
5973

60-
// Render app to HTML using Ivy or VE
61-
const html = await import(platformServerPackage)
62-
// tslint:disable-next-line:no-implicit-dependencies
63-
.then((m: typeof import('@angular/platform-server')) =>
64-
ivy
65-
? m.renderModule(require(serverBundlePath).AppServerModule, renderOpts)
66-
: m.renderModuleFactory(require(serverBundlePath).AppServerModuleNgFactory, renderOpts),
67-
);
68-
74+
const html = await renderModuleFn(AppServerModuleDef, renderOpts);
6975
// Overwrite the client index file.
7076
const outputIndexPath = options.outputIndexPath
7177
? path.join(root, options.outputIndexPath)

packages/angular_devkit/build_angular/test/app-shell/app-shell_spec_large.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99
import { Architect } from '@angular-devkit/architect/src/architect';
1010
import { getSystemPath, join, normalize, virtualFs } from '@angular-devkit/core';
1111
import * as express from 'express'; // tslint:disable-line:no-implicit-dependencies
12-
import { createArchitect, host, veEnabled } from '../utils';
12+
import { createArchitect, host } from '../utils';
1313

14-
15-
// DISABLED_FOR_IVY These should pass but are currently not supported
16-
// See https://github.com/angular/angular-cli/issues/15383 for details.
17-
(veEnabled ? describe : xdescribe)('AppShell Builder', () => {
14+
describe('AppShell Builder', () => {
1815
const target = { project: 'app', target: 'app-shell' };
1916
let architect: Architect;
2017

packages/angular_devkit/build_angular/test/server/base_spec_large.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Architect } from '@angular-devkit/architect';
1010
import { getSystemPath, join, normalize, virtualFs } from '@angular-devkit/core';
1111
import { take, tap } from 'rxjs/operators';
1212
import { BrowserBuilderOutput } from '../../src/browser';
13+
import { BundleDependencies } from '../../src/server/schema';
1314
import { createArchitect, host, veEnabled } from '../utils';
1415

1516

@@ -85,6 +86,7 @@ describe('Server Builder', () => {
8586
});
8687

8788
const run = await architect.scheduleTarget(target, {
89+
bundleDependencies: BundleDependencies.None,
8890
sourceMap: {
8991
styles: false,
9092
scripts: true,
@@ -103,9 +105,10 @@ describe('Server Builder', () => {
103105

104106
await run.stop();
105107
});
106-
//
108+
107109
it('supports component styles sourcemaps', async () => {
108110
const overrides = {
111+
bundleDependencies: BundleDependencies.None,
109112
sourceMap: {
110113
styles: true,
111114
scripts: true,

tests/angular_devkit/build_angular/hello-world-app-ve/src/main.server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ if (environment.production) {
1414
}
1515

1616
export { AppServerModule } from './app/app.server.module';
17+
export { renderModuleFactory } from '@angular/platform-server';

tests/angular_devkit/build_angular/hello-world-app/src/app/app.module.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@
88
import { BrowserModule } from '@angular/platform-browser';
99
import { NgModule } from '@angular/core';
1010

11+
1112
import { AppComponent } from './app.component';
1213

14+
1315
@NgModule({
14-
declarations: [AppComponent],
15-
imports: [BrowserModule],
16+
declarations: [
17+
AppComponent
18+
],
19+
imports: [
20+
BrowserModule
21+
],
1622
providers: [],
17-
bootstrap: [AppComponent],
23+
bootstrap: [AppComponent]
1824
})
19-
export class AppModule {}
25+
export class AppModule { }

tests/angular_devkit/build_angular/hello-world-app/src/app/app.server.module.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import { AppModule } from './app.module';
1212
import { AppComponent } from './app.component';
1313

1414
@NgModule({
15-
imports: [AppModule, ServerModule],
15+
imports: [
16+
AppModule,
17+
ServerModule,
18+
],
1619
bootstrap: [AppComponent],
1720
})
1821
export class AppServerModule {}

tests/angular_devkit/build_angular/hello-world-app/src/main.server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ if (environment.production) {
1414
}
1515

1616
export { AppServerModule } from './app/app.server.module';
17+
export { renderModule } from '@angular/platform-server';

tests/legacy-cli/e2e/tests/build/build-app-shell-with-schematic.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
import { getGlobalVariable } from '../../utils/env';
2-
import { appendToFile, expectFileToMatch } from '../../utils/fs';
2+
import { appendToFile, expectFileToMatch, replaceInFile } from '../../utils/fs';
33
import { ng, silentNpm } from '../../utils/process';
44
import { updateJsonFile } from '../../utils/project';
55
import { readNgVersion } from '../../utils/version';
66

77

88
export default async function () {
9-
// Skip this test in Angular 2/4.
10-
if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) {
11-
return;
12-
}
13-
149
await appendToFile('src/app/app.component.html', '<router-outlet></router-outlet>');
1510
await ng('generate', 'appShell', '--client-project', 'test-project');
1611
await updateJsonFile('package.json', packageJson => {
@@ -20,6 +15,10 @@ export default async function () {
2015
: readNgVersion();
2116
});
2217

18+
if (argv['ve']) {
19+
await replaceInFile('src/main.server.ts', /renderModule/g, 'renderModuleFactory');
20+
}
21+
2322
await silentNpm('install');
2423
await ng('run', 'test-project:app-shell');
2524
await expectFileToMatch('dist/test-project/index.html', /app-shell works!/);

tests/legacy-cli/e2e/tests/build/build-app-shell.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@ import { updateJsonFile } from '../../utils/project';
66
import { readNgVersion } from '../../utils/version';
77

88
export default function() {
9-
// Skip this test in Angular 2/4.
10-
if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) {
11-
return Promise.resolve();
12-
}
13-
149
let platformServerVersion = readNgVersion();
15-
let httpVersion = readNgVersion();
1610

1711
if (getGlobalVariable('argv')['ng-snapshots']) {
1812
platformServerVersion = 'github:angular/platform-server-builds';
@@ -78,6 +72,7 @@ export default function() {
7872
}
7973
8074
export { AppServerModule } from './app/app.server.module';
75+
export { renderModule${veProject ? 'Factory' : ''} } from '@angular/platform-server';
8176
`,
8277
),
8378
)

tests/legacy-cli/e2e/tests/build/platform-server.ts

+37-24
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import { normalize } from 'path';
22
import { getGlobalVariable } from '../../utils/env';
3-
import { expectFileToMatch, writeFile, appendToFile } from '../../utils/fs';
3+
import { expectFileToMatch, writeFile } from '../../utils/fs';
44
import { exec, ng, silentNpm } from '../../utils/process';
55
import { updateJsonFile } from '../../utils/project';
66
import { readNgVersion } from '../../utils/version';
77

88
export default async function () {
9-
// Skip this test in Angular 2/4.
10-
if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) {
11-
return;
12-
}
9+
const argv = getGlobalVariable('argv');
10+
const veEnabled = argv['ve'];
1311

1412
await ng('add', '@nguniversal/express-engine', '--client-project', 'test-project');
1513

@@ -21,28 +19,43 @@ export default async function () {
2119
});
2220

2321
await silentNpm('install');
24-
await appendToFile(
25-
'src/main.server.ts',
26-
`export { renderModuleFactory } from '@angular/platform-server';`,
27-
);
22+
if (veEnabled) {
23+
await replaceInFile('src/main.server.ts', /renderModule/g, 'renderModuleFactory');
24+
await writeFile(
25+
'./index.js',
26+
` require('zone.js/dist/zone-node');
27+
const fs = require('fs');
28+
const { AppServerModuleNgFactory, renderModuleFactory } = require('./dist/server/main');
29+
30+
renderModuleFactory(AppServerModuleNgFactory, {
31+
url: '/',
32+
document: '<app-root></app-root>'
33+
}).then(html => {
34+
fs.writeFileSync('dist/server/index.html', html);
35+
});
36+
`,
37+
);
38+
} else {
39+
await writeFile(
40+
'./index.js',
41+
` require('zone.js/dist/zone-node');
42+
const fs = require('fs');
43+
const { AppServerModule, renderModule } = require('./dist/server/main');
44+
45+
renderModule(AppServerModule, {
46+
url: '/',
47+
document: '<app-root></app-root>'
48+
}).then(html => {
49+
fs.writeFileSync('dist/server/index.html', html);
50+
});
51+
`,
52+
);
53+
}
2854

29-
await writeFile(
30-
'./index.js',
31-
` require('zone.js/dist/zone-node');
32-
const fs = require('fs');
33-
const { AppServerModuleNgFactory, renderModuleFactory } = require('./dist/server/main');
34-
35-
renderModuleFactory(AppServerModuleNgFactory, {
36-
url: '/',
37-
document: '<app-root></app-root>'
38-
}).then(html => {
39-
fs.writeFileSync('dist/server/index.html', html);
40-
});
41-
`,
42-
);
4355

4456
await ng('run', 'test-project:server:production');
45-
await expectFileToMatch('dist/server/main.js', /exports.*AppServerModuleNgFactory/);
57+
58+
await expectFileToMatch('dist/server/main.js', veEnabled ? /exports.*AppServerModuleNgFactory/ : /exports.*AppServerModule/);
4659
await exec(normalize('node'), 'index.js');
4760
await expectFileToMatch(
4861
'dist/server/index.html',

tests/legacy-cli/e2e_runner.ts

-7
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,6 @@ if (!argv.ve) {
102102
// Ivy doesn't support i18n externally at the moment.
103103
.filter(name => !name.includes('tests/i18n/'))
104104
.filter(name => !name.endsWith('tests/build/aot/aot-i18n.ts'))
105-
// We don't have a platform-server usage story yet for Ivy.
106-
// It's contingent on lazy loading and factory shim considerations that are still being
107-
// discussed.
108-
// Broken currently https://github.com/angular/angular-cli/issues/15383
109-
.filter(name => !name.endsWith('tests/build/platform-server.ts'))
110-
.filter(name => !name.endsWith('tests/build/build-app-shell.ts'))
111-
.filter(name => !name.endsWith('tests/build/build-app-shell-with-schematic.ts'));
112105
}
113106

114107
const shardId = 'shard' in argv ? argv['shard'] : null;

0 commit comments

Comments
 (0)