Skip to content

fix(@angular-devkit/build-angular): set base-href in service worker manifest when using i18n and app-shell #23454

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 28, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions goldens/public-api/angular_devkit/build_angular/index.md
Original file line number Diff line number Diff line change
@@ -76,6 +76,11 @@ export type BrowserBuilderOutput = BuilderOutput & {
baseOutputPath: string;
outputPaths: string[];
outputPath: string;
outputs: {
locale?: string;
path: string;
baseHref: string;
}[];
};

// @public (undocumented)
@@ -272,6 +277,10 @@ export type ServerBuilderOutput = BuilderOutput & {
baseOutputPath: string;
outputPaths: string[];
outputPath: string;
outputs: {
locale?: string;
path: string;
}[];
};

// @public (undocumented)
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ async function _renderUniversal(
})
: undefined;

for (const outputPath of browserResult.outputPaths) {
for (const { path: outputPath, baseHref } of browserResult.outputs) {
const localeDirectory = path.relative(browserResult.baseOutputPath, outputPath);
const browserIndexOutputPath = path.join(outputPath, 'index.html');
const indexHtml = await fs.promises.readFile(browserIndexOutputPath, 'utf8');
@@ -118,7 +118,7 @@ async function _renderUniversal(
projectRoot,
root,
outputPath,
browserOptions.baseHref || '/',
baseHref,
browserOptions.ngswConfigPath,
);
}
Original file line number Diff line number Diff line change
@@ -67,11 +67,20 @@ import { Schema as BrowserBuilderSchema } from './schema';
*/
export type BrowserBuilderOutput = BuilderOutput & {
baseOutputPath: string;
/**
* @deprecated in version 14. Use 'outputs' instead.
*/
outputPaths: string[];
/**
* @deprecated in version 9. Use 'outputPaths' instead.
* @deprecated in version 9. Use 'outputs' instead.
*/
outputPath: string;

outputs: {
locale?: string;
path: string;
baseHref: string;
}[];
};

/**
@@ -174,6 +183,8 @@ export function buildWebpackBrowser(
({ config, projectRoot, projectSourceRoot, i18n, target, cacheOptions }) => {
const normalizedOptimization = normalizeOptimization(options.optimization);

const defaultBaseHref = options.baseHref ?? '/';

return runWebpack(config, context, {
webpackFactory: require('webpack') as typeof webpack,
logging:
@@ -308,7 +319,7 @@ export function buildWebpackBrowser(
for (const [locale, outputPath] of outputPaths.entries()) {
try {
const { content, warnings, errors } = await indexHtmlGenerator.process({
baseHref: getLocaleBaseHref(i18n, locale) || options.baseHref,
baseHref: getLocaleBaseHref(i18n, locale) || defaultBaseHref,
// i18nLocale is used when Ivy is disabled
lang: locale || undefined,
outputPath,
@@ -352,7 +363,7 @@ export function buildWebpackBrowser(
projectRoot,
context.workspaceRoot,
outputPath,
getLocaleBaseHref(i18n, locale) || options.baseHref || '/',
getLocaleBaseHref(i18n, locale) ?? defaultBaseHref,
options.ngswConfigPath,
);
} catch (error) {
@@ -378,6 +389,15 @@ export function buildWebpackBrowser(
baseOutputPath,
outputPath: baseOutputPath,
outputPaths: (outputPaths && Array.from(outputPaths.values())) || [baseOutputPath],
outputs: (outputPaths &&
[...outputPaths.entries()].map(([locale, path]) => ({
locale,
path,
baseHref: getLocaleBaseHref(i18n, locale) ?? defaultBaseHref,
}))) || {
path: baseOutputPath,
baseHref: defaultBaseHref,
},
} as BrowserBuilderOutput),
),
);
Original file line number Diff line number Diff line change
@@ -31,11 +31,19 @@ import { Schema as ServerBuilderOptions } from './schema';
*/
export type ServerBuilderOutput = BuilderOutput & {
baseOutputPath: string;
/**
* @deprecated in version 14. Use 'outputs' instead.
*/
outputPaths: string[];
/**
* @deprecated in version 9. Use 'outputPaths' instead.
* @deprecated in version 9. Use 'outputs' instead.
*/
outputPath: string;

outputs: {
locale?: string;
path: string;
}[];
};

export { ServerBuilderOptions };
@@ -130,6 +138,13 @@ export function execute(
baseOutputPath,
outputPath: baseOutputPath,
outputPaths: outputPaths || [baseOutputPath],
outputs: (outputPaths &&
[...outputPaths.entries()].map(([locale, path]) => ({
locale,
path,
}))) || {
path: baseOutputPath,
},
} as ServerBuilderOutput;
}),
);
Original file line number Diff line number Diff line change
@@ -83,8 +83,10 @@ export async function browserBuild(
};
}

