Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): make app-shell work with Ivy
Browse files Browse the repository at this point in the history
Fixes #15383
  • Loading branch information
alan-agius4 authored and Keen Yee Liau committed Sep 10, 2019
1 parent 9602003 commit fb0a6af
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 69 deletions.
36 changes: 21 additions & 15 deletions packages/angular_devkit/build_angular/src/app-shell/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { JsonObject, experimental, join, normalize, resolve, schema } from '@ang
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import * as fs from 'fs';
import * as path from 'path';
import { readTsconfig } from '../angular-cli-files/utilities/read-tsconfig';
import { augmentAppWithServiceWorker } from '../angular-cli-files/utilities/service-worker';
import { BrowserBuilderOutput } from '../browser';
import { Schema as BrowserBuilderSchema } from '../browser/schema';
Expand All @@ -42,30 +41,37 @@ async function _renderUniversal(
browserBuilderName,
);

// Determine if browser app was compiled using Ivy.
const { options: compilerOptions } = readTsconfig(browserOptions.tsConfig, context.workspaceRoot);
const ivy = compilerOptions.enableIvy;

// Initialize zone.js
const zonePackage = require.resolve('zone.js', { paths: [root] });
await import(zonePackage);

const {
AppServerModule,
AppServerModuleNgFactory,
renderModule,
renderModuleFactory,
} = await import(serverBundlePath);

let renderModuleFn: (module: unknown, options: {}) => Promise<string>;
let AppServerModuleDef: unknown;

if (renderModuleFactory && AppServerModuleNgFactory) {
renderModuleFn = renderModuleFactory;
AppServerModuleDef = AppServerModuleNgFactory;
} else if (renderModule && AppServerModule) {
renderModuleFn = renderModule;
AppServerModuleDef = AppServerModule;
} else {
throw new Error(`renderModule method and/or AppServerModule were not exported from: ${serverBundlePath}.`);
}

// Load platform server module renderer
const platformServerPackage = require.resolve('@angular/platform-server', { paths: [root] });
const renderOpts = {
document: indexHtml,
url: options.route,
};

// Render app to HTML using Ivy or VE
const html = await import(platformServerPackage)
// tslint:disable-next-line:no-implicit-dependencies
.then((m: typeof import('@angular/platform-server')) =>
ivy
? m.renderModule(require(serverBundlePath).AppServerModule, renderOpts)
: m.renderModuleFactory(require(serverBundlePath).AppServerModuleNgFactory, renderOpts),
);

const html = await renderModuleFn(AppServerModuleDef, renderOpts);
// Overwrite the client index file.
const outputIndexPath = options.outputIndexPath
? path.join(root, options.outputIndexPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@
import { Architect } from '@angular-devkit/architect/src/architect';
import { getSystemPath, join, normalize, virtualFs } from '@angular-devkit/core';
import * as express from 'express'; // tslint:disable-line:no-implicit-dependencies
import { createArchitect, host, veEnabled } from '../utils';
import { createArchitect, host } from '../utils';


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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Architect } from '@angular-devkit/architect';
import { getSystemPath, join, normalize, virtualFs } from '@angular-devkit/core';
import { take, tap } from 'rxjs/operators';
import { BrowserBuilderOutput } from '../../src/browser';
import { BundleDependencies } from '../../src/server/schema';
import { createArchitect, host, veEnabled } from '../utils';


Expand Down Expand Up @@ -85,6 +86,7 @@ describe('Server Builder', () => {
});

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

await run.stop();
});
//

