From f5d019f9d6ad6d8fdea37836564d9ee190deb23c Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Thu, 5 Aug 2021 10:28:20 -0400 Subject: [PATCH] fix(@angular-devkit/build-angular): avoid attempting to optimize copied JavaScript assets When in watch mode (`ng serve`/`ng build --watch`), Webpack's `copy-webpack-plugin` is currently used to implement the project `assets` option within `angular.json`. Files specified by the `assets` option are intended to be copied to the output unmodified and in their original form. However, if any JavaScript assets were present they previously would have been unintentionally subject to their respective optimization plugins which would result in modified asset outputs and the potential for breakage of the assets. `ng build` outside of watch mode uses a direct file copy with copy-on-write (where supported) to process assets and is therefore not affected by this situation. When the `copy-webpack-plugin` is used, it adds a `copied` flag to an asset's info metadata. This flag is now used to exclude all such copied JavaScript assets from optimization. --- .../tests/behavior/build-assets_spec.ts | 41 +++++++++++++++ .../plugins/javascript-optimizer-plugin.ts | 52 ++++++++++--------- 2 files changed, 68 insertions(+), 25 deletions(-) create mode 100644 packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/build-assets_spec.ts diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/build-assets_spec.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/build-assets_spec.ts new file mode 100644 index 000000000000..48559d704967 --- /dev/null +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/build-assets_spec.ts @@ -0,0 +1,41 @@ +/** + * @license + * Copyright Google LLC 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 { serveWebpackBrowser } from '../../index'; +import { executeOnceAndFetch } from '../execute-fetch'; +import { + BASE_OPTIONS, + DEV_SERVER_BUILDER_INFO, + describeBuilder, + setupBrowserTarget, +} from '../setup'; + +describeBuilder(serveWebpackBrowser, DEV_SERVER_BUILDER_INFO, (harness) => { + describe('Behavior: "browser builder assets"', () => { + it('serves a project JavaScript asset unmodified', async () => { + const javascriptFileContent = '/* a comment */const foo = `bar`;\n\n\n'; + await harness.writeFile('src/extra.js', javascriptFileContent); + + setupBrowserTarget(harness, { + assets: ['src/extra.js'], + optimization: { + scripts: true, + }, + }); + + harness.useTarget('serve', { + ...BASE_OPTIONS, + }); + + const { result, response } = await executeOnceAndFetch(harness, 'extra.js'); + + expect(result?.success).toBeTrue(); + expect(await response?.text()).toBe(javascriptFileContent); + }); + }); +}); diff --git a/packages/angular_devkit/build_angular/src/webpack/plugins/javascript-optimizer-plugin.ts b/packages/angular_devkit/build_angular/src/webpack/plugins/javascript-optimizer-plugin.ts index c861dd6636e1..0e7c4ba4ef59 100644 --- a/packages/angular_devkit/build_angular/src/webpack/plugins/javascript-optimizer-plugin.ts +++ b/packages/angular_devkit/build_angular/src/webpack/plugins/javascript-optimizer-plugin.ts @@ -96,34 +96,36 @@ export class JavaScriptOptimizerPlugin { } const scriptAsset = compilation.getAsset(assetName); + // Skip assets that have already been optimized or are verbatim copies (project assets) + if (!scriptAsset || scriptAsset.info.minimized || scriptAsset.info.copied) { + continue; + } - if (scriptAsset && !scriptAsset.info.minimized) { - const { source: scriptAssetSource, name } = scriptAsset; - let cacheItem; - - if (cache) { - const eTag = cache.getLazyHashedEtag(scriptAssetSource); - cacheItem = cache.getItemCache(name, eTag); - const cachedOutput = await cacheItem.getPromise< - { source: sources.Source } | undefined - >(); - - if (cachedOutput) { - compilation.updateAsset(name, cachedOutput.source, { - minimized: true, - }); - continue; - } + const { source: scriptAssetSource, name } = scriptAsset; + let cacheItem; + + if (cache) { + const eTag = cache.getLazyHashedEtag(scriptAssetSource); + cacheItem = cache.getItemCache(name, eTag); + const cachedOutput = await cacheItem.getPromise< + { source: sources.Source } | undefined + >(); + + if (cachedOutput) { + compilation.updateAsset(name, cachedOutput.source, { + minimized: true, + }); + continue; } - - const { source, map } = scriptAssetSource.sourceAndMap(); - scriptsToOptimize.push({ - name: scriptAsset.name, - code: typeof source === 'string' ? source : source.toString(), - map, - cacheItem, - }); } + + const { source, map } = scriptAssetSource.sourceAndMap(); + scriptsToOptimize.push({ + name: scriptAsset.name, + code: typeof source === 'string' ? source : source.toString(), + map, + cacheItem, + }); } if (scriptsToOptimize.length === 0) {