feat(@schematics/angular): add migration to add new i18n options for Ivy

This migration will update current projects by adding the `i18n` project level option and add `localize` option in the server and browser builder configurations when both `i18nLocale` and `i18nFile` are defined.
This commit is contained in:
Alan Agius 2019-10-10 19:07:26 +02:00 committed by vikerman
parent 7045a781ea
commit 750baf92d6
3 changed files with 176 additions and 20 deletions

View File

@ -15,7 +15,7 @@ import {
removePropertyInAstObject, removePropertyInAstObject,
} from '../../utility/json-utils'; } from '../../utility/json-utils';
import { Builders } from '../../utility/workspace-models'; import { Builders } from '../../utility/workspace-models';
import { getAllOptions, getTargets, getWorkspace, isIvyEnabled } from './utils'; import { getAllOptions, getProjectTarget, getTargets, getWorkspace, isIvyEnabled } from './utils';
export const ANY_COMPONENT_STYLE_BUDGET = { export const ANY_COMPONENT_STYLE_BUDGET = {
type: 'anyComponentStyle', type: 'anyComponentStyle',
@ -33,6 +33,7 @@ export function updateWorkspaceConfig(): Rule {
updateStyleOrScriptOption('scripts', recorder, target); updateStyleOrScriptOption('scripts', recorder, target);
addAnyComponentStyleBudget(recorder, target); addAnyComponentStyleBudget(recorder, target);
updateAotOption(tree, recorder, target); updateAotOption(tree, recorder, target);
addBuilderI18NOptions(recorder, target);
} }
for (const { target } of getTargets(workspace, 'test', Builders.Karma)) { for (const { target } of getTargets(workspace, 'test', Builders.Karma)) {
@ -42,6 +43,11 @@ export function updateWorkspaceConfig(): Rule {
for (const { target } of getTargets(workspace, 'server', Builders.Server)) { for (const { target } of getTargets(workspace, 'server', Builders.Server)) {
updateOptimizationOption(recorder, target); updateOptimizationOption(recorder, target);
addBuilderI18NOptions(recorder, target);
}
for (const { target, project } of getTargets(workspace, 'extract-i18n', Builders.ExtractI18n)) {
addProjectI18NOptions(recorder, target, project);
} }
tree.commitUpdate(recorder); tree.commitUpdate(recorder);
@ -50,6 +56,72 @@ export function updateWorkspaceConfig(): Rule {
}; };
} }
function addProjectI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject, projectConfig: JsonAstObject) {
const browserConfig = getProjectTarget(projectConfig, 'build', Builders.Browser);
if (!browserConfig || browserConfig.kind !== 'object') {
return;
}
// browser builder options
let locales: Record<string, string> | undefined;
const options = getAllOptions(browserConfig);
for (const option of options) {
const localeId = findPropertyInAstObject(option, 'i18nLocale');
if (!localeId || localeId.kind !== 'string') {
continue;
}
const localeFile = findPropertyInAstObject(option, 'i18nFile');
if (!localeFile || localeFile.kind !== 'string') {
continue;
}
const localIdValue = localeId.value;
const localeFileValue = localeFile.value;
if (!locales) {
locales = {
[localIdValue]: localeFileValue,
};
} else {
locales[localIdValue] = localeFileValue;
}
}
if (locales) {
// Get sourceLocale from extract-i18n builder
const i18nOptions = getAllOptions(builderConfig);
const sourceLocale = i18nOptions
.map(o => {
const sourceLocale = findPropertyInAstObject(o, 'i18nLocale');
return sourceLocale && sourceLocale.value;
})
.find(x => !!x);
// Add i18n project configuration
insertPropertyInAstObjectInOrder(recorder, projectConfig, 'i18n', {
locales,
// tslint:disable-next-line: no-any
sourceLocale: sourceLocale as any,
}, 6);
}
}
function addBuilderI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject) {
const options = getAllOptions(builderConfig);
for (const option of options) {
const localeId = findPropertyInAstObject(option, 'i18nLocale');
if (!localeId || localeId.kind !== 'string') {
continue;
}
// add new localize option
insertPropertyInAstObjectInOrder(recorder, option, 'localize', [localeId.value], 12);
}
}
function updateAotOption(tree: Tree, recorder: UpdateRecorder, builderConfig: JsonAstObject) { function updateAotOption(tree: Tree, recorder: UpdateRecorder, builderConfig: JsonAstObject) {
const options = findPropertyInAstObject(builderConfig, 'options'); const options = findPropertyInAstObject(builderConfig, 'options');
if (!options || options.kind !== 'object') { if (!options || options.kind !== 'object') {

View File

@ -296,5 +296,76 @@ describe('Migration to version 9', () => {
expect(config.production.optimization).toBe(true); expect(config.production.optimization).toBe(true);
}); });
}); });
describe('i18n configuration', () => {
function getI18NConfig(localId: string): object {
return {
outputPath: `dist/my-project-${localId}/`,
i18nFile: `src/locale/messages.${localId}.xlf`,
i18nFormat: 'xlf',
i18nLocale: localId,
};
}
describe('when i18n builder options are set', () => {
it(`should add 'localize' option in configuration`, async () => {
let config = getWorkspaceTargets(tree);
config.build.options.aot = false;
config.build.options = getI18NConfig('fr');
config.build.configurations.de = getI18NConfig('de');
updateWorkspaceTargets(tree, config);
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2).build;
expect(config.options.localize).toEqual(['fr']);
expect(config.configurations.de.localize).toEqual(['de']);
});
it(`should add i18n 'sourceLocale' project config when 'extract-i18n' 'i18nLocale' is defined`, async () => {
const config = getWorkspaceTargets(tree);
config.build.options.aot = false;
config.build.options = getI18NConfig('fr');
config['extract-i18n'].options.i18nLocale = 'en-GB';
config.build.configurations.de = getI18NConfig('de');
updateWorkspaceTargets(tree, config);
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
const projectConfig = JSON.parse(tree2.readContent(workspacePath)).projects['migration-test'];
expect(projectConfig.i18n.sourceLocale).toBe('en-GB');
expect(projectConfig.i18n.locales).toBeDefined();
});
it(`should add i18n 'locales' project config`, async () => {
const config = getWorkspaceTargets(tree);
config.build.options.aot = false;
config.build.options = getI18NConfig('fr');
config.build.configurations.de = getI18NConfig('de');
updateWorkspaceTargets(tree, config);
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
const projectConfig = JSON.parse(tree2.readContent(workspacePath)).projects['migration-test'];
expect(projectConfig.i18n.sourceLocale).toBeUndefined();
expect(projectConfig.i18n.locales).toEqual({
de: 'src/locale/messages.de.xlf',
fr: 'src/locale/messages.fr.xlf',
});
});
});
describe('when i18n builder options are not set', () => {
it(`should not add 'localize' option`, async () => {
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
const config = getWorkspaceTargets(tree2).build;
expect(config.options.localize).toBeUndefined();
expect(config.configurations.production.localize).toBeUndefined();
});
it('should not add i18n project config', async () => {
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
const projectConfig = JSON.parse(tree2.readContent(workspacePath)).projects['migration-test'];
expect(projectConfig.i18n).toBeUndefined();
});
});
});
}); });
}); });

