Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): i18n app shell with Ivy
Browse files Browse the repository at this point in the history
  • Loading branch information
alan-agius4 authored and vikerman committed Oct 30, 2019
1 parent 669abae commit 588baa5
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 70 deletions.
135 changes: 73 additions & 62 deletions packages/angular_devkit/build_angular/src/app-shell/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
createBuilder,
targetFromTargetString,
} from '@angular-devkit/architect';
import { JsonObject, join, normalize, resolve, schema } from '@angular-devkit/core';
import { JsonObject, join, normalize, resolve } from '@angular-devkit/core';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import * as fs from 'fs';
import * as path from 'path';
Expand All @@ -27,11 +27,6 @@ async function _renderUniversal(
browserResult: BrowserBuilderOutput,
serverResult: ServerBuilderOutput,
): Promise<BrowserBuilderOutput> {
const browserIndexOutputPath = path.join(browserResult.outputPath || '', 'index.html');
const indexHtml = fs.readFileSync(browserIndexOutputPath, 'utf8');
const serverBundlePath = await _getServerModuleBundlePath(options, context, serverResult);
const root = context.workspaceRoot;

// Get browser target options.
const browserTarget = targetFromTargetString(options.browserTarget);
const rawBrowserOptions = await context.getTargetOptions(browserTarget);
Expand All @@ -42,65 +37,72 @@ async function _renderUniversal(
);

// Initialize zone.js
const root = context.workspaceRoot;
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}.`);
const host = new NodeJsSyncHost();
const projectName = context.target && context.target.project;
if (!projectName) {
throw new Error('The builder requires a target.');
}

// Load platform server module renderer
const renderOpts = {
document: indexHtml,
url: options.route,
};

const html = await renderModuleFn(AppServerModuleDef, renderOpts);
// Overwrite the client index file.
const outputIndexPath = options.outputIndexPath
? path.join(root, options.outputIndexPath)
: browserIndexOutputPath;

fs.writeFileSync(outputIndexPath, html);

if (browserOptions.serviceWorker) {
const host = new NodeJsSyncHost();
const projectMetadata = await context.getProjectMetadata(projectName);
const projectRoot = resolve(
normalize(root),
normalize((projectMetadata.root as string) || ''),
);

const projectName = context.target && context.target.project;
if (!projectName) {
throw new Error('The builder requires a target.');
for (const outputPath of browserResult.outputPaths) {
const localeDirectory = path.relative(browserResult.baseOutputPath, outputPath);
const browserIndexOutputPath = path.join(outputPath, 'index.html');
const indexHtml = fs.readFileSync(browserIndexOutputPath, 'utf8');
const serverBundlePath = await _getServerModuleBundlePath(options, context, serverResult, localeDirectory);

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}.`);
}

const projectMetadata = await context.getProjectMetadata(projectName);
const projectRoot = resolve(
normalize(context.workspaceRoot),
normalize((projectMetadata.root as string) || ''),
);

await augmentAppWithServiceWorker(
host,
normalize(root),
projectRoot,
join(normalize(root), browserOptions.outputPath),
browserOptions.baseHref || '/',
browserOptions.ngswConfigPath,
);
// Load platform server module renderer
const renderOpts = {
document: indexHtml,
url: options.route,
};

const html = await renderModuleFn(AppServerModuleDef, renderOpts);
// Overwrite the client index file.
const outputIndexPath = options.outputIndexPath
? path.join(root, options.outputIndexPath)
: browserIndexOutputPath;

fs.writeFileSync(outputIndexPath, html);

if (browserOptions.serviceWorker) {
await augmentAppWithServiceWorker(
host,
normalize(root),
projectRoot,
normalize(outputPath),
browserOptions.baseHref || '/',
browserOptions.ngswConfigPath,
);
}
}

