Skip to content
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
1 change: 0 additions & 1 deletion packages/schematics/angular/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ function addAppToWorkspaceFile(options: ApplicationOptions, appDir: string): Rul
sourceMap: false,
extractCss: true,
namedChunks: false,
aot: true,
extractLicenses: true,
vendorChunk: false,
buildOptimizer: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
removePropertyInAstObject,
} from '../../utility/json-utils';
import { Builders } from '../../utility/workspace-models';
import { getAllOptions, getTargets, getWorkspace } from './utils';
import { getAllOptions, getTargets, getWorkspace, isIvyEnabled } from './utils';

export const ANY_COMPONENT_STYLE_BUDGET = {
type: 'anyComponentStyle',
Expand All @@ -32,6 +32,7 @@ export function UpdateWorkspaceConfig(): Rule {
updateStyleOrScriptOption('styles', recorder, target);
updateStyleOrScriptOption('scripts', recorder, target);
addAnyComponentStyleBudget(recorder, target);
updateAotOption(tree, recorder, target);
}

for (const { target } of getTargets(workspace, 'test', Builders.Karma)) {
Expand All @@ -45,6 +46,41 @@ export function UpdateWorkspaceConfig(): Rule {
};
}

function updateAotOption(tree: Tree, recorder: UpdateRecorder, builderConfig: JsonAstObject) {
const options = findPropertyInAstObject(builderConfig, 'options');
if (!options || options.kind !== 'object') {
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)) {
return;
}

// Add aot to options.
const aotOption = findPropertyInAstObject(options, 'aot');

if (!aotOption) {
insertPropertyInAstObjectInOrder(recorder, options, 'aot', true, 12);

return;
}

if (aotOption.kind !== 'true') {
const { start, end } = aotOption;
recorder.remove(start.offset, end.offset - start.offset);
recorder.insertLeft(start.offset, 'true');
}

// Remove aot properties from other configurations as they are no redundant
const configOptions = getAllOptions(builderConfig, true);
for (const options of configOptions) {
removePropertyInAstObject(recorder, options, 'aot');
}
}

function updateStyleOrScriptOption(property: 'scripts' | 'styles', recorder: UpdateRecorder, builderConfig: JsonAstObject) {
const options = getAllOptions(builderConfig);

Expand Down Expand Up @@ -75,12 +111,6 @@ function addAnyComponentStyleBudget(recorder: UpdateRecorder, builderConfig: Jso
const options = getAllOptions(builderConfig, true);

for (const option of options) {
const aotOption = findPropertyInAstObject(option, 'aot');
if (!aotOption || aotOption.kind !== 'true') {
// AnyComponentStyle only works for AOT
continue;
}

const budgetOption = findPropertyInAstObject(option, 'budgets');
if (!budgetOption) {
// add
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,84 @@ describe('Migration to version 9', () => {
expect(config.configurations.production.budgets).toEqual([ANY_COMPONENT_STYLE_BUDGET]);
});
});

describe('aot option', () => {
it('should update aot option when false', async () => {
let config = getWorkspaceTargets(tree);
config.build.options.aot = false;
updateWorkspaceTargets(tree, config);

const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2).build;
expect(config.options.aot).toBe(true);
});

it('should add aot option when not defined', async () => {
let config = getWorkspaceTargets(tree);
config.build.options.aot = undefined;
updateWorkspaceTargets(tree, config);

const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2).build;
expect(config.options.aot).toBe(true);
});

it('should not aot option when opted-out of Ivy', async () => {
const tsConfig = JSON.stringify(
{
extends: './tsconfig.json',
angularCompilerOptions: {
enableIvy: false,
},
},
null,
2,
);

tree.overwrite('/tsconfig.app.json', tsConfig);

let config = getWorkspaceTargets(tree);
config.build.options.aot = false;
updateWorkspaceTargets(tree, config);

const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2).build;
expect(config.options.aot).toBe(false);
});

it('should not aot option when opted-out of Ivy in workspace', async () => {
const tsConfig = JSON.stringify(
{
angularCompilerOptions: {
enableIvy: false,
},
},
null,
2,
);

tree.overwrite('/tsconfig.json', tsConfig);

let config = getWorkspaceTargets(tree);
config.build.options.aot = false;
updateWorkspaceTargets(tree, config);

const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2).build;
expect(config.options.aot).toBe(false);
});

it('should remove aot option from production configuration', async () => {
let config = getWorkspaceTargets(tree);
config.build.options.aot = false;
config.build.configurations.production.aot = true;
updateWorkspaceTargets(tree, config);

const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
config = getWorkspaceTargets(tree2).build;
expect(config.options.aot).toBe(true);
expect(config.configurations.production.aot).toBeUndefined();
});
});
});
});
39 changes: 38 additions & 1 deletion packages/schematics/angular/migrations/update-9/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import { JsonAstObject, JsonParseMode, parseJsonAst } from '@angular-devkit/core';
import { JsonAstObject, JsonParseMode, dirname, normalize, parseJsonAst, resolve } from '@angular-devkit/core';
import { SchematicsException, Tree } from '@angular-devkit/schematics';
import { getWorkspacePath } from '../../utility/config';
import { findPropertyInAstObject } from '../../utility/json-utils';
Expand Down Expand Up @@ -81,3 +81,40 @@ export function getWorkspace(host: Tree): JsonAstObject {

return parseJsonAst(content, JsonParseMode.Loose) as JsonAstObject;
}

export function isIvyEnabled(tree: Tree, tsConfigPath: string): boolean {
// In version 9, Ivy is turned on by default
// Ivy is opted out only when 'enableIvy' is set to false.

const buffer = tree.read(tsConfigPath);
if (!buffer) {
return true;
}

const tsCfgAst = parseJsonAst(buffer.toString(), JsonParseMode.Loose);

if (tsCfgAst.kind !== 'object') {
return true;
}

const ngCompilerOptions = findPropertyInAstObject(tsCfgAst, 'angularCompilerOptions');
if (ngCompilerOptions && ngCompilerOptions.kind === 'object') {
const enableIvy = findPropertyInAstObject(ngCompilerOptions, 'enableIvy');

if (enableIvy) {
return !!enableIvy.value;
}
}

const configExtends = findPropertyInAstObject(tsCfgAst, 'extends');
if (configExtends && configExtends.kind === 'string') {
const extendedTsConfigPath = resolve(
dirname(normalize(tsConfigPath)),
normalize(configExtends.value),
);

return isIvyEnabled(tree, extendedTsConfigPath);
}

return true;
}
107 changes: 107 additions & 0 deletions packages/schematics/angular/migrations/update-9/utils_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { HostTree } from '@angular-devkit/schematics';
import { isIvyEnabled } from './utils';

describe('migrations update-9 utils', () => {
describe('isIvyEnabled', () => {
let tree: HostTree;

beforeEach(() => {
tree = new HostTree();
});

it('should return false when disabled in base tsconfig', () => {
tree.create('tsconfig.json', JSON.stringify({
angularCompilerOptions: {
enableIvy: false,
},
}));

tree.create('foo/tsconfig.app.json', JSON.stringify({
extends: '../tsconfig.json',
}));

expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(false);
});

it('should return true when enable in child tsconfig but disabled in base tsconfig', () => {
tree.create('tsconfig.json', JSON.stringify({
angularCompilerOptions: {
enableIvy: false,
},
}));

tree.create('foo/tsconfig.app.json', JSON.stringify({
extends: '../tsconfig.json',
angularCompilerOptions: {
enableIvy: true,
},
}));

expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(true);
});

it('should return false when disabled in child tsconfig but enabled in base tsconfig', () => {
tree.create('tsconfig.json', JSON.stringify({
angularCompilerOptions: {
enableIvy: true,
},
}));

tree.create('foo/tsconfig.app.json', JSON.stringify({
extends: '../tsconfig.json',
angularCompilerOptions: {
enableIvy: false,
},
}));

expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(false);
});

it('should return false when disabled in base with multiple extends', () => {
tree.create('tsconfig.json', JSON.stringify({
angularCompilerOptions: {
enableIvy: false,
},
}));

tree.create('foo/tsconfig.project.json', JSON.stringify({
extends: '../tsconfig.json',
}));

tree.create('foo/tsconfig.app.json', JSON.stringify({
extends: './tsconfig.project.json',
}));

expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(false);
});

it('should return true when enable in intermediate tsconfig with multiple extends', () => {
tree.create('tsconfig.json', JSON.stringify({
angularCompilerOptions: {
enableIvy: false,
},
}));

tree.create('foo/tsconfig.project.json', JSON.stringify({
extends: '../tsconfig.json',
angularCompilerOptions: {
enableIvy: true,
},
}));

tree.create('foo/tsconfig.app.json', JSON.stringify({
extends: './tsconfig.project.json',
}));

expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(true);
});
});
});
4 changes: 3 additions & 1 deletion tests/legacy-cli/e2e/setup/500-create-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export default async function() {

// In VE non prod builds are non AOT by default
await updateJsonFile('angular.json', config => {
config.projects['test-project'].architect.build.options.aot = false;
const build = config.projects['test-project'].architect.build;
build.options.aot = false;
build.configurations.production.aot = true;
});
}
}
Expand Down