Skip to content

Commit

Permalink
fix(@schematics/angular): migrate project to use new i18n options
Browse files Browse the repository at this point in the history
(cherry picked from commit 8b23e64)
  • Loading branch information
alan-agius4 authored and dgp1130 committed Nov 19, 2019
1 parent c78c81a commit b8b3351
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
import { JsonAstObject } from '@angular-devkit/core';
import { Rule, Tree, UpdateRecorder } from '@angular-devkit/schematics';
import { getWorkspacePath } from '../../utility/config';
import { NodeDependencyType, addPackageJsonDependency, getPackageJsonDependency } from '../../utility/dependencies';
import {
appendValueInAstArray,
findPropertyInAstObject,
insertPropertyInAstObjectInOrder,
removePropertyInAstObject,
} from '../../utility/json-utils';
import { latestVersions } from '../../utility/latest-versions';
import { Builders } from '../../utility/workspace-models';
import { getAllOptions, getProjectTarget, getTargets, getWorkspace, isIvyEnabled } from './utils';

Expand All @@ -28,29 +30,41 @@ export function updateWorkspaceConfig(): Rule {
const workspace = getWorkspace(tree);
const recorder = tree.beginUpdate(workspacePath);

for (const { target } of getTargets(workspace, 'build', Builders.Browser)) {
for (const { target, project } of getTargets(workspace, 'build', Builders.Browser)) {
updateStyleOrScriptOption('styles', recorder, target);
updateStyleOrScriptOption('scripts', recorder, target);
addAnyComponentStyleBudget(recorder, target);
updateAotOption(tree, recorder, target);
addBuilderI18NOptions(recorder, target, project);
}

for (const { target } of getTargets(workspace, 'test', Builders.Karma)) {
for (const { target, project } of getTargets(workspace, 'test', Builders.Karma)) {
updateStyleOrScriptOption('styles', recorder, target);
updateStyleOrScriptOption('scripts', recorder, target);
addBuilderI18NOptions(recorder, target, project);
}

for (const { target } of getTargets(workspace, 'server', Builders.Server)) {
updateOptimizationOption(recorder, target);
}

for (const { target, project } of getTargets(workspace, 'extract-i18n', Builders.ExtractI18n)) {
addProjectI18NOptions(recorder, tree, target, project);
removeExtracti18nDeprecatedOptions(recorder, target);
}

tree.commitUpdate(recorder);

return tree;
};
}

function addProjectI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject, projectConfig: JsonAstObject) {
function addProjectI18NOptions(
recorder: UpdateRecorder,
tree: Tree,
builderConfig: JsonAstObject,
projectConfig: JsonAstObject,
) {
const browserConfig = getProjectTarget(projectConfig, 'build', Builders.Browser);
if (!browserConfig || browserConfig.kind !== 'object') {
return;
Expand Down Expand Up @@ -86,33 +100,101 @@ function addProjectI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstO
// Get sourceLocale from extract-i18n builder
const i18nOptions = getAllOptions(builderConfig);
const sourceLocale = i18nOptions
.map(o => {
const sourceLocale = findPropertyInAstObject(o, 'i18nLocale');
.map(o => {
const sourceLocale = findPropertyInAstObject(o, 'i18nLocale');

return sourceLocale && sourceLocale.value;
})
.find(x => !!x);
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);

// Add @angular/localize if not already a dependency
if (!getPackageJsonDependency(tree, '@angular/localize')) {
addPackageJsonDependency(tree, {
name: '@angular/localize',
version: latestVersions.Angular,
type: NodeDependencyType.Default,
});
}
}
}

function addBuilderI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject) {
function addBuilderI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject, projectConfig: JsonAstObject) {
const options = getAllOptions(builderConfig);

let hasi18n = false;
for (const option of options) {
const localeId = findPropertyInAstObject(option, 'i18nLocale');
if (!localeId || localeId.kind !== 'string') {
continue;
if (localeId && localeId.kind === 'string') {
// add new localize option
insertPropertyInAstObjectInOrder(recorder, option, 'localize', [localeId.value], 12);
removePropertyInAstObject(recorder, option, 'i18nLocale');
}

const i18nFile = findPropertyInAstObject(option, 'i18nFile');
if (i18nFile) {
removePropertyInAstObject(recorder, option, 'i18nFile');
}

const i18nFormat = findPropertyInAstObject(option, 'i18nFormat');
if (i18nFormat) {
removePropertyInAstObject(recorder, option, 'i18nFormat');
}

// add new localize option
insertPropertyInAstObjectInOrder(recorder, option, 'localize', [localeId.value], 12);
hasi18n = !!(hasi18n || i18nFormat || i18nFile || localeId);
}

if (hasi18n) {
const options = findPropertyInAstObject(builderConfig, 'options');
if (!options || options.kind !== 'object') {
return;
}

// Don't add localize option of it's already present in the main options
if (findPropertyInAstObject(options, 'i18nLocale') || findPropertyInAstObject(options, 'localize')) {
return;
}

// Get sourceLocale from extract-i18n builder
const extractI18nConfig = getProjectTarget(projectConfig, 'extract-i18n', Builders.ExtractI18n);
let sourceLocale: string | undefined;

if (extractI18nConfig && extractI18nConfig.kind === 'object') {
const i18nOptions = getAllOptions(extractI18nConfig);
sourceLocale = i18nOptions
.map(o => {
const sourceLocale = findPropertyInAstObject(o, 'i18nLocale');

return sourceLocale && sourceLocale.value;
})
.find(x => !!x) as string;
}

insertPropertyInAstObjectInOrder(recorder, options, 'localize', [sourceLocale || 'en-US'], 12);
}
}

function removeExtracti18nDeprecatedOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject) {
const options = getAllOptions(builderConfig);

for (const option of options) {
// deprecated options
removePropertyInAstObject(recorder, option, 'i18nLocale');
const i18nFormat = option.properties.find(({ key }) => key.value === 'i18nFormat');

if (i18nFormat) {
// i18nFormat has been changed to format
const key = i18nFormat.key;
const offset = key.start.offset + 1;
recorder.remove(offset, key.value.length);
recorder.insertLeft(offset, 'format');
}
}
}

Expand All @@ -122,7 +204,6 @@ function updateAotOption(tree: Tree, recorder: UpdateRecorder, builderConfig: Js
return;
}


const tsConfig = findPropertyInAstObject(options, 'tsConfig');
// Do not add aot option if the users already opted out from Ivy.
if (tsConfig && tsConfig.kind === 'string' && !isIvyEnabled(tree, tsConfig.value)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { latestVersions } from '../../utility/latest-versions';
import { WorkspaceTargets } from '../../utility/workspace-models';
import { ANY_COMPONENT_STYLE_BUDGET } from './update-workspace-config';

Expand Down Expand Up @@ -297,7 +298,7 @@ describe('Migration to version 9', () => {
});
});