return browserResult;
Expand All @@ -110,11 +112,18 @@ async function _getServerModuleBundlePath(
options: BuildWebpackAppShellSchema,
context: BuilderContext,
serverResult: ServerBuilderOutput,
browserLocaleDirectory: string,
) {
if (options.appModuleBundle) {
return path.join(context.workspaceRoot, options.appModuleBundle);
} else {
const outputPath = serverResult.outputPath || '/';
const { baseOutputPath = '' } = serverResult;
const outputPath = path.join(baseOutputPath, browserLocaleDirectory);

if (!fs.existsSync(outputPath)) {
throw new Error(`Could not find server output directory: ${outputPath}.`);
}

const files = fs.readdirSync(outputPath, 'utf8');
const re = /^main\.(?:[a-zA-Z0-9]{20}\.)?(?:bundle\.)?js$/;
const maybeMain = files.filter(x => re.test(x))[0];
Expand All @@ -140,15 +149,17 @@ async function _appShellBuilder(
watch: false,
serviceWorker: false,
});
const serverTargetRun = await context.scheduleTarget(serverTarget, {});
const serverTargetRun = await context.scheduleTarget(serverTarget, {
watch: false,
});

try {
const [browserResult, serverResult] = await Promise.all([
(browserTargetRun.result as {}) as BrowserBuilderOutput,
serverTargetRun.result,
browserTargetRun.result as unknown as BrowserBuilderOutput,
serverTargetRun.result as unknown as ServerBuilderOutput,
]);

if (browserResult.success === false || browserResult.outputPath === undefined) {
if (browserResult.success === false || browserResult.baseOutputPath === undefined) {
return browserResult;
} else if (serverResult.success === false) {
return serverResult;
Expand Down
11 changes: 9 additions & 2 deletions packages/angular_devkit/build_angular/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ const cacheDownlevelPath = cachingDisabled ? undefined : findCachePath('angular-

export type BrowserBuilderOutput = json.JsonObject &
BuilderOutput & {
baseOutputPath: string;
outputPaths: string[];
/**
* @deprecated in version 9. Use 'outputPaths' instead.
*/
outputPath: string;
};

Expand Down Expand Up @@ -228,6 +233,7 @@ export function buildWebpackBrowser(
const host = new NodeJsSyncHost();
const root = normalize(context.workspaceRoot);
const baseOutputPath = path.resolve(context.workspaceRoot, options.outputPath);
let outputPaths: undefined | string[];

// Check Angular version.
assertCompatibleAngularVersion(context.workspaceRoot, context.logger);
Expand Down Expand Up @@ -278,7 +284,7 @@ export function buildWebpackBrowser(

return { success };
} else if (success) {
const outputPaths = ensureOutputPaths(baseOutputPath, i18n);
outputPaths = ensureOutputPaths(baseOutputPath, i18n);

let noModuleFiles: EmittedFiles[] | undefined;
let moduleFiles: EmittedFiles[] | undefined;
Expand Down Expand Up @@ -699,8 +705,9 @@ export function buildWebpackBrowser(
event =>
({
...event,
// If we use differential loading, both configs have the same outputs
baseOutputPath,
outputPath: baseOutputPath,
outputPaths: outputPaths || [baseOutputPath],
} as BrowserBuilderOutput),
),
);
Expand Down
14 changes: 11 additions & 3 deletions packages/angular_devkit/build_angular/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ import { Schema as ServerBuilderOptions } from './schema';

// If success is true, outputPath should be set.
export type ServerBuilderOutput = json.JsonObject & BuilderOutput & {
outputPath?: string;
baseOutputPath: string;
outputPaths: string[];
/**
* @deprecated in version 9. Use 'outputPaths' instead.
*/
outputPath: string;
};

export { ServerBuilderOptions };
Expand All @@ -52,6 +57,7 @@ export function execute(
const tsConfig = readTsconfig(options.tsConfig, root);
const target = tsConfig.options.target || ScriptTarget.ES5;
const baseOutputPath = path.resolve(root, options.outputPath);
let outputPaths: undefined | string[];

return from(initialize(options, context, transforms.webpackConfiguration)).pipe(
concatMap(({ config, i18n }) => {
Expand All @@ -66,7 +72,7 @@ export function execute(
throw new Error('Webpack stats build result is required.');
}

const outputPaths = ensureOutputPaths(baseOutputPath, i18n);
outputPaths = ensureOutputPaths(baseOutputPath, i18n);

const success = await i18nInlineEmittedFiles(
context,
Expand All @@ -92,7 +98,9 @@ export function execute(

return {
...output,
outputPath: path.resolve(root, options.outputPath),
baseOutputPath,
outputPath: baseOutputPath,
outputPaths: outputPaths || [baseOutputPath],
} as ServerBuilderOutput;
}),
);
Expand Down
4 changes: 2 additions & 2 deletions packages/angular_devkit/build_angular/test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ export async function browserBuild(
const output = (await run.result) as BrowserBuilderOutput;
expect(output.success).toBe(true);

expect(output.outputPath).not.toBeUndefined();
const outputPath = normalize(output.outputPath);
expect(output.outputPaths[0]).not.toBeUndefined();
const outputPath = normalize(output.outputPaths[0]);

const fileNames = await host.list(outputPath).toPromise();
const files = fileNames.reduce((acc: { [name: string]: Promise<string> }, path) => {
Expand Down
Loading

0 comments on commit 588baa5

Please sign in to comment.