expect(output.outputPaths[0]).not.toBeUndefined();
const outputPath = normalize(output.outputPaths[0]);
const [{ path, baseHref }] = output.outputs;
expect(baseHref).toBeTruthy();
expect(path).toBeTruthy();
const outputPath = normalize(path);

const fileNames = await host.list(outputPath).toPromise();
const files = fileNames.reduce((acc: { [name: string]: Promise<string> }, path) => {
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { getGlobalVariable } from '../../utils/env';
import { appendToFile, createDir, expectFileToMatch, writeFile } from '../../utils/fs';
import { installWorkspacePackages } from '../../utils/packages';
import { silentNg } from '../../utils/process';
import { updateJsonFile } from '../../utils/project';
import { readNgVersion } from '../../utils/version';

const snapshots = require('../../ng-snapshot/package.json');

export default async function () {
const isSnapshotBuild = getGlobalVariable('argv')['ng-snapshots'];

await updateJsonFile('package.json', (packageJson) => {
const dependencies = packageJson['dependencies'];
dependencies['@angular/localize'] = isSnapshotBuild
? snapshots.dependencies['@angular/localize']
: readNgVersion();
});

await appendToFile('src/app/app.component.html', '<router-outlet></router-outlet>');

// Add app-shell and service-worker
await silentNg('generate', 'app-shell');
await silentNg('generate', 'service-worker');

if (isSnapshotBuild) {
await updateJsonFile('package.json', (packageJson) => {
const dependencies = packageJson['dependencies'];
dependencies['@angular/platform-server'] = snapshots.dependencies['@angular/platform-server'];
dependencies['@angular/service-worker'] = snapshots.dependencies['@angular/service-worker'];
dependencies['@angular/router'] = snapshots.dependencies['@angular/router'];
});
}

await installWorkspacePackages();

const browserBaseDir = 'dist/test-project/browser';

// Set configurations for each locale.
const langTranslations = [
{ lang: 'en-US', translation: 'Hello i18n!' },
{ lang: 'fr', translation: 'Bonjour i18n!' },
];

await updateJsonFile('angular.json', (workspaceJson) => {
const appProject = workspaceJson.projects['test-project'];
const appArchitect = appProject.architect;
const buildOptions = appArchitect['build'].options;
const serverOptions = appArchitect['server'].options;

// Enable localization for all locales
buildOptions.localize = true;
buildOptions.outputHashing = 'none';
serverOptions.localize = true;
serverOptions.outputHashing = 'none';

// Add locale definitions to the project
const i18n: Record<string, any> = (appProject.i18n = { locales: {} });
for (const { lang } of langTranslations) {
if (lang == 'en-US') {
i18n.sourceLocale = lang;
} else {
i18n.locales[lang] = `src/locale/messages.${lang}.xlf`;
}
}
});

await createDir('src/locale');

for (const { lang } of langTranslations) {
// dummy translation file.
await writeFile(
`src/locale/messages.${lang}.xlf`,
`
<?xml version='1.0' encoding='utf-8'?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
</xliff>
`,
);
}

// Build each locale and verify the SW output.
await silentNg('run', 'test-project:app-shell:development');
for (const { lang } of langTranslations) {
await Promise.all([
expectFileToMatch(`${browserBaseDir}/${lang}/ngsw.json`, `/${lang}/main.js`),
expectFileToMatch(`${browserBaseDir}/${lang}/ngsw.json`, `/${lang}/index.html`),
]);
}
}