View File

@ -12,7 +12,36 @@ import { getWorkspacePath } from '../../utility/config';
import { findPropertyInAstObject } from '../../utility/json-utils'; import { findPropertyInAstObject } from '../../utility/json-utils';
import { Builders, WorkspaceTargets } from '../../utility/workspace-models'; import { Builders, WorkspaceTargets } from '../../utility/workspace-models';
/** Get all workspace targets which builder and target names matches the provided. */ /** Get a project target which builder and target names matches the provided. */
export function getProjectTarget(
project: JsonAstObject,
targetName: Exclude<keyof WorkspaceTargets, number>,
builderName: Builders,
): JsonAstObject | undefined {
const projectRoot = findPropertyInAstObject(project, 'root');
if (!projectRoot || projectRoot.kind !== 'string') {
return undefined;
}
const architect = findPropertyInAstObject(project, 'architect');
if (!architect || architect.kind !== 'object') {
return undefined;
}
const target = findPropertyInAstObject(architect, targetName);
if (!target || target.kind !== 'object') {
return undefined;
}
const builder = findPropertyInAstObject(target, 'builder');
// Projects who's build builder is @angular-devkit/build-ng-packagr
if (builder && builder.kind === 'string' && builder.value === builderName) {
return target;
}
return undefined;
}
export function getTargets( export function getTargets(
workspace: JsonAstObject, workspace: JsonAstObject,
targetName: Exclude<keyof WorkspaceTargets, number>, targetName: Exclude<keyof WorkspaceTargets, number>,
@ -30,24 +59,8 @@ export function getTargets(
continue; continue;
} }
const projectRoot = findPropertyInAstObject(projectConfig, 'root'); const target = getProjectTarget(projectConfig, targetName, builderName);
if (!projectRoot || projectRoot.kind !== 'string') { if (target) {
continue;
}
const architect = findPropertyInAstObject(projectConfig, 'architect');
if (!architect || architect.kind !== 'object') {
continue;
}
const target = findPropertyInAstObject(architect, targetName);
if (!target || target.kind !== 'object') {
continue;
}
const builder = findPropertyInAstObject(target, 'builder');
// Projects who's build builder is @angular-devkit/build-ng-packagr
if (builder && builder.kind === 'string' && builder.value === builderName) {
targets.push({ target, project: projectConfig }); targets.push({ target, project: projectConfig });
} }
} }