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
Show file tree
Hide file tree
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
Expand Up @@ -76,6 +76,11 @@ export type BrowserBuilderOutput = BuilderOutput & {
baseOutputPath: string;
outputPaths: string[];
outputPath: string;
outputs: {
locale?: string;
path: string;
baseHref: string;
}[];
};

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

// @public (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -118,7 +118,7 @@ async function _renderUniversal(
projectRoot,
root,
outputPath,
browserOptions.baseHref || '/',
baseHref,
browserOptions.ngswConfigPath,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}[];
};

/**
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -352,7 +363,7 @@ export function buildWebpackBrowser(
projectRoot,
context.workspaceRoot,
outputPath,
getLocaleBaseHref(i18n, locale) || options.baseHref || '/',
getLocaleBaseHref(i18n, locale) ?? defaultBaseHref,
options.ngswConfigPath,
);
} catch (error) {
Expand All @@ -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),
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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;
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
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`),
]);
}
}