diff --git a/packages/angular_devkit/build_angular/src/app-shell/index.ts b/packages/angular_devkit/build_angular/src/app-shell/index.ts index 75fb4d9c18..4639928a0f 100644 --- a/packages/angular_devkit/build_angular/src/app-shell/index.ts +++ b/packages/angular_devkit/build_angular/src/app-shell/index.ts @@ -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'; @@ -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; + 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) diff --git a/packages/angular_devkit/build_angular/test/app-shell/app-shell_spec_large.ts b/packages/angular_devkit/build_angular/test/app-shell/app-shell_spec_large.ts index 8213aa8f1a..15085642c1 100644 --- a/packages/angular_devkit/build_angular/test/app-shell/app-shell_spec_large.ts +++ b/packages/angular_devkit/build_angular/test/app-shell/app-shell_spec_large.ts @@ -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; diff --git a/packages/angular_devkit/build_angular/test/server/base_spec_large.ts b/packages/angular_devkit/build_angular/test/server/base_spec_large.ts index 1bbdab2171..ec8f526dd4 100644 --- a/packages/angular_devkit/build_angular/test/server/base_spec_large.ts +++ b/packages/angular_devkit/build_angular/test/server/base_spec_large.ts @@ -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'; @@ -85,6 +86,7 @@ describe('Server Builder', () => { }); const run = await architect.scheduleTarget(target, { + bundleDependencies: BundleDependencies.None, sourceMap: { styles: false, scripts: true, @@ -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, diff --git a/tests/angular_devkit/build_angular/hello-world-app-ve/src/main.server.ts b/tests/angular_devkit/build_angular/hello-world-app-ve/src/main.server.ts index 00f62942d0..a143d3a033 100644 --- a/tests/angular_devkit/build_angular/hello-world-app-ve/src/main.server.ts +++ b/tests/angular_devkit/build_angular/hello-world-app-ve/src/main.server.ts @@ -14,3 +14,4 @@ if (environment.production) { } export { AppServerModule } from './app/app.server.module'; +export { renderModuleFactory } from '@angular/platform-server'; diff --git a/tests/angular_devkit/build_angular/hello-world-app/src/app/app.module.ts b/tests/angular_devkit/build_angular/hello-world-app/src/app/app.module.ts index ba889e142a..c61881ebb3 100644 --- a/tests/angular_devkit/build_angular/hello-world-app/src/app/app.module.ts +++ b/tests/angular_devkit/build_angular/hello-world-app/src/app/app.module.ts @@ -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 { } diff --git a/tests/angular_devkit/build_angular/hello-world-app/src/app/app.server.module.ts b/tests/angular_devkit/build_angular/hello-world-app/src/app/app.server.module.ts index e6203b203b..bb56e2795c 100644 --- a/tests/angular_devkit/build_angular/hello-world-app/src/app/app.server.module.ts +++ b/tests/angular_devkit/build_angular/hello-world-app/src/app/app.server.module.ts @@ -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 {} diff --git a/tests/angular_devkit/build_angular/hello-world-app/src/main.server.ts b/tests/angular_devkit/build_angular/hello-world-app/src/main.server.ts index 00f62942d0..4a5a5a1c72 100644 --- a/tests/angular_devkit/build_angular/hello-world-app/src/main.server.ts +++ b/tests/angular_devkit/build_angular/hello-world-app/src/main.server.ts @@ -14,3 +14,4 @@ if (environment.production) { } export { AppServerModule } from './app/app.server.module'; +export { renderModule } from '@angular/platform-server'; diff --git a/tests/legacy-cli/e2e/tests/build/build-app-shell-with-schematic.ts b/tests/legacy-cli/e2e/tests/build/build-app-shell-with-schematic.ts index 940fb3d286..dcc28b6bf5 100644 --- a/tests/legacy-cli/e2e/tests/build/build-app-shell-with-schematic.ts +++ b/tests/legacy-cli/e2e/tests/build/build-app-shell-with-schematic.ts @@ -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', ''); await ng('generate', 'appShell', '--client-project', 'test-project'); await updateJsonFile('package.json', packageJson => { @@ -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!/); diff --git a/tests/legacy-cli/e2e/tests/build/build-app-shell.ts b/tests/legacy-cli/e2e/tests/build/build-app-shell.ts index d12350b13f..a5ed96424c 100644 --- a/tests/legacy-cli/e2e/tests/build/build-app-shell.ts +++ b/tests/legacy-cli/e2e/tests/build/build-app-shell.ts @@ -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'; @@ -78,6 +72,7 @@ export default function() { } export { AppServerModule } from './app/app.server.module'; + export { renderModule${veProject ? 'Factory' : ''} } from '@angular/platform-server'; `, ), ) diff --git a/tests/legacy-cli/e2e/tests/build/platform-server.ts b/tests/legacy-cli/e2e/tests/build/platform-server.ts index 423deebefa..f2b004c109 100644 --- a/tests/legacy-cli/e2e/tests/build/platform-server.ts +++ b/tests/legacy-cli/e2e/tests/build/platform-server.ts @@ -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'); @@ -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: '' + }).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: '' + }).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: '' - }).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', diff --git a/tests/legacy-cli/e2e_runner.ts b/tests/legacy-cli/e2e_runner.ts index b828747018..00edf37edd 100644 --- a/tests/legacy-cli/e2e_runner.ts +++ b/tests/legacy-cli/e2e_runner.ts @@ -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;