xdescribe('i18n configuration', () => {
describe('i18n configuration', () => {
function getI18NConfig(localId: string): object {
return {
outputPath: `dist/my-project-${localId}/`,
Expand All @@ -308,6 +309,17 @@ describe('Migration to version 9', () => {
}

describe('when i18n builder options are set', () => {
it(`should add '@angular/localize' as a dependency`, async () => {
const config = getWorkspaceTargets(tree);
config.build.options = getI18NConfig('fr');
config.build.configurations.de = getI18NConfig('de');
updateWorkspaceTargets(tree, config);

const tree2 = await schematicRunner.runSchematicAsync('workspace-version-9', {}, tree.branch()).toPromise();
const { dependencies } = JSON.parse(tree2.readContent('/package.json'));
expect(dependencies['@angular/localize']).toBe(latestVersions.Angular);
});

it(`should add 'localize' option in configuration`, async () => {
let config = getWorkspaceTargets(tree);
config.build.options.aot = false;
Expand All @@ -321,6 +333,50 @@ describe('Migration to version 9', () => {
expect(config.configurations.de.localize).toEqual(['de']);
});

it(`should add 'localize' option in main options`, async () => {
let config = getWorkspaceTargets(tree);
config.build.options.aot = false;
config.build.configurations.de = getI18NConfig('de');
config['extract-i18n'].options.i18nFormat = 'xmb';
config['extract-i18n'].options.i18nLocale = 'en-GB';
updateWorkspaceTargets(tree, config);

const tree2 = await schematicRunner.runSchematicAsync('workspace-version-9', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2).build;
expect(config.options.localize).toEqual(['en-GB']);
expect(config.configurations.de.localize).toEqual(['de']);
});

it('should remove deprecated i18n options', 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('workspace-version-9', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2).build;
expect(config.options.i18nFormat).toBeUndefined();
expect(config.options.i18nFile).toBeUndefined();
expect(config.options.i18nLocale).toBeUndefined();
expect(config.configurations.de.i18nFormat).toBeUndefined();
expect(config.configurations.de.i18nFile).toBeUndefined();
expect(config.configurations.de.i18nLocale).toBeUndefined();
});

it('should remove deprecated extract-i18n options', async () => {
let config = getWorkspaceTargets(tree);
config['extract-i18n'].options.i18nFormat = 'xmb';
config['extract-i18n'].options.i18nLocale = 'en-GB';
updateWorkspaceTargets(tree, config);

const tree2 = await schematicRunner.runSchematicAsync('workspace-version-9', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2)['extract-i18n'];
expect(config.options.i18nFormat).toBeUndefined();
expect(config.options.i18nLocale).toBeUndefined();
expect(config.options.format).toBe('xmb');
});

it(`should add i18n 'sourceLocale' project config when 'extract-i18n' 'i18nLocale' is defined`, async () => {
const config = getWorkspaceTargets(tree);
config.build.options.aot = false;
Expand Down

0 comments on commit b8b3351

Please sign in to comment.