it('supports component styles sourcemaps', async () => {
const overrides = {
bundleDependencies: BundleDependencies.None,
sourceMap: {
styles: true,
scripts: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ if (environment.production) {
}

export { AppServerModule } from './app/app.server.module';
export { renderModuleFactory } from '@angular/platform-server';
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';


import { AppComponent } from './app.component';


@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
export class AppModule { }
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
imports: [AppModule, ServerModule],
imports: [
AppModule,
ServerModule,
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ if (environment.production) {
}

export { AppServerModule } from './app/app.server.module';
export { renderModule } from '@angular/platform-server';
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import { getGlobalVariable } from '../../utils/env';
import { appendToFile, expectFileToMatch } from '../../utils/fs';
import { appendToFile, expectFileToMatch, replaceInFile } from '../../utils/fs';
import { ng, silentNpm } from '../../utils/process';
import { updateJsonFile } from '../../utils/project';
import { readNgVersion } from '../../utils/version';


export default async function () {
// Skip this test in Angular 2/4.
if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) {
return;
}

await appendToFile('src/app/app.component.html', '<router-outlet></router-outlet>');
await ng('generate', 'appShell', '--client-project', 'test-project');
await updateJsonFile('package.json', packageJson => {
Expand All @@ -20,6 +15,10 @@ export default async function () {
: readNgVersion();
});

if (argv['ve']) {
await replaceInFile('src/main.server.ts', /renderModule/g, 'renderModuleFactory');
}

await silentNpm('install');
await ng('run', 'test-project:app-shell');
await expectFileToMatch('dist/test-project/index.html', /app-shell works!/);
Expand Down
7 changes: 1 addition & 6 deletions tests/legacy-cli/e2e/tests/build/build-app-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ import { updateJsonFile } from '../../utils/project';
import { readNgVersion } from '../../utils/version';

export default function() {
// Skip this test in Angular 2/4.
if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) {
return Promise.resolve();
}

let platformServerVersion = readNgVersion();
let httpVersion = readNgVersion();

if (getGlobalVariable('argv')['ng-snapshots']) {
platformServerVersion = 'github:angular/platform-server-builds';
Expand Down Expand Up @@ -78,6 +72,7 @@ export default function() {
}
export { AppServerModule } from './app/app.server.module';
export { renderModule${veProject ? 'Factory' : ''} } from '@angular/platform-server';
`,
),
)
Expand Down
61 changes: 37 additions & 24 deletions tests/legacy-cli/e2e/tests/build/platform-server.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { normalize } from 'path';
import { getGlobalVariable } from '../../utils/env';
import { expectFileToMatch, writeFile, appendToFile } from '../../utils/fs';
import { expectFileToMatch, writeFile } from '../../utils/fs';
import { exec, ng, silentNpm } from '../../utils/process';
import { updateJsonFile } from '../../utils/project';
import { readNgVersion } from '../../utils/version';

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

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

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

await silentNpm('install');
await appendToFile(
'src/main.server.ts',
`export { renderModuleFactory } from '@angular/platform-server';`,
);
if (veEnabled) {
await replaceInFile('src/main.server.ts', /renderModule/g, 'renderModuleFactory');
await writeFile(
'./index.js',
` require('zone.js/dist/zone-node');
const fs = require('fs');
const { AppServerModuleNgFactory, renderModuleFactory } = require('./dist/server/main');
renderModuleFactory(AppServerModuleNgFactory, {
url: '/',
document: '<app-root></app-root>'
}).then(html => {
fs.writeFileSync('dist/server/index.html', html);
});
`,
);
} else {
await writeFile(
'./index.js',
` require('zone.js/dist/zone-node');
const fs = require('fs');
const { AppServerModule, renderModule } = require('./dist/server/main');
renderModule(AppServerModule, {
url: '/',
document: '<app-root></app-root>'
}).then(html => {
fs.writeFileSync('dist/server/index.html', html);
});
`,
);
}

await writeFile(
'./index.js',
` require('zone.js/dist/zone-node');
const fs = require('fs');
const { AppServerModuleNgFactory, renderModuleFactory } = require('./dist/server/main');
renderModuleFactory(AppServerModuleNgFactory, {
url: '/',
document: '<app-root></app-root>'
}).then(html => {
fs.writeFileSync('dist/server/index.html', html);
});
`,
);

await ng('run', 'test-project:server:production');
await expectFileToMatch('dist/server/main.js', /exports.*AppServerModuleNgFactory/);

await expectFileToMatch('dist/server/main.js', veEnabled ? /exports.*AppServerModuleNgFactory/ : /exports.*AppServerModule/);
await exec(normalize('node'), 'index.js');
await expectFileToMatch(
'dist/server/index.html',
Expand Down
7 changes: 0 additions & 7 deletions tests/legacy-cli/e2e_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,6 @@ if (!argv.ve) {
// Ivy doesn't support i18n externally at the moment.
.filter(name => !name.includes('tests/i18n/'))
.filter(name => !name.endsWith('tests/build/aot/aot-i18n.ts'))
// We don't have a platform-server usage story yet for Ivy.
// It's contingent on lazy loading and factory shim considerations that are still being
// discussed.
// Broken currently https://github.com/angular/angular-cli/issues/15383
.filter(name => !name.endsWith('tests/build/platform-server.ts'))
.filter(name => !name.endsWith('tests/build/build-app-shell.ts'))
.filter(name => !name.endsWith('tests/build/build-app-shell-with-schematic.ts'));
}

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

0 comments on commit fb0a6af

Please sign in to comment.