mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-18 03:23:57 +08:00
fix(@angular-devkit/build-angular): make app-shell work with Ivy
Fixes #15383
This commit is contained in:
parent
9602003c49
commit
fb0a6af9f0
@ -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<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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -14,3 +14,4 @@ if (environment.production) {
|
||||
}
|
||||
|
||||
export { AppServerModule } from './app/app.server.module';
|
||||
export { renderModuleFactory } from '@angular/platform-server';
|
||||
|
@ -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 { }
|
||||
|
@ -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 {}
|
||||
|
@ -14,3 +14,4 @@ if (environment.production) {
|
||||
}
|
||||
|
||||
export { AppServerModule } from './app/app.server.module';
|
||||
export { renderModule } from '@angular/platform-server';
|
||||
|
@ -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 => {
|
||||
@ -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!/);
|
||||
|
@ -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';
|
||||
`,
|
||||
),
|
||||
)
|
||||
|
@ -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: '<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',
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user