mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-23 07:19:58 +08:00
feat(@schematics/angular): add migration to add anyComponentStyle
bundle budget
This commit is contained in:
parent
f32c9dde1a
commit
19b120947d
@ -12,11 +12,17 @@ import {
|
||||
} from '@angular-devkit/core';
|
||||
import { Rule, Tree, UpdateRecorder } from '@angular-devkit/schematics';
|
||||
import {
|
||||
appendValueInAstArray,
|
||||
findPropertyInAstObject,
|
||||
insertPropertyInAstObjectInOrder,
|
||||
removePropertyInAstObject,
|
||||
} from '../../utility/json-utils';
|
||||
|
||||
export const ANY_COMPONENT_STYLE_BUDGET = {
|
||||
type: 'anyComponentStyle',
|
||||
maximumWarning: '6kb',
|
||||
};
|
||||
|
||||
export function UpdateWorkspaceConfig(): Rule {
|
||||
return (tree: Tree) => {
|
||||
let workspaceConfigPath = 'angular.json';
|
||||
@ -59,8 +65,9 @@ export function UpdateWorkspaceConfig(): Rule {
|
||||
const builder = findPropertyInAstObject(buildTarget, 'builder');
|
||||
// Projects who's build builder is not build-angular:browser
|
||||
if (builder && builder.kind === 'string' && builder.value === '@angular-devkit/build-angular:browser') {
|
||||
updateOption('styles', recorder, buildTarget);
|
||||
updateOption('scripts', recorder, buildTarget);
|
||||
updateStyleOrScriptOption('styles', recorder, buildTarget);
|
||||
updateStyleOrScriptOption('scripts', recorder, buildTarget);
|
||||
addAnyComponentStyleBudget(recorder, buildTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,8 +76,8 @@ export function UpdateWorkspaceConfig(): Rule {
|
||||
const builder = findPropertyInAstObject(testTarget, 'builder');
|
||||
// Projects who's build builder is not build-angular:browser
|
||||
if (builder && builder.kind === 'string' && builder.value === '@angular-devkit/build-angular:karma') {
|
||||
updateOption('styles', recorder, testTarget);
|
||||
updateOption('scripts', recorder, testTarget);
|
||||
updateStyleOrScriptOption('styles', recorder, testTarget);
|
||||
updateStyleOrScriptOption('scripts', recorder, testTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,19 +91,21 @@ export function UpdateWorkspaceConfig(): Rule {
|
||||
/**
|
||||
* Helper to retreive all the options in various configurations
|
||||
*/
|
||||
function getAllOptions(builderConfig: JsonAstObject): JsonAstObject[] {
|
||||
function getAllOptions(builderConfig: JsonAstObject, configurationsOnly = false): JsonAstObject[] {
|
||||
const options = [];
|
||||
const configurations = findPropertyInAstObject(builderConfig, 'configurations');
|
||||
if (configurations && configurations.kind === 'object') {
|
||||
options.push(...configurations.properties.map(x => x.value));
|
||||
}
|
||||
|
||||
options.push(findPropertyInAstObject(builderConfig, 'options'));
|
||||
if (!configurationsOnly) {
|
||||
options.push(findPropertyInAstObject(builderConfig, 'options'));
|
||||
}
|
||||
|
||||
return options.filter(o => o && o.kind === 'object') as JsonAstObject[];
|
||||
}
|
||||
|
||||
function updateOption(property: 'scripts' | 'styles', recorder: UpdateRecorder, builderConfig: JsonAstObject) {
|
||||
function updateStyleOrScriptOption(property: 'scripts' | 'styles', recorder: UpdateRecorder, builderConfig: JsonAstObject) {
|
||||
const options = getAllOptions(builderConfig);
|
||||
|
||||
for (const option of options) {
|
||||
@ -121,3 +130,42 @@ function updateOption(property: 'scripts' | 'styles', recorder: UpdateRecorder,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addAnyComponentStyleBudget(recorder: UpdateRecorder, builderConfig: JsonAstObject) {
|
||||
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
|
||||
insertPropertyInAstObjectInOrder(recorder, option, 'budgets', [ANY_COMPONENT_STYLE_BUDGET], 14);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (budgetOption.kind !== 'array') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if 'anyComponentStyle' budget already exists don't add.
|
||||
const hasAnyComponentStyle = budgetOption.elements.some(node => {
|
||||
if (!node || node.kind !== 'object') {
|
||||
// skip non complex objects
|
||||
return false;
|
||||
}
|
||||
|
||||
const budget = findPropertyInAstObject(node, 'type');
|
||||
|
||||
return !!budget && budget.kind === 'string' && budget.value === 'anyComponentStyle';
|
||||
});
|
||||
|
||||
if (!hasAnyComponentStyle) {
|
||||
appendValueInAstArray(recorder, budgetOption, ANY_COMPONENT_STYLE_BUDGET, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,19 @@
|
||||
|
||||
import { EmptyTree } from '@angular-devkit/schematics';
|
||||
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||
import { WorkspaceTargets } from '../../utility/workspace-models';
|
||||
import { ANY_COMPONENT_STYLE_BUDGET } from './update-workspace-config';
|
||||
|
||||
function readWorkspaceConfig(tree: UnitTestTree) {
|
||||
return JSON.parse(tree.readContent('/angular.json'));
|
||||
// tslint:disable-next-line: no-any
|
||||
function getWorkspaceTargets(tree: UnitTestTree): any {
|
||||
return JSON.parse(tree.readContent(workspacePath))
|
||||
.projects['migration-test'].architect;
|
||||
}
|
||||
|
||||
function updateWorkspaceTargets(tree: UnitTestTree, workspaceTargets: WorkspaceTargets) {
|
||||
const config = JSON.parse(tree.readContent(workspacePath));
|
||||
config.projects['migration-test'].architect = workspaceTargets;
|
||||
tree.overwrite(workspacePath, JSON.stringify(config, undefined, 2));
|
||||
}
|
||||
|
||||
const scriptsWithLazy = [
|
||||
@ -69,60 +79,95 @@ describe('Migration to version 9', () => {
|
||||
.toPromise();
|
||||
});
|
||||
|
||||
it('should update scripts in build target', () => {
|
||||
let config = readWorkspaceConfig(tree);
|
||||
let build = config.projects['migration-test'].architect.build;
|
||||
build.options.scripts = scriptsWithLazy;
|
||||
build.configurations.production.scripts = scriptsWithLazy;
|
||||
describe('scripts and style options', () => {
|
||||
it('should update scripts in build target', () => {
|
||||
let config = getWorkspaceTargets(tree);
|
||||
config.build.options.scripts = scriptsWithLazy;
|
||||
config.build.configurations.production.scripts = scriptsWithLazy;
|
||||
|
||||
tree.overwrite(workspacePath, JSON.stringify(config));
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = readWorkspaceConfig(tree2);
|
||||
build = config.projects['migration-test'].architect.build;
|
||||
expect(build.options.scripts).toEqual(scriptsExpectWithLazy);
|
||||
expect(build.configurations.production.scripts).toEqual(scriptsExpectWithLazy);
|
||||
updateWorkspaceTargets(tree, config);
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = getWorkspaceTargets(tree2).build;
|
||||
expect(config.options.scripts).toEqual(scriptsExpectWithLazy);
|
||||
expect(config.configurations.production.scripts).toEqual(scriptsExpectWithLazy);
|
||||
});
|
||||
|
||||
it('should update styles in build target', () => {
|
||||
let config = getWorkspaceTargets(tree);
|
||||
config.build.options.styles = stylesWithLazy;
|
||||
config.build.configurations.production.styles = stylesWithLazy;
|
||||
|
||||
updateWorkspaceTargets(tree, config);
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = getWorkspaceTargets(tree2).build;
|
||||
expect(config.options.styles).toEqual(stylesExpectWithLazy);
|
||||
expect(config.configurations.production.styles).toEqual(stylesExpectWithLazy);
|
||||
});
|
||||
|
||||
it('should update scripts in test target', () => {
|
||||
let config = getWorkspaceTargets(tree);
|
||||
config.test.options.scripts = scriptsWithLazy;
|
||||
config.test.configurations = { production: { scripts: scriptsWithLazy } };
|
||||
|
||||
updateWorkspaceTargets(tree, config);
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = getWorkspaceTargets(tree2).test;
|
||||
expect(config.options.scripts).toEqual(scriptsExpectWithLazy);
|
||||
expect(config.configurations.production.scripts).toEqual(scriptsExpectWithLazy);
|
||||
});
|
||||
|
||||
it('should update styles in test target', () => {
|
||||
let config = getWorkspaceTargets(tree);
|
||||
config.test.options.styles = stylesWithLazy;
|
||||
config.test.configurations = { production: { styles: stylesWithLazy } };
|
||||
|
||||
updateWorkspaceTargets(tree, config);
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = getWorkspaceTargets(tree2).test;
|
||||
expect(config.options.styles).toEqual(stylesExpectWithLazy);
|
||||
expect(config.configurations.production.styles).toEqual(stylesExpectWithLazy);
|
||||
});
|
||||
});
|
||||
|
||||
it('should update styles in build target', () => {
|
||||
let config = readWorkspaceConfig(tree);
|
||||
let build = config.projects['migration-test'].architect.build;
|
||||
build.options.styles = stylesWithLazy;
|
||||
build.configurations.production.styles = stylesWithLazy;
|
||||
describe('anyComponentStyle bundle budget', () => {
|
||||
it('should not append budget when already exists', () => {
|
||||
const defaultBudget = [
|
||||
{ type: 'initial', maximumWarning: '2mb', maximumError: '5mb' },
|
||||
{ type: 'anyComponentStyle', maximumWarning: '10kb', maximumError: '50kb' },
|
||||
];
|
||||
|
||||
tree.overwrite(workspacePath, JSON.stringify(config));
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = readWorkspaceConfig(tree2);
|
||||
build = config.projects['migration-test'].architect.build;
|
||||
expect(build.options.styles).toEqual(stylesExpectWithLazy);
|
||||
expect(build.configurations.production.styles).toEqual(stylesExpectWithLazy);
|
||||
});
|
||||
let config = getWorkspaceTargets(tree);
|
||||
config.build.configurations.production.budgets = defaultBudget;
|
||||
updateWorkspaceTargets(tree, config);
|
||||
|
||||
it('should update scripts in test target', () => {
|
||||
let config = readWorkspaceConfig(tree);
|
||||
let test = config.projects['migration-test'].architect.test;
|
||||
test.options.scripts = scriptsWithLazy;
|
||||
test.configurations = { production: { scripts: scriptsWithLazy } };
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = getWorkspaceTargets(tree2).build;
|
||||
expect(config.configurations.production.budgets).toEqual(defaultBudget);
|
||||
});
|
||||
|
||||
tree.overwrite(workspacePath, JSON.stringify(config));
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = readWorkspaceConfig(tree2);
|
||||
test = config.projects['migration-test'].architect.test;
|
||||
expect(test.options.scripts).toEqual(scriptsExpectWithLazy);
|
||||
expect(test.configurations.production.scripts).toEqual(scriptsExpectWithLazy);
|
||||
});
|
||||
it('should append budget in build target', () => {
|
||||
const defaultBudget = [{ type: 'initial', maximumWarning: '2mb', maximumError: '5mb' }];
|
||||
let config = getWorkspaceTargets(tree);
|
||||
config.build.configurations.production.budgets = defaultBudget;
|
||||
updateWorkspaceTargets(tree, config);
|
||||
|
||||
it('should update styles in test target', () => {
|
||||
let config = readWorkspaceConfig(tree);
|
||||
let test = config.projects['migration-test'].architect.test;
|
||||
test.options.styles = stylesWithLazy;
|
||||
test.configurations = { production: { styles: stylesWithLazy } };
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = getWorkspaceTargets(tree2).build;
|
||||
expect(config.configurations.production.budgets).toEqual([
|
||||
...defaultBudget,
|
||||
ANY_COMPONENT_STYLE_BUDGET,
|
||||
]);
|
||||
});
|
||||
|
||||
tree.overwrite(workspacePath, JSON.stringify(config));
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = readWorkspaceConfig(tree2);
|
||||
test = config.projects['migration-test'].architect.test;
|
||||
expect(test.options.styles).toEqual(stylesExpectWithLazy);
|
||||
expect(test.configurations.production.styles).toEqual(stylesExpectWithLazy);
|
||||
it('should add budget in build target', () => {
|
||||
let config = getWorkspaceTargets(tree);
|
||||
config.build.configurations.production.budgets = undefined;
|
||||
updateWorkspaceTargets(tree, config);
|
||||
|
||||
const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch());
|
||||
config = getWorkspaceTargets(tree2).build;
|
||||
expect(config.configurations.production.budgets).toEqual([ANY_COMPONENT_STYLE_BUDGET]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -130,7 +130,7 @@ export interface AppConfig {
|
||||
/**
|
||||
* The type of budget
|
||||
*/
|
||||
type?: ('bundle' | 'initial' | 'allScript' | 'all' | 'anyScript' | 'any');
|
||||
type?: ('bundle' | 'initial' | 'allScript' | 'all' | 'anyScript' | 'any' | 'anyComponentStyle');
|
||||
/**
|
||||
* The name of the bundle
|
||||
*/
|
||||
|
@ -31,7 +31,7 @@ export function appendPropertyInAstObject(
|
||||
recorder.insertRight(commaIndex, ',');
|
||||
index = end.offset;
|
||||
}
|
||||
const content = JSON.stringify(value, null, indent).replace(/\n/g, indentStr);
|
||||
const content = _stringifyContent(value, indentStr);
|
||||
recorder.insertRight(
|
||||
index,
|
||||
(node.properties.length === 0 && indent ? '\n' : '')
|
||||
@ -84,7 +84,7 @@ export function insertPropertyInAstObjectInOrder(
|
||||
const insertIndex = insertAfterProp === null
|
||||
? node.start.offset + 1
|
||||
: insertAfterProp.end.offset + 1;
|
||||
const content = JSON.stringify(value, null, indent).replace(/\n/g, indentStr);
|
||||
const content = _stringifyContent(value, indentStr);
|
||||
recorder.insertRight(
|
||||
insertIndex,
|
||||
indentStr
|
||||
@ -168,7 +168,7 @@ export function appendValueInAstArray(
|
||||
index,
|
||||
(node.elements.length === 0 && indent ? '\n' : '')
|
||||
+ ' '.repeat(indent)
|
||||
+ JSON.stringify(value, null, indent).replace(/\n/g, indentStr)
|
||||
+ _stringifyContent(value, indentStr)
|
||||
+ indentStr.slice(0, -indent),
|
||||
);
|
||||
}
|
||||
@ -191,3 +191,24 @@ export function findPropertyInAstObject(
|
||||
function _buildIndent(count: number): string {
|
||||
return count ? '\n' + ' '.repeat(count) : '';
|
||||
}
|
||||
|
||||
function _stringifyContent(value: JsonValue, indentStr: string): string {
|
||||
// TODO: Add snapshot tests
|
||||
|
||||
// The 'space' value is 2, because we want to add 2 additional
|
||||
// indents from the 'key' node.
|
||||
|
||||
// If we use the indent provided we will have double indents:
|
||||
// "budgets": [
|
||||
// {
|
||||
// "type": "initial",
|
||||
// "maximumWarning": "2mb",
|
||||
// "maximumError": "5mb"
|
||||
// },
|
||||
// {
|
||||
// "type": "anyComponentStyle",
|
||||
// 'maximumWarning": "5kb"
|
||||
// }
|
||||
// ]
|
||||
return JSON.stringify(value, null, 2).replace(/\n/g, indentStr);
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ export interface BrowserBuilderBaseOptions {
|
||||
index?: string;
|
||||
polyfills: string;
|
||||
assets?: (object|string)[];
|
||||
styles?: string[];
|
||||
scripts?: string[];
|
||||
styles?: (object|string)[];
|
||||
scripts?: (object|string)[];
|
||||
sourceMap?: boolean;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user