diff --git a/packages/angular_devkit/build_angular/src/browser/schema.json b/packages/angular_devkit/build_angular/src/browser/schema.json index b229e7b5627f..65d4542c030e 100644 --- a/packages/angular_devkit/build_angular/src/browser/schema.json +++ b/packages/angular_devkit/build_angular/src/browser/schema.json @@ -69,7 +69,7 @@ "optimization": { "description": "Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.io/guide/workspace-config#optimization-configuration.", "x-user-analytics": 16, - "default": false, + "default": true, "oneOf": [ { "type": "object", @@ -153,11 +153,11 @@ "type": "boolean", "description": "Build using Ahead of Time compilation.", "x-user-analytics": 13, - "default": false + "default": true }, "sourceMap": { "description": "Output source maps for scripts and styles. For more information, see https://angular.io/guide/workspace-config#source-map-configuration.", - "default": true, + "default": false, "oneOf": [ { "type": "object", @@ -193,7 +193,7 @@ "vendorChunk": { "type": "boolean", "description": "Generate a seperate bundle containing only vendor libraries. This option should only used for development.", - "default": true + "default": false }, "commonChunk": { "type": "boolean", @@ -280,7 +280,7 @@ "extractLicenses": { "type": "boolean", "description": "Extract all licenses in a separate file.", - "default": false + "default": true }, "showCircularDependencies": { "type": "boolean", @@ -291,12 +291,12 @@ "buildOptimizer": { "type": "boolean", "description": "Enables '@angular-devkit/build-optimizer' optimizations when using the 'aot' option.", - "default": false + "default": true }, "namedChunks": { "type": "boolean", "description": "Use file name for lazy loaded chunks.", - "default": true + "default": false }, "subresourceIntegrity": { "type": "boolean", diff --git a/packages/angular_devkit/build_angular/src/browser/specs/works_spec.ts b/packages/angular_devkit/build_angular/src/browser/specs/works_spec.ts index 8de151c2ca8c..e8a0f633f4a4 100644 --- a/packages/angular_devkit/build_angular/src/browser/specs/works_spec.ts +++ b/packages/angular_devkit/build_angular/src/browser/specs/works_spec.ts @@ -24,6 +24,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { polyfills: 'src/polyfills.ts', tsConfig: 'src/tsconfig.app.json', progress: false, + vendorChunk: true, assets: ['src/favicon.ico', 'src/assets'], styles: ['src/styles.css'], scripts: [], @@ -33,13 +34,13 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { await harness.executeOnce(); // Default files should be in outputPath. - expect(harness.hasFile('dist/runtime.js')).toBe(true); - expect(harness.hasFile('dist/main.js')).toBe(true); - expect(harness.hasFile('dist/polyfills.js')).toBe(true); - expect(harness.hasFile('dist/vendor.js')).toBe(true); - expect(harness.hasFile('dist/favicon.ico')).toBe(true); - expect(harness.hasFile('dist/styles.css')).toBe(true); - expect(harness.hasFile('dist/index.html')).toBe(true); + expect(harness.hasFile('dist/runtime.js')).toBeTrue(); + expect(harness.hasFile('dist/main.js')).toBeTrue(); + expect(harness.hasFile('dist/polyfills.js')).toBeTrue(); + expect(harness.hasFile('dist/vendor.js')).toBeTrue(); + expect(harness.hasFile('dist/favicon.ico')).toBeTrue(); + expect(harness.hasFile('dist/styles.css')).toBeTrue(); + expect(harness.hasFile('dist/index.html')).toBeTrue(); }); }); }); diff --git a/packages/angular_devkit/build_angular/src/browser/tests/behavior/rebuild-errors_spec.ts b/packages/angular_devkit/build_angular/src/browser/tests/behavior/rebuild-errors_spec.ts index 06d766cd23fa..116942a78444 100644 --- a/packages/angular_devkit/build_angular/src/browser/tests/behavior/rebuild-errors_spec.ts +++ b/packages/angular_devkit/build_angular/src/browser/tests/behavior/rebuild-errors_spec.ts @@ -281,6 +281,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, watch: true, + aot: false, }); const buildCount = await harness diff --git a/packages/angular_devkit/build_angular/src/browser/tests/behavior/typescript-target_spec.ts b/packages/angular_devkit/build_angular/src/browser/tests/behavior/typescript-target_spec.ts index 8ebb5f784d66..c7b28159fa4d 100644 --- a/packages/angular_devkit/build_angular/src/browser/tests/behavior/typescript-target_spec.ts +++ b/packages/angular_devkit/build_angular/src/browser/tests/behavior/typescript-target_spec.ts @@ -39,6 +39,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, + vendorChunk: true, }); const { result, logs } = await harness.executeOnce(); @@ -87,6 +88,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, + vendorChunk: true, sourceMap: { scripts: true, }, @@ -119,6 +121,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, + vendorChunk: true, }); const { result, logs } = await harness.executeOnce(); diff --git a/packages/angular_devkit/build_angular/src/browser/tests/options/extract-licenses_spec.ts b/packages/angular_devkit/build_angular/src/browser/tests/options/extract-licenses_spec.ts index 50b29d23ee1b..0210fdd8ab2f 100644 --- a/packages/angular_devkit/build_angular/src/browser/tests/options/extract-licenses_spec.ts +++ b/packages/angular_devkit/build_angular/src/browser/tests/options/extract-licenses_spec.ts @@ -32,14 +32,14 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.expectFile('dist/3rdpartylicenses.txt').toNotExist(); }); - it(`should not generate '3rdpartylicenses.txt' when 'extractLicenses' is not set`, async () => { + it(`should generate '3rdpartylicenses.txt' when 'extractLicenses' is not set`, async () => { harness.useTarget('build', { ...BASE_OPTIONS, }); const { result } = await harness.executeOnce(); expect(result?.success).toBe(true); - harness.expectFile('dist/3rdpartylicenses.txt').toNotExist(); + harness.expectFile('dist/3rdpartylicenses.txt').content.toContain('MIT'); }); }); }); diff --git a/packages/angular_devkit/build_angular/src/browser/tests/options/main_spec.ts b/packages/angular_devkit/build_angular/src/browser/tests/options/main_spec.ts index 021039529a92..dfd79cc0f16a 100644 --- a/packages/angular_devkit/build_angular/src/browser/tests/options/main_spec.ts +++ b/packages/angular_devkit/build_angular/src/browser/tests/options/main_spec.ts @@ -22,7 +22,6 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.expectFile('dist/runtime.js').toExist(); harness.expectFile('dist/main.js').toExist(); - harness.expectFile('dist/vendor.js').toExist(); harness.expectFile('dist/index.html').toExist(); }); @@ -40,7 +39,6 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.expectFile('dist/runtime.js').toExist(); harness.expectFile('dist/main.js').toExist(); - harness.expectFile('dist/vendor.js').toNotExist(); harness.expectFile('dist/index.html').toExist(); harness.expectFile('dist/main.js').content.toContain(`console.log('main')`); @@ -61,7 +59,6 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.expectFile('dist/runtime.js').toNotExist(); harness.expectFile('dist/main.js').toNotExist(); - harness.expectFile('dist/vendor.js').toNotExist(); harness.expectFile('dist/index.html').toNotExist(); }); }); diff --git a/packages/angular_devkit/build_angular/src/browser/tests/options/named-chunks_spec.ts b/packages/angular_devkit/build_angular/src/browser/tests/options/named-chunks_spec.ts index 85fcb4796240..1bace45fcfb1 100644 --- a/packages/angular_devkit/build_angular/src/browser/tests/options/named-chunks_spec.ts +++ b/packages/angular_devkit/build_angular/src/browser/tests/options/named-chunks_spec.ts @@ -62,8 +62,8 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { expect(result?.success).toBe(true); harness.expectFile(MAIN_OUTPUT).toExist(); - harness.expectFile(NAMED_LAZY_OUTPUT).toExist(); - harness.expectFile(UNNAMED_LAZY_OUTPUT).toNotExist(); + harness.expectFile(NAMED_LAZY_OUTPUT).toNotExist(); + harness.expectFile(UNNAMED_LAZY_OUTPUT).toExist(); }); }); }); diff --git a/packages/angular_devkit/build_angular/src/browser/tests/options/output-hashing_spec.ts b/packages/angular_devkit/build_angular/src/browser/tests/options/output-hashing_spec.ts index 6f046fcede67..e43d462f7c00 100644 --- a/packages/angular_devkit/build_angular/src/browser/tests/options/output-hashing_spec.ts +++ b/packages/angular_devkit/build_angular/src/browser/tests/options/output-hashing_spec.ts @@ -16,6 +16,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { beforeEach(async () => { // Application code is not needed for asset tests await harness.writeFile('src/main.ts', ''); + await harness.writeFile('src/polyfills.ts', ''); }); it('hashes all filenames when set to "all"', async () => { @@ -27,6 +28,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, styles: ['src/styles.css'], + polyfills: 'src/polyfills.ts', outputHashing: OutputHashing.All, }); @@ -48,6 +50,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, + polyfills: 'src/polyfills.ts', styles: ['src/styles.css'], }); @@ -70,6 +73,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, styles: ['src/styles.css'], + polyfills: 'src/polyfills.ts', outputHashing: OutputHashing.None, }); @@ -92,6 +96,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, styles: ['src/styles.css'], + polyfills: 'src/polyfills.ts', outputHashing: OutputHashing.Media, }); @@ -114,6 +119,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, styles: ['src/styles.css'], + polyfills: 'src/polyfills.ts', outputHashing: OutputHashing.Bundles, }); @@ -131,6 +137,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.useTarget('build', { ...BASE_OPTIONS, outputHashing: OutputHashing.All, + sourceMap: true, styles: [{ input: 'src/styles.css', inject: false, diff --git a/packages/angular_devkit/build_angular/src/browser/tests/setup.ts b/packages/angular_devkit/build_angular/src/browser/tests/setup.ts index 6c44764e064f..0dcece4f7734 100644 --- a/packages/angular_devkit/build_angular/src/browser/tests/setup.ts +++ b/packages/angular_devkit/build_angular/src/browser/tests/setup.ts @@ -24,4 +24,8 @@ export const BASE_OPTIONS = Object.freeze({ outputPath: 'dist', tsConfig: 'src/tsconfig.app.json', progress: false, + + // Disable optimizations + optimization: false, + buildOptimizer: false, }); diff --git a/packages/angular_devkit/build_angular/src/dev-server/tests/setup.ts b/packages/angular_devkit/build_angular/src/dev-server/tests/setup.ts index cc864a9922a6..c1d513833191 100644 --- a/packages/angular_devkit/build_angular/src/dev-server/tests/setup.ts +++ b/packages/angular_devkit/build_angular/src/dev-server/tests/setup.ts @@ -24,7 +24,7 @@ export const DEV_SERVER_BUILDER_INFO = Object.freeze({ }); /** - * Contains all required extract-i18n builder fields. + * Contains all required dev-server builder fields. * The port is also set to zero to ensure a free port is used for each test which * supports parallel test execution. */ diff --git a/packages/angular_devkit/build_angular/src/extract-i18n/index.ts b/packages/angular_devkit/build_angular/src/extract-i18n/index.ts index bb7fdbdb7587..f300335a30e6 100644 --- a/packages/angular_devkit/build_angular/src/extract-i18n/index.ts +++ b/packages/angular_devkit/build_angular/src/extract-i18n/index.ts @@ -17,7 +17,7 @@ import type { Diagnostics } from '@angular/localize/src/tools/src/diagnostics'; import * as fs from 'fs'; import * as path from 'path'; import * as webpack from 'webpack'; -import { Schema as BrowserBuilderOptions } from '../browser/schema'; +import { OutputHashing, Schema as BrowserBuilderOptions } from '../browser/schema'; import { ExecutionTransformer } from '../transforms'; import { createI18nOptions } from '../utils/i18n-options'; import { assertCompatibleAngularVersion } from '../utils/version'; @@ -177,12 +177,15 @@ export async function execute( buildOptimizer: false, aot: true, progress: options.progress, + budgets: [], assets: [], scripts: [], styles: [], deleteOutputPath: false, extractLicenses: false, subresourceIntegrity: false, + outputHashing: OutputHashing.None, + namedChunks: true, }, context, (wco) => { diff --git a/packages/angular_devkit/build_angular/src/karma/index.ts b/packages/angular_devkit/build_angular/src/karma/index.ts index 542a8d435870..a2f7c557e3a7 100644 --- a/packages/angular_devkit/build_angular/src/karma/index.ts +++ b/packages/angular_devkit/build_angular/src/karma/index.ts @@ -12,7 +12,7 @@ import { dirname, resolve } from 'path'; import { Observable, from } from 'rxjs'; import { defaultIfEmpty, switchMap } from 'rxjs/operators'; import * as webpack from 'webpack'; -import { Schema as BrowserBuilderOptions } from '../browser/schema'; +import { OutputHashing, Schema as BrowserBuilderOptions } from '../browser/schema'; import { ExecutionTransformer } from '../transforms'; import { assertCompatibleAngularVersion } from '../utils/version'; import { generateBrowserWebpackConfigFromContext } from '../utils/webpack-browser-config'; @@ -41,7 +41,18 @@ async function initialize( // only two properties are missing: // * `outputPath` which is fixed for tests // * `budgets` which might be incorrect due to extra dev libs - { ...((options as unknown) as BrowserBuilderOptions), outputPath: '', budgets: undefined }, + { + ...((options as unknown) as BrowserBuilderOptions), + outputPath: '', + budgets: undefined, + optimization: false, + buildOptimizer: false, + aot: false, + vendorChunk: true, + namedChunks: true, + extractLicenses: false, + outputHashing: OutputHashing.None, + }, context, wco => [ getCommonConfig(wco), diff --git a/packages/angular_devkit/build_angular/src/server/schema.json b/packages/angular_devkit/build_angular/src/server/schema.json index aa4c01521026..4a9b7b6f9d11 100644 --- a/packages/angular_devkit/build_angular/src/server/schema.json +++ b/packages/angular_devkit/build_angular/src/server/schema.json @@ -42,7 +42,7 @@ "optimization": { "description": "Enables optimization of the build output. Including minification of scripts and styles, tree-shaking and dead-code elimination. For more information, see https://angular.io/guide/workspace-config#optimization-configuration.", "x-user-analytics": 16, - "default": false, + "default": true, "oneOf": [ { "type": "object", @@ -84,7 +84,7 @@ }, "sourceMap": { "description": "Output source maps for scripts and styles. For more information, see https://angular.io/guide/workspace-config#source-map-configuration.", - "default": true, + "default": false, "oneOf": [ { "type": "object", @@ -189,7 +189,7 @@ "namedChunks": { "type": "boolean", "description": "Use file name for lazy loaded chunks.", - "default": true + "default": false }, "bundleDependencies": { "description": "Which external dependencies to bundle into the bundle. By default, all of node_modules will be bundled.", diff --git a/packages/angular_devkit/build_angular/src/utils/normalize-optimization.ts b/packages/angular_devkit/build_angular/src/utils/normalize-optimization.ts index 6946a3039f1a..8f72b05b1c9c 100644 --- a/packages/angular_devkit/build_angular/src/utils/normalize-optimization.ts +++ b/packages/angular_devkit/build_angular/src/utils/normalize-optimization.ts @@ -13,7 +13,7 @@ export type NormalizedOptimizationOptions = Required; + +const BrowserBuilderOptions: BuilderOptionsType = [ + ['aot', false, true], + ['vendorChunk', true, false], + ['extractLicenses', true, false], + ['buildOptimizer', false, true], + ['sourceMap', true, false], + ['optimization', false, true], + ['namedChunks', false, true], +]; + +const ServerBuilderOptions: BuilderOptionsType = [ + ['sourceMap', true, false], + ['optimization', false, true], +]; + export default function (): Rule { return updateWorkspace(workspace => { for (const [, target] of allWorkspaceTargets(workspace)) { - if (!target.builder.startsWith('@angular-devkit/build-angular')) { + if (!target?.builder.startsWith('@angular-devkit/build-angular')) { continue; } + // Only interested in Angular Devkit browser and server builders + switch (target.builder) { + case '@angular-devkit/build-angular:server': + updateOptions(target, ServerBuilderOptions); + break; + case '@angular-devkit/build-angular:browser': + updateOptions(target, BrowserBuilderOptions); + break; + } + for (const [, options] of allTargetOptions(target)) { delete options.experimentalRollupPass; delete options.lazyModules; @@ -23,3 +51,29 @@ export default function (): Rule { } }); } + +function updateOptions( + target: workspaces.TargetDefinition, optionsToUpdate: typeof ServerBuilderOptions | typeof BrowserBuilderOptions, +): void { + if (!target.options) { + target.options = {}; + } + + const configurationOptions = target.configurations && Object.values(target.configurations); + + for (const [optionName, oldDefault, newDefault] of optionsToUpdate) { + let value = target.options[optionName]; + if (value === newDefault) { + // Value is same as new default + delete target.options[optionName]; + } else if (value === undefined) { + // Value is not defined, hence the default in the builder was used. + target.options[optionName] = oldDefault; + value = oldDefault; + } + + // Remove overrides in configurations which are no longer needed. + configurationOptions?.filter(o => o && o[optionName] === value) + .forEach(o => o && delete o[optionName]); + } +} diff --git a/packages/schematics/angular/migrations/update-12/update-angular-config_spec.ts b/packages/schematics/angular/migrations/update-12/update-angular-config_spec.ts index b3cc6f6c3374..3f35fddd6c9e 100644 --- a/packages/schematics/angular/migrations/update-12/update-angular-config_spec.ts +++ b/packages/schematics/angular/migrations/update-12/update-angular-config_spec.ts @@ -27,33 +27,20 @@ function createWorkSpaceConfig(tree: UnitTestTree) { build: { builder: Builders.Browser, options: { - scripts: [ - { lazy: true, name: 'bundle-1.js' }, - ], + aot: true, + optimization: true, experimentalRollupPass: false, - sourceMaps: true, buildOptimizer: false, // tslint:disable-next-line:no-any } as any, configurations: { one: { aot: true, - scripts: [ - { lazy: true, name: 'bundle-1.js' }, - { lazy: false, name: 'bundle-2.js' }, - { inject: true, name: 'bundle-3.js' }, - 'bundle-4.js', - ], - styles: [ - { lazy: true, name: 'bundle-1.css' }, - { lazy: false, name: 'bundle-2.css' }, - { inject: true, name: 'bundle-3.css' }, - 'bundle-3.css', - ], }, two: { experimentalRollupPass: true, - aot: true, + aot: false, + optimization: false, }, // tslint:disable-next-line:no-any } as any, @@ -85,9 +72,37 @@ describe(`Migration to update 'angular.json'. ${schematicName}`, () => { const { options, configurations } = getBuildTarget(newTree); expect(options.experimentalRollupPass).toBeUndefined(); - expect(options.sourceMaps).toBeTrue(); + expect(options.buildOptimizer).toBeFalse(); expect(configurations).toBeDefined(); expect(configurations?.one.experimentalRollupPass).toBeUndefined(); expect(configurations?.two.experimentalRollupPass).toBeUndefined(); }); + + it(`should remove value from "options" section which value is now the new default`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { options, configurations } = getBuildTarget(newTree); + + expect(options.aot).toBeUndefined(); + expect(configurations?.one.aot).toBeUndefined(); + expect(configurations?.two.aot).toBeFalse(); + }); + + it(`should remove value from "configuration" section when value is the same as that of "options"`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { options, configurations } = getBuildTarget(newTree); + + expect(options.aot).toBeUndefined(); + expect(configurations?.one.aot).toBeUndefined(); + expect(configurations?.two.aot).toBeFalse(); + }); + + it(`should add value in "options" section when option was not defined`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { options, configurations } = getBuildTarget(newTree); + + expect(options.sourceMap).toBeTrue(); + expect(configurations?.one.sourceMap).toBeUndefined(); + expect(configurations?.two.sourceMap).toBeUndefined(); + expect(configurations?.two.optimization).toBeFalse(); + }); });