From 80d352d9de3eb098732632bc38887ca5c6d271af Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 9 Feb 2021 08:40:04 +0100 Subject: [PATCH 1/3] refactor(@angular-devkit/build-angular): remove experimental rollup pass The experimental rollup pass has significant issues with the new Ivy webpack plugin. It also, didn't produce as well as we hoped in real world scenarios infact in many cases this caused build to fail. REAKING CHANGE: The experimental rollup pass `--experimental-rollup-pass` option has been removed. Closes #15836 --- package.json | 1 - packages/angular/cli/lib/config/schema.json | 5 - .../angular_devkit/build_angular/BUILD.bazel | 1 - .../angular_devkit/build_angular/package.json | 1 - .../src/browser/specs/rollup_spec.ts | 102 ------------ .../build_angular/src/utils/build-options.ts | 1 - .../src/utils/webpack-browser-config.ts | 12 -- .../src/webpack/configs/common.ts | 48 +----- .../src/webpack/configs/typescript.ts | 5 - .../src/webpack/plugins/index.ts | 3 - .../webpack/plugins/webpack-rollup-loader.ts | 148 ------------------ tests/legacy-cli/e2e/tests/build/rollup.ts | 68 -------- yarn.lock | 2 +- 13 files changed, 2 insertions(+), 395 deletions(-) delete mode 100644 packages/angular_devkit/build_angular/src/browser/specs/rollup_spec.ts delete mode 100644 packages/angular_devkit/build_angular/src/webpack/plugins/webpack-rollup-loader.ts delete mode 100644 tests/legacy-cli/e2e/tests/build/rollup.ts diff --git a/package.json b/package.json index 5bf980eadb8d..70717ed14202 100644 --- a/package.json +++ b/package.json @@ -201,7 +201,6 @@ "regenerator-runtime": "0.13.7", "resolve-url-loader": "3.1.2", "rimraf": "3.0.2", - "rollup": "2.38.5", "rxjs": "6.6.3", "sass": "1.32.6", "sass-loader": "10.1.1", diff --git a/packages/angular/cli/lib/config/schema.json b/packages/angular/cli/lib/config/schema.json index 14de20de7617..45f14b1bf82b 100644 --- a/packages/angular/cli/lib/config/schema.json +++ b/packages/angular/cli/lib/config/schema.json @@ -1018,11 +1018,6 @@ "use-credentials" ] }, - "experimentalRollupPass": { - "type": "boolean", - "description": "Concatenate modules with Rollup before bundling them with Webpack.", - "default": false - }, "allowedCommonJsDependencies": { "description": "A list of CommonJS packages that are allowed to be used without a build time warning.", "type": "array", diff --git a/packages/angular_devkit/build_angular/BUILD.bazel b/packages/angular_devkit/build_angular/BUILD.bazel index 85c68bd54f83..f9490abcc809 100644 --- a/packages/angular_devkit/build_angular/BUILD.bazel +++ b/packages/angular_devkit/build_angular/BUILD.bazel @@ -175,7 +175,6 @@ ts_library( "@npm//regenerator-runtime", "@npm//resolve-url-loader", "@npm//rimraf", - "@npm//rollup", "@npm//rxjs", "@npm//sass", "@npm//sass-loader", diff --git a/packages/angular_devkit/build_angular/package.json b/packages/angular_devkit/build_angular/package.json index 0be5ee0e1a08..b86d18eac834 100644 --- a/packages/angular_devkit/build_angular/package.json +++ b/packages/angular_devkit/build_angular/package.json @@ -55,7 +55,6 @@ "regenerator-runtime": "0.13.7", "resolve-url-loader": "3.1.2", "rimraf": "3.0.2", - "rollup": "2.38.5", "rxjs": "6.6.3", "sass": "1.32.6", "sass-loader": "10.1.1", diff --git a/packages/angular_devkit/build_angular/src/browser/specs/rollup_spec.ts b/packages/angular_devkit/build_angular/src/browser/specs/rollup_spec.ts deleted file mode 100644 index 127e406cea24..000000000000 --- a/packages/angular_devkit/build_angular/src/browser/specs/rollup_spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @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 { Architect } from '@angular-devkit/architect'; -import { - BrowserBuildOutput, - browserBuild, - createArchitect, - host, - lazyModuleFiles, - lazyModuleFnImport, -} from '../../test-utils'; - - -describe('Browser Builder Rollup Concatenation test', () => { - const target = { project: 'app', target: 'build' }; - const overrides = { - experimentalRollupPass: true, - // JIT Rollup bundles will include require calls to .css and .html file, that have lost their - // path context. AOT code already inlines resources so that's not a problem. - aot: true, - // Webpack can't separate rolled-up modules into chunks. - vendorChunk: false, - commonChunk: false, - namedChunks: false, - }; - const prodOverrides = { - // Usual prod options. - fileReplacements: [{ - replace: 'src/environments/environment.ts', - with: 'src/environments/environment.prod.ts', - }], - optimization: true, - sourceMap: false, - extractCss: true, - namedChunks: false, - aot: true, - extractLicenses: true, - vendorChunk: false, - buildOptimizer: true, - // Extra prod options we need for experimentalRollupPass. - commonChunk: false, - // Just for convenience. - outputHashing: 'none', - }; - const rollupProdOverrides = { - ...prodOverrides, - experimentalRollupPass: true, - }; - let architect: Architect; - - const getOutputSize = async (output: BrowserBuildOutput) => - (await Promise.all( - Object.keys(output.files) - .filter(name => name.endsWith('.js') && - // These aren't concatenated by Rollup so no point comparing. - !['runtime.js', 'polyfills.js'].includes(name)) - .map(name => output.files[name]), - )) - .map(content => content.length) - .reduce((acc, curr) => acc + curr, 0); - - beforeEach(async () => { - await host.initialize().toPromise(); - architect = (await createArchitect(host.root())).architect; - }); - - afterEach(async () => host.restore().toPromise()); - - it('works', async () => { - await browserBuild(architect, host, target, overrides); - }); - - it('works with lazy modules', async () => { - host.writeMultipleFiles(lazyModuleFiles); - host.writeMultipleFiles(lazyModuleFnImport); - await browserBuild(architect, host, target, overrides); - }); - - xit('creates smaller or same size bundles for app without lazy bundles', async () => { - const prodOutput = await browserBuild(architect, host, target, prodOverrides); - const prodSize = await getOutputSize(prodOutput); - const rollupProdOutput = await browserBuild(architect, host, target, rollupProdOverrides); - const rollupProd = await getOutputSize(rollupProdOutput); - expect(prodSize).toBeGreaterThan(rollupProd); - }); - - xit('creates smaller bundles for apps with lazy bundles', async () => { - host.writeMultipleFiles(lazyModuleFiles); - host.writeMultipleFiles(lazyModuleFnImport); - const prodOutput = await browserBuild(architect, host, target, prodOverrides); - const prodSize = await getOutputSize(prodOutput); - const rollupProdOutput = await browserBuild(architect, host, target, rollupProdOverrides); - const rollupProd = await getOutputSize(rollupProdOutput); - expect(prodSize).toBeGreaterThan(rollupProd); - }); -}); diff --git a/packages/angular_devkit/build_angular/src/utils/build-options.ts b/packages/angular_devkit/build_angular/src/utils/build-options.ts index 213dd5a80396..70834f6bcfbe 100644 --- a/packages/angular_devkit/build_angular/src/utils/build-options.ts +++ b/packages/angular_devkit/build_angular/src/utils/build-options.ts @@ -76,7 +76,6 @@ export interface BuildOptions { platform?: 'browser' | 'server'; fileReplacements: NormalizedFileReplacement[]; - experimentalRollupPass?: boolean; allowedCommonJsDependencies?: string[]; differentialLoadingNeeded?: boolean; diff --git a/packages/angular_devkit/build_angular/src/utils/webpack-browser-config.ts b/packages/angular_devkit/build_angular/src/utils/webpack-browser-config.ts index 92654c02c15c..656582b45fdf 100644 --- a/packages/angular_devkit/build_angular/src/utils/webpack-browser-config.ts +++ b/packages/angular_devkit/build_angular/src/utils/webpack-browser-config.ts @@ -44,18 +44,6 @@ export async function generateWebpackConfig( throw new Error(`The 'buildOptimizer' option cannot be used without 'aot'.`); } - // Ensure Rollup Concatenation is only used with compatible options. - if (options.experimentalRollupPass) { - if (!options.aot) { - throw new Error(`The 'experimentalRollupPass' option cannot be used without 'aot'.`); - } - - if (options.vendorChunk || options.commonChunk || options.namedChunks) { - throw new Error(`The 'experimentalRollupPass' option cannot be used with the` - + `'vendorChunk', 'commonChunk', 'namedChunks' options set to true.`); - } - } - const tsConfigPath = path.resolve(workspaceRoot, options.tsConfig); const tsConfig = readTsconfig(tsConfigPath); diff --git a/packages/angular_devkit/build_angular/src/webpack/configs/common.ts b/packages/angular_devkit/build_angular/src/webpack/configs/common.ts index d46b6eb2d931..b5c2ff8177bf 100644 --- a/packages/angular_devkit/build_angular/src/webpack/configs/common.ts +++ b/packages/angular_devkit/build_angular/src/webpack/configs/common.ts @@ -12,7 +12,6 @@ import { import * as CopyWebpackPlugin from 'copy-webpack-plugin'; import { existsSync } from 'fs'; import * as path from 'path'; -import { RollupOptions } from 'rollup'; import { ScriptTarget } from 'typescript'; import { Compiler, @@ -43,7 +42,6 @@ import { NamedLazyChunksPlugin, OptimizeCssWebpackPlugin, ScriptsWebpackPlugin, - WebpackRollupLoader, } from '../plugins'; import { getEsVersionForFileName, getOutputHashFormat, getWatchOptions, normalizeExtraEntryPoints } from '../utils/helpers'; import { IGNORE_WARNINGS } from '../utils/stats'; @@ -82,49 +80,6 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (buildOptions.main) { const mainPath = path.resolve(root, buildOptions.main); entryPoints['main'] = [mainPath]; - - if (buildOptions.experimentalRollupPass) { - // NOTE: the following are known problems with experimentalRollupPass - // - vendorChunk, commonChunk, namedChunks: these won't work, because by the time webpack - // sees the chunks, the context of where they came from is lost. - // - webWorkerTsConfig: workers must be imported via a root relative path (e.g. - // `app/search/search.worker`) instead of a relative path (`/search.worker`) because - // of the same reason as above. - // - loadChildren string syntax: doesn't work because rollup cannot follow the imports. - - // Rollup options, except entry module, which is automatically inferred. - const rollupOptions: RollupOptions = {}; - - // Add rollup plugins/rules. - extraRules.push({ - test: mainPath, - // Ensure rollup loader executes after other loaders. - enforce: 'post', - use: [{ - loader: WebpackRollupLoader, - options: rollupOptions, - }], - }); - - // Rollup bundles will include the dynamic System.import that was inside Angular and webpack - // will emit warnings because it can't resolve it. We just ignore it. - // TODO: maybe use https://webpack.js.org/configuration/stats/#statswarningsfilter instead. - - // Ignore all "Critical dependency: the request of a dependency is an expression" warnings. - extraPlugins.push(new ContextReplacementPlugin(/./)); - // Ignore "System.import() is deprecated" warnings for the main file and js files. - // Might still get them if @angular/core gets split into a lazy module. - extraRules.push({ - test: mainPath, - enforce: 'post', - parser: { system: true }, - }); - extraRules.push({ - test: /\.js$/, - enforce: 'post', - parser: { system: true }, - }); - } } const differentialLoadingMode = buildOptions.differentialLoadingNeeded && !buildOptions.watch; @@ -408,9 +363,8 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { output: { ecma: terserEcma, // For differential loading, this is handled in the bundle processing. - // This should also work with just true but the experimental rollup support breaks without this check. ascii_only: !differentialLoadingMode, - // default behavior (undefined value) is to keep only important comments (licenses, etc.) + // Default behavior (undefined value) is to keep only important comments (licenses, etc.) comments: !buildOptions.extractLicenses && undefined, webkit: true, beautify: shouldBeautify, diff --git a/packages/angular_devkit/build_angular/src/webpack/configs/typescript.ts b/packages/angular_devkit/build_angular/src/webpack/configs/typescript.ts index 74578d669edf..dc612a2bd76a 100644 --- a/packages/angular_devkit/build_angular/src/webpack/configs/typescript.ts +++ b/packages/angular_devkit/build_angular/src/webpack/configs/typescript.ts @@ -42,11 +42,6 @@ function canUseIvyPlugin(wco: WebpackConfigOptions): boolean { return false; } - // This pass relies on internals of the original plugin - if (wco.buildOptions.experimentalRollupPass) { - return false; - } - return true; } diff --git a/packages/angular_devkit/build_angular/src/webpack/plugins/index.ts b/packages/angular_devkit/build_angular/src/webpack/plugins/index.ts index c8cbcc181ee5..55b91ea66cf7 100644 --- a/packages/angular_devkit/build_angular/src/webpack/plugins/index.ts +++ b/packages/angular_devkit/build_angular/src/webpack/plugins/index.ts @@ -19,6 +19,3 @@ export { default as PostcssCliResources, PostcssCliResourcesOptions, } from './postcss-cli-resources'; - -import { join } from 'path'; -export const WebpackRollupLoader = require.resolve(join(__dirname, 'webpack-rollup-loader')); diff --git a/packages/angular_devkit/build_angular/src/webpack/plugins/webpack-rollup-loader.ts b/packages/angular_devkit/build_angular/src/webpack/plugins/webpack-rollup-loader.ts deleted file mode 100644 index 2253f1ccfb28..000000000000 --- a/packages/angular_devkit/build_angular/src/webpack/plugins/webpack-rollup-loader.ts +++ /dev/null @@ -1,148 +0,0 @@ -/** - * @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 - */ - -/** - * @license - * @author Erik Desjardins - * Forked as of SHA 10fb020f997a146725963b202d79290c8798a7a0 from https://github.com/erikdesjardins/webpack-rollup-loader. - * Licensed under a MIT license. - * See https://github.com/erikdesjardins/webpack-rollup-loader/blob/10fb020f997a146725963b202d79290c8798a7a0/LICENSE for full license. - */ - -import { VirtualFileSystemDecorator } from '@ngtools/webpack'; -import { dirname, join } from 'path'; -import { OutputAsset, OutputChunk, rollup } from 'rollup'; -import { RawSourceMap } from 'source-map'; -import webpack = require('webpack'); - -function splitRequest(request: string) { - const inx = request.lastIndexOf('!'); - if (inx === -1) { - return { - loaders: '', - resource: request, - }; - } else { - return { - loaders: request.slice(0, inx + 1), - resource: request.slice(inx + 1), - }; - } -} - -// Load resolve paths using Webpack. -function webpackResolutionPlugin( - loaderContext: webpack.loader.LoaderContext, - entryId: string, - entryIdCodeAndMap: { code: string, map: RawSourceMap }, -) { - return { - name: 'webpack-resolution-plugin', - resolveId: (id: string, importerId: string) => { - if (id === entryId) { - return entryId; - } else { - return new Promise((resolve, reject) => { - // split apart resource paths because Webpack's this.resolve() can't handle `loader!` - // prefixes - const parts = splitRequest(id); - const importerParts = splitRequest(importerId); - - // resolve the full path of the imported file with Webpack's module loader - // this will figure out node_modules imports, Webpack aliases, etc. - loaderContext.resolve( - dirname(importerParts.resource), - parts.resource, - (err, fullPath) => err ? reject(err) : resolve(parts.loaders + fullPath), - ); - }); - } - }, - load: (id: string) => { - if (id === entryId) { - return entryIdCodeAndMap; - } - - return new Promise((resolve, reject) => { - // load the module with Webpack - // this will apply all relevant loaders, etc. - loaderContext.loadModule( - id, - (err, source, map) => err ? reject(err) : resolve({ code: source, map: map }), - ); - }); - }, - }; -} - -export default function webpackRollupLoader( - this: webpack.loader.LoaderContext, - source: string, - sourceMap: RawSourceMap, -) { - // Note: this loader isn't cacheable because it will add the lazy chunks to the - // virtual file system on completion. - const callback = this.async(); - if (!callback) { - throw new Error('Async loader support is required.'); - } - const options = this.query || {}; - const entryId = this.resourcePath; - const sourcemap = this.sourceMap; - - // Get the VirtualFileSystemDecorator that AngularCompilerPlugin added so we can write to it. - // Since we use webpackRollupLoader as a post loader, this should be there. - // TODO: we should be able to do this in a more elegant way by again decorating webpacks - // input file system inside a custom WebpackRollupPlugin, modelled after AngularCompilerPlugin. - const vfs = this._compiler.inputFileSystem as VirtualFileSystemDecorator; - const virtualWrite = (path: string, data: string) => - vfs.getWebpackCompilerHost().writeFile(path, data, false); - - // Bundle with Rollup - const rollupOptions = { - ...options, - input: entryId, - plugins: [ - ...(options.plugins || []), - webpackResolutionPlugin(this, entryId, { code: source, map: sourceMap }), - ], - }; - - rollup(rollupOptions) - .then(build => build.generate({ format: 'es', sourcemap })) - .then( - (result) => { - const [mainChunk, ...otherChunksOrAssets] = result.output; - - // Write other chunks and assets to the virtual file system so that webpack can load them. - const resultDir = dirname(entryId); - otherChunksOrAssets.forEach(chunkOrAsset => { - const { fileName, type } = chunkOrAsset; - if (type == 'chunk') { - const { code, map } = chunkOrAsset as OutputChunk; - virtualWrite(join(resultDir, fileName), code); - if (map) { - // Also write the map if there's one. - // Probably need scriptsSourceMap set on CLI to load it. - virtualWrite(join(resultDir, `${fileName}.map`), map.toString()); - } - } else if (type == 'asset') { - const { source } = chunkOrAsset as OutputAsset; - // Source might be a Buffer. Just assuming it's a string for now. - virtualWrite(join(resultDir, fileName), source as string); - } - }); - - // Always return the main chunk from webpackRollupLoader. - // Cast to any here is needed because of a typings incompatibility between source-map versions. - // tslint:disable-next-line:no-any - callback(null, mainChunk.code, (mainChunk as any).map); - }, - (err) => callback(err), - ); -} diff --git a/tests/legacy-cli/e2e/tests/build/rollup.ts b/tests/legacy-cli/e2e/tests/build/rollup.ts deleted file mode 100644 index 8b480092b64a..000000000000 --- a/tests/legacy-cli/e2e/tests/build/rollup.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @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 { appendToFile, prependToFile, replaceInFile, writeFile } from '../../utils/fs'; -import { ng } from '../../utils/process'; -import { updateJsonFile } from '../../utils/project'; - -export default async function () { - // Add initial app routing. - const appRoutingModulePath = 'src/app/app-routing.module.ts'; - await writeFile(appRoutingModulePath, ` - import { NgModule } from '@angular/core'; - import { Routes, RouterModule } from '@angular/router'; - const routes: Routes = []; - @NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] - }) - export class AppRoutingModule { } - `); - await prependToFile('src/app/app.module.ts', - `import { AppRoutingModule } from './app-routing.module';`); - await replaceInFile('src/app/app.module.ts', `imports: [`, `imports: [ AppRoutingModule,`); - await appendToFile('src/app/app.component.html', ''); - - // Add a lazy route. - await ng('generate', 'module', 'lazy', '--route=lazy', '--module=app.module'); - - // Add lazy route e2e - await writeFile('e2e/src/app.e2e-spec.ts', ` - import { browser, logging, element, by } from 'protractor'; - - describe('workspace-project App', () => { - it('should display lazy route', async () => { - await browser.get(browser.baseUrl + '/lazy'); - expect(await element(by.css('app-lazy p')).getText()).toEqual('lazy works!'); - }); - - afterEach(async () => { - // Assert that there are no errors emitted from the browser - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - expect(logs).not.toContain(jasmine.objectContaining({ - level: logging.Level.SEVERE, - })); - }); - }); - `); - - // Set options needed for Rollup. - await updateJsonFile('angular.json', workspaceJson => { - const appArchitect = workspaceJson.projects['test-project'].architect; - const prodOptions = appArchitect.build.configurations.production; - prodOptions.vendorChunk = false; - prodOptions.commonChunk = false; - prodOptions.namedChunks = false; - prodOptions.experimentalRollupPass = true; - }); - - // Build for prod. - await ng('build', '--prod'); - - // E2E to make sure it's working. - await ng('e2e', '--prod'); -} diff --git a/yarn.lock b/yarn.lock index ef69a6844376..27fc6c472a32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10229,7 +10229,7 @@ rollup-plugin-sourcemaps@^0.6.3: "@rollup/pluginutils" "^3.0.9" source-map-resolve "^0.6.0" -rollup@2.38.5, rollup@^2.37.0: +rollup@^2.37.0: version "2.38.5" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.38.5.tgz#be41ad4fe0c103a8794377afceb5f22b8f603d6a" integrity sha512-VoWt8DysFGDVRGWuHTqZzT02J0ASgjVq/hPs9QcBOGMd7B+jfTr/iqMVEyOi901rE3xq+Deq66GzIT1yt7sGwQ== From cc148b622cd85d623b60cbc4f4a53db12bb7669f Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 9 Feb 2021 08:40:42 +0100 Subject: [PATCH 2/3] refactor(@ngtools/webpack): remove export of `VirtualFileSystemDecorator` type This was only needed for the rollup loader. --- packages/ngtools/webpack/src/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/ngtools/webpack/src/index.ts b/packages/ngtools/webpack/src/index.ts index 35de314a7db3..8b90d4eff002 100644 --- a/packages/ngtools/webpack/src/index.ts +++ b/packages/ngtools/webpack/src/index.ts @@ -12,7 +12,4 @@ export { ngcLoader as default } from './loader'; export const NgToolsLoader = __filename; -// We shouldn't need to export this, but webpack-rollup-loader uses it. -export type { VirtualFileSystemDecorator } from './virtual_file_system_decorator'; - export * as ivy from './ivy'; From a33d33c7c3167a96500598b9301755952c20b4ea Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 9 Feb 2021 08:50:54 +0100 Subject: [PATCH 3/3] feat(@schematics/angular): add migration to remove deprecated options from 'angular.json' --- .../migrations/migration-collection.json | 5 + .../update-12/update-angular-config.ts | 45 +++++++++ .../update-12/update-angular-config_spec.ts | 92 +++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 packages/schematics/angular/migrations/update-12/update-angular-config.ts create mode 100644 packages/schematics/angular/migrations/update-12/update-angular-config_spec.ts diff --git a/packages/schematics/angular/migrations/migration-collection.json b/packages/schematics/angular/migrations/migration-collection.json index 0710dc776f88..97a86e70f208 100644 --- a/packages/schematics/angular/migrations/migration-collection.json +++ b/packages/schematics/angular/migrations/migration-collection.json @@ -124,6 +124,11 @@ "version": "11.0.0", "factory": "./update-11/update-dependencies", "description": "Update workspace dependencies to match a new v11 project." + }, + "update-angular-config-v12": { + "version": "12.0.0-next.0", + "factory": "./update-12/update-angular-config", + "description": "Remove deprecated options from 'angular.json' that are no longer present in v12." } } } diff --git a/packages/schematics/angular/migrations/update-12/update-angular-config.ts b/packages/schematics/angular/migrations/update-12/update-angular-config.ts new file mode 100644 index 000000000000..b15ac2196d2b --- /dev/null +++ b/packages/schematics/angular/migrations/update-12/update-angular-config.ts @@ -0,0 +1,45 @@ +/** + * @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 { Rule } from '@angular-devkit/schematics'; +import { updateWorkspace } from '../../utility/workspace'; + +export default function (): Rule { + return updateWorkspace(workspace => { + const optionsToRemove: Record = { + experimentalRollupPass: undefined, + }; + + for (const [, project] of workspace.projects) { + for (const [, target] of project.targets) { + // Only interested in Angular Devkit builders + if (!target?.builder.startsWith('@angular-devkit/build-angular')) { + continue; + } + + // Check options + if (target.options) { + target.options = { + ...optionsToRemove, + }; + } + + // Go through each configuration entry + if (!target.configurations) { + continue; + } + + for (const configurationName of Object.keys(target.configurations)) { + target.configurations[configurationName] = { + ...optionsToRemove, + }; + } + } + } + }); +} 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 new file mode 100644 index 000000000000..96101c521936 --- /dev/null +++ b/packages/schematics/angular/migrations/update-12/update-angular-config_spec.ts @@ -0,0 +1,92 @@ +/** + * @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 { JsonObject } from '@angular-devkit/core'; +import { EmptyTree } from '@angular-devkit/schematics'; +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; +import { BuilderTarget, Builders, ProjectType, WorkspaceSchema } from '../../utility/workspace-models'; + +function getBuildTarget(tree: UnitTestTree): BuilderTarget { + return JSON.parse(tree.readContent('/angular.json')).projects.app.architect.build; +} + +function createWorkSpaceConfig(tree: UnitTestTree) { + const angularConfig: WorkspaceSchema = { + version: 1, + projects: { + app: { + root: '', + sourceRoot: 'src', + projectType: ProjectType.Application, + prefix: 'app', + architect: { + build: { + builder: Builders.Browser, + options: { + scripts: [ + { lazy: true, name: 'bundle-1.js' }, + ], + 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, + }, + // tslint:disable-next-line:no-any + } as any, + }, + }, + }, + }, + }; + + tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2)); +} + +const schematicName = 'update-angular-config-v12'; + +describe(`Migration to update 'angular.json'. ${schematicName}`, () => { + const schematicRunner = new SchematicTestRunner( + 'migrations', + require.resolve('../migration-collection.json'), + ); + + let tree: UnitTestTree; + beforeEach(() => { + tree = new UnitTestTree(new EmptyTree()); + createWorkSpaceConfig(tree); + }); + + it(`should remove 'experimentalRollupPass'`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { options, configurations } = getBuildTarget(newTree); + + expect(options.experimentalRollupPass).toBeUndefined(); + expect(configurations).toBeDefined(); + expect(configurations?.one.experimentalRollupPass).toBeUndefined(); + expect(configurations?.two.experimentalRollupPass).toBeUndefined(); + }); +});