diff --git a/packages/node/src/utils/config.ts b/packages/node/src/utils/config.ts index 13dfd069e5..8c9ba4e702 100644 --- a/packages/node/src/utils/config.ts +++ b/packages/node/src/utils/config.ts @@ -81,16 +81,14 @@ export function getBaseWebpackPartial( } if (options.extractLicenses) { - extraPlugins.push( - new LicenseWebpackPlugin({ - stats: { - warnings: false, - errors: false, - }, - perChunkOutput: false, - outputFilename: `3rdpartylicenses.txt`, - }) as unknown as webpack.Plugin - ); + extraPlugins.push((new LicenseWebpackPlugin({ + stats: { + warnings: false, + errors: false + }, + perChunkOutput: false, + outputFilename: `3rdpartylicenses.txt` + }) as unknown) as webpack.Plugin); } // process asset entries diff --git a/packages/web/package.json b/packages/web/package.json index 3a304d8631..becb1e97d5 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -71,12 +71,12 @@ "license-webpack-plugin": "^1.4.0", "loader-utils": "1.2.3", "mini-css-extract-plugin": "0.8.0", - "minimatch": "3.0.4", + "minimatch": "3.0.4", "parse5": "4.0.0", "open": "6.4.0", "postcss": "7.0.17", "postcss-import": "12.0.1", - "postcss-loader": "3.0.0", + "postcss-loader": "3.0.0", "raw-loader": "3.1.0", "rxjs": "6.4.0", "regenerator-runtime": "0.13.3", @@ -93,15 +93,15 @@ "sass-loader": "7.2.0", "semver": "6.3.0", "source-map": "0.7.3", - "source-map-loader": "0.2.4", + "source-map-loader": "0.2.4", "source-map-support": "0.5.12", "speed-measure-webpack-plugin": "1.3.1", "style-loader": "1.0.0", "stylus": "0.54.5", - "stylus-loader": "3.0.2", + "stylus-loader": "3.0.2", "tree-kill": "1.2.1", "terser": "4.3.8", - "terser-webpack-plugin": "1.4.1", + "terser-webpack-plugin": "1.4.1", "ts-loader": "5.4.5", "tsconfig-paths-webpack-plugin": "3.2.0", "webpack": "4.39.2", @@ -109,7 +109,7 @@ "webpack-merge": "4.2.1", "webpack-sources": "1.4.3", "webpack-subresource-integrity": "1.1.0-rc.6", - "worker-plugin": "3.2.0", + "worker-plugin": "3.2.0", "webpack-dev-server": "3.1.14", "webpack-node-externals": "1.7.2" } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/build-options.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/build-options.ts index 5ed595b83f..74ab71fc71 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/build-options.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/build-options.ts @@ -16,7 +16,7 @@ import { Budget, ExtraEntryPoint, OptimizationClass, - SourceMapClass, + SourceMapClass } from '../../browser/schema'; import { NormalizedFileReplacement } from '../../utils/normalize-file-replacements'; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/es5-polyfills.js b/packages/web/src/utils/build-angular/angular-cli-files/models/es5-polyfills.js index 4590e60a26..1e107f43bc 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/es5-polyfills.js +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/es5-polyfills.js @@ -101,4 +101,4 @@ import 'core-js/modules/web.dom-collections.iterator'; import 'core-js/modules/es.promise'; import 'core-js/modules/es.json.to-string-tag'; -import 'regenerator-runtime/runtime'; \ No newline at end of file +import 'regenerator-runtime/runtime'; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/safari-nomodule.js b/packages/web/src/utils/build-angular/angular-cli-files/models/safari-nomodule.js index 1d2dbd1436..0e01bcddc8 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/safari-nomodule.js +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/safari-nomodule.js @@ -1,19 +1,23 @@ -(function () { +(function() { var check = document.createElement('script'); if (!('noModule' in check) && 'onbeforeload' in check) { var support = false; - document.addEventListener('beforeload', function (e) { - if (e.target === check) { - support = true; - } else if (!e.target.hasAttribute('nomodule') || !support) { - return; - } - e.preventDefault(); - }, true); + document.addEventListener( + 'beforeload', + function(e) { + if (e.target === check) { + support = true; + } else if (!e.target.hasAttribute('nomodule') || !support) { + return; + } + e.preventDefault(); + }, + true + ); check.type = 'module'; check.src = '.'; document.head.appendChild(check); check.remove(); } -}()); +})(); diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/browser.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/browser.ts index 36dd096b71..8e771e06ed 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/browser.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/browser.ts @@ -8,70 +8,92 @@ import { LicenseWebpackPlugin } from 'license-webpack-plugin'; import * as webpack from 'webpack'; import { WebpackConfigOptions } from '../build-options'; -import { getSourceMapDevTool, isPolyfillsEntry, normalizeExtraEntryPoints } from './utils'; +import { + getSourceMapDevTool, + isPolyfillsEntry, + normalizeExtraEntryPoints +} from './utils'; const SubresourceIntegrityPlugin = require('webpack-subresource-integrity'); - -export function getBrowserConfig(wco: WebpackConfigOptions): webpack.Configuration { +export function getBrowserConfig( + wco: WebpackConfigOptions +): webpack.Configuration { const { buildOptions } = wco; const extraPlugins = []; let isEval = false; - const { styles: stylesOptimization, scripts: scriptsOptimization } = buildOptions.optimization; + const { + styles: stylesOptimization, + scripts: scriptsOptimization + } = buildOptions.optimization; const { styles: stylesSourceMap, scripts: scriptsSourceMap, - hidden: hiddenSourceMap, + hidden: hiddenSourceMap } = buildOptions.sourceMap; // See https://webpack.js.org/configuration/devtool/ for sourcemap types. - if ((stylesSourceMap || scriptsSourceMap) && + if ( + (stylesSourceMap || scriptsSourceMap) && buildOptions.evalSourceMap && !stylesOptimization && - !scriptsOptimization) { + !scriptsOptimization + ) { // Produce eval sourcemaps for development with serve, which are faster. isEval = true; } if (buildOptions.subresourceIntegrity) { - extraPlugins.push(new SubresourceIntegrityPlugin({ - hashFuncNames: ['sha384'], - })); + extraPlugins.push( + new SubresourceIntegrityPlugin({ + hashFuncNames: ['sha384'] + }) + ); } if (buildOptions.extractLicenses) { - extraPlugins.push(new LicenseWebpackPlugin({ - stats: { - warnings: false, - errors: false, - }, - perChunkOutput: false, - outputFilename: `3rdpartylicenses.txt`, - })); + extraPlugins.push( + new LicenseWebpackPlugin({ + stats: { + warnings: false, + errors: false + }, + perChunkOutput: false, + outputFilename: `3rdpartylicenses.txt` + }) + ); } if (!isEval && (scriptsSourceMap || stylesSourceMap)) { - extraPlugins.push(getSourceMapDevTool( - !!scriptsSourceMap, - !!stylesSourceMap, - hiddenSourceMap, - )); + extraPlugins.push( + getSourceMapDevTool( + !!scriptsSourceMap, + !!stylesSourceMap, + hiddenSourceMap + ) + ); } - const globalStylesBundleNames = normalizeExtraEntryPoints(buildOptions.styles, 'styles') - .map(style => style.bundleName); + const globalStylesBundleNames = normalizeExtraEntryPoints( + buildOptions.styles, + 'styles' + ).map(style => style.bundleName); return { devtool: isEval ? 'eval' : false, resolve: { mainFields: [ ...(wco.supportES2015 ? ['es2015'] : []), - 'browser', 'module', 'main', - ], + 'browser', + 'module', + 'main' + ] }, output: { - crossOriginLoading: buildOptions.subresourceIntegrity ? 'anonymous' : false, + crossOriginLoading: buildOptions.subresourceIntegrity + ? 'anonymous' + : false }, optimization: { runtimeChunk: 'single', @@ -81,32 +103,42 @@ export function getBrowserConfig(wco: WebpackConfigOptions): webpack.Configurati default: !!buildOptions.commonChunk && { chunks: 'async', minChunks: 2, - priority: 10, + priority: 10 }, common: !!buildOptions.commonChunk && { name: 'common', chunks: 'async', minChunks: 2, enforce: true, - priority: 5, + priority: 5 }, vendors: false, vendor: !!buildOptions.vendorChunk && { name: 'vendor', chunks: 'initial', enforce: true, - test: (module: { nameForCondition?: Function }, chunks: Array<{ name: string }>) => { - const moduleName = module.nameForCondition ? module.nameForCondition() : ''; + test: ( + module: { nameForCondition?: Function }, + chunks: Array<{ name: string }> + ) => { + const moduleName = module.nameForCondition + ? module.nameForCondition() + : ''; - return /[\\/]node_modules[\\/]/.test(moduleName) - && !chunks.some(({ name }) => isPolyfillsEntry(name) - || globalStylesBundleNames.includes(name)); - }, - }, - }, - }, + return ( + /[\\/]node_modules[\\/]/.test(moduleName) && + !chunks.some( + ({ name }) => + isPolyfillsEntry(name) || + globalStylesBundleNames.includes(name) + ) + ); + } + } + } + } }, plugins: extraPlugins, - node: false, + node: false }; } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/common.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/common.ts index a726524e7c..b9d9afc658 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/common.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/common.ts @@ -7,7 +7,7 @@ */ import { BuildOptimizerWebpackPlugin, - buildOptimizerLoaderPath, + buildOptimizerLoaderPath } from '@angular-devkit/build-optimizer'; import { tags } from '@angular-devkit/core'; import * as CopyWebpackPlugin from 'copy-webpack-plugin'; @@ -19,7 +19,7 @@ import { ContextReplacementPlugin, HashedModuleIdsPlugin, compilation, - debug, + debug } from 'webpack'; import { RawSource } from 'webpack-sources'; import { AssetPatternClass, ExtraEntryPoint } from '../../../browser/schema'; @@ -31,7 +31,11 @@ import { NamedLazyChunksPlugin } from '../../plugins/named-chunks-plugin'; import { ScriptsWebpackPlugin } from '../../plugins/scripts-webpack-plugin'; import { findAllNodeModules, findUp } from '../../utilities/find-up'; import { WebpackConfigOptions } from '../build-options'; -import { getEsVersionForFileName, getOutputHashFormat, normalizeExtraEntryPoints } from './utils'; +import { + getEsVersionForFileName, + getOutputHashFormat, + normalizeExtraEntryPoints +} from './utils'; const ProgressPlugin = require('webpack/lib/ProgressPlugin'); const CircularDependencyPlugin = require('circular-dependency-plugin'); @@ -43,11 +47,14 @@ const g: any = typeof global !== 'undefined' ? global : {}; // tslint:disable-next-line:no-big-function export function getCommonConfig(wco: WebpackConfigOptions): Configuration { const { root, projectRoot, buildOptions, tsConfig } = wco; - const { styles: stylesOptimization, scripts: scriptsOptimization } = buildOptions.optimization; + const { + styles: stylesOptimization, + scripts: scriptsOptimization + } = buildOptions.optimization; const { styles: stylesSourceMap, scripts: scriptsSourceMap, - vendor: vendorSourceMap, + vendor: vendorSourceMap } = buildOptions.sourceMap; const nodeModules = findUp('node_modules', projectRoot); @@ -60,8 +67,10 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { const entryPoints: { [key: string]: string[] } = {}; const targetInFileName = getEsVersionForFileName( - fullDifferential ? buildOptions.scriptTargetOverride : tsConfig.options.target, - buildOptions.esVersionInFileName, + fullDifferential + ? buildOptions.scriptTargetOverride + : tsConfig.options.target, + buildOptions.esVersionInFileName ); if (buildOptions.main) { @@ -72,15 +81,19 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (wco.buildOptions.platform !== 'server') { const buildBrowserFeatures = new BuildBrowserFeatures( projectRoot, - tsConfig.options.target || ScriptTarget.ES5, + tsConfig.options.target || ScriptTarget.ES5 ); differentialLoadingNeeded = buildBrowserFeatures.isDifferentialLoadingNeeded(); - if ((buildOptions.scriptTargetOverride || tsConfig.options.target) === ScriptTarget.ES5) { + if ( + (buildOptions.scriptTargetOverride || tsConfig.options.target) === + ScriptTarget.ES5 + ) { if ( buildOptions.es5BrowserSupport || - (buildOptions.es5BrowserSupport === undefined && buildBrowserFeatures.isEs5SupportNeeded()) + (buildOptions.es5BrowserSupport === undefined && + buildBrowserFeatures.isEs5SupportNeeded()) ) { // The nomodule polyfill needs to be inject prior to any script and be // outside of webpack compilation because otherwise webpack will cause the @@ -88,7 +101,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (buildBrowserFeatures.isNoModulePolyfillNeeded()) { const noModuleScript: ExtraEntryPoint = { bundleName: 'polyfills-nomodule-es5', - input: path.join(__dirname, '..', 'safari-nomodule.js'), + input: path.join(__dirname, '..', 'safari-nomodule.js') }; buildOptions.scripts = buildOptions.scripts ? [...buildOptions.scripts, noModuleScript] @@ -98,9 +111,13 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { // For full build differential loading we don't need to generate a seperate polyfill file // because they will be loaded exclusivly based on module and nomodule const polyfillsChunkName = - fullDifferential && differentialLoadingNeeded ? 'polyfills' : 'polyfills-es5'; + fullDifferential && differentialLoadingNeeded + ? 'polyfills' + : 'polyfills-es5'; - entryPoints[polyfillsChunkName] = [path.join(__dirname, '..', 'es5-polyfills.js')]; + entryPoints[polyfillsChunkName] = [ + path.join(__dirname, '..', 'es5-polyfills.js') + ]; if (!fullDifferential && differentialLoadingNeeded) { // Add zone.js legacy support to the es5 polyfills // This is a noop execution-wise if zone-evergreen is not used. @@ -109,13 +126,19 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (!buildOptions.aot) { // If not performing a full differential build the JIT polyfills need to be added to ES5 if (!fullDifferential && differentialLoadingNeeded) { - entryPoints[polyfillsChunkName].push(path.join(__dirname, '..', 'jit-polyfills.js')); + entryPoints[polyfillsChunkName].push( + path.join(__dirname, '..', 'jit-polyfills.js') + ); } - entryPoints[polyfillsChunkName].push(path.join(__dirname, '..', 'es5-jit-polyfills.js')); + entryPoints[polyfillsChunkName].push( + path.join(__dirname, '..', 'es5-jit-polyfills.js') + ); } // If not performing a full differential build the polyfills need to be added to ES5 bundle if (!fullDifferential && buildOptions.polyfills) { - entryPoints[polyfillsChunkName].push(path.resolve(root, buildOptions.polyfills)); + entryPoints[polyfillsChunkName].push( + path.resolve(root, buildOptions.polyfills) + ); } } } @@ -123,14 +146,14 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (buildOptions.polyfills) { entryPoints['polyfills'] = [ ...(entryPoints['polyfills'] || []), - path.resolve(root, buildOptions.polyfills), + path.resolve(root, buildOptions.polyfills) ]; } if (!buildOptions.aot) { entryPoints['polyfills'] = [ ...(entryPoints['polyfills'] || []), - path.join(__dirname, '..', 'jit-polyfills.js'), + path.join(__dirname, '..', 'jit-polyfills.js') ]; } } @@ -138,8 +161,11 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (buildOptions.profile || process.env['NG_BUILD_PROFILING']) { extraPlugins.push( new debug.ProfilingPlugin({ - outputPath: path.resolve(root, `chrome-profiler-events${targetInFileName}.json`), - }), + outputPath: path.resolve( + root, + `chrome-profiler-events${targetInFileName}.json` + ) + }) ); } @@ -149,30 +175,36 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { // process global scripts const globalScriptsByBundleName = normalizeExtraEntryPoints( buildOptions.scripts, - 'scripts', - ).reduce((prev: { bundleName: string; paths: string[]; inject: boolean }[], curr) => { - const bundleName = curr.bundleName; - const resolvedPath = path.resolve(root, curr.input); - const existingEntry = prev.find(el => el.bundleName === bundleName); - if (existingEntry) { - if (existingEntry.inject && !curr.inject) { - // All entries have to be lazy for the bundle to be lazy. - throw new Error( - `The ${curr.bundleName} bundle is mixing injected and non-injected scripts.`, - ); + 'scripts' + ).reduce( + ( + prev: { bundleName: string; paths: string[]; inject: boolean }[], + curr + ) => { + const bundleName = curr.bundleName; + const resolvedPath = path.resolve(root, curr.input); + const existingEntry = prev.find(el => el.bundleName === bundleName); + if (existingEntry) { + if (existingEntry.inject && !curr.inject) { + // All entries have to be lazy for the bundle to be lazy. + throw new Error( + `The ${curr.bundleName} bundle is mixing injected and non-injected scripts.` + ); + } + + existingEntry.paths.push(resolvedPath); + } else { + prev.push({ + bundleName, + paths: [resolvedPath], + inject: curr.inject + }); } - existingEntry.paths.push(resolvedPath); - } else { - prev.push({ - bundleName, - paths: [resolvedPath], - inject: curr.inject, - }); - } - - return prev; - }, []); + return prev; + }, + [] + ); if (globalScriptsByBundleName.length > 0) { // Add a new asset for each entry. @@ -187,42 +219,51 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { sourceMap: scriptsSourceMap, filename: `${path.basename(bundleName)}${hash}.js`, scripts: script.paths, - basePath: projectRoot, - }), + basePath: projectRoot + }) ); }); } // process asset entries if (buildOptions.assets) { - const copyWebpackPluginPatterns = buildOptions.assets.map((asset: AssetPatternClass) => { - // Resolve input paths relative to workspace root and add slash at the end. - asset.input = path.resolve(root, asset.input).replace(/\\/g, '/'); - asset.input = asset.input.endsWith('/') ? asset.input : asset.input + '/'; - asset.output = asset.output.endsWith('/') ? asset.output : asset.output + '/'; + const copyWebpackPluginPatterns = buildOptions.assets.map( + (asset: AssetPatternClass) => { + // Resolve input paths relative to workspace root and add slash at the end. + asset.input = path.resolve(root, asset.input).replace(/\\/g, '/'); + asset.input = asset.input.endsWith('/') + ? asset.input + : asset.input + '/'; + asset.output = asset.output.endsWith('/') + ? asset.output + : asset.output + '/'; - if (asset.output.startsWith('..')) { - const message = 'An asset cannot be written to a location outside of the output path.'; - throw new Error(message); + if (asset.output.startsWith('..')) { + const message = + 'An asset cannot be written to a location outside of the output path.'; + throw new Error(message); + } + + return { + context: asset.input, + // Now we remove starting slash to make Webpack place it from the output root. + to: asset.output.replace(/^\//, ''), + ignore: asset.ignore, + from: { + glob: asset.glob, + dot: true + } + }; } + ); - return { - context: asset.input, - // Now we remove starting slash to make Webpack place it from the output root. - to: asset.output.replace(/^\//, ''), - ignore: asset.ignore, - from: { - glob: asset.glob, - dot: true, - }, - }; - }); - - const copyWebpackPluginOptions = { ignore: ['.gitkeep', '**/.DS_Store', '**/Thumbs.db'] }; + const copyWebpackPluginOptions = { + ignore: ['.gitkeep', '**/.DS_Store', '**/Thumbs.db'] + }; const copyWebpackPluginInstance = new CopyWebpackPlugin( copyWebpackPluginPatterns, - copyWebpackPluginOptions, + copyWebpackPluginOptions ); extraPlugins.push(copyWebpackPluginInstance); } @@ -234,8 +275,8 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (buildOptions.showCircularDependencies) { extraPlugins.push( new CircularDependencyPlugin({ - exclude: /([\\\/]node_modules[\\\/])|(ngfactory\.js$)/, - }), + exclude: /([\\\/]node_modules[\\\/])|(ngfactory\.js$)/ + }) ); } @@ -244,11 +285,15 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { new (class { apply(compiler: Compiler) { compiler.hooks.emit.tap('angular-cli-stats', compilation => { - const data = JSON.stringify(compilation.getStats().toJson('verbose')); - compilation.assets[`stats${targetInFileName}.json`] = new RawSource(data); + const data = JSON.stringify( + compilation.getStats().toJson('verbose') + ); + compilation.assets[`stats${targetInFileName}.json`] = new RawSource( + data + ); }); } - })(), + })() ); } @@ -261,9 +306,9 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { sourceMapUseRule = { use: [ { - loader: 'source-map-loader', - }, - ], + loader: 'source-map-loader' + } + ] }; } @@ -274,9 +319,9 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { use: [ { loader: buildOptimizerLoaderPath, - options: { sourceMap: scriptsSourceMap }, - }, - ], + options: { sourceMap: scriptsSourceMap } + } + ] }; } @@ -293,7 +338,9 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { const rxjsPathMappingImport = wco.supportES2015 ? 'rxjs/_esm2015/path-mapping' : 'rxjs/_esm5/path-mapping'; - const rxPaths = require(require.resolve(rxjsPathMappingImport, { paths: [projectRoot] })); + const rxPaths = require(require.resolve(rxjsPathMappingImport, { + paths: [projectRoot] + })); alias = rxPaths(nodeModules); } catch {} @@ -303,19 +350,20 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { new CleanCssWebpackPlugin({ sourceMap: stylesSourceMap, // component styles retain their original file name - test: file => /\.(?:css|scss|sass|less|styl)$/.test(file), - }), + test: file => /\.(?:css|scss|sass|less|styl)$/.test(file) + }) ); } if (scriptsOptimization) { let angularGlobalDefinitions = { ngDevMode: false, - ngI18nClosureMode: false, + ngI18nClosureMode: false }; // Try to load known global definitions from @angular/compiler-cli. - const GLOBAL_DEFS_FOR_TERSER = require('@angular/compiler-cli').GLOBAL_DEFS_FOR_TERSER; + const GLOBAL_DEFS_FOR_TERSER = require('@angular/compiler-cli') + .GLOBAL_DEFS_FOR_TERSER; if (GLOBAL_DEFS_FOR_TERSER) { angularGlobalDefinitions = GLOBAL_DEFS_FOR_TERSER; } @@ -327,7 +375,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (GLOBAL_DEFS_FOR_TERSER_WITH_AOT) { angularGlobalDefinitions = { ...angularGlobalDefinitions, - ...GLOBAL_DEFS_FOR_TERSER_WITH_AOT, + ...GLOBAL_DEFS_FOR_TERSER_WITH_AOT }; } } @@ -341,7 +389,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { output: { ecma: terserEcma, comments: false, - webkit: true, + webkit: true }, // On server, we don't want to compress anything. We still set the ngDevMode = false for it // to remove dev code, and ngI18nClosureMode to remove Closure compiler i18n code @@ -350,7 +398,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { ? { ecma: terserEcma, global_defs: angularGlobalDefinitions, - keep_fnames: true, + keep_fnames: true } : { ecma: terserEcma, @@ -358,14 +406,15 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { // PURE comments work best with 3 passes. // See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926. passes: buildOptions.buildOptimizer ? 3 : 1, - global_defs: angularGlobalDefinitions, + global_defs: angularGlobalDefinitions }, // We also want to avoid mangling on server. // Name mangling is handled within the browser builder mangle: !manglingDisabled && buildOptions.platform !== 'server' && - (!differentialLoadingNeeded || (differentialLoadingNeeded && fullDifferential)), + (!differentialLoadingNeeded || + (differentialLoadingNeeded && fullDifferential)) }; extraMinimizers.push( @@ -375,7 +424,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { cache: true, chunkFilter: (chunk: compilation.Chunk) => !globalScriptsByBundleName.some(s => s.bundleName === chunk.name), - terserOptions, + terserOptions }), // Script bundles are fully optimized here in one step since they are never downleveled. // They are shared between ES2015 & ES5 outputs so must support ES5. @@ -389,15 +438,15 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { ...terserOptions, compress: { ...terserOptions.compress, - ecma: 5, + ecma: 5 }, output: { ...terserOptions.output, - ecma: 5, + ecma: 5 }, - mangle: !manglingDisabled && buildOptions.platform !== 'server', - }, - }), + mangle: !manglingDisabled && buildOptions.platform !== 'server' + } + }) ); } @@ -413,17 +462,18 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { } return { - mode: scriptsOptimization || stylesOptimization ? 'production' : 'development', + mode: + scriptsOptimization || stylesOptimization ? 'production' : 'development', devtool: false, profile: buildOptions.statsJson, resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js'], symlinks: !buildOptions.preserveSymlinks, modules: [wco.tsConfig.options.baseUrl || projectRoot, 'node_modules'], - alias, + alias }, resolveLoader: { - modules: loaderNodeModules, + modules: loaderNodeModules }, context: projectRoot, entry: entryPoints, @@ -431,14 +481,14 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { futureEmitAssets: true, path: path.resolve(root, buildOptions.outputPath as string), publicPath: buildOptions.deployUrl, - filename: `[name]${targetInFileName}${hashFormat.chunk}.js`, + filename: `[name]${targetInFileName}${hashFormat.chunk}.js` }, watch: buildOptions.watch, watchOptions: { - poll: buildOptions.poll, + poll: buildOptions.poll }, performance: { - hints: false, + hints: false }, module: { // Show an error for missing exports instead of a warning. @@ -448,36 +498,36 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { test: /\.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)$/, loader: 'file-loader', options: { - name: `[name]${hashFormat.file}.[ext]`, - }, + name: `[name]${hashFormat.file}.[ext]` + } }, { // Mark files inside `@angular/core` as using SystemJS style dynamic imports. // Removing this will cause deprecation warnings to appear. test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/, - parser: { system: true }, + parser: { system: true } }, { test: /[\/\\]hot[\/\\]emitter\.js$/, - parser: { node: { events: true } }, + parser: { node: { events: true } } }, { test: /[\/\\]webpack-dev-server[\/\\]client[\/\\]utils[\/\\]createSocketUrl\.js$/, - parser: { node: { querystring: true } }, + parser: { node: { querystring: true } } }, { test: /\.js$/, // Factory files are processed by BO in the rules added in typescript.ts. exclude: /(ngfactory|ngstyle)\.js$/, - ...buildOptimizerUseRule, + ...buildOptimizerUseRule }, { test: /\.js$/, exclude: /(ngfactory|ngstyle)\.js$/, enforce: 'pre', - ...sourceMapUseRule, - }, - ], + ...sourceMapUseRule + } + ] }, optimization: { noEmitOnErrors: true, @@ -485,15 +535,15 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { new HashedModuleIdsPlugin(), // TODO: check with Mike what this feature needs. new BundleBudgetPlugin({ budgets: buildOptions.budgets }), - ...extraMinimizers, - ], + ...extraMinimizers + ] }, plugins: [ // Always replace the context for the System.import in angular/core to prevent warnings. // https://github.com/angular/angular/issues/11580 // With VE the correct context is added in @ngtools/webpack, but Ivy doesn't need it at all. new ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)/), - ...extraPlugins, - ], + ...extraPlugins + ] }; } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/server.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/server.ts index 6079bf1c16..6417034732 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/server.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/server.ts @@ -19,25 +19,31 @@ export function getServerConfig(wco: WebpackConfigOptions): Configuration { if (wco.buildOptions.sourceMap) { const { scripts, styles, hidden } = wco.buildOptions.sourceMap; - extraPlugins.push(getSourceMapDevTool(scripts || false, styles || false, hidden || false)); + extraPlugins.push( + getSourceMapDevTool(scripts || false, styles || false, hidden || false) + ); } const config: Configuration = { resolve: { - mainFields: [...(wco.supportES2015 ? ['es2015'] : []), 'main', 'module'], + mainFields: [...(wco.supportES2015 ? ['es2015'] : []), 'main', 'module'] }, target: 'node', output: { - libraryTarget: 'commonjs', + libraryTarget: 'commonjs' }, plugins: extraPlugins, - node: false, + node: false }; if (wco.buildOptions.bundleDependencies == 'none') { config.externals = [ /^@angular/, - (context: string, request: string, callback: (error?: null, result?: string) => void) => { + ( + context: string, + request: string, + callback: (error?: null, result?: string) => void + ) => { // Absolute & Relative paths are not externals if (/^\.{0,2}\//.test(request) || isAbsolute(request)) { return callback(); @@ -50,7 +56,7 @@ export function getServerConfig(wco: WebpackConfigOptions): Configuration { // Node couldn't find it, so it must be user-aliased callback(); } - }, + } ]; } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/stats.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/stats.ts index 6c7ffc76a4..bcf6c9c663 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/stats.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/stats.ts @@ -22,7 +22,7 @@ const webpackOutputOptions = { assets: true, // required by custom stat output version: false, errorDetails: false, - moduleTrace: false, + moduleTrace: false }; const verboseWebpackOutputOptions = { @@ -37,7 +37,7 @@ const verboseWebpackOutputOptions = { version: true, chunkModules: true, errorDetails: true, - moduleTrace: true, + moduleTrace: true }; export function getWebpackStatsConfig(verbose = false) { diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/styles.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/styles.ts index 55a4ff9f55..b5573474b2 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/styles.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/styles.ts @@ -12,7 +12,7 @@ import { PostcssCliResources, RawCssLoader, RemoveHashPlugin, - SuppressExtractedTextChunksWebpackPlugin, + SuppressExtractedTextChunksWebpackPlugin } from '../../plugins/webpack'; import { WebpackConfigOptions } from '../build-options'; import { getOutputHashFormat, normalizeExtraEntryPoints } from './utils'; @@ -63,7 +63,7 @@ export function getStylesConfig(wco: WebpackConfigOptions) { resolve(content); }); }); - }, + } }), PostcssCliResources({ baseHref: buildOptions.baseHref, @@ -71,9 +71,9 @@ export function getStylesConfig(wco: WebpackConfigOptions) { resourcesOutputPath: buildOptions.resourcesOutputPath, loader, rebaseRootRelative: buildOptions.rebaseRootRelativeCssUrls, - filename: `[name]${hashFormat.file}.[ext]`, + filename: `[name]${hashFormat.file}.[ext]` }), - autoprefixer(), + autoprefixer() ]; }; @@ -86,11 +86,12 @@ export function getStylesConfig(wco: WebpackConfigOptions) { buildOptions.stylePreprocessorOptions.includePaths && buildOptions.stylePreprocessorOptions.includePaths.length > 0 ) { - buildOptions.stylePreprocessorOptions.includePaths.forEach((includePath: string) => - includePaths.push(path.resolve(root, includePath)), + buildOptions.stylePreprocessorOptions.includePaths.forEach( + (includePath: string) => + includePaths.push(path.resolve(root, includePath)) ); lessPathOptions = { - paths: includePaths, + paths: includePaths }; } @@ -150,10 +151,10 @@ export function getStylesConfig(wco: WebpackConfigOptions) { sourceMap: cssSourceMap, // bootstrap-sass requires a minimum precision of 8 precision: 8, - includePaths, - }, - }, - ], + includePaths + } + } + ] }, { test: /\.less$/, @@ -163,10 +164,10 @@ export function getStylesConfig(wco: WebpackConfigOptions) { options: { sourceMap: cssSourceMap, javascriptEnabled: true, - ...lessPathOptions, - }, - }, - ], + ...lessPathOptions + } + } + ] }, { test: /\.styl$/, @@ -175,11 +176,11 @@ export function getStylesConfig(wco: WebpackConfigOptions) { loader: 'stylus-loader', options: { sourceMap: cssSourceMap, - paths: includePaths, - }, - }, - ], - }, + paths: includePaths + } + } + ] + } ]; // load component css as raw strings @@ -193,17 +194,20 @@ export function getStylesConfig(wco: WebpackConfigOptions) { options: { ident: 'embedded', plugins: postcssPluginCreator, - sourceMap: cssSourceMap + sourceMap: + cssSourceMap && // Never use component css sourcemap when style optimizations are on. // It will just increase bundle size without offering good debug experience. - && !buildOptions.optimization.styles + !buildOptions.optimization.styles && // Inline all sourcemap types except hidden ones, which are the same as no sourcemaps // for component css. - && !buildOptions.sourceMap.hidden ? 'inline' : false, - }, + !buildOptions.sourceMap.hidden + ? 'inline' + : false + } }, - ...(use as webpack.Loader[]), - ], + ...(use as webpack.Loader[]) + ] })); // load global css as css files @@ -214,7 +218,9 @@ export function getStylesConfig(wco: WebpackConfigOptions) { include: globalStylePaths, test, use: [ - buildOptions.extractCss ? MiniCssExtractPlugin.loader : 'style-loader', + buildOptions.extractCss + ? MiniCssExtractPlugin.loader + : 'style-loader', RawCssLoader, { loader: 'postcss-loader', @@ -222,15 +228,17 @@ export function getStylesConfig(wco: WebpackConfigOptions) { ident: buildOptions.extractCss ? 'extracted' : 'embedded', plugins: postcssPluginCreator, sourceMap: - cssSourceMap && !buildOptions.extractCss && !buildOptions.sourceMap.hidden + cssSourceMap && + !buildOptions.extractCss && + !buildOptions.sourceMap.hidden ? 'inline' - : cssSourceMap, - }, + : cssSourceMap + } }, - ...(use as webpack.Loader[]), - ], + ...(use as webpack.Loader[]) + ] }; - }), + }) ); } @@ -239,13 +247,13 @@ export function getStylesConfig(wco: WebpackConfigOptions) { // extract global css from js files into own css file new MiniCssExtractPlugin({ filename: `[name]${hashFormat.extract}.css` }), // suppress empty .js files in css only entry points - new SuppressExtractedTextChunksWebpackPlugin(), + new SuppressExtractedTextChunksWebpackPlugin() ); } return { entry: entryPoints, module: { rules }, - plugins: extraPlugins, + plugins: extraPlugins }; } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/test.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/test.ts index 6e51b322ce..24b74edc3e 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/test.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/test.ts @@ -12,7 +12,6 @@ import * as webpack from 'webpack'; import { WebpackConfigOptions, WebpackTestOptions } from '../build-options'; import { getSourceMapDevTool, isPolyfillsEntry } from './utils'; - /** * Enumerate loaders and their dependencies from this file to let the dependency validator * know they are used. @@ -22,7 +21,7 @@ import { getSourceMapDevTool, isPolyfillsEntry } from './utils'; */ export function getTestConfig( - wco: WebpackConfigOptions, + wco: WebpackConfigOptions ): webpack.Configuration { const { root, buildOptions, sourceRoot: include } = wco; @@ -32,10 +31,7 @@ export function getTestConfig( // if (buildOptions.codeCoverage && CliConfig.fromProject()) { if (buildOptions.codeCoverage) { const codeCoverageExclude = buildOptions.codeCoverageExclude; - const exclude: (string | RegExp)[] = [ - /\.(e2e|spec)\.ts$/, - /node_modules/, - ]; + const exclude: (string | RegExp)[] = [/\.(e2e|spec)\.ts$/, /node_modules/]; if (codeCoverageExclude) { codeCoverageExclude.forEach((excludeGlob: string) => { @@ -52,19 +48,16 @@ export function getTestConfig( options: { esModules: true }, enforce: 'post', exclude, - include, + include }); } if (wco.buildOptions.sourceMap) { const { styles, scripts } = wco.buildOptions.sourceMap; - extraPlugins.push(getSourceMapDevTool( - scripts || false, - styles || false, - false, - true, - )); + extraPlugins.push( + getSourceMapDevTool(scripts || false, styles || false, false, true) + ); } return { @@ -72,34 +65,43 @@ export function getTestConfig( resolve: { mainFields: [ ...(wco.supportES2015 ? ['es2015'] : []), - 'browser', 'module', 'main', - ], + 'browser', + 'module', + 'main' + ] }, devtool: buildOptions.sourceMap ? false : 'eval', entry: { - main: path.resolve(root, buildOptions.main), + main: path.resolve(root, buildOptions.main) }, module: { - rules: extraRules, + rules: extraRules }, plugins: extraPlugins, optimization: { splitChunks: { - chunks: ((chunk: { name: string }) => !isPolyfillsEntry(chunk.name)), + chunks: (chunk: { name: string }) => !isPolyfillsEntry(chunk.name), cacheGroups: { vendors: false, vendor: { name: 'vendor', chunks: 'initial', - test: (module: { nameForCondition?: () => string }, chunks: { name: string }[]) => { - const moduleName = module.nameForCondition ? module.nameForCondition() : ''; + test: ( + module: { nameForCondition?: () => string }, + chunks: { name: string }[] + ) => { + const moduleName = module.nameForCondition + ? module.nameForCondition() + : ''; - return /[\\/]node_modules[\\/]/.test(moduleName) - && !chunks.some(({ name }) => isPolyfillsEntry(name)); - }, - }, - }, - }, - }, + return ( + /[\\/]node_modules[\\/]/.test(moduleName) && + !chunks.some(({ name }) => isPolyfillsEntry(name)) + ); + } + } + } + } + } }; } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/typescript.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/typescript.ts index 1ea900c6a7..360e05bca1 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/typescript.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/typescript.ts @@ -24,7 +24,7 @@ function _pluginOptionsOverrides( ): AngularCompilerPluginOptions { const compilerOptions = { ...(pluginOptions.compilerOptions || {}) - } + }; const hostReplacementPaths: { [replace: string]: string } = {}; if (buildOptions.fileReplacements) { @@ -51,7 +51,7 @@ function _pluginOptionsOverrides( function _createAotPlugin( wco: WebpackConfigOptions, options: AngularCompilerPluginOptions, - i18nExtract = false, + i18nExtract = false ) { const { root, buildOptions } = wco; @@ -61,20 +61,18 @@ function _createAotPlugin( const i18nFileAndFormat = i18nExtract ? { - i18nOutFile: buildOptions.i18nFile, - i18nOutFormat: buildOptions.i18nFormat, - } : { - i18nInFile: i18nInFile, - i18nInFormat: buildOptions.i18nFormat, - }; + i18nOutFile: buildOptions.i18nFile, + i18nOutFormat: buildOptions.i18nFormat + } + : { + i18nInFile: i18nInFile, + i18nInFormat: buildOptions.i18nFormat + }; const additionalLazyModules: { [module: string]: string } = {}; if (buildOptions.lazyModules) { for (const lazyModule of buildOptions.lazyModules) { - additionalLazyModules[lazyModule] = path.resolve( - root, - lazyModule, - ); + additionalLazyModules[lazyModule] = path.resolve(root, lazyModule); } } @@ -82,7 +80,8 @@ function _createAotPlugin( mainPath: path.join(root, buildOptions.main), ...i18nFileAndFormat, locale: buildOptions.i18nLocale, - platform: buildOptions.platform === 'server' ? PLATFORM.Server : PLATFORM.Browser, + platform: + buildOptions.platform === 'server' ? PLATFORM.Server : PLATFORM.Browser, missingTranslation: buildOptions.i18nMissingTranslation, sourceMap: buildOptions.sourceMap.scripts, additionalLazyModules, @@ -91,7 +90,7 @@ function _createAotPlugin( contextElementDependencyConstructor: require('webpack/lib/dependencies/ContextElementDependency'), logger: wco.logger, directTemplateLoading: true, - ...options, + ...options }; pluginOptions = _pluginOptionsOverrides(buildOptions, pluginOptions); @@ -127,7 +126,10 @@ export function getAotConfig(wco: WebpackConfigOptions, i18nExtract = false) { }; } -export function getTypescriptWorkerPlugin(wco: WebpackConfigOptions, workerTsConfigPath: string) { +export function getTypescriptWorkerPlugin( + wco: WebpackConfigOptions, + workerTsConfigPath: string +) { const { buildOptions } = wco; let pluginOptions: AngularCompilerPluginOptions = { @@ -142,7 +144,7 @@ export function getTypescriptWorkerPlugin(wco: WebpackConfigOptions, workerTsCon // Run no transformers. platformTransformers: [], // Don't attempt lazy route discovery. - discoverLazyRoutes: false, + discoverLazyRoutes: false }; pluginOptions = _pluginOptionsOverrides(buildOptions, pluginOptions); diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/utils.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/utils.ts index a00fe9c063..f7df21616c 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/utils.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/utils.ts @@ -28,14 +28,14 @@ export function getOutputHashFormat(option: string, length = 20): HashFormat { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: '', - script: `.[hash:${length}]`, + script: `.[hash:${length}]` }, all: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: `.[hash:${length}]`, - script: `.[hash:${length}]`, - }, + script: `.[hash:${length}]` + } }; return hashFormats[option] || hashFormats['none']; } @@ -46,12 +46,16 @@ export type NormalizedEntryPoint = Required>; export function normalizeExtraEntryPoints( extraEntryPoints: ExtraEntryPoint[], - defaultBundleName: string, + defaultBundleName: string ): NormalizedEntryPoint[] { return extraEntryPoints.map(entry => { let normalizedEntry; if (typeof entry === 'string') { - normalizedEntry = { input: entry, inject: true, bundleName: defaultBundleName }; + normalizedEntry = { + input: entry, + inject: true, + bundleName: defaultBundleName + }; } else { const { lazy, inject = true, ...newEntry } = entry; const injectNormalized = entry.lazy !== undefined ? !entry.lazy : inject; @@ -62,7 +66,7 @@ export function normalizeExtraEntryPoints( } else if (!injectNormalized) { // Lazy entry points use the file name as bundle name. bundleName = basename( - normalize(entry.input.replace(/\.(js|css|scss|sass|less|styl)$/i, '')), + normalize(entry.input.replace(/\.(js|css|scss|sass|less|styl)$/i, '')) ); } else { bundleName = defaultBundleName; @@ -79,7 +83,7 @@ export function getSourceMapDevTool( scriptsSourceMap: boolean, stylesSourceMap: boolean, hiddenSourceMap = false, - inlineSourceMap = false, + inlineSourceMap = false ): SourceMapDevToolPlugin { const include = []; if (scriptsSourceMap) { @@ -99,7 +103,7 @@ export function getSourceMapDevTool( // there is no way to set the 'webRoot' sourceRoot: inlineSourceMap ? '' : 'webpack:///', moduleFilenameTemplate: '[resource-path]', - append: hiddenSourceMap ? false : undefined, + append: hiddenSourceMap ? false : undefined }); } @@ -108,7 +112,7 @@ export function getSourceMapDevTool( */ export function getEsVersionForFileName( scriptTargetOverride: ScriptTarget | undefined, - esVersionInFileName = false, + esVersionInFileName = false ): string { return scriptTargetOverride && esVersionInFileName ? '-' + ScriptTarget[scriptTargetOverride].toLowerCase() diff --git a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/worker.ts b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/worker.ts index fc3b8d5b38..4e97458df7 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/worker.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/models/webpack-configs/worker.ts @@ -12,7 +12,6 @@ import { getTypescriptWorkerPlugin } from './typescript'; const WorkerPlugin = require('worker-plugin'); - export function getWorkerConfig(wco: WebpackConfigOptions): Configuration { const { buildOptions } = wco; @@ -27,9 +26,11 @@ export function getWorkerConfig(wco: WebpackConfigOptions): Configuration { const workerTsConfigPath = resolve(wco.root, buildOptions.webWorkerTsConfig); return { - plugins: [new WorkerPlugin({ - globalObject: false, - plugins: [getTypescriptWorkerPlugin(wco, workerTsConfigPath)], - })], + plugins: [ + new WorkerPlugin({ + globalObject: false, + plugins: [getTypescriptWorkerPlugin(wco, workerTsConfigPath)] + }) + ] }; } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/bundle-budget.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/bundle-budget.ts index 911e4c55d8..728b8544b1 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/bundle-budget.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/bundle-budget.ts @@ -7,7 +7,11 @@ */ import { Compiler, compilation } from 'webpack'; import { Budget, Type } from '../../browser/schema'; -import { Size, calculateBytes, calculateSizes } from '../utilities/bundle-calculator'; +import { + Size, + calculateBytes, + calculateSizes +} from '../utilities/bundle-calculator'; import { formatSize } from '../utilities/stats'; interface Thresholds { @@ -26,7 +30,7 @@ export interface BundleBudgetPluginOptions { } export class BundleBudgetPlugin { - constructor(private options: BundleBudgetPluginOptions) { } + constructor(private options: BundleBudgetPluginOptions) {} apply(compiler: Compiler): void { const { budgets } = this.options; @@ -35,42 +39,70 @@ export class BundleBudgetPlugin { return; } - compiler.hooks.compilation.tap('BundleBudgetPlugin', (compilation: compilation.Compilation) => { - compilation.hooks.afterOptimizeChunkAssets.tap('BundleBudgetPlugin', () => { - // In AOT compilations component styles get processed in child compilations. - // tslint:disable-next-line: no-any - const parentCompilation = (compilation.compiler as any).parentCompilation; - if (!parentCompilation) { - return; - } + compiler.hooks.compilation.tap( + 'BundleBudgetPlugin', + (compilation: compilation.Compilation) => { + compilation.hooks.afterOptimizeChunkAssets.tap( + 'BundleBudgetPlugin', + () => { + // In AOT compilations component styles get processed in child compilations. + // tslint:disable-next-line: no-any + const parentCompilation = (compilation.compiler as any) + .parentCompilation; + if (!parentCompilation) { + return; + } - const filteredBudgets = budgets.filter(budget => budget.type === Type.AnyComponentStyle); + const filteredBudgets = budgets.filter( + budget => budget.type === Type.AnyComponentStyle + ); + this.runChecks(filteredBudgets, compilation); + } + ); + } + ); + + compiler.hooks.afterEmit.tap( + 'BundleBudgetPlugin', + (compilation: compilation.Compilation) => { + const filteredBudgets = budgets.filter( + budget => budget.type !== Type.AnyComponentStyle + ); this.runChecks(filteredBudgets, compilation); - }); - }); - - compiler.hooks.afterEmit.tap('BundleBudgetPlugin', (compilation: compilation.Compilation) => { - const filteredBudgets = budgets.filter(budget => budget.type !== Type.AnyComponentStyle); - this.runChecks(filteredBudgets, compilation); - }); + } + ); } - private checkMinimum(threshold: number | undefined, size: Size, messages: string[]) { + private checkMinimum( + threshold: number | undefined, + size: Size, + messages: string[] + ) { if (threshold) { if (threshold > size.size) { const sizeDifference = formatSize(threshold - size.size); - messages.push(`budgets, minimum exceeded for ${size.label}. ` - + `Budget ${formatSize(threshold)} was not reached by ${sizeDifference}.`); + messages.push( + `budgets, minimum exceeded for ${size.label}. ` + + `Budget ${formatSize( + threshold + )} was not reached by ${sizeDifference}.` + ); } } } - private checkMaximum(threshold: number | undefined, size: Size, messages: string[]) { + private checkMaximum( + threshold: number | undefined, + size: Size, + messages: string[] + ) { if (threshold) { if (threshold < size.size) { const sizeDifference = formatSize(size.size - threshold); - messages.push(`budgets, maximum exceeded for ${size.label}. ` - + `Budget ${formatSize(threshold)} was exceeded by ${sizeDifference}.`); + messages.push( + `budgets, maximum exceeded for ${size.label}. ` + + `Budget ${formatSize(threshold)} was exceeded by ${sizeDifference}.` + ); } } } @@ -78,27 +110,51 @@ export class BundleBudgetPlugin { private calculate(budget: Budget): Thresholds { const thresholds: Thresholds = {}; if (budget.maximumWarning) { - thresholds.maximumWarning = calculateBytes(budget.maximumWarning, budget.baseline, 1); + thresholds.maximumWarning = calculateBytes( + budget.maximumWarning, + budget.baseline, + 1 + ); } if (budget.maximumError) { - thresholds.maximumError = calculateBytes(budget.maximumError, budget.baseline, 1); + thresholds.maximumError = calculateBytes( + budget.maximumError, + budget.baseline, + 1 + ); } if (budget.minimumWarning) { - thresholds.minimumWarning = calculateBytes(budget.minimumWarning, budget.baseline, -1); + thresholds.minimumWarning = calculateBytes( + budget.minimumWarning, + budget.baseline, + -1 + ); } if (budget.minimumError) { - thresholds.minimumError = calculateBytes(budget.minimumError, budget.baseline, -1); + thresholds.minimumError = calculateBytes( + budget.minimumError, + budget.baseline, + -1 + ); } if (budget.warning) { - thresholds.warningLow = calculateBytes(budget.warning, budget.baseline, -1); + thresholds.warningLow = calculateBytes( + budget.warning, + budget.baseline, + -1 + ); } if (budget.warning) { - thresholds.warningHigh = calculateBytes(budget.warning, budget.baseline, 1); + thresholds.warningHigh = calculateBytes( + budget.warning, + budget.baseline, + 1 + ); } if (budget.error) { @@ -117,18 +173,50 @@ export class BundleBudgetPlugin { .map(budget => ({ budget, thresholds: this.calculate(budget), - sizes: calculateSizes(budget, compilation), + sizes: calculateSizes(budget, compilation) })) .forEach(budgetCheck => { budgetCheck.sizes.forEach(size => { - this.checkMaximum(budgetCheck.thresholds.maximumWarning, size, compilation.warnings); - this.checkMaximum(budgetCheck.thresholds.maximumError, size, compilation.errors); - this.checkMinimum(budgetCheck.thresholds.minimumWarning, size, compilation.warnings); - this.checkMinimum(budgetCheck.thresholds.minimumError, size, compilation.errors); - this.checkMinimum(budgetCheck.thresholds.warningLow, size, compilation.warnings); - this.checkMaximum(budgetCheck.thresholds.warningHigh, size, compilation.warnings); - this.checkMinimum(budgetCheck.thresholds.errorLow, size, compilation.errors); - this.checkMaximum(budgetCheck.thresholds.errorHigh, size, compilation.errors); + this.checkMaximum( + budgetCheck.thresholds.maximumWarning, + size, + compilation.warnings + ); + this.checkMaximum( + budgetCheck.thresholds.maximumError, + size, + compilation.errors + ); + this.checkMinimum( + budgetCheck.thresholds.minimumWarning, + size, + compilation.warnings + ); + this.checkMinimum( + budgetCheck.thresholds.minimumError, + size, + compilation.errors + ); + this.checkMinimum( + budgetCheck.thresholds.warningLow, + size, + compilation.warnings + ); + this.checkMaximum( + budgetCheck.thresholds.warningHigh, + size, + compilation.warnings + ); + this.checkMinimum( + budgetCheck.thresholds.errorLow, + size, + compilation.errors + ); + this.checkMaximum( + budgetCheck.thresholds.errorHigh, + size, + compilation.errors + ); }); }); } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/cleancss-webpack-plugin.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/cleancss-webpack-plugin.ts index 1438f73141..da86f37fa7 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/cleancss-webpack-plugin.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/cleancss-webpack-plugin.ts @@ -18,16 +18,17 @@ function hook( compiler: Compiler, action: ( compilation: compilation.Compilation, - chunks: compilation.Chunk[], - ) => Promise, + chunks: compilation.Chunk[] + ) => Promise ) { compiler.hooks.compilation.tap( 'cleancss-webpack-plugin', (compilation: compilation.Compilation) => { - compilation.hooks.optimizeChunkAssets.tapPromise('cleancss-webpack-plugin', chunks => - action(compilation, chunks), + compilation.hooks.optimizeChunkAssets.tapPromise( + 'cleancss-webpack-plugin', + chunks => action(compilation, chunks) ); - }, + } ); } @@ -38,95 +39,100 @@ export class CleanCssWebpackPlugin { this._options = { sourceMap: false, test: file => file.endsWith('.css'), - ...options, + ...options }; } apply(compiler: Compiler): void { - hook(compiler, (compilation: compilation.Compilation, chunks: compilation.Chunk[]) => { - const cleancss = new CleanCSS({ - compatibility: 'ie9', - level: { - 2: { - skipProperties: [ - 'transition', // Fixes #12408 - 'font', // Fixes #9648 - ], + hook( + compiler, + (compilation: compilation.Compilation, chunks: compilation.Chunk[]) => { + const cleancss = new CleanCSS({ + compatibility: 'ie9', + level: { + 2: { + skipProperties: [ + 'transition', // Fixes #12408 + 'font' // Fixes #9648 + ] + } }, - }, - inline: false, - returnPromise: true, - sourceMap: this._options.sourceMap, - }); - - const files: string[] = [...compilation.additionalChunkAssets]; - - chunks.forEach(chunk => { - if (chunk.files && chunk.files.length > 0) { - files.push(...chunk.files); - } - }); - - const actions = files - .filter(file => this._options.test(file)) - .map(async file => { - const asset = compilation.assets[file] as Source; - if (!asset) { - return; - } - - let content: string; - // tslint:disable-next-line: no-any - let map: any; - if (this._options.sourceMap && asset.sourceAndMap) { - const sourceAndMap = asset.sourceAndMap(); - content = sourceAndMap.source; - map = sourceAndMap.map; - } else { - content = asset.source(); - } - - if (content.length === 0) { - return; - } - - const output = await cleancss.minify(content, map); - - let hasWarnings = false; - if (output.warnings && output.warnings.length > 0) { - compilation.warnings.push(...output.warnings); - hasWarnings = true; - } - - if (output.errors && output.errors.length > 0) { - output.errors.forEach((error: string) => compilation.errors.push(new Error(error))); - - return; - } - - // generally means invalid syntax so bail - if (hasWarnings && output.stats.minifiedSize === 0) { - return; - } - - let newSource; - if (output.sourceMap) { - newSource = new SourceMapSource( - output.styles, - file, - // tslint:disable-next-line: no-any - output.sourceMap.toString() as any, - content, - map, - ); - } else { - newSource = new RawSource(output.styles); - } - - compilation.assets[file] = newSource; + inline: false, + returnPromise: true, + sourceMap: this._options.sourceMap }); - return Promise.all(actions); - }); + const files: string[] = [...compilation.additionalChunkAssets]; + + chunks.forEach(chunk => { + if (chunk.files && chunk.files.length > 0) { + files.push(...chunk.files); + } + }); + + const actions = files + .filter(file => this._options.test(file)) + .map(async file => { + const asset = compilation.assets[file] as Source; + if (!asset) { + return; + } + + let content: string; + // tslint:disable-next-line: no-any + let map: any; + if (this._options.sourceMap && asset.sourceAndMap) { + const sourceAndMap = asset.sourceAndMap(); + content = sourceAndMap.source; + map = sourceAndMap.map; + } else { + content = asset.source(); + } + + if (content.length === 0) { + return; + } + + const output = await cleancss.minify(content, map); + + let hasWarnings = false; + if (output.warnings && output.warnings.length > 0) { + compilation.warnings.push(...output.warnings); + hasWarnings = true; + } + + if (output.errors && output.errors.length > 0) { + output.errors.forEach((error: string) => + compilation.errors.push(new Error(error)) + ); + + return; + } + + // generally means invalid syntax so bail + if (hasWarnings && output.stats.minifiedSize === 0) { + return; + } + + let newSource; + if (output.sourceMap) { + newSource = new SourceMapSource( + output.styles, + file, + // tslint:disable-next-line: no-any + output.sourceMap.toString() as any, + content, + map + ); + } else { + newSource = new RawSource(output.styles); + } + + compilation.assets[file] = newSource; + }); + + return Promise.all(actions); + } + ); } } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/index-html-webpack-plugin.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/index-html-webpack-plugin.ts index eff0ba5af8..aabaa3f84d 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/index-html-webpack-plugin.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/index-html-webpack-plugin.ts @@ -11,7 +11,7 @@ import { RawSource } from 'webpack-sources'; import { CrossOriginValue, FileInfo, - augmentIndexHtml, + augmentIndexHtml } from '../utilities/index-file/augment-index-html'; import { IndexHtmlTransform } from '../utilities/index-file/write-index-html'; import { stripBom } from '../utilities/strip-bom'; @@ -29,17 +29,23 @@ export interface IndexHtmlWebpackPluginOptions { crossOrigin?: CrossOriginValue; } -function readFile(filename: string, compilation: compilation.Compilation): Promise { +function readFile( + filename: string, + compilation: compilation.Compilation +): Promise { return new Promise((resolve, reject) => { - compilation.inputFileSystem.readFile(filename, (err: Error, data: Buffer) => { - if (err) { - reject(err); + compilation.inputFileSystem.readFile( + filename, + (err: Error, data: Buffer) => { + if (err) { + reject(err); - return; + return; + } + + resolve(stripBom(data.toString())); } - - resolve(stripBom(data.toString())); - }); + ); }); } @@ -54,60 +60,67 @@ export class IndexHtmlWebpackPlugin { noModuleEntrypoints: [], moduleEntrypoints: [], sri: false, - ...options, + ...options }; } apply(compiler: Compiler) { - compiler.hooks.emit.tapPromise('index-html-webpack-plugin', async compilation => { - // Get input html file - const inputContent = await readFile(this._options.input, compilation); - compilation.fileDependencies.add(this._options.input); + compiler.hooks.emit.tapPromise( + 'index-html-webpack-plugin', + async compilation => { + // Get input html file + const inputContent = await readFile(this._options.input, compilation); + compilation.fileDependencies.add(this._options.input); - // Get all files for selected entrypoints - const files: FileInfo[] = []; - const noModuleFiles: FileInfo[] = []; - const moduleFiles: FileInfo[] = []; + // Get all files for selected entrypoints + const files: FileInfo[] = []; + const noModuleFiles: FileInfo[] = []; + const moduleFiles: FileInfo[] = []; - for (const [entryName, entrypoint] of compilation.entrypoints) { - const entryFiles: FileInfo[] = ((entrypoint && entrypoint.getFiles()) || []).map( - (f: string): FileInfo => ({ - name: entryName, - file: f, - extension: path.extname(f), - }), - ); + for (const [entryName, entrypoint] of compilation.entrypoints) { + const entryFiles: FileInfo[] = ( + (entrypoint && entrypoint.getFiles()) || + [] + ).map( + (f: string): FileInfo => ({ + name: entryName, + file: f, + extension: path.extname(f) + }) + ); - if (this._options.noModuleEntrypoints.includes(entryName)) { - noModuleFiles.push(...entryFiles); - } else if (this._options.moduleEntrypoints.includes(entryName)) { - moduleFiles.push(...entryFiles); - } else { - files.push(...entryFiles); + if (this._options.noModuleEntrypoints.includes(entryName)) { + noModuleFiles.push(...entryFiles); + } else if (this._options.moduleEntrypoints.includes(entryName)) { + moduleFiles.push(...entryFiles); + } else { + files.push(...entryFiles); + } } + + const loadOutputFile = (name: string) => + compilation.assets[name].source(); + let indexSource = await augmentIndexHtml({ + input: this._options.input, + inputContent, + baseHref: this._options.baseHref, + deployUrl: this._options.deployUrl, + sri: this._options.sri, + crossOrigin: this._options.crossOrigin, + files, + noModuleFiles, + loadOutputFile, + moduleFiles, + entrypoints: this._options.entrypoints + }); + + if (this._options.postTransform) { + indexSource = await this._options.postTransform(indexSource); + } + + // Add to compilation assets + compilation.assets[this._options.output] = new RawSource(indexSource); } - - const loadOutputFile = (name: string) => compilation.assets[name].source(); - let indexSource = await augmentIndexHtml({ - input: this._options.input, - inputContent, - baseHref: this._options.baseHref, - deployUrl: this._options.deployUrl, - sri: this._options.sri, - crossOrigin: this._options.crossOrigin, - files, - noModuleFiles, - loadOutputFile, - moduleFiles, - entrypoints: this._options.entrypoints, - }); - - if (this._options.postTransform) { - indexSource = await this._options.postTransform(indexSource); - } - - // Add to compilation assets - compilation.assets[this._options.output] = new RawSource(indexSource); - }); + ); } } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/named-chunks-plugin.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/named-chunks-plugin.ts index 79769dcb0c..190830d2ef 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/named-chunks-plugin.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/named-chunks-plugin.ts @@ -14,26 +14,28 @@ const ImportDependenciesBlock = require('webpack/lib/dependencies/ImportDependen const Template = require('webpack/lib/Template'); export class NamedLazyChunksPlugin { - constructor() { } + constructor() {} apply(compiler: Compiler): void { compiler.hooks.compilation.tap('named-lazy-chunks-plugin', compilation => { // The dependencyReference hook isn't in the webpack typings so we have to type it as any. // tslint:disable-next-line: no-any - (compilation.hooks as any).dependencyReference.tap('named-lazy-chunks-plugin', + (compilation.hooks as any).dependencyReference.tap( + 'named-lazy-chunks-plugin', // tslint:disable-next-line: no-any (_: any, dependency: any) => { if ( // Check this dependency is from an `import()` statement. - dependency instanceof ImportDependency - && dependency.block instanceof ImportDependenciesBlock + dependency instanceof ImportDependency && + dependency.block instanceof ImportDependenciesBlock && // Don't rename chunks that already have a name. - && dependency.block.chunkName === null + dependency.block.chunkName === null ) { // Convert the request to a valid chunk name using the same logic used // in webpack/lib/ContextModule.js dependency.block.chunkName = Template.toPath(dependency.request); } - }); + } + ); }); } } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/postcss-cli-resources.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/postcss-cli-resources.ts index 2f709376f2..c20e648611 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/postcss-cli-resources.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/postcss-cli-resources.ts @@ -13,7 +13,7 @@ import * as webpack from 'webpack'; function wrapUrl(url: string): string { let wrappedUrl; - const hasSingleQuotes = url.indexOf('\'') >= 0; + const hasSingleQuotes = url.indexOf("'") >= 0; if (hasSingleQuotes) { wrappedUrl = `"${url}"`; @@ -36,7 +36,7 @@ export interface PostcssCliResourcesOptions { async function resolve( file: string, base: string, - resolver: (file: string, base: string) => Promise, + resolver: (file: string, base: string) => Promise ): Promise { try { return await resolver('./' + file, base); @@ -45,171 +45,191 @@ async function resolve( } } -export default postcss.plugin('postcss-cli-resources', (options: PostcssCliResourcesOptions) => { - const { - deployUrl = '', - baseHref = '', - resourcesOutputPath = '', - rebaseRootRelative = false, - filename, - loader, - } = options; +export default postcss.plugin( + 'postcss-cli-resources', + (options: PostcssCliResourcesOptions) => { + const { + deployUrl = '', + baseHref = '', + resourcesOutputPath = '', + rebaseRootRelative = false, + filename, + loader + } = options; - const dedupeSlashes = (url: string) => url.replace(/\/\/+/g, '/'); + const dedupeSlashes = (url: string) => url.replace(/\/\/+/g, '/'); - const process = async (inputUrl: string, context: string, resourceCache: Map) => { - // If root-relative, absolute or protocol relative url, leave as is - if (/^((?:\w+:)?\/\/|data:|chrome:|#)/.test(inputUrl)) { - return inputUrl; - } - - if (!rebaseRootRelative && /^\//.test(inputUrl)) { - return inputUrl; - } - - // If starts with a caret, remove and return remainder - // this supports bypassing asset processing - if (inputUrl.startsWith('^')) { - return inputUrl.substr(1); - } - - const cacheKey = path.resolve(context, inputUrl); - const cachedUrl = resourceCache.get(cacheKey); - if (cachedUrl) { - return cachedUrl; - } - - if (inputUrl.startsWith('~')) { - inputUrl = inputUrl.substr(1); - } - - if (inputUrl.startsWith('/')) { - let outputUrl = ''; - if (deployUrl.match(/:\/\//) || deployUrl.startsWith('/')) { - // If deployUrl is absolute or root relative, ignore baseHref & use deployUrl as is. - outputUrl = `${deployUrl.replace(/\/$/, '')}${inputUrl}`; - } else if (baseHref.match(/:\/\//)) { - // If baseHref contains a scheme, include it as is. - outputUrl = baseHref.replace(/\/$/, '') + dedupeSlashes(`/${deployUrl}/${inputUrl}`); - } else { - // Join together base-href, deploy-url and the original URL. - outputUrl = dedupeSlashes(`/${baseHref}/${deployUrl}/${inputUrl}`); + const process = async ( + inputUrl: string, + context: string, + resourceCache: Map + ) => { + // If root-relative, absolute or protocol relative url, leave as is + if (/^((?:\w+:)?\/\/|data:|chrome:|#)/.test(inputUrl)) { + return inputUrl; } - resourceCache.set(cacheKey, outputUrl); + if (!rebaseRootRelative && /^\//.test(inputUrl)) { + return inputUrl; + } - return outputUrl; - } + // If starts with a caret, remove and return remainder + // this supports bypassing asset processing + if (inputUrl.startsWith('^')) { + return inputUrl.substr(1); + } - const { pathname, hash, search } = url.parse(inputUrl.replace(/\\/g, '/')); - const resolver = (file: string, base: string) => new Promise((resolve, reject) => { - loader.resolve(base, decodeURI(file), (err, result) => { - if (err) { - reject(err); + const cacheKey = path.resolve(context, inputUrl); + const cachedUrl = resourceCache.get(cacheKey); + if (cachedUrl) { + return cachedUrl; + } - return; - } - resolve(result); - }); - }); + if (inputUrl.startsWith('~')) { + inputUrl = inputUrl.substr(1); + } - const result = await resolve(pathname as string, context, resolver); - - return new Promise((resolve, reject) => { - loader.fs.readFile(result, (err: Error, content: Buffer) => { - if (err) { - reject(err); - - return; - } - - let outputPath = interpolateName( - { resourcePath: result } as webpack.loader.LoaderContext, - filename, - { content }, - ); - - if (resourcesOutputPath) { - outputPath = path.posix.join(resourcesOutputPath, outputPath); - } - - loader.addDependency(result); - loader.emitFile(outputPath, content, undefined); - - let outputUrl = outputPath.replace(/\\/g, '/'); - if (hash || search) { - outputUrl = url.format({ pathname: outputUrl, hash, search }); - } - - if (deployUrl && loader.loaders[loader.loaderIndex].options.ident !== 'extracted') { - outputUrl = url.resolve(deployUrl, outputUrl); + if (inputUrl.startsWith('/')) { + let outputUrl = ''; + if (deployUrl.match(/:\/\//) || deployUrl.startsWith('/')) { + // If deployUrl is absolute or root relative, ignore baseHref & use deployUrl as is. + outputUrl = `${deployUrl.replace(/\/$/, '')}${inputUrl}`; + } else if (baseHref.match(/:\/\//)) { + // If baseHref contains a scheme, include it as is. + outputUrl = + baseHref.replace(/\/$/, '') + + dedupeSlashes(`/${deployUrl}/${inputUrl}`); + } else { + // Join together base-href, deploy-url and the original URL. + outputUrl = dedupeSlashes(`/${baseHref}/${deployUrl}/${inputUrl}`); } resourceCache.set(cacheKey, outputUrl); - resolve(outputUrl); + + return outputUrl; + } + + const { pathname, hash, search } = url.parse( + inputUrl.replace(/\\/g, '/') + ); + const resolver = (file: string, base: string) => + new Promise((resolve, reject) => { + loader.resolve(base, decodeURI(file), (err, result) => { + if (err) { + reject(err); + + return; + } + resolve(result); + }); + }); + + const result = await resolve(pathname as string, context, resolver); + + return new Promise((resolve, reject) => { + loader.fs.readFile(result, (err: Error, content: Buffer) => { + if (err) { + reject(err); + + return; + } + + let outputPath = interpolateName( + { resourcePath: result } as webpack.loader.LoaderContext, + filename, + { content } + ); + + if (resourcesOutputPath) { + outputPath = path.posix.join(resourcesOutputPath, outputPath); + } + + loader.addDependency(result); + loader.emitFile(outputPath, content, undefined); + + let outputUrl = outputPath.replace(/\\/g, '/'); + if (hash || search) { + outputUrl = url.format({ pathname: outputUrl, hash, search }); + } + + if ( + deployUrl && + loader.loaders[loader.loaderIndex].options.ident !== 'extracted' + ) { + outputUrl = url.resolve(deployUrl, outputUrl); + } + + resourceCache.set(cacheKey, outputUrl); + resolve(outputUrl); + }); }); - }); - }; + }; - return (root) => { - const urlDeclarations: Array = []; - root.walkDecls(decl => { - if (decl.value && decl.value.includes('url')) { - urlDeclarations.push(decl); - } - }); - - if (urlDeclarations.length === 0) { - return; - } - - const resourceCache = new Map(); - - return Promise.all(urlDeclarations.map(async decl => { - const value = decl.value; - const urlRegex = /url\(\s*(?:"([^"]+)"|'([^']+)'|(.+?))\s*\)/g; - const segments: string[] = []; - - let match; - let lastIndex = 0; - let modified = false; - - // We want to load it relative to the file that imports - const inputFile = decl.source && decl.source.input.file; - const context = inputFile && path.dirname(inputFile) || loader.context; - - // tslint:disable-next-line:no-conditional-assignment - while (match = urlRegex.exec(value)) { - const originalUrl = match[1] || match[2] || match[3]; - let processedUrl; - try { - processedUrl = await process(originalUrl, context, resourceCache); - } catch (err) { - loader.emitError(decl.error(err.message, { word: originalUrl }).toString()); - continue; + return root => { + const urlDeclarations: Array = []; + root.walkDecls(decl => { + if (decl.value && decl.value.includes('url')) { + urlDeclarations.push(decl); } + }); - if (lastIndex < match.index) { - segments.push(value.slice(lastIndex, match.index)); - } - - if (!processedUrl || originalUrl === processedUrl) { - segments.push(match[0]); - } else { - segments.push(wrapUrl(processedUrl)); - modified = true; - } - - lastIndex = match.index + match[0].length; + if (urlDeclarations.length === 0) { + return; } - if (lastIndex < value.length) { - segments.push(value.slice(lastIndex)); - } + const resourceCache = new Map(); - if (modified) { - decl.value = segments.join(''); - } - })); - }; -}); + return Promise.all( + urlDeclarations.map(async decl => { + const value = decl.value; + const urlRegex = /url\(\s*(?:"([^"]+)"|'([^']+)'|(.+?))\s*\)/g; + const segments: string[] = []; + + let match; + let lastIndex = 0; + let modified = false; + + // We want to load it relative to the file that imports + const inputFile = decl.source && decl.source.input.file; + const context = + (inputFile && path.dirname(inputFile)) || loader.context; + + // tslint:disable-next-line:no-conditional-assignment + while ((match = urlRegex.exec(value))) { + const originalUrl = match[1] || match[2] || match[3]; + let processedUrl; + try { + processedUrl = await process(originalUrl, context, resourceCache); + } catch (err) { + loader.emitError( + decl.error(err.message, { word: originalUrl }).toString() + ); + continue; + } + + if (lastIndex < match.index) { + segments.push(value.slice(lastIndex, match.index)); + } + + if (!processedUrl || originalUrl === processedUrl) { + segments.push(match[0]); + } else { + segments.push(wrapUrl(processedUrl)); + modified = true; + } + + lastIndex = match.index + match[0].length; + } + + if (lastIndex < value.length) { + segments.push(value.slice(lastIndex)); + } + + if (modified) { + decl.value = segments.join(''); + } + }) + ); + }; + } +); diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/remove-hash-plugin.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/remove-hash-plugin.ts index 0a9717bea1..9d341ab60e 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/remove-hash-plugin.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/remove-hash-plugin.ts @@ -8,15 +8,13 @@ import { Compiler, compilation } from 'webpack'; import { HashFormat } from '../models/webpack-configs/utils'; - export interface RemoveHashPluginOptions { chunkNames: string[]; hashFormat: HashFormat; } export class RemoveHashPlugin { - - constructor(private options: RemoveHashPluginOptions) { } + constructor(private options: RemoveHashPluginOptions) {} apply(compiler: Compiler): void { compiler.hooks.compilation.tap('remove-hash-plugin', compilation => { @@ -24,7 +22,8 @@ export class RemoveHashPlugin { hooks: compilation.CompilationHooks; }; - mainTemplate.hooks.assetPath.tap('remove-hash-plugin', + mainTemplate.hooks.assetPath.tap( + 'remove-hash-plugin', (path: string, data: { chunk?: { name: string } }) => { const chunkName = data.chunk && data.chunk.name; const { chunkNames, hashFormat } = this.options; @@ -37,7 +36,7 @@ export class RemoveHashPlugin { } return path; - }, + } ); }); } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/scripts-webpack-plugin.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/scripts-webpack-plugin.ts index acb79134e8..576fe01eb6 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/scripts-webpack-plugin.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/scripts-webpack-plugin.ts @@ -16,7 +16,13 @@ * found in the LICENSE file at https://angular.io/license */ import { Compiler, loader } from 'webpack'; -import { CachedSource, ConcatSource, OriginalSource, RawSource, Source } from 'webpack-sources'; +import { + CachedSource, + ConcatSource, + OriginalSource, + RawSource, + Source +} from 'webpack-sources'; import { interpolateName } from 'loader-utils'; import * as path from 'path'; @@ -42,20 +48,26 @@ function addDependencies(compilation: any, scripts: string[]): void { } } -function hook(compiler: any, action: (compilation: any, callback: (err?: Error) => void) => void) { - compiler.hooks.thisCompilation.tap('scripts-webpack-plugin', (compilation: any) => { - compilation.hooks.additionalAssets.tapAsync( - 'scripts-webpack-plugin', - (callback: (err?: Error) => void) => action(compilation, callback), - ); - }); +function hook( + compiler: any, + action: (compilation: any, callback: (err?: Error) => void) => void +) { + compiler.hooks.thisCompilation.tap( + 'scripts-webpack-plugin', + (compilation: any) => { + compilation.hooks.additionalAssets.tapAsync( + 'scripts-webpack-plugin', + (callback: (err?: Error) => void) => action(compilation, callback) + ); + } + ); } export class ScriptsWebpackPlugin { private _lastBuildTime?: number; private _cachedOutput?: ScriptOutput; - constructor(private options: Partial = {}) { } + constructor(private options: Partial = {}) {} shouldSkip(compilation: any, scripts: string[]): boolean { if (this._lastBuildTime == undefined) { @@ -74,7 +86,11 @@ export class ScriptsWebpackPlugin { return true; } - private _insertOutput(compilation: any, { filename, source }: ScriptOutput, cached = false) { + private _insertOutput( + compilation: any, + { filename, source }: ScriptOutput, + cached = false + ) { const chunk = new Chunk(this.options.name); chunk.rendered = !cached; chunk.id = this.options.name; @@ -112,29 +128,32 @@ export class ScriptsWebpackPlugin { const sourceGetters = scripts.map(fullPath => { return new Promise((resolve, reject) => { - compilation.inputFileSystem.readFile(fullPath, (err: Error, data: Buffer) => { - if (err) { - reject(err); - return; - } - - const content = data.toString(); - - let source; - if (this.options.sourceMap) { - // TODO: Look for source map file (for '.min' scripts, etc.) - - let adjustedPath = fullPath; - if (this.options.basePath) { - adjustedPath = path.relative(this.options.basePath, fullPath); + compilation.inputFileSystem.readFile( + fullPath, + (err: Error, data: Buffer) => { + if (err) { + reject(err); + return; } - source = new OriginalSource(content, adjustedPath); - } else { - source = new RawSource(content); - } - resolve(source); - }); + const content = data.toString(); + + let source; + if (this.options.sourceMap) { + // TODO: Look for source map file (for '.min' scripts, etc.) + + let adjustedPath = fullPath; + if (this.options.basePath) { + adjustedPath = path.relative(this.options.basePath, fullPath); + } + source = new OriginalSource(content, adjustedPath); + } else { + source = new RawSource(content); + } + + resolve(source); + } + ); }); }); @@ -150,7 +169,7 @@ export class ScriptsWebpackPlugin { const filename = interpolateName( { resourcePath: 'scripts.js' } as loader.LoaderContext, this.options.filename as string, - { content: combinedSource.source() }, + { content: combinedSource.source() } ); const output = { filename, source: combinedSource }; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/single-test-transform.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/single-test-transform.ts index ea8b09fdc0..427dec0fb7 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/single-test-transform.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/single-test-transform.ts @@ -15,7 +15,9 @@ export interface SingleTestTransformLoaderOptions { logger: logging.Logger; } -export const SingleTestTransformLoader = require.resolve(join(__dirname, 'single-test-transform')); +export const SingleTestTransformLoader = require.resolve( + join(__dirname, 'single-test-transform') +); /** * This loader transforms the default test file to only run tests @@ -46,12 +48,13 @@ export default function loader(this: loader.LoaderContext, source: string) { const message = [ `The 'include' option requires that the 'main' file for tests include the line below:`, `const context = require.context('./', true, /\.spec\.ts$/);`, - `Arguments passed to require.context are not strict and can be changed`, + `Arguments passed to require.context are not strict and can be changed` ]; options.logger.error(message.join(lineSeparator)); } - const mockedRequireContext = '{ keys: () => ({ map: (_a) => { } }) };' + lineSeparator; + const mockedRequireContext = + '{ keys: () => ({ map: (_a) => { } }) };' + lineSeparator; source = source.replace(regex, mockedRequireContext + targettedImports); return source; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/suppress-entry-chunks-webpack-plugin.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/suppress-entry-chunks-webpack-plugin.ts index c02bf346b9..f1cf2b1686 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/suppress-entry-chunks-webpack-plugin.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/suppress-entry-chunks-webpack-plugin.ts @@ -12,51 +12,57 @@ // To be used together with ExtractTextPlugin. export class SuppressExtractedTextChunksWebpackPlugin { - constructor() { } + constructor() {} apply(compiler: any): void { - compiler.hooks.compilation.tap('SuppressExtractedTextChunks', (compilation: any) => { - // find which chunks have css only entry points - const cssOnlyChunks: string[] = []; - const entryPoints = compilation.options.entry; - // determine which entry points are composed entirely of css files - for (let entryPoint of Object.keys(entryPoints)) { - let entryFiles: string[] | string = entryPoints[entryPoint]; - // when type of entryFiles is not array, make it as an array - entryFiles = entryFiles instanceof Array ? entryFiles : [entryFiles]; - if (entryFiles.every((el: string) => - el.match(/\.(css|scss|sass|less|styl)$/) !== null)) { - cssOnlyChunks.push(entryPoint); + compiler.hooks.compilation.tap( + 'SuppressExtractedTextChunks', + (compilation: any) => { + // find which chunks have css only entry points + const cssOnlyChunks: string[] = []; + const entryPoints = compilation.options.entry; + // determine which entry points are composed entirely of css files + for (let entryPoint of Object.keys(entryPoints)) { + let entryFiles: string[] | string = entryPoints[entryPoint]; + // when type of entryFiles is not array, make it as an array + entryFiles = entryFiles instanceof Array ? entryFiles : [entryFiles]; + if ( + entryFiles.every( + (el: string) => el.match(/\.(css|scss|sass|less|styl)$/) !== null + ) + ) { + cssOnlyChunks.push(entryPoint); + } } - } - // Remove the js file for supressed chunks - compilation.hooks.afterSeal.tap('SuppressExtractedTextChunks', () => { - compilation.chunks - .filter((chunk: any) => cssOnlyChunks.indexOf(chunk.name) !== -1) - .forEach((chunk: any) => { - let newFiles: string[] = []; - chunk.files.forEach((file: string) => { - if (file.match(/\.js(\.map)?$/)) { - // remove js files - delete compilation.assets[file]; - } else { - newFiles.push(file); - } + // Remove the js file for supressed chunks + compilation.hooks.afterSeal.tap('SuppressExtractedTextChunks', () => { + compilation.chunks + .filter((chunk: any) => cssOnlyChunks.indexOf(chunk.name) !== -1) + .forEach((chunk: any) => { + let newFiles: string[] = []; + chunk.files.forEach((file: string) => { + if (file.match(/\.js(\.map)?$/)) { + // remove js files + delete compilation.assets[file]; + } else { + newFiles.push(file); + } + }); + chunk.files = newFiles; }); - chunk.files = newFiles; - }); - }); - // Remove scripts tags with a css file as source, because HtmlWebpackPlugin will use - // a css file as a script for chunks without js files. - // TODO: Enable this once HtmlWebpackPlugin supports Webpack 4 - // compilation.plugin('html-webpack-plugin-alter-asset-tags', - // (htmlPluginData: any, callback: any) => { - // const filterFn = (tag: any) => - // !(tag.tagName === 'script' && tag.attributes.src.match(/\.css$/)); - // htmlPluginData.head = htmlPluginData.head.filter(filterFn); - // htmlPluginData.body = htmlPluginData.body.filter(filterFn); - // callback(null, htmlPluginData); - // }); - }); + }); + // Remove scripts tags with a css file as source, because HtmlWebpackPlugin will use + // a css file as a script for chunks without js files. + // TODO: Enable this once HtmlWebpackPlugin supports Webpack 4 + // compilation.plugin('html-webpack-plugin-alter-asset-tags', + // (htmlPluginData: any, callback: any) => { + // const filterFn = (tag: any) => + // !(tag.tagName === 'script' && tag.attributes.src.match(/\.css$/)); + // htmlPluginData.head = htmlPluginData.head.filter(filterFn); + // htmlPluginData.body = htmlPluginData.body.filter(filterFn); + // callback(null, htmlPluginData); + // }); + } + ); } } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/plugins/webpack.ts b/packages/web/src/utils/build-angular/angular-cli-files/plugins/webpack.ts index de7d1a2425..58308cc639 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/plugins/webpack.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/plugins/webpack.ts @@ -7,15 +7,28 @@ */ // Exports the webpack plugins we use internally. -export { CleanCssWebpackPlugin, CleanCssWebpackPluginOptions } from './cleancss-webpack-plugin'; +export { + CleanCssWebpackPlugin, + CleanCssWebpackPluginOptions +} from './cleancss-webpack-plugin'; export { BundleBudgetPlugin, BundleBudgetPluginOptions } from './bundle-budget'; -export { ScriptsWebpackPlugin, ScriptsWebpackPluginOptions } from './scripts-webpack-plugin'; -export { SuppressExtractedTextChunksWebpackPlugin } from './suppress-entry-chunks-webpack-plugin'; -export { RemoveHashPlugin, RemoveHashPluginOptions } from './remove-hash-plugin'; -export { NamedLazyChunksPlugin as NamedChunksPlugin } from './named-chunks-plugin'; +export { + ScriptsWebpackPlugin, + ScriptsWebpackPluginOptions +} from './scripts-webpack-plugin'; +export { + SuppressExtractedTextChunksWebpackPlugin +} from './suppress-entry-chunks-webpack-plugin'; +export { + RemoveHashPlugin, + RemoveHashPluginOptions +} from './remove-hash-plugin'; +export { + NamedLazyChunksPlugin as NamedChunksPlugin +} from './named-chunks-plugin'; export { default as PostcssCliResources, - PostcssCliResourcesOptions, + PostcssCliResourcesOptions } from './postcss-cli-resources'; import { join } from 'path'; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/bundle-calculator.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/bundle-calculator.ts index 8ef3cb9388..b5bbc57bbf 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/bundle-calculator.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/bundle-calculator.ts @@ -9,7 +9,7 @@ import { Budget } from '../../browser/schema'; export interface Compilation { assets: { [name: string]: { size: () => number } }; - chunks: { name: string, files: string[], isOnlyInitial: () => boolean }[]; + chunks: { name: string; files: string[]; isOnlyInitial: () => boolean }[]; warnings: string[]; errors: string[]; } @@ -19,7 +19,10 @@ export interface Size { label?: string; } -export function calculateSizes(budget: Budget, compilation: Compilation): Size[] { +export function calculateSizes( + budget: Budget, + compilation: Compilation +): Size[] { const calculatorMap = { all: AllCalculator, allScript: AllScriptCalculator, @@ -27,7 +30,7 @@ export function calculateSizes(budget: Budget, compilation: Compilation): Size[] anyScript: AnyScriptCalculator, anyComponentStyle: AnyComponentStyleCalculator, bundle: BundleCalculator, - initial: InitialCalculator, + initial: InitialCalculator }; const ctor = calculatorMap[budget.type]; @@ -37,7 +40,7 @@ export function calculateSizes(budget: Budget, compilation: Compilation): Size[] } export abstract class Calculator { - constructor (protected budget: Budget, protected compilation: Compilation) {} + constructor(protected budget: Budget, protected compilation: Compilation) {} abstract calculate(): Size[]; } @@ -54,7 +57,7 @@ class BundleCalculator extends Calculator { .map((file: string) => this.compilation.assets[file].size()) .reduce((total: number, size: number) => total + size, 0); - return [{size, label: this.budget.name}]; + return [{ size, label: this.budget.name }]; } } @@ -63,14 +66,16 @@ class BundleCalculator extends Calculator { */ class InitialCalculator extends Calculator { calculate() { - const initialChunks = this.compilation.chunks.filter(chunk => chunk.isOnlyInitial()); + const initialChunks = this.compilation.chunks.filter(chunk => + chunk.isOnlyInitial() + ); const size: number = initialChunks .reduce((files, chunk) => [...files, ...chunk.files], []) .filter((file: string) => !file.endsWith('.map')) .map((file: string) => this.compilation.assets[file].size()) .reduce((total: number, size: number) => total + size, 0); - return [{size, label: 'initial'}]; + return [{ size, label: 'initial' }]; } } @@ -85,7 +90,7 @@ class AllScriptCalculator extends Calculator { .map(asset => asset.size()) .reduce((total: number, size: number) => total + size, 0); - return [{size, label: 'total scripts'}]; + return [{ size, label: 'total scripts' }]; } } @@ -99,7 +104,7 @@ class AllCalculator extends Calculator { .map(key => this.compilation.assets[key].size()) .reduce((total: number, size: number) => total + size, 0); - return [{size, label: 'total'}]; + return [{ size, label: 'total' }]; } } @@ -112,7 +117,7 @@ class AnyComponentStyleCalculator extends Calculator { .filter(key => key.endsWith('.css')) .map(key => ({ size: this.compilation.assets[key].size(), - label: key, + label: key })); } } @@ -129,7 +134,7 @@ class AnyScriptCalculator extends Calculator { return { size: asset.size(), - label: key, + label: key }; }); } @@ -147,7 +152,7 @@ class AnyCalculator extends Calculator { return { size: asset.size(), - label: key, + label: key }; }); } @@ -159,19 +164,21 @@ class AnyCalculator extends Calculator { export function calculateBytes( input: string, baseline?: string, - factor: 1 | -1 = 1, + factor: 1 | -1 = 1 ): number { - const matches = input.match(/^\s*(\d+(?:\.\d+)?)\s*(%|(?:[mM]|[kK]|[gG])?[bB])?\s*$/); + const matches = input.match( + /^\s*(\d+(?:\.\d+)?)\s*(%|(?:[mM]|[kK]|[gG])?[bB])?\s*$/ + ); if (!matches) { return NaN; } - const baselineBytes = baseline && calculateBytes(baseline) || 0; + const baselineBytes = (baseline && calculateBytes(baseline)) || 0; let value = Number(matches[1]); switch (matches[2] && matches[2].toLowerCase()) { case '%': - value = baselineBytes * value / 100; + value = (baselineBytes * value) / 100; break; case 'kb': value *= 1024; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/bundle-calculator_spec.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/bundle-calculator_spec.ts index c2d3774b7e..0f21ba97e1 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/bundle-calculator_spec.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/bundle-calculator_spec.ts @@ -67,16 +67,16 @@ describe('bundle-calculator', () => { expect(calculateBytes('25.0gB')).toBe(25 * 1024 * 1024 * 1024); }); - it ('converts a decimal with mb and baseline', () => { + it('converts a decimal with mb and baseline', () => { expect(calculateBytes('3mb', '5mb', -1)).toBe(2 * 1024 * 1024); }); - it ('converts a percentage with baseline', () => { + it('converts a percentage with baseline', () => { expect(calculateBytes('20%', '1mb')).toBe(1024 * 1024 * 1.2); expect(calculateBytes('20%', '1mb', -1)).toBe(1024 * 1024 * 0.8); }); - it ('supports whitespace', () => { + it('supports whitespace', () => { expect(calculateBytes(' 5kb ')).toBe(5 * 1024); expect(calculateBytes('0.25 MB')).toBe(0.25 * 1024 * 1024); expect(calculateBytes(' 20 % ', ' 1 mb ')).toBe(1024 * 1024 * 1.2); diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/check-port.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/check-port.ts index 9c649cad13..534a5ed8c8 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/check-port.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/check-port.ts @@ -7,7 +7,11 @@ */ import * as net from 'net'; -export function checkPort(port: number, host: string, basePort = 49152): Promise { +export function checkPort( + port: number, + host: string, + basePort = 49152 +): Promise { return new Promise((resolve, reject) => { function _getPort(portNumber: number) { if (portNumber > 65535) { @@ -16,23 +20,26 @@ export function checkPort(port: number, host: string, basePort = 49152): Promise const server = net.createServer(); - server.once('error', (err: Error & {code: string}) => { - if (err.code !== 'EADDRINUSE') { - reject(err); - } else if (port === 0) { - _getPort(portNumber + 1); - } else { - // If the port isn't available and we weren't looking for any port, throw error. - reject( - new Error(`Port ${port} is already in use. Use '--port' to specify a different port.`), - ); - } - }) - .once('listening', () => { - server.close(); - resolve(portNumber); - }) - .listen(portNumber, host); + server + .once('error', (err: Error & { code: string }) => { + if (err.code !== 'EADDRINUSE') { + reject(err); + } else if (port === 0) { + _getPort(portNumber + 1); + } else { + // If the port isn't available and we weren't looking for any port, throw error. + reject( + new Error( + `Port ${port} is already in use. Use '--port' to specify a different port.` + ) + ); + } + }) + .once('listening', () => { + server.close(); + resolve(portNumber); + }) + .listen(portNumber, host); } _getPort(port || basePort); diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/find-tests.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/find-tests.ts index 72ec0019f1..d0326c2733 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/find-tests.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/find-tests.ts @@ -11,7 +11,11 @@ import { basename, dirname, extname, join } from 'path'; import { isDirectory } from './is-directory'; // go through all patterns and find unique list of files -export function findTests(patterns: string[], cwd: string, workspaceRoot: string): string[] { +export function findTests( + patterns: string[], + cwd: string, + workspaceRoot: string +): string[] { return patterns.reduce( (files, pattern) => { const relativePathToMain = cwd.replace(workspaceRoot, '').substr(1); // remove leading slash @@ -24,11 +28,15 @@ export function findTests(patterns: string[], cwd: string, workspaceRoot: string return files; }, - [] as string[], + [] as string[] ); } -function findMatchingTests(pattern: string, cwd: string, relativePathToMain: string): string[] { +function findMatchingTests( + pattern: string, + cwd: string, + relativePathToMain: string +): string[] { // normalize pattern, glob lib only accepts forward slashes pattern = pattern.replace(/\\/g, '/'); relativePathToMain = relativePathToMain.replace(/\\/g, '/'); @@ -55,7 +63,7 @@ function findMatchingTests(pattern: string, cwd: string, relativePathToMain: str } const files = glob.sync(pattern, { - cwd, + cwd }); return files; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/find-up.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/find-up.ts index 037e992359..e207f632c8 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/find-up.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/find-up.ts @@ -9,7 +9,11 @@ import { existsSync } from 'fs'; import * as path from 'path'; import { isDirectory } from './is-directory'; -export function findUp(names: string | string[], from: string, stopOnNodeModules = false) { +export function findUp( + names: string | string[], + from: string, + stopOnNodeModules = false +) { if (!Array.isArray(names)) { names = [names]; } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/augment-index-html.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/augment-index-html.ts index d78fac2d45..a7b8c2935d 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/augment-index-html.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/augment-index-html.ts @@ -56,8 +56,16 @@ export interface FileInfo { * after processing several configurations in order to build different sets of * bundles for differential serving. */ -export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise { - const { loadOutputFile, files, noModuleFiles = [], moduleFiles = [], entrypoints } = params; +export async function augmentIndexHtml( + params: AugmentIndexHtmlOptions +): Promise { + const { + loadOutputFile, + files, + noModuleFiles = [], + moduleFiles = [], + entrypoints + } = params; let { crossOrigin = 'none' } = params; if (params.sri && crossOrigin === 'none') { @@ -88,7 +96,10 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise // Find the head and body elements const treeAdapter = parse5.treeAdapters.default; - const document = parse5.parse(params.inputContent, { treeAdapter, locationInfo: true }); + const document = parse5.parse(params.inputContent, { + treeAdapter, + locationInfo: true + }); let headElement; let bodyElement; for (const docChild of document.childNodes) { @@ -127,12 +138,15 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise } // Inject into the html - const indexSource = new ReplaceSource(new RawSource(params.inputContent), params.input); + const indexSource = new ReplaceSource( + new RawSource(params.inputContent), + params.input + ); let scriptElements = ''; for (const script of scripts) { const attrs: { name: string; value: string | null }[] = [ - { name: 'src', value: (params.deployUrl || '') + script }, + { name: 'src', value: (params.deployUrl || '') + script } ]; if (crossOrigin !== 'none') { @@ -169,7 +183,9 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise } const attributes = attrs - .map(attr => (attr.value === null ? attr.name : `${attr.name}="${attr.value}"`)) + .map(attr => + attr.value === null ? attr.name : `${attr.name}="${attr.value}"` + ) .join(' '); scriptElements += ``; } @@ -189,13 +205,13 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise if (!baseElement) { baseElement = treeAdapter.createElement('base', undefined, [ - { name: 'href', value: params.baseHref }, + { name: 'href', value: params.baseHref } ]); treeAdapter.appendChild(baseFragment, baseElement); indexSource.insert( headElement.__location.startTag.endOffset, - parse5.serialize(baseFragment, { treeAdapter }), + parse5.serialize(baseFragment, { treeAdapter }) ); } else { let hrefAttribute; @@ -214,7 +230,7 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise indexSource.replace( baseElement.__location.startOffset, baseElement.__location.endOffset, - parse5.serialize(baseFragment, { treeAdapter }), + parse5.serialize(baseFragment, { treeAdapter }) ); } } @@ -223,7 +239,7 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise for (const stylesheet of stylesheets) { const attrs = [ { name: 'rel', value: 'stylesheet' }, - { name: 'href', value: (params.deployUrl || '') + stylesheet }, + { name: 'href', value: (params.deployUrl || '') + stylesheet } ]; if (crossOrigin !== 'none') { @@ -239,7 +255,10 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise treeAdapter.appendChild(styleElements, element); } - indexSource.insert(styleInsertionPoint, parse5.serialize(styleElements, { treeAdapter })); + indexSource.insert( + styleInsertionPoint, + parse5.serialize(styleElements, { treeAdapter }) + ); return indexSource.source(); } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/augment-index-html_spec.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/augment-index-html_spec.ts index 269c0ef43c..161ec718e5 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/augment-index-html_spec.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/augment-index-html_spec.ts @@ -6,7 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; -import { AugmentIndexHtmlOptions, FileInfo, augmentIndexHtml } from './augment-index-html'; +import { + AugmentIndexHtmlOptions, + FileInfo, + augmentIndexHtml +} from './augment-index-html'; describe('augment-index-html', () => { const indexGeneratorOptions: AugmentIndexHtmlOptions = { @@ -16,7 +20,7 @@ describe('augment-index-html', () => { sri: false, files: [], loadOutputFile: async (_fileName: string) => '', - entrypoints: ['scripts', 'polyfills', 'main', 'styles'], + entrypoints: ['scripts', 'polyfills', 'main', 'styles'] }; const oneLineHtml = (html: TemplateStringsArray) => @@ -30,8 +34,8 @@ describe('augment-index-html', () => { { file: 'runtime.js', extension: '.js', name: 'main' }, { file: 'main.js', extension: '.js', name: 'main' }, { file: 'runtime.js', extension: '.js', name: 'polyfills' }, - { file: 'polyfills.js', extension: '.js', name: 'polyfills' }, - ], + { file: 'polyfills.js', extension: '.js', name: 'polyfills' } + ] }); const html = await source; @@ -54,24 +58,24 @@ describe('augment-index-html', () => { { file: 'runtime-es2015.js', extension: '.js', name: 'main' }, { file: 'main-es2015.js', extension: '.js', name: 'main' }, { file: 'runtime-es2015.js', extension: '.js', name: 'polyfills' }, - { file: 'polyfills-es2015.js', extension: '.js', name: 'polyfills' }, + { file: 'polyfills-es2015.js', extension: '.js', name: 'polyfills' } ]; const es5JsFiles: FileInfo[] = [ { file: 'runtime-es5.js', extension: '.js', name: 'main' }, { file: 'main-es5.js', extension: '.js', name: 'main' }, { file: 'runtime-es5.js', extension: '.js', name: 'polyfills' }, - { file: 'polyfills-es5.js', extension: '.js', name: 'polyfills' }, + { file: 'polyfills-es5.js', extension: '.js', name: 'polyfills' } ]; const source = augmentIndexHtml({ ...indexGeneratorOptions, files: [ { file: 'styles.css', extension: '.css', name: 'styles' }, - { file: 'styles.css', extension: '.css', name: 'styles' }, + { file: 'styles.css', extension: '.css', name: 'styles' } ], moduleFiles: es2015JsFiles, - noModuleFiles: es5JsFiles, + noModuleFiles: es5JsFiles }); const html = await source; @@ -93,31 +97,29 @@ describe('augment-index-html', () => { `); }); - it( - `should not add 'module' and 'non-module' attr to js files which are in both module formats`, - async () => { - const es2015JsFiles: FileInfo[] = [ - { file: 'scripts.js', extension: '.js', name: 'scripts' }, - { file: 'main-es2015.js', extension: '.js', name: 'main' }, - ]; + it(`should not add 'module' and 'non-module' attr to js files which are in both module formats`, async () => { + const es2015JsFiles: FileInfo[] = [ + { file: 'scripts.js', extension: '.js', name: 'scripts' }, + { file: 'main-es2015.js', extension: '.js', name: 'main' } + ]; - const es5JsFiles: FileInfo[] = [ - { file: 'scripts.js', extension: '.js', name: 'scripts' }, - { file: 'main-es5.js', extension: '.js', name: 'main' }, - ]; + const es5JsFiles: FileInfo[] = [ + { file: 'scripts.js', extension: '.js', name: 'scripts' }, + { file: 'main-es5.js', extension: '.js', name: 'main' } + ]; - const source = augmentIndexHtml({ - ...indexGeneratorOptions, - files: [ - { file: 'styles.css', extension: '.css', name: 'styles' }, - { file: 'styles.css', extension: '.css', name: 'styles' }, - ], - moduleFiles: es2015JsFiles, - noModuleFiles: es5JsFiles, - }); + const source = augmentIndexHtml({ + ...indexGeneratorOptions, + files: [ + { file: 'styles.css', extension: '.css', name: 'styles' }, + { file: 'styles.css', extension: '.css', name: 'styles' } + ], + moduleFiles: es2015JsFiles, + noModuleFiles: es5JsFiles + }); - const html = await source; - expect(html).toEqual(oneLineHtml` + const html = await source; + expect(html).toEqual(oneLineHtml` @@ -130,6 +132,5 @@ describe('augment-index-html', () => { `); - }); - + }); }); diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/write-index-html.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/write-index-html.ts index 06694394ef..c604544626 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/write-index-html.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/index-file/write-index-html.ts @@ -7,13 +7,23 @@ */ import { EmittedFiles } from '@angular-devkit/build-webpack'; -import { Path, dirname, getSystemPath, join, virtualFs } from '@angular-devkit/core'; +import { + Path, + dirname, + getSystemPath, + join, + virtualFs +} from '@angular-devkit/core'; import { Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { ExtraEntryPoint } from '../../../browser/schema'; import { generateEntryPoints } from '../package-chunk-sort'; import { stripBom } from '../strip-bom'; -import { CrossOriginValue, FileInfo, augmentIndexHtml } from './augment-index-html'; +import { + CrossOriginValue, + FileInfo, + augmentIndexHtml +} from './augment-index-html'; type ExtensionFilter = '.js' | '.css'; @@ -48,7 +58,7 @@ export function writeIndexHtml({ scripts = [], styles = [], postTransform, - crossOrigin, + crossOrigin }: WriteIndexHtmlOptions): Observable { return host.read(indexPath).pipe( map(content => stripBom(virtualFs.fileBufferToString(content))), @@ -69,18 +79,20 @@ export function writeIndexHtml({ .read(join(dirname(outputPath), filePath)) .pipe(map(data => virtualFs.fileBufferToString(data))) .toPromise(); - }, - }), + } + }) + ), + switchMap(content => + postTransform ? postTransform(content) : of(content) ), - switchMap(content => (postTransform ? postTransform(content) : of(content))), map(content => virtualFs.stringToFileBuffer(content)), - switchMap(content => host.write(outputPath, content)), + switchMap(content => host.write(outputPath, content)) ); } function filterAndMapBuildFiles( files: EmittedFiles[], - extensionFilter: ExtensionFilter | ExtensionFilter[], + extensionFilter: ExtensionFilter | ExtensionFilter[] ): FileInfo[] { const filteredFiles: FileInfo[] = []; const validExtensions: string[] = Array.isArray(extensionFilter) diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/package-chunk-sort.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/package-chunk-sort.ts index 442b80ebb3..57dee51ccc 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/package-chunk-sort.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/package-chunk-sort.ts @@ -15,9 +15,12 @@ export function generateEntryPoints(appConfig: { // Add all styles/scripts, except lazy-loaded ones. const extraEntryPoints = ( extraEntryPoints: ExtraEntryPoint[], - defaultBundleName: string, + defaultBundleName: string ): string[] => { - const entryPoints = normalizeExtraEntryPoints(extraEntryPoints, defaultBundleName) + const entryPoints = normalizeExtraEntryPoints( + extraEntryPoints, + defaultBundleName + ) .filter(entry => entry.inject) .map(entry => entry.bundleName); @@ -34,15 +37,21 @@ export function generateEntryPoints(appConfig: { ...extraEntryPoints(appConfig.styles, 'styles'), ...extraEntryPoints(appConfig.scripts, 'scripts'), 'vendor', - 'main', + 'main' ]; const duplicates = [ - ...new Set(entryPoints.filter(x => entryPoints.indexOf(x) !== entryPoints.lastIndexOf(x))), + ...new Set( + entryPoints.filter( + x => entryPoints.indexOf(x) !== entryPoints.lastIndexOf(x) + ) + ) ]; if (duplicates.length > 0) { - throw new Error(`Multiple bundles have been named the same: '${duplicates.join(`', '`)}'.`); + throw new Error( + `Multiple bundles have been named the same: '${duplicates.join(`', '`)}'.` + ); } return entryPoints; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/read-tsconfig.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/read-tsconfig.ts index 9d58349af8..63895219b7 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/read-tsconfig.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/read-tsconfig.ts @@ -16,7 +16,10 @@ import * as path from 'path'; * @param workspaceRoot - workspaceRoot root location when provided * it will resolve 'tsconfigPath' from this path. */ -export function readTsconfig(tsconfigPath: string, workspaceRoot?: string): ParsedConfiguration { +export function readTsconfig( + tsconfigPath: string, + workspaceRoot?: string +): ParsedConfiguration { const tsConfigFullPath = workspaceRoot ? path.resolve(workspaceRoot, tsconfigPath) : tsconfigPath; diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/service-worker/index.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/service-worker/index.ts index bb712e5b78..d9527a8403 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/service-worker/index.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/service-worker/index.ts @@ -13,16 +13,13 @@ import { normalize, relative, tags, - virtualFs, + virtualFs } from '@angular-devkit/core'; -import { - Filesystem, - Generator, -} from '@angular/service-worker/config'; // tslint:disable-line:no-implicit-dependencies +import { Filesystem, Generator } from '@angular/service-worker/config'; // tslint:disable-line:no-implicit-dependencies import * as crypto from 'crypto'; class CliFilesystem implements Filesystem { - constructor(private _host: virtualFs.Host, private base: string) { } + constructor(private _host: virtualFs.Host, private base: string) {} list(path: string): Promise { return this._recursiveList(this._resolve(path), []).catch(() => []); @@ -40,7 +37,8 @@ class CliFilesystem implements Filesystem { } write(path: string, content: string): Promise { - return this._host.write(this._resolve(path), virtualFs.stringToFileBuffer(content)) + return this._host + .write(this._resolve(path), virtualFs.stringToFileBuffer(content)) .toPromise(); } @@ -75,19 +73,20 @@ export async function augmentAppWithServiceWorker( appRoot: Path, outputPath: Path, baseHref: string, - ngswConfigPath?: string, + ngswConfigPath?: string ): Promise { const distPath = normalize(outputPath); const systemProjectRoot = getSystemPath(projectRoot); // Find the service worker package const workerPath = normalize( - require.resolve('@angular/service-worker/ngsw-worker.js', { paths: [systemProjectRoot] }), - ); - const swConfigPath = require.resolve( - '@angular/service-worker/config', - { paths: [systemProjectRoot] }, + require.resolve('@angular/service-worker/ngsw-worker.js', { + paths: [systemProjectRoot] + }) ); + const swConfigPath = require.resolve('@angular/service-worker/config', { + paths: [systemProjectRoot] + }); // Determine the configuration file path let configPath; @@ -102,22 +101,32 @@ export async function augmentAppWithServiceWorker( if (!configExists) { throw new Error(tags.oneLine` Error: Expected to find an ngsw-config.json configuration - file in the ${getSystemPath(appRoot)} folder. Either provide one or disable Service Worker + file in the ${getSystemPath( + appRoot + )} folder. Either provide one or disable Service Worker in your angular.json configuration file. `); } // Read the configuration file - const config = JSON.parse(virtualFs.fileBufferToString(await host.read(configPath).toPromise())); + const config = JSON.parse( + virtualFs.fileBufferToString(await host.read(configPath).toPromise()) + ); // Generate the manifest - const GeneratorConstructor = require(swConfigPath).Generator as typeof Generator; - const generator = new GeneratorConstructor(new CliFilesystem(host, outputPath), baseHref); + const GeneratorConstructor = require(swConfigPath) + .Generator as typeof Generator; + const generator = new GeneratorConstructor( + new CliFilesystem(host, outputPath), + baseHref + ); const output = await generator.process(config); // Write the manifest const manifest = JSON.stringify(output, null, 2); - await host.write(join(distPath, 'ngsw.json'), virtualFs.stringToFileBuffer(manifest)).toPromise(); + await host + .write(join(distPath, 'ngsw.json'), virtualFs.stringToFileBuffer(manifest)) + .toPromise(); // Write the worker code // NOTE: This is inefficient (kernel -> userspace -> kernel). @@ -130,7 +139,11 @@ export async function augmentAppWithServiceWorker( if (await host.exists(safetyPath).toPromise()) { const safetyCode = await host.read(safetyPath).toPromise(); - await host.write(join(distPath, 'worker-basic.min.js'), safetyCode).toPromise(); - await host.write(join(distPath, 'safety-worker.js'), safetyCode).toPromise(); + await host + .write(join(distPath, 'worker-basic.min.js'), safetyCode) + .toPromise(); + await host + .write(join(distPath, 'safety-worker.js'), safetyCode) + .toPromise(); } } diff --git a/packages/web/src/utils/build-angular/angular-cli-files/utilities/stats.ts b/packages/web/src/utils/build-angular/angular-cli-files/utilities/stats.ts index ac2bc7aa8f..2c2db5081a 100644 --- a/packages/web/src/utils/build-angular/angular-cli-files/utilities/stats.ts +++ b/packages/web/src/utils/build-angular/angular-cli-files/utilities/stats.ts @@ -10,7 +10,6 @@ import { tags, terminal } from '@angular-devkit/core'; import * as path from 'path'; - const { bold, green, red, reset, white, yellow } = terminal; export function formatSize(size: number): string { @@ -21,7 +20,9 @@ export function formatSize(size: number): string { const abbreviations = ['bytes', 'kB', 'MB', 'GB']; const index = Math.floor(Math.log(size) / Math.log(1024)); - return `${+(size / Math.pow(1024, index)).toPrecision(3)} ${abbreviations[index]}`; + return `${+(size / Math.pow(1024, index)).toPrecision(3)} ${ + abbreviations[index] + }`; } export function generateBundleStats( @@ -34,7 +35,7 @@ export function generateBundleStats( initial: boolean; rendered?: boolean; }, - colors: boolean, + colors: boolean ): string { const g = (x: string) => (colors ? bold(green(x)) : x); const y = (x: string) => (colors ? bold(yellow(x)) : x); @@ -47,55 +48,81 @@ export function generateBundleStats( .map(f => (f && (info as any)[f] ? g(` [${f}]`) : '')) .join(''); - return `chunk {${y(info.id.toString())}} ${g(files)}${names}${size} ${initial}${flags}`; + return `chunk {${y(info.id.toString())}} ${g( + files + )}${names}${size} ${initial}${flags}`; } -export function generateBuildStats(hash: string, time: number, colors: boolean): string { - const w = (x: string) => colors ? bold(white(x)) : x; - return `Date: ${w(new Date().toISOString())} - Hash: ${w(hash)} - Time: ${w('' + time)}ms` +export function generateBuildStats( + hash: string, + time: number, + colors: boolean +): string { + const w = (x: string) => (colors ? bold(white(x)) : x); + return `Date: ${w(new Date().toISOString())} - Hash: ${w(hash)} - Time: ${w( + '' + time + )}ms`; } export function statsToString(json: any, statsConfig: any) { const colors = statsConfig.colors; - const rs = (x: string) => colors ? reset(x) : x; - const w = (x: string) => colors ? bold(white(x)) : x; + const rs = (x: string) => (colors ? reset(x) : x); + const w = (x: string) => (colors ? bold(white(x)) : x); const changedChunksStats = json.chunks .filter((chunk: any) => chunk.rendered) .map((chunk: any) => { const asset = json.assets.filter((x: any) => x.name == chunk.files[0])[0]; - return generateBundleStats({ ...chunk, size: asset && asset.size }, colors); + return generateBundleStats( + { ...chunk, size: asset && asset.size }, + colors + ); }); const unchangedChunkNumber = json.chunks.length - changedChunksStats.length; if (unchangedChunkNumber > 0) { - return '\n' + rs(tags.stripIndents` + return ( + '\n' + + rs(tags.stripIndents` Date: ${w(new Date().toISOString())} - Hash: ${w(json.hash)} ${unchangedChunkNumber} unchanged chunks ${changedChunksStats.join('\n')} Time: ${w('' + json.time)}ms - `); + `) + ); } else { - return '\n' + rs(tags.stripIndents` + return ( + '\n' + + rs(tags.stripIndents` ${changedChunksStats.join('\n')} - Date: ${w(new Date().toISOString())} - Hash: ${w(json.hash)} - Time: ${w('' + json.time)}ms - `); + Date: ${w(new Date().toISOString())} - Hash: ${w(json.hash)} - Time: ${w( + '' + json.time + )}ms + `) + ); } } export function statsWarningsToString(json: any, statsConfig: any) { const colors = statsConfig.colors; - const rs = (x: string) => colors ? reset(x) : x; - const y = (x: string) => colors ? bold(yellow(x)) : x; + const rs = (x: string) => (colors ? reset(x) : x); + const y = (x: string) => (colors ? bold(yellow(x)) : x); - return rs('\n' + json.warnings.map((warning: any) => y(`WARNING in ${warning}`)).join('\n\n')); + return rs( + '\n' + + json.warnings + .map((warning: any) => y(`WARNING in ${warning}`)) + .join('\n\n') + ); } export function statsErrorsToString(json: any, statsConfig: any) { const colors = statsConfig.colors; - const rs = (x: string) => colors ? reset(x) : x; - const r = (x: string) => colors ? bold(red(x)) : x; + const rs = (x: string) => (colors ? reset(x) : x); + const r = (x: string) => (colors ? bold(red(x)) : x); - return rs('\n' + json.errors.map((error: any) => r(`ERROR in ${error}`)).join('\n')); + return rs( + '\n' + json.errors.map((error: any) => r(`ERROR in ${error}`)).join('\n') + ); } diff --git a/packages/web/src/utils/build-angular/browser/action-cache.ts b/packages/web/src/utils/build-angular/browser/action-cache.ts index f6a4d2bb3b..9ba6d42f86 100644 --- a/packages/web/src/utils/build-angular/browser/action-cache.ts +++ b/packages/web/src/utils/build-angular/browser/action-cache.ts @@ -9,7 +9,11 @@ import { createHash } from 'crypto'; import * as findCacheDirectory from 'find-cache-dir'; import * as fs from 'fs'; import { manglingDisabled } from '../utils/mangle-options'; -import { CacheKey, ProcessBundleOptions, ProcessBundleResult } from '../utils/process-bundle'; +import { + CacheKey, + ProcessBundleOptions, + ProcessBundleResult +} from '../utils/process-bundle'; const cacache = require('cacache'); const cacheDownlevelPath = findCacheDirectory({ name: 'angular-build-dl' }); @@ -20,7 +24,11 @@ const packageVersion = require('../../package.json').version; let copyFileWorkaround = false; if (process.platform === 'darwin') { const version = process.versions.node.split('.').map(part => Number(part)); - if (version[0] < 10 || version[0] === 11 || (version[0] === 10 && version[1] < 16)) { + if ( + version[0] < 10 || + version[0] === 11 || + (version[0] === 10 && version[1] < 16) + ) { copyFileWorkaround = true; } } @@ -44,7 +52,7 @@ export class BundleActionCache { fs.copyFileSync( typeof entry === 'string' ? entry : entry.path, dest, - fs.constants.COPYFILE_EXCL, + fs.constants.COPYFILE_EXCL ); if (process.platform !== 'win32') { // The cache writes entries as readonly and when using copyFile the permissions will also be copied. @@ -74,7 +82,8 @@ export class BundleActionCache { // Postfix added to sourcemap cache keys when vendor sourcemaps are present // Allows non-destructive caching of both variants - const SourceMapVendorPostfix = !!action.sourceMaps && action.vendorSourceMaps ? '|vendor' : ''; + const SourceMapVendorPostfix = + !!action.sourceMaps && action.vendorSourceMaps ? '|vendor' : ''; // Determine cache entries required based on build settings const cacheKeys = []; @@ -85,7 +94,8 @@ export class BundleActionCache { // If sourcemaps are enabled, add original sourcemap as required if (action.sourceMaps) { - cacheKeys[CacheKey.OriginalMap] = baseCacheKey + SourceMapVendorPostfix + '|orig-map'; + cacheKeys[CacheKey.OriginalMap] = + baseCacheKey + SourceMapVendorPostfix + '|orig-map'; } } // If not only optimizing, add downlevel as required @@ -94,14 +104,17 @@ export class BundleActionCache { // If sourcemaps are enabled, add downlevel sourcemap as required if (action.sourceMaps) { - cacheKeys[CacheKey.DownlevelMap] = baseCacheKey + SourceMapVendorPostfix + '|dl-map'; + cacheKeys[CacheKey.DownlevelMap] = + baseCacheKey + SourceMapVendorPostfix + '|dl-map'; } } return cacheKeys; } - async getCacheEntries(cacheKeys: (string | null)[]): Promise<(CacheEntry | null)[] | false> { + async getCacheEntries( + cacheKeys: (string | null)[] + ): Promise<(CacheEntry | null)[] | false> { // Attempt to get required cache entries const cacheEntries = []; for (const key of cacheKeys) { @@ -113,7 +126,7 @@ export class BundleActionCache { cacheEntries.push({ path: entry.path, size: entry.size, - integrity: entry.metadata && entry.metadata.integrity, + integrity: entry.metadata && entry.metadata.integrity }); } else { cacheEntries.push(null); @@ -123,8 +136,11 @@ export class BundleActionCache { return cacheEntries; } - async getCachedBundleResult(action: ProcessBundleOptions): Promise { - const entries = action.cacheKeys && await this.getCacheEntries(action.cacheKeys); + async getCachedBundleResult( + action: ProcessBundleOptions + ): Promise { + const entries = + action.cacheKeys && (await this.getCacheEntries(action.cacheKeys)); if (!entries) { return null; } @@ -136,7 +152,7 @@ export class BundleActionCache { result.original = { filename: action.filename, size: cacheEntry.size, - integrity: cacheEntry.integrity, + integrity: cacheEntry.integrity }; BundleActionCache.copyEntryContent(cacheEntry, result.original.filename); @@ -145,10 +161,13 @@ export class BundleActionCache { if (cacheEntry) { result.original.map = { filename: action.filename + '.map', - size: cacheEntry.size, + size: cacheEntry.size }; - BundleActionCache.copyEntryContent(cacheEntry, result.original.filename + '.map'); + BundleActionCache.copyEntryContent( + cacheEntry, + result.original.filename + '.map' + ); } } else if (!action.ignoreOriginal) { // If the original wasn't processed (and therefore not cached), add info @@ -160,8 +179,8 @@ export class BundleActionCache { ? undefined : { filename: action.filename + '.map', - size: Buffer.byteLength(action.map, 'utf8'), - }, + size: Buffer.byteLength(action.map, 'utf8') + } }; } @@ -170,7 +189,7 @@ export class BundleActionCache { result.downlevel = { filename: action.filename.replace('es2015', 'es5'), size: cacheEntry.size, - integrity: cacheEntry.integrity, + integrity: cacheEntry.integrity }; BundleActionCache.copyEntryContent(cacheEntry, result.downlevel.filename); @@ -179,10 +198,13 @@ export class BundleActionCache { if (cacheEntry) { result.downlevel.map = { filename: action.filename.replace('es2015', 'es5') + '.map', - size: cacheEntry.size, + size: cacheEntry.size }; - BundleActionCache.copyEntryContent(cacheEntry, result.downlevel.filename + '.map'); + BundleActionCache.copyEntryContent( + cacheEntry, + result.downlevel.filename + '.map' + ); } } diff --git a/packages/web/src/utils/build-angular/browser/action-executor.ts b/packages/web/src/utils/build-angular/browser/action-executor.ts index 8e24c3d5d2..7fa4253905 100644 --- a/packages/web/src/utils/build-angular/browser/action-executor.ts +++ b/packages/web/src/utils/build-angular/browser/action-executor.ts @@ -8,7 +8,10 @@ import JestWorker from 'jest-worker'; import * as os from 'os'; import * as path from 'path'; -import { ProcessBundleOptions, ProcessBundleResult } from '../utils/process-bundle'; +import { + ProcessBundleOptions, + ProcessBundleResult +} from '../utils/process-bundle'; import { BundleActionCache } from './action-cache'; let workerFile = require.resolve('../utils/process-bundle'); @@ -25,13 +28,19 @@ export class BundleActionExecutor { constructor( private workerOptions: unknown, integrityAlgorithm?: string, - private readonly sizeThreshold = 32 * 1024, + private readonly sizeThreshold = 32 * 1024 ) { this.cache = new BundleActionCache(integrityAlgorithm); } - private static executeMethod(worker: JestWorker, method: string, input: unknown): Promise { - return ((worker as unknown) as Record Promise>)[method](input); + private static executeMethod( + worker: JestWorker, + method: string, + input: unknown + ): Promise { + return ((worker as unknown) as Record Promise>)[ + method + ](input); } private ensureLarge(): JestWorker { @@ -42,7 +51,7 @@ export class BundleActionExecutor { // larger files are processed in a separate process to limit memory usage in the main process return (this.largeWorker = new JestWorker(workerFile, { exposedMethods: ['process'], - setupArgs: [this.workerOptions], + setupArgs: [this.workerOptions] })); } @@ -58,16 +67,27 @@ export class BundleActionExecutor { setupArgs: [this.workerOptions], numWorkers: os.cpus().length < 2 ? 1 : 2, // Will automatically fallback to processes if not supported - enableWorkerThreads: true, + enableWorkerThreads: true })); } - private executeAction(method: string, action: { code: string }): Promise { + private executeAction( + method: string, + action: { code: string } + ): Promise { // code.length is not an exact byte count but close enough for this if (action.code.length > this.sizeThreshold) { - return BundleActionExecutor.executeMethod(this.ensureLarge(), method, action); + return BundleActionExecutor.executeMethod( + this.ensureLarge(), + method, + action + ); } else { - return BundleActionExecutor.executeMethod(this.ensureSmall(), method, action); + return BundleActionExecutor.executeMethod( + this.ensureSmall(), + method, + action + ); } } @@ -87,7 +107,10 @@ export class BundleActionExecutor { } async *processAll(actions: Iterable) { - const executions = new Map, Promise>(); + const executions = new Map< + Promise, + Promise + >(); for (const action of actions) { const execution = this.process(action); executions.set( @@ -96,7 +119,7 @@ export class BundleActionExecutor { executions.delete(execution); return result; - }), + }) ); } diff --git a/packages/web/src/utils/build-angular/browser/index.ts b/packages/web/src/utils/build-angular/browser/index.ts index a30da820be..3dca6b1b52 100644 --- a/packages/web/src/utils/build-angular/browser/index.ts +++ b/packages/web/src/utils/build-angular/browser/index.ts @@ -5,12 +5,16 @@ * 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 { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'; +import { + BuilderContext, + BuilderOutput, + createBuilder +} from '@angular-devkit/architect'; import { BuildResult, EmittedFiles, WebpackLoggingCallback, - runWebpack, + runWebpack } from '@angular-devkit/build-webpack'; import { experimental, @@ -21,7 +25,7 @@ import { normalize, resolve, tags, - virtualFs, + virtualFs } from '@angular-devkit/core'; import { NodeJsSyncHost } from '@angular-devkit/core/node'; import { createHash } from 'crypto'; @@ -30,7 +34,14 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { from, of } from 'rxjs'; -import { bufferCount, catchError, concatMap, map, mergeScan, switchMap } from 'rxjs/operators'; +import { + bufferCount, + catchError, + concatMap, + map, + mergeScan, + switchMap +} from 'rxjs/operators'; import { ScriptTarget } from 'typescript'; import * as webpack from 'webpack'; // import { NgBuildAnalyticsPlugin } from '../../plugins/webpack/analytics'; @@ -43,11 +54,11 @@ import { getStatsConfig, getStylesConfig, getWorkerConfig, - normalizeExtraEntryPoints, + normalizeExtraEntryPoints } from '../angular-cli-files/models/webpack-configs'; import { IndexHtmlTransform, - writeIndexHtml, + writeIndexHtml } from '../angular-cli-files/utilities/index-file/write-index-html'; import { readTsconfig } from '../angular-cli-files/utilities/read-tsconfig'; import { augmentAppWithServiceWorker } from '../angular-cli-files/utilities/service-worker'; @@ -56,7 +67,7 @@ import { generateBundleStats, statsErrorsToString, statsToString, - statsWarningsToString, + statsWarningsToString } from '../angular-cli-files/utilities/stats'; import { ExecutionTransformer } from '../transforms'; import { @@ -64,18 +75,18 @@ import { deleteOutputDir, fullDifferential, normalizeOptimization, - normalizeSourceMaps, + normalizeSourceMaps } from '../utils'; import { ProcessBundleFile, ProcessBundleOptions, - ProcessBundleResult, + ProcessBundleResult } from '../utils/process-bundle'; import { assertCompatibleAngularVersion } from '../utils/version'; import { generateBrowserWebpackConfigFromContext, getIndexInputFile, - getIndexOutputFile, + getIndexOutputFile } from '../utils/webpack-browser-config'; import { BundleActionExecutor } from './action-executor'; import { Schema as BrowserBuilderSchema } from './schema'; @@ -91,7 +102,7 @@ export type BrowserBuilderOutput = json.JsonObject & export function createBrowserLoggingCallback( verbose: boolean, - logger: logging.LoggerApi, + logger: logging.LoggerApi ): WebpackLoggingCallback { return (stats, config) => { // config.stats contains our own stats settings, added during buildWebpackConfig(). @@ -114,8 +125,11 @@ export function createBrowserLoggingCallback( export async function buildBrowserWebpackConfigFromContext( options: BrowserBuilderSchema, context: BuilderContext, - host: virtualFs.Host = new NodeJsSyncHost(), -): Promise<{ workspace: experimental.workspace.Workspace; config: webpack.Configuration[] }> { + host: virtualFs.Host = new NodeJsSyncHost() +): Promise<{ + workspace: experimental.workspace.Workspace; + config: webpack.Configuration[]; +}> { return generateBrowserWebpackConfigFromContext( options, context, @@ -126,15 +140,15 @@ export async function buildBrowserWebpackConfigFromContext( getStatsConfig(wco), getAnalyticsConfig(wco, context), getCompilerConfig(wco), - wco.buildOptions.webWorkerTsConfig ? getWorkerConfig(wco) : {}, + wco.buildOptions.webWorkerTsConfig ? getWorkerConfig(wco) : {} ], - host, + host ); } function getAnalyticsConfig( wco: WebpackConfigOptions, - context: BuilderContext, + context: BuilderContext ): webpack.Configuration { if (context.analytics) { // If there's analytics, add our plugin. Otherwise no need to slow down the build. @@ -142,12 +156,14 @@ function getAnalyticsConfig( if (context.builder) { // We already vetted that this is a "safe" package, otherwise the analytics would be noop. category = - context.builder.builderName.split(':')[1] || context.builder.builderName || 'build'; + context.builder.builderName.split(':')[1] || + context.builder.builderName || + 'build'; } // The category is the builder name if it's an angular builder. return { - plugins: [], + plugins: [] }; } @@ -166,9 +182,16 @@ async function initialize( options: BrowserBuilderSchema, context: BuilderContext, host: virtualFs.Host, - webpackConfigurationTransform?: ExecutionTransformer, -): Promise<{ workspace: experimental.workspace.Workspace; config: webpack.Configuration[] }> { - const { config, workspace } = await buildBrowserWebpackConfigFromContext(options, context, host); + webpackConfigurationTransform?: ExecutionTransformer +): Promise<{ + workspace: experimental.workspace.Workspace; + config: webpack.Configuration[]; +}> { + const { config, workspace } = await buildBrowserWebpackConfigFromContext( + options, + context, + host + ); let transformedConfig; if (webpackConfigurationTransform) { @@ -182,7 +205,7 @@ async function initialize( await deleteOutputDir( normalize(context.workspaceRoot), normalize(options.outputPath), - host, + host ).toPromise(); } @@ -197,7 +220,7 @@ export function buildWebpackBrowser( webpackConfiguration?: ExecutionTransformer; logging?: WebpackLoggingCallback; indexHtml?: IndexHtmlTransform; - } = {}, + } = {} ) { const host = new NodeJsSyncHost(); const root = normalize(context.workspaceRoot); @@ -205,7 +228,9 @@ export function buildWebpackBrowser( // Check Angular version. assertCompatibleAngularVersion(context.workspaceRoot, context.logger); - return from(initialize(options, context, host, transforms.webpackConfiguration)).pipe( + return from( + initialize(options, context, host, transforms.webpackConfiguration) + ).pipe( // tslint:disable-next-line: no-big-function switchMap(({ workspace, config: configs }) => { const projectName = context.target @@ -213,17 +238,22 @@ export function buildWebpackBrowser( : workspace.getDefaultProjectName(); if (!projectName) { - throw new Error('Must either have a target from the context or a default project.'); + throw new Error( + 'Must either have a target from the context or a default project.' + ); } const projectRoot = resolve( workspace.root, - normalize(workspace.getProject(projectName).root), + normalize(workspace.getProject(projectName).root) ); const tsConfig = readTsconfig(options.tsConfig, context.workspaceRoot); const target = tsConfig.options.target || ScriptTarget.ES5; - const buildBrowserFeatures = new BuildBrowserFeatures(getSystemPath(projectRoot), target); + const buildBrowserFeatures = new BuildBrowserFeatures( + getSystemPath(projectRoot), + target + ); const isDifferentialLoadingNeeded = buildBrowserFeatures.isDifferentialLoadingNeeded(); @@ -251,14 +281,17 @@ export function buildWebpackBrowser( transforms.logging || (useBundleDownleveling ? () => {} - : createBrowserLoggingCallback(!!options.verbose, context.logger)), + : createBrowserLoggingCallback( + !!options.verbose, + context.logger + )) }); } else { return of(); } }, { success: true } as BuildResult, - 1, + 1 ), bufferCount(configs.length), // tslint:disable-next-line: no-big-function @@ -270,10 +303,14 @@ export function buildWebpackBrowser( // If it fails show any diagnostic messages and bail const webpackStats = buildEvents[0].webpackStats; if (webpackStats && webpackStats.warnings.length > 0) { - context.logger.warn(statsWarningsToString(webpackStats, { colors: true })); + context.logger.warn( + statsWarningsToString(webpackStats, { colors: true }) + ); } if (webpackStats && webpackStats.errors.length > 0) { - context.logger.error(statsErrorsToString(webpackStats, { colors: true })); + context.logger.error( + statsErrorsToString(webpackStats, { colors: true }) + ); } return { success }; @@ -284,14 +321,19 @@ export function buildWebpackBrowser( const scriptsEntryPointName = normalizeExtraEntryPoints( options.scripts || [], - 'scripts', + 'scripts' ).map(x => x.bundleName); const [firstBuild, secondBuild] = buildEvents; - if (isDifferentialLoadingNeeded && (fullDifferential || options.watch)) { + if ( + isDifferentialLoadingNeeded && + (fullDifferential || options.watch) + ) { moduleFiles = firstBuild.emittedFiles || []; files = moduleFiles.filter( - x => x.extension === '.css' || (x.name && scriptsEntryPointName.includes(x.name)), + x => + x.extension === '.css' || + (x.name && scriptsEntryPointName.includes(x.name)) ); if (buildEvents.length === 2) { @@ -303,13 +345,17 @@ export function buildWebpackBrowser( noModuleFiles = []; // Common options for all bundle process actions - const sourceMapOptions = normalizeSourceMaps(options.sourceMap || false); + const sourceMapOptions = normalizeSourceMaps( + options.sourceMap || false + ); const actionOptions: Partial = { optimize: normalizeOptimization(options.optimization).scripts, sourceMaps: sourceMapOptions.scripts, hiddenSourceMaps: sourceMapOptions.hidden, vendorSourceMaps: sourceMapOptions.vendor, - integrityAlgorithm: options.subresourceIntegrity ? 'sha384' : undefined, + integrityAlgorithm: options.subresourceIntegrity + ? 'sha384' + : undefined }; const actions: ProcessBundleOptions[] = []; @@ -347,14 +393,20 @@ export function buildWebpackBrowser( } // If not optimizing then ES2015 polyfills do not need processing // Unlike other module scripts, it is never downleveled - const es2015Polyfills = file.file.startsWith('polyfills-es2015'); + const es2015Polyfills = file.file.startsWith( + 'polyfills-es2015' + ); if (!actionOptions.optimize && es2015Polyfills) { continue; } // Retrieve the content/map for the file // NOTE: Additional future optimizations will read directly from memory - let filename = path.resolve(getSystemPath(root), options.outputPath, file.file); + let filename = path.resolve( + getSystemPath(root), + options.outputPath, + file.file + ); const code = fs.readFileSync(filename, 'utf8'); let map; if (actionOptions.sourceMaps) { @@ -383,7 +435,7 @@ export function buildWebpackBrowser( name: file.id!, runtime: file.file.startsWith('runtime'), ignoreOriginal: es5Polyfills, - optimizeOnly: es2015Polyfills, + optimizeOnly: es2015Polyfills }); // ES2015 polyfills are only optimized; optimization check was performed above @@ -399,7 +451,9 @@ export function buildWebpackBrowser( } // Execute the bundle processing actions - context.logger.info('Generating ES5 bundles for differential loading...'); + context.logger.info( + 'Generating ES5 bundles for differential loading...' + ); const processActions: typeof actions = []; let processRuntimeAction: ProcessBundleOptions | undefined; @@ -416,11 +470,13 @@ export function buildWebpackBrowser( const executor = new BundleActionExecutor( { cachePath: cacheDownlevelPath }, - options.subresourceIntegrity ? 'sha384' : undefined, + options.subresourceIntegrity ? 'sha384' : undefined ); try { - for await (const result of executor.processAll(processActions)) { + for await (const result of executor.processAll( + processActions + )) { processResults.push(result); } } finally { @@ -431,32 +487,40 @@ export function buildWebpackBrowser( if (processRuntimeAction) { const runtimeOptions = { ...processRuntimeAction, - runtimeData: processResults, + runtimeData: processResults }; processResults.push( - await import('../utils/process-bundle').then(m => m.process(runtimeOptions)), + await import('../utils/process-bundle').then(m => + m.process(runtimeOptions) + ) ); } context.logger.info('ES5 bundle generation complete.'); - type ArrayElement = A extends ReadonlyArray ? T : never; + type ArrayElement = A extends ReadonlyArray + ? T + : never; function generateBundleInfoStats( id: string | number, bundle: ProcessBundleFile, - chunk: ArrayElement | undefined, + chunk: + | ArrayElement + | undefined ): string { return generateBundleStats( { id, size: bundle.size, - files: bundle.map ? [bundle.filename, bundle.map.filename] : [bundle.filename], + files: bundle.map + ? [bundle.filename, bundle.map.filename] + : [bundle.filename], names: chunk && chunk.names, entry: !!chunk && chunk.names.includes('runtime'), initial: !!chunk && chunk.initial, - rendered: true, + rendered: true }, - true, + true ); } @@ -468,14 +532,26 @@ export function buildWebpackBrowser( const chunk = webpackStats && webpackStats.chunks && - webpackStats.chunks.find(c => result.name === c.id.toString()); + webpackStats.chunks.find( + c => result.name === c.id.toString() + ); if (result.original) { bundleInfoText += - '\n' + generateBundleInfoStats(result.name, result.original, chunk); + '\n' + + generateBundleInfoStats( + result.name, + result.original, + chunk + ); } if (result.downlevel) { bundleInfoText += - '\n' + generateBundleInfoStats(result.name, result.downlevel, chunk); + '\n' + + generateBundleInfoStats( + result.name, + result.downlevel, + chunk + ); } } @@ -486,9 +562,14 @@ export function buildWebpackBrowser( } const asset = - webpackStats.assets && webpackStats.assets.find(a => a.name === chunk.files[0]); + webpackStats.assets && + webpackStats.assets.find(a => a.name === chunk.files[0]); bundleInfoText += - '\n' + generateBundleStats({ ...chunk, size: asset && asset.size }, true); + '\n' + + generateBundleStats( + { ...chunk, size: asset && asset.size }, + true + ); } } @@ -497,19 +578,25 @@ export function buildWebpackBrowser( generateBuildStats( (webpackStats && webpackStats.hash) || '', Date.now() - startTime, - true, + true ); context.logger.info(bundleInfoText); if (webpackStats && webpackStats.warnings.length > 0) { - context.logger.warn(statsWarningsToString(webpackStats, { colors: true })); + context.logger.warn( + statsWarningsToString(webpackStats, { colors: true }) + ); } if (webpackStats && webpackStats.errors.length > 0) { - context.logger.error(statsErrorsToString(webpackStats, { colors: true })); + context.logger.error( + statsErrorsToString(webpackStats, { colors: true }) + ); } } else { const { emittedFiles = [] } = firstBuild; files = emittedFiles.filter(x => x.name !== 'polyfills-es5'); - noModuleFiles = emittedFiles.filter(x => x.name === 'polyfills-es5'); + noModuleFiles = emittedFiles.filter( + x => x.name === 'polyfills-es5' + ); } if (options.index) { @@ -517,7 +604,10 @@ export function buildWebpackBrowser( host, outputPath: resolve( root, - join(normalize(options.outputPath), getIndexOutputFile(options)), + join( + normalize(options.outputPath), + getIndexOutputFile(options) + ) ), indexPath: join(root, getIndexInputFile(options)), files, @@ -529,11 +619,13 @@ export function buildWebpackBrowser( scripts: options.scripts, styles: options.styles, postTransform: transforms.indexHtml, - crossOrigin: options.crossOrigin, + crossOrigin: options.crossOrigin }) .pipe( map(() => ({ success: true })), - catchError(error => of({ success: false, error: mapErrorToMessage(error) })), + catchError(error => + of({ success: false, error: mapErrorToMessage(error) }) + ) ) .toPromise(); } else { @@ -552,11 +644,11 @@ export function buildWebpackBrowser( projectRoot, resolve(root, normalize(options.outputPath)), options.baseHref || '/', - options.ngswConfigPath, + options.ngswConfigPath ).then( () => ({ success: true }), - error => ({ success: false, error: mapErrorToMessage(error) }), - ), + error => ({ success: false, error: mapErrorToMessage(error) }) + ) ); } else { return of(buildEvent); @@ -567,11 +659,14 @@ export function buildWebpackBrowser( ({ ...event, // If we use differential loading, both configs have the same outputs - outputPath: path.resolve(context.workspaceRoot, options.outputPath), - } as BrowserBuilderOutput), - ), + outputPath: path.resolve( + context.workspaceRoot, + options.outputPath + ) + } as BrowserBuilderOutput) + ) ); - }), + }) ); } @@ -587,4 +682,6 @@ function mapErrorToMessage(error: unknown): string | undefined { return undefined; } -export default createBuilder(buildWebpackBrowser); +export default createBuilder( + buildWebpackBrowser +); diff --git a/packages/web/src/utils/build-angular/browser/schema.ts b/packages/web/src/utils/build-angular/browser/schema.ts index 3f01d2bffb..ac80a6b48e 100644 --- a/packages/web/src/utils/build-angular/browser/schema.ts +++ b/packages/web/src/utils/build-angular/browser/schema.ts @@ -1,4 +1,3 @@ - // THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE // CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...). @@ -9,324 +8,324 @@ * Browser target options */ export interface Schema { - /** - * Build using Ahead of Time compilation. - */ - aot?: boolean; - /** - * List of static application assets. - */ - assets?: AssetPattern[]; - /** - * Base url for the application being built. - */ - baseHref?: string; - /** - * Budget thresholds to ensure parts of your application stay within boundaries which you - * set. - */ - budgets?: Budget[]; - /** - * Enables '@angular-devkit/build-optimizer' optimizations when using the 'aot' option. - */ - buildOptimizer?: boolean; - /** - * Use a separate bundle containing code used across multiple bundles. - */ - commonChunk?: boolean; - /** - * Define the crossorigin attribute setting of elements that provide CORS support. - */ - crossOrigin?: CrossOrigin; - /** - * Delete the output path before building. - */ - deleteOutputPath?: boolean; - /** - * URL where files will be deployed. - */ - deployUrl?: string; - /** - * Enables conditionally loaded ES2015 polyfills. - * @deprecated This will be determined from the list of supported browsers specified in the - * 'browserslist' file. - */ - es5BrowserSupport?: boolean; - /** - * Output in-file eval sourcemaps. - * @deprecated - */ - evalSourceMap?: boolean; - /** - * Concatenate modules with Rollup before bundling them with Webpack. - */ - experimentalRollupPass?: boolean; - /** - * Extract css from global styles into css files instead of js ones. - */ - extractCss?: boolean; - /** - * Extract all licenses in a separate file. - */ - extractLicenses?: boolean; - /** - * Replace files with other files in the build. - */ - fileReplacements?: FileReplacement[]; - /** - * Run the TypeScript type checker in a forked process. - */ - forkTypeChecker?: boolean; - /** - * Localization file to use for i18n. - * @deprecated Use 'locales' object in the project metadata instead. - */ - i18nFile?: string; - /** - * Format of the localization file specified with --i18n-file. - * @deprecated No longer needed as the format will be determined automatically. - */ - i18nFormat?: string; - /** - * Locale to use for i18n. - * @deprecated Use 'localize' instead. - */ - i18nLocale?: string; - /** - * How to handle missing translations for i18n. - */ - i18nMissingTranslation?: I18NMissingTranslation; - /** - * Configures the generation of the application's HTML index. - */ - index: IndexUnion; - /** - * List of additional NgModule files that will be lazy loaded. Lazy router modules will be - * discovered automatically. - * @deprecated 'SystemJsNgModuleLoader' is deprecated, and this is part of its usage. Use - * 'import()' syntax instead. - */ - lazyModules?: string[]; - localize?: Localize; - /** - * The full path for the main entry point to the app, relative to the current workspace. - */ - main: string; - /** - * Use file name for lazy loaded chunks. - */ - namedChunks?: boolean; - /** - * Path to ngsw-config.json. - */ - ngswConfigPath?: string; - /** - * Enables optimization of the build output. - */ - optimization?: OptimizationUnion; - /** - * Define the output filename cache-busting hashing mode. - */ - outputHashing?: OutputHashing; - /** - * The full path for the new output directory, relative to the current workspace. - * - * By default, writes output to a folder named dist/ in the current project. - */ - outputPath: string; - /** - * Enable and define the file watching poll time period in milliseconds. - */ - poll?: number; - /** - * The full path for the polyfills file, relative to the current workspace. - */ - polyfills?: string; - /** - * Do not use the real path when resolving modules. - */ - preserveSymlinks?: boolean; - /** - * Output profile events for Chrome profiler. - * @deprecated Use "NG_BUILD_PROFILING" environment variable instead. - */ - profile?: boolean; - /** - * Log progress to the console while building. - */ - progress?: boolean; - /** - * Change root relative URLs in stylesheets to include base HREF and deploy URL. Use only - * for compatibility and transition. The behavior of this option is non-standard and will be - * removed in the next major release. - * @deprecated - */ - rebaseRootRelativeCssUrls?: boolean; - /** - * The path where style resources will be placed, relative to outputPath. - */ - resourcesOutputPath?: string; - /** - * Global scripts to be included in the build. - */ - scripts?: ExtraEntryPoint[]; - /** - * Generates a service worker config for production builds. - */ - serviceWorker?: boolean; - /** - * Show circular dependency warnings on builds. - */ - showCircularDependencies?: boolean; - /** - * Flag to prevent building an app shell. - * @deprecated - */ - skipAppShell?: boolean; - /** - * Output sourcemaps. - */ - sourceMap?: SourceMapUnion; - /** - * Generates a 'stats.json' file which can be analyzed using tools such as - * 'webpack-bundle-analyzer'. - */ - statsJson?: boolean; - /** - * Options to pass to style preprocessors. - */ - stylePreprocessorOptions?: StylePreprocessorOptions; - /** - * Global styles to be included in the build. - */ - styles?: ExtraEntryPoint[]; - /** - * Enables the use of subresource integrity validation. - */ - subresourceIntegrity?: boolean; - /** - * The full path for the TypeScript configuration file, relative to the current workspace. - */ - tsConfig: string; - /** - * Use a separate bundle containing only vendor libraries. - */ - vendorChunk?: boolean; - /** - * Resolve vendor packages sourcemaps. - * @deprecated - */ - vendorSourceMap?: boolean; - /** - * Adds more details to output logging. - */ - verbose?: boolean; - /** - * Run build when files change. - */ - watch?: boolean; - /** - * TypeScript configuration for Web Worker modules. - */ - webWorkerTsConfig?: string; + /** + * Build using Ahead of Time compilation. + */ + aot?: boolean; + /** + * List of static application assets. + */ + assets?: AssetPattern[]; + /** + * Base url for the application being built. + */ + baseHref?: string; + /** + * Budget thresholds to ensure parts of your application stay within boundaries which you + * set. + */ + budgets?: Budget[]; + /** + * Enables '@angular-devkit/build-optimizer' optimizations when using the 'aot' option. + */ + buildOptimizer?: boolean; + /** + * Use a separate bundle containing code used across multiple bundles. + */ + commonChunk?: boolean; + /** + * Define the crossorigin attribute setting of elements that provide CORS support. + */ + crossOrigin?: CrossOrigin; + /** + * Delete the output path before building. + */ + deleteOutputPath?: boolean; + /** + * URL where files will be deployed. + */ + deployUrl?: string; + /** + * Enables conditionally loaded ES2015 polyfills. + * @deprecated This will be determined from the list of supported browsers specified in the + * 'browserslist' file. + */ + es5BrowserSupport?: boolean; + /** + * Output in-file eval sourcemaps. + * @deprecated + */ + evalSourceMap?: boolean; + /** + * Concatenate modules with Rollup before bundling them with Webpack. + */ + experimentalRollupPass?: boolean; + /** + * Extract css from global styles into css files instead of js ones. + */ + extractCss?: boolean; + /** + * Extract all licenses in a separate file. + */ + extractLicenses?: boolean; + /** + * Replace files with other files in the build. + */ + fileReplacements?: FileReplacement[]; + /** + * Run the TypeScript type checker in a forked process. + */ + forkTypeChecker?: boolean; + /** + * Localization file to use for i18n. + * @deprecated Use 'locales' object in the project metadata instead. + */ + i18nFile?: string; + /** + * Format of the localization file specified with --i18n-file. + * @deprecated No longer needed as the format will be determined automatically. + */ + i18nFormat?: string; + /** + * Locale to use for i18n. + * @deprecated Use 'localize' instead. + */ + i18nLocale?: string; + /** + * How to handle missing translations for i18n. + */ + i18nMissingTranslation?: I18NMissingTranslation; + /** + * Configures the generation of the application's HTML index. + */ + index: IndexUnion; + /** + * List of additional NgModule files that will be lazy loaded. Lazy router modules will be + * discovered automatically. + * @deprecated 'SystemJsNgModuleLoader' is deprecated, and this is part of its usage. Use + * 'import()' syntax instead. + */ + lazyModules?: string[]; + localize?: Localize; + /** + * The full path for the main entry point to the app, relative to the current workspace. + */ + main: string; + /** + * Use file name for lazy loaded chunks. + */ + namedChunks?: boolean; + /** + * Path to ngsw-config.json. + */ + ngswConfigPath?: string; + /** + * Enables optimization of the build output. + */ + optimization?: OptimizationUnion; + /** + * Define the output filename cache-busting hashing mode. + */ + outputHashing?: OutputHashing; + /** + * The full path for the new output directory, relative to the current workspace. + * + * By default, writes output to a folder named dist/ in the current project. + */ + outputPath: string; + /** + * Enable and define the file watching poll time period in milliseconds. + */ + poll?: number; + /** + * The full path for the polyfills file, relative to the current workspace. + */ + polyfills?: string; + /** + * Do not use the real path when resolving modules. + */ + preserveSymlinks?: boolean; + /** + * Output profile events for Chrome profiler. + * @deprecated Use "NG_BUILD_PROFILING" environment variable instead. + */ + profile?: boolean; + /** + * Log progress to the console while building. + */ + progress?: boolean; + /** + * Change root relative URLs in stylesheets to include base HREF and deploy URL. Use only + * for compatibility and transition. The behavior of this option is non-standard and will be + * removed in the next major release. + * @deprecated + */ + rebaseRootRelativeCssUrls?: boolean; + /** + * The path where style resources will be placed, relative to outputPath. + */ + resourcesOutputPath?: string; + /** + * Global scripts to be included in the build. + */ + scripts?: ExtraEntryPoint[]; + /** + * Generates a service worker config for production builds. + */ + serviceWorker?: boolean; + /** + * Show circular dependency warnings on builds. + */ + showCircularDependencies?: boolean; + /** + * Flag to prevent building an app shell. + * @deprecated + */ + skipAppShell?: boolean; + /** + * Output sourcemaps. + */ + sourceMap?: SourceMapUnion; + /** + * Generates a 'stats.json' file which can be analyzed using tools such as + * 'webpack-bundle-analyzer'. + */ + statsJson?: boolean; + /** + * Options to pass to style preprocessors. + */ + stylePreprocessorOptions?: StylePreprocessorOptions; + /** + * Global styles to be included in the build. + */ + styles?: ExtraEntryPoint[]; + /** + * Enables the use of subresource integrity validation. + */ + subresourceIntegrity?: boolean; + /** + * The full path for the TypeScript configuration file, relative to the current workspace. + */ + tsConfig: string; + /** + * Use a separate bundle containing only vendor libraries. + */ + vendorChunk?: boolean; + /** + * Resolve vendor packages sourcemaps. + * @deprecated + */ + vendorSourceMap?: boolean; + /** + * Adds more details to output logging. + */ + verbose?: boolean; + /** + * Run build when files change. + */ + watch?: boolean; + /** + * TypeScript configuration for Web Worker modules. + */ + webWorkerTsConfig?: string; } export type AssetPattern = AssetPatternClass | string; export interface AssetPatternClass { - /** - * The pattern to match. - */ - glob: string; - /** - * An array of globs to ignore. - */ - ignore?: string[]; - /** - * The input directory path in which to apply 'glob'. Defaults to the project root. - */ - input: string; - /** - * Absolute path within the output. - */ - output: string; + /** + * The pattern to match. + */ + glob: string; + /** + * An array of globs to ignore. + */ + ignore?: string[]; + /** + * The input directory path in which to apply 'glob'. Defaults to the project root. + */ + input: string; + /** + * Absolute path within the output. + */ + output: string; } export interface Budget { - /** - * The baseline size for comparison. - */ - baseline?: string; - /** - * The threshold for error relative to the baseline (min & max). - */ - error?: string; - /** - * The maximum threshold for error relative to the baseline. - */ - maximumError?: string; - /** - * The maximum threshold for warning relative to the baseline. - */ - maximumWarning?: string; - /** - * The minimum threshold for error relative to the baseline. - */ - minimumError?: string; - /** - * The minimum threshold for warning relative to the baseline. - */ - minimumWarning?: string; - /** - * The name of the bundle. - */ - name?: string; - /** - * The type of budget. - */ - type: Type; - /** - * The threshold for warning relative to the baseline (min & max). - */ - warning?: string; + /** + * The baseline size for comparison. + */ + baseline?: string; + /** + * The threshold for error relative to the baseline (min & max). + */ + error?: string; + /** + * The maximum threshold for error relative to the baseline. + */ + maximumError?: string; + /** + * The maximum threshold for warning relative to the baseline. + */ + maximumWarning?: string; + /** + * The minimum threshold for error relative to the baseline. + */ + minimumError?: string; + /** + * The minimum threshold for warning relative to the baseline. + */ + minimumWarning?: string; + /** + * The name of the bundle. + */ + name?: string; + /** + * The type of budget. + */ + type: Type; + /** + * The threshold for warning relative to the baseline (min & max). + */ + warning?: string; } /** * The type of budget. */ export enum Type { - All = "all", - AllScript = "allScript", - Any = "any", - AnyComponentStyle = "anyComponentStyle", - AnyScript = "anyScript", - Bundle = "bundle", - Initial = "initial", + All = 'all', + AllScript = 'allScript', + Any = 'any', + AnyComponentStyle = 'anyComponentStyle', + AnyScript = 'anyScript', + Bundle = 'bundle', + Initial = 'initial' } /** * Define the crossorigin attribute setting of elements that provide CORS support. */ export enum CrossOrigin { - Anonymous = "anonymous", - None = "none", - UseCredentials = "use-credentials", + Anonymous = 'anonymous', + None = 'none', + UseCredentials = 'use-credentials' } export interface FileReplacement { - replace?: string; - replaceWith?: string; - src?: string; - with?: string; + replace?: string; + replaceWith?: string; + src?: string; + with?: string; } /** * How to handle missing translations for i18n. */ export enum I18NMissingTranslation { - Error = "error", - Ignore = "ignore", - Warning = "warning", + Error = 'error', + Ignore = 'ignore', + Warning = 'warning' } /** @@ -335,15 +334,15 @@ export enum I18NMissingTranslation { export type IndexUnion = IndexObject | string; export interface IndexObject { - /** - * The path of a file to use for the application's generated HTML index. - */ - input: string; - /** - * The output path of the application's generated HTML index file. The full provided path - * will be used and will be considered relative to the application's configured output path. - */ - output?: string; + /** + * The path of a file to use for the application's generated HTML index. + */ + input: string; + /** + * The output path of the application's generated HTML index file. The full provided path + * will be used and will be considered relative to the application's configured output path. + */ + output?: string; } export type Localize = string[] | boolean; @@ -354,45 +353,45 @@ export type Localize = string[] | boolean; export type OptimizationUnion = boolean | OptimizationClass; export interface OptimizationClass { - /** - * Enables optimization of the scripts output. - */ - scripts?: boolean; - /** - * Enables optimization of the styles output. - */ - styles?: boolean; + /** + * Enables optimization of the scripts output. + */ + scripts?: boolean; + /** + * Enables optimization of the styles output. + */ + styles?: boolean; } /** * Define the output filename cache-busting hashing mode. */ export enum OutputHashing { - All = "all", - Bundles = "bundles", - Media = "media", - None = "none", + All = 'all', + Bundles = 'bundles', + Media = 'media', + None = 'none' } export type ExtraEntryPoint = ExtraEntryPointClass | string; export interface ExtraEntryPointClass { - /** - * The bundle name for this extra entry point. - */ - bundleName?: string; - /** - * If the bundle will be referenced in the HTML file. - */ - inject?: boolean; - /** - * The file to include. - */ - input: string; - /** - * If the bundle will be lazy loaded. - */ - lazy?: boolean; + /** + * The bundle name for this extra entry point. + */ + bundleName?: string; + /** + * If the bundle will be referenced in the HTML file. + */ + inject?: boolean; + /** + * The file to include. + */ + input: string; + /** + * If the bundle will be lazy loaded. + */ + lazy?: boolean; } /** @@ -401,30 +400,30 @@ export interface ExtraEntryPointClass { export type SourceMapUnion = boolean | SourceMapClass; export interface SourceMapClass { - /** - * Output sourcemaps used for error reporting tools. - */ - hidden?: boolean; - /** - * Output sourcemaps for all scripts. - */ - scripts?: boolean; - /** - * Output sourcemaps for all styles. - */ - styles?: boolean; - /** - * Resolve vendor packages sourcemaps. - */ - vendor?: boolean; + /** + * Output sourcemaps used for error reporting tools. + */ + hidden?: boolean; + /** + * Output sourcemaps for all scripts. + */ + scripts?: boolean; + /** + * Output sourcemaps for all styles. + */ + styles?: boolean; + /** + * Resolve vendor packages sourcemaps. + */ + vendor?: boolean; } /** * Options to pass to style preprocessors. */ export interface StylePreprocessorOptions { - /** - * Paths to include. Paths will be resolved to project root. - */ - includePaths?: string[]; + /** + * Paths to include. Paths will be resolved to project root. + */ + includePaths?: string[]; } diff --git a/packages/web/src/utils/build-angular/utils/build-browser-features.ts b/packages/web/src/utils/build-angular/utils/build-browser-features.ts index f579105c3d..10f4f58939 100644 --- a/packages/web/src/utils/build-angular/utils/build-browser-features.ts +++ b/packages/web/src/utils/build-angular/utils/build-browser-features.ts @@ -22,9 +22,11 @@ export class BuildBrowserFeatures { constructor( private projectRoot: string, - private scriptTarget: ts.ScriptTarget, + private scriptTarget: ts.ScriptTarget ) { - this._supportedBrowsers = browserslist(undefined, { path: this.projectRoot }); + this._supportedBrowsers = browserslist(undefined, { + path: this.projectRoot + }); this._es6TargetOrLater = this.scriptTarget > ts.ScriptTarget.ES5; } @@ -54,12 +56,11 @@ export class BuildBrowserFeatures { return false; } - const safariBrowsers = [ - 'safari 10.1', - 'ios_saf 10.3', - ]; + const safariBrowsers = ['safari 10.1', 'ios_saf 10.3']; - return this._supportedBrowsers.some(browser => safariBrowsers.includes(browser)); + return this._supportedBrowsers.some(browser => + safariBrowsers.includes(browser) + ); } /** @@ -70,24 +71,22 @@ export class BuildBrowserFeatures { // n: feature is unavailable // a: feature is partially supported // x: feature is prefixed - const criteria = [ - 'y', - 'a', - ]; + const criteria = ['y', 'a']; const data = feature(features[featureId]); - return !this._supportedBrowsers - .some(browser => { - const [agentId, version] = browser.split(' '); + return !this._supportedBrowsers.some(browser => { + const [agentId, version] = browser.split(' '); - const browserData = data.stats[agentId]; - const featureStatus = (browserData && browserData[version]) as string | undefined; + const browserData = data.stats[agentId]; + const featureStatus = (browserData && browserData[version]) as + | string + | undefined; - // We are only interested in the first character - // Ex: when 'a #4 #5', we only need to check for 'a' - // as for such cases we should polyfill these features as needed - return !featureStatus || !criteria.includes(featureStatus.charAt(0)); - }); + // We are only interested in the first character + // Ex: when 'a #4 #5', we only need to check for 'a' + // as for such cases we should polyfill these features as needed + return !featureStatus || !criteria.includes(featureStatus.charAt(0)); + }); } } diff --git a/packages/web/src/utils/build-angular/utils/build-browser-features_spec.ts b/packages/web/src/utils/build-angular/utils/build-browser-features_spec.ts index 0f111b563e..692eeed84d 100644 --- a/packages/web/src/utils/build-angular/utils/build-browser-features_spec.ts +++ b/packages/web/src/utils/build-angular/utils/build-browser-features_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ - import { TestProjectHost } from '@angular-devkit/architect/testing'; import { getSystemPath, join } from '@angular-devkit/core'; import { ScriptTarget } from 'typescript'; @@ -15,7 +14,8 @@ import { BuildBrowserFeatures } from './build-browser-features'; const devkitRoot = (global as any)._DevKitRoot; // tslint:disable-line:no-any const workspaceRoot = join( devkitRoot, - 'tests/angular_devkit/build_angular/hello-world-app/'); + 'tests/angular_devkit/build_angular/hello-world-app/' +); const host = new TestProjectHost(workspaceRoot); @@ -31,48 +31,48 @@ describe('BuildBrowserFeatures', () => { describe('isDifferentialLoadingNeeded', () => { it('should be true for for IE 9-11 and ES2015', () => { host.writeMultipleFiles({ - 'browserslist': 'IE 9-11', + browserslist: 'IE 9-11' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isDifferentialLoadingNeeded()).toBe(true); }); it('should be false for Chrome and ES2015', () => { host.writeMultipleFiles({ - 'browserslist': 'last 1 chrome version', + browserslist: 'last 1 chrome version' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isDifferentialLoadingNeeded()).toBe(false); }); it('detects no need for differential loading for target is ES5', () => { host.writeMultipleFiles({ - 'browserslist': 'last 1 chrome version', + browserslist: 'last 1 chrome version' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES5, + ScriptTarget.ES5 ); expect(buildBrowserFeatures.isDifferentialLoadingNeeded()).toBe(false); }); it('should be false for Safari 10.1 when target is ES2015', () => { host.writeMultipleFiles({ - 'browserslist': 'Safari 10.1', + browserslist: 'Safari 10.1' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isDifferentialLoadingNeeded()).toBe(false); }); @@ -81,48 +81,48 @@ describe('BuildBrowserFeatures', () => { describe('isFeatureSupported', () => { it('should be true for es6-module and Safari 10.1', () => { host.writeMultipleFiles({ - 'browserslist': 'Safari 10.1', + browserslist: 'Safari 10.1' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isFeatureSupported('es6-module')).toBe(true); }); it('should be false for es6-module and IE9', () => { host.writeMultipleFiles({ - 'browserslist': 'IE 9', + browserslist: 'IE 9' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isFeatureSupported('es6-module')).toBe(false); }); it('should be true for es6-module and last 1 chrome version', () => { host.writeMultipleFiles({ - 'browserslist': 'last 1 chrome version', + browserslist: 'last 1 chrome version' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isFeatureSupported('es6-module')).toBe(true); }); it('should be true for es6-module and Edge 18', () => { host.writeMultipleFiles({ - 'browserslist': 'Edge 18', + browserslist: 'Edge 18' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isFeatureSupported('es6-module')).toBe(true); }); @@ -131,63 +131,63 @@ describe('BuildBrowserFeatures', () => { describe('isNoModulePolyfillNeeded', () => { it('should be false for Safari 10.1 when target is ES5', () => { host.writeMultipleFiles({ - 'browserslist': 'Safari 10.1', + browserslist: 'Safari 10.1' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES5, + ScriptTarget.ES5 ); expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(false); }); it('should be false for Safari 10.1 when target is ES2015', () => { host.writeMultipleFiles({ - 'browserslist': 'Safari 10.1', + browserslist: 'Safari 10.1' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(false); }); it('should be true for Safari 9+ when target is ES2015', () => { host.writeMultipleFiles({ - 'browserslist': 'Safari >= 9', + browserslist: 'Safari >= 9' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(true); }); it('should be false for Safari 9+ when target is ES5', () => { host.writeMultipleFiles({ - 'browserslist': 'Safari >= 9', + browserslist: 'Safari >= 9' }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES5, + ScriptTarget.ES5 ); expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(false); }); it('should be false when not supporting Safari 10.1 target is ES2015', () => { host.writeMultipleFiles({ - 'browserslist': ` + browserslist: ` Edge 18 IE 9 - `, + ` }); const buildBrowserFeatures = new BuildBrowserFeatures( workspaceRootSysPath, - ScriptTarget.ES2015, + ScriptTarget.ES2015 ); expect(buildBrowserFeatures.isNoModulePolyfillNeeded()).toBe(false); }); diff --git a/packages/web/src/utils/build-angular/utils/delete-output-dir.ts b/packages/web/src/utils/build-angular/utils/delete-output-dir.ts index 30682efc6e..3af6fa2b6b 100644 --- a/packages/web/src/utils/build-angular/utils/delete-output-dir.ts +++ b/packages/web/src/utils/build-angular/utils/delete-output-dir.ts @@ -12,14 +12,18 @@ import { concatMap, last } from 'rxjs/operators'; /** * Delete an output directory, but error out if it's the root of the project. */ -export function deleteOutputDir(root: Path, outputPath: Path, host: virtualFs.Host) { +export function deleteOutputDir( + root: Path, + outputPath: Path, + host: virtualFs.Host +) { const resolvedOutputPath = resolve(root, outputPath); if (resolvedOutputPath === root) { throw new Error('Output path MUST not be project root directory!'); } return host.exists(resolvedOutputPath).pipe( - concatMap(exists => exists ? host.delete(resolvedOutputPath) : EMPTY), - last(null, null), + concatMap(exists => (exists ? host.delete(resolvedOutputPath) : EMPTY)), + last(null, null) ); } diff --git a/packages/web/src/utils/build-angular/utils/mangle-options.ts b/packages/web/src/utils/build-angular/utils/mangle-options.ts index 84be67375d..60085e43c8 100644 --- a/packages/web/src/utils/build-angular/utils/mangle-options.ts +++ b/packages/web/src/utils/build-angular/utils/mangle-options.ts @@ -7,4 +7,5 @@ */ const mangleVariable = process.env['NG_BUILD_MANGLE']; export const manglingDisabled = - !!mangleVariable && (mangleVariable === '0' || mangleVariable.toLowerCase() === 'false'); + !!mangleVariable && + (mangleVariable === '0' || mangleVariable.toLowerCase() === 'false'); diff --git a/packages/web/src/utils/build-angular/utils/normalize-asset-patterns.ts b/packages/web/src/utils/build-angular/utils/normalize-asset-patterns.ts index 528d526fab..68b3aabd31 100644 --- a/packages/web/src/utils/build-angular/utils/normalize-asset-patterns.ts +++ b/packages/web/src/utils/build-angular/utils/normalize-asset-patterns.ts @@ -14,11 +14,10 @@ import { normalize, relative, resolve, - virtualFs, + virtualFs } from '@angular-devkit/core'; import { AssetPattern, AssetPatternClass } from '../browser/schema'; - export class MissingAssetSourceRootException extends BaseException { constructor(path: String) { super(`The ${path} asset path must start with the project source root.`); @@ -30,7 +29,7 @@ export function normalizeAssetPatterns( host: virtualFs.SyncDelegateHost, root: Path, projectRoot: Path, - maybeSourceRoot: Path | undefined, + maybeSourceRoot: Path | undefined ): AssetPatternClass[] { // When sourceRoot is not available, we default to ${projectRoot}/src. const sourceRoot = maybeSourceRoot || join(projectRoot, 'src'); @@ -40,47 +39,46 @@ export function normalizeAssetPatterns( return []; } - return assetPatterns - .map(assetPattern => { - // Normalize string asset patterns to objects. - if (typeof assetPattern === 'string') { - const assetPath = normalize(assetPattern); - const resolvedAssetPath = resolve(root, assetPath); + return assetPatterns.map(assetPattern => { + // Normalize string asset patterns to objects. + if (typeof assetPattern === 'string') { + const assetPath = normalize(assetPattern); + const resolvedAssetPath = resolve(root, assetPath); - // Check if the string asset is within sourceRoot. - if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) { - throw new MissingAssetSourceRootException(assetPattern); - } - - let glob: string, input: Path, output: Path; - let isDirectory = false; - - try { - isDirectory = host.isDirectory(resolvedAssetPath); - } catch { - isDirectory = true; - } - - if (isDirectory) { - // Folders get a recursive star glob. - glob = '**/*'; - // Input directory is their original path. - input = assetPath; - } else { - // Files are their own glob. - glob = basename(assetPath); - // Input directory is their original dirname. - input = dirname(assetPath); - } - - // Output directory for both is the relative path from source root to input. - output = relative(resolvedSourceRoot, resolve(root, input)); - - // Return the asset pattern in object format. - return { glob, input, output }; - } else { - // It's already an AssetPatternObject, no need to convert. - return assetPattern; + // Check if the string asset is within sourceRoot. + if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) { + throw new MissingAssetSourceRootException(assetPattern); } - }); + + let glob: string, input: Path, output: Path; + let isDirectory = false; + + try { + isDirectory = host.isDirectory(resolvedAssetPath); + } catch { + isDirectory = true; + } + + if (isDirectory) { + // Folders get a recursive star glob. + glob = '**/*'; + // Input directory is their original path. + input = assetPath; + } else { + // Files are their own glob. + glob = basename(assetPath); + // Input directory is their original dirname. + input = dirname(assetPath); + } + + // Output directory for both is the relative path from source root to input. + output = relative(resolvedSourceRoot, resolve(root, input)); + + // Return the asset pattern in object format. + return { glob, input, output }; + } else { + // It's already an AssetPatternObject, no need to convert. + return assetPattern; + } + }); } diff --git a/packages/web/src/utils/build-angular/utils/normalize-builder-schema.ts b/packages/web/src/utils/build-angular/utils/normalize-builder-schema.ts index 47e663088b..4a385935b0 100644 --- a/packages/web/src/utils/build-angular/utils/normalize-builder-schema.ts +++ b/packages/web/src/utils/build-angular/utils/normalize-builder-schema.ts @@ -1,4 +1,3 @@ - /** * @license * Copyright Google Inc. All Rights Reserved. @@ -7,50 +6,62 @@ * found in the LICENSE file at https://angular.io/license */ - import { Path, virtualFs } from '@angular-devkit/core'; import { BuildOptions } from '../angular-cli-files/models/build-options'; import { AssetPatternClass, OptimizationClass, Schema as BrowserBuilderSchema, - SourceMapClass, + SourceMapClass } from '../browser/schema'; import { normalizeAssetPatterns } from './normalize-asset-patterns'; import { NormalizedFileReplacement, - normalizeFileReplacements, + normalizeFileReplacements } from './normalize-file-replacements'; import { normalizeOptimization } from './normalize-optimization'; import { normalizeSourceMaps } from './normalize-source-maps'; - /** * A normalized browser builder schema. */ -export type NormalizedBrowserBuilderSchema = BrowserBuilderSchema & BuildOptions & { - sourceMap: SourceMapClass; - assets: AssetPatternClass[]; - fileReplacements: NormalizedFileReplacement[]; - optimization: OptimizationClass; -}; +export type NormalizedBrowserBuilderSchema = BrowserBuilderSchema & + BuildOptions & { + sourceMap: SourceMapClass; + assets: AssetPatternClass[]; + fileReplacements: NormalizedFileReplacement[]; + optimization: OptimizationClass; + }; export function normalizeBrowserSchema( host: virtualFs.Host<{}>, root: Path, projectRoot: Path, sourceRoot: Path | undefined, - options: BrowserBuilderSchema, + options: BrowserBuilderSchema ): NormalizedBrowserBuilderSchema { const syncHost = new virtualFs.SyncDelegateHost(host); - const normalizedSourceMapOptions = normalizeSourceMaps(options.sourceMap || false); - normalizedSourceMapOptions.vendor = normalizedSourceMapOptions.vendor || options.vendorSourceMap; + const normalizedSourceMapOptions = normalizeSourceMaps( + options.sourceMap || false + ); + normalizedSourceMapOptions.vendor = + normalizedSourceMapOptions.vendor || options.vendorSourceMap; return { ...options, - assets: normalizeAssetPatterns(options.assets || [], syncHost, root, projectRoot, sourceRoot), - fileReplacements: normalizeFileReplacements(options.fileReplacements || [], syncHost, root), + assets: normalizeAssetPatterns( + options.assets || [], + syncHost, + root, + projectRoot, + sourceRoot + ), + fileReplacements: normalizeFileReplacements( + options.fileReplacements || [], + syncHost, + root + ), optimization: normalizeOptimization(options.optimization), sourceMap: normalizedSourceMapOptions, @@ -60,10 +71,11 @@ export function normalizeBrowserSchema( scripts: options.scripts || [], styles: options.styles || [], stylePreprocessorOptions: { - includePaths: options.stylePreprocessorOptions - && options.stylePreprocessorOptions.includePaths - || [], + includePaths: + (options.stylePreprocessorOptions && + options.stylePreprocessorOptions.includePaths) || + [] }, - lazyModules: options.lazyModules || [], + lazyModules: options.lazyModules || [] }; } diff --git a/packages/web/src/utils/build-angular/utils/normalize-file-replacements.ts b/packages/web/src/utils/build-angular/utils/normalize-file-replacements.ts index d5c3425a4a..b1097ef06b 100644 --- a/packages/web/src/utils/build-angular/utils/normalize-file-replacements.ts +++ b/packages/web/src/utils/build-angular/utils/normalize-file-replacements.ts @@ -12,11 +12,10 @@ import { getSystemPath, join, normalize, - virtualFs, + virtualFs } from '@angular-devkit/core'; import { FileReplacement } from '../browser/schema'; - export class MissingFileReplacementException extends BaseException { constructor(path: String) { super(`The ${path} path in file replacements does not exist.`); @@ -31,14 +30,15 @@ export interface NormalizedFileReplacement { export function normalizeFileReplacements( fileReplacements: FileReplacement[], host: virtualFs.SyncDelegateHost, - root: Path, + root: Path ): NormalizedFileReplacement[] { if (fileReplacements.length === 0) { return []; } - const normalizedReplacement = fileReplacements - .map(replacement => normalizeFileReplacement(replacement, root)); + const normalizedReplacement = fileReplacements.map(replacement => + normalizeFileReplacement(replacement, root) + ); for (const { replace, with: replacementWith } of normalizedReplacement) { if (!host.exists(replacementWith)) { @@ -55,7 +55,7 @@ export function normalizeFileReplacements( function normalizeFileReplacement( fileReplacement: FileReplacement, - root?: Path, + root?: Path ): NormalizedFileReplacement { let replacePath: Path; let withPath: Path; @@ -66,7 +66,9 @@ function normalizeFileReplacement( replacePath = normalize(fileReplacement.replace); withPath = normalize(fileReplacement.with); } else { - throw new Error(`Invalid file replacement: ${JSON.stringify(fileReplacement)}`); + throw new Error( + `Invalid file replacement: ${JSON.stringify(fileReplacement)}` + ); } // TODO: For 7.x should this only happen if not absolute? diff --git a/packages/web/src/utils/build-angular/utils/normalize-optimization.ts b/packages/web/src/utils/build-angular/utils/normalize-optimization.ts index 1ebec9c2a7..ce71a90b87 100644 --- a/packages/web/src/utils/build-angular/utils/normalize-optimization.ts +++ b/packages/web/src/utils/build-angular/utils/normalize-optimization.ts @@ -9,10 +9,12 @@ import { OptimizationClass, OptimizationUnion } from '../browser/schema'; export function normalizeOptimization( - optimization: OptimizationUnion = false, + optimization: OptimizationUnion = false ): Required { return { - scripts: typeof optimization === 'object' ? !!optimization.scripts : optimization, - styles: typeof optimization === 'object' ? !!optimization.styles : optimization, + scripts: + typeof optimization === 'object' ? !!optimization.scripts : optimization, + styles: + typeof optimization === 'object' ? !!optimization.styles : optimization }; } diff --git a/packages/web/src/utils/build-angular/utils/normalize-source-maps.ts b/packages/web/src/utils/build-angular/utils/normalize-source-maps.ts index ca873b59fa..b70ffdccc9 100644 --- a/packages/web/src/utils/build-angular/utils/normalize-source-maps.ts +++ b/packages/web/src/utils/build-angular/utils/normalize-source-maps.ts @@ -11,13 +11,13 @@ import { SourceMapClass, SourceMapUnion } from '../browser/schema'; export function normalizeSourceMaps(sourceMap: SourceMapUnion): SourceMapClass { const scripts = typeof sourceMap === 'object' ? sourceMap.scripts : sourceMap; const styles = typeof sourceMap === 'object' ? sourceMap.styles : sourceMap; - const hidden = typeof sourceMap === 'object' && sourceMap.hidden || false; - const vendor = typeof sourceMap === 'object' && sourceMap.vendor || false; + const hidden = (typeof sourceMap === 'object' && sourceMap.hidden) || false; + const vendor = (typeof sourceMap === 'object' && sourceMap.vendor) || false; return { vendor, hidden, scripts, - styles, + styles }; } diff --git a/packages/web/src/utils/build-angular/utils/process-bundle-bootstrap.js b/packages/web/src/utils/build-angular/utils/process-bundle-bootstrap.js index b2b1965f4c..f92255e87b 100644 --- a/packages/web/src/utils/build-angular/utils/process-bundle-bootstrap.js +++ b/packages/web/src/utils/build-angular/utils/process-bundle-bootstrap.js @@ -6,4 +6,4 @@ * found in the LICENSE file at https://angular.io/license */ require('../../../../../lib/bootstrap-local'); -module.exports = require('./process-bundle.ts'); \ No newline at end of file +module.exports = require('./process-bundle.ts'); diff --git a/packages/web/src/utils/build-angular/utils/process-bundle.ts b/packages/web/src/utils/build-angular/utils/process-bundle.ts index bca7f8a16a..5a9442db6c 100644 --- a/packages/web/src/utils/build-angular/utils/process-bundle.ts +++ b/packages/web/src/utils/build-angular/utils/process-bundle.ts @@ -8,7 +8,11 @@ import { createHash } from 'crypto'; import * as fs from 'fs'; import * as path from 'path'; -import { RawSourceMap, SourceMapConsumer, SourceMapGenerator } from 'source-map'; +import { + RawSourceMap, + SourceMapConsumer, + SourceMapGenerator +} from 'source-map'; import { minify } from 'terser'; import { ScriptTarget, transpileModule } from 'typescript'; import { SourceMapSource } from 'webpack-sources'; @@ -54,7 +58,7 @@ export const enum CacheKey { OriginalCode = 0, OriginalMap = 1, DownlevelCode = 2, - DownlevelMap = 3, + DownlevelMap = 3 } let cachePath: string | undefined; @@ -63,15 +67,21 @@ export function setup(options: { cachePath: string }): void { cachePath = options.cachePath; } -async function cachePut(content: string, key: string | null, integrity?: string): Promise { +async function cachePut( + content: string, + key: string | null, + integrity?: string +): Promise { if (cachePath && key) { await cacache.put(cachePath, key, content, { - metadata: { integrity }, + metadata: { integrity } }); } } -export async function process(options: ProcessBundleOptions): Promise { +export async function process( + options: ProcessBundleOptions +): Promise { if (!options.cacheKeys) { options.cacheKeys = []; } @@ -79,7 +89,10 @@ export async function process(options: ProcessBundleOptions): Promise { +async function mangleOriginal( + options: ProcessBundleOptions +): Promise { const result = terserMangle(options.code, { filename: path.basename(options.filename), map: options.map ? JSON.parse(options.map) : undefined, - ecma: 6, + ecma: 6 }); let mapContent; if (result.map) { if (!options.hiddenSourceMaps) { - result.code += `\n//# sourceMappingURL=${path.basename(options.filename)}.map`; + result.code += `\n//# sourceMappingURL=${path.basename( + options.filename + )}.map`; } mapContent = JSON.stringify(result.map); await cachePut( mapContent, - (options.cacheKeys && options.cacheKeys[CacheKey.OriginalMap]) || null, + (options.cacheKeys && options.cacheKeys[CacheKey.OriginalMap]) || null ); fs.writeFileSync(options.filename + '.map', mapContent); } @@ -268,13 +288,13 @@ async function mangleOriginal(options: ProcessBundleOptions): Promise terser is already supported; need babel -> estree/terser @@ -296,7 +321,7 @@ function terserMangle( safari10: true, output: { ascii_only: true, - webkit: true, + webkit: true }, sourceMap: !!options.map && @@ -305,10 +330,10 @@ function terserMangle( // terser uses an old version of the sourcemap typings // tslint:disable-next-line: no-any content: options.map as any, - asObject: true, + asObject: true // typings don't include asObject option // tslint:disable-next-line: no-any - } as any), + } as any) }); if (minifyOutput.error) { @@ -316,25 +341,29 @@ function terserMangle( } // tslint:disable-next-line: no-non-null-assertion - return { code: minifyOutput.code!, map: minifyOutput.map as unknown | RawSourceMap | undefined }; + return { + code: minifyOutput.code!, + map: minifyOutput.map as unknown | RawSourceMap | undefined + }; } function createFileEntry( filename: string, code: string, map: string | undefined, - integrityAlgorithm?: string, + integrityAlgorithm?: string ): ProcessBundleFile { return { filename: filename, size: Buffer.byteLength(code), - integrity: integrityAlgorithm && generateIntegrityValue(integrityAlgorithm, code), + integrity: + integrityAlgorithm && generateIntegrityValue(integrityAlgorithm, code), map: !map ? undefined : { filename: filename + '.map', - size: Buffer.byteLength(map), - }, + size: Buffer.byteLength(map) + } }; } @@ -352,7 +381,7 @@ function generateIntegrityValue(hashAlgorithm: string, code: string) { // However, two variants are still needed due to lazy routing and SRI differences // NOTE: This should eventually be a babel plugin async function processRuntime( - options: ProcessBundleOptions, + options: ProcessBundleOptions ): Promise> { let originalCode = options.code; let downlevelCode = options.code; @@ -365,10 +394,16 @@ async function processRuntime( } if (data.original && data.original.integrity) { - originalCode = originalCode.replace(data.integrity, data.original.integrity); + originalCode = originalCode.replace( + data.integrity, + data.original.integrity + ); } if (data.downlevel && data.downlevel.integrity) { - downlevelCode = downlevelCode.replace(data.integrity, data.downlevel.integrity); + downlevelCode = downlevelCode.replace( + data.integrity, + data.downlevel.integrity + ); } } } @@ -383,7 +418,7 @@ async function processRuntime( if (options.optimize) { const minifiyResults = terserMangle(downlevelCode, { filename: path.basename(downlevelFilePath), - map: options.map === undefined ? undefined : JSON.parse(options.map), + map: options.map === undefined ? undefined : JSON.parse(options.map) }); downlevelCode = minifiyResults.code; downlevelMap = JSON.stringify(minifiyResults.map); @@ -394,8 +429,8 @@ async function processRuntime( downlevelFilePath, downlevelCode, downlevelMap, - options.integrityAlgorithm, - ), + options.integrityAlgorithm + ) }; } else { if (options.map) { @@ -409,28 +444,30 @@ async function processRuntime( options.filename, originalCode, options.map, - options.integrityAlgorithm, + options.integrityAlgorithm ), downlevel: createFileEntry( downlevelFilePath, downlevelCode, downlevelMap, - options.integrityAlgorithm, - ), + options.integrityAlgorithm + ) }; } if (downlevelMap) { await cachePut( downlevelMap, - (options.cacheKeys && options.cacheKeys[CacheKey.DownlevelMap]) || null, + (options.cacheKeys && options.cacheKeys[CacheKey.DownlevelMap]) || null ); fs.writeFileSync(downlevelFilePath + '.map', downlevelMap); - downlevelCode += `\n//# sourceMappingURL=${path.basename(downlevelFilePath)}.map`; + downlevelCode += `\n//# sourceMappingURL=${path.basename( + downlevelFilePath + )}.map`; } await cachePut( downlevelCode, - (options.cacheKeys && options.cacheKeys[CacheKey.DownlevelCode]) || null, + (options.cacheKeys && options.cacheKeys[CacheKey.DownlevelCode]) || null ); fs.writeFileSync(downlevelFilePath, downlevelCode); diff --git a/packages/web/src/utils/build-angular/utils/run-module-as-observable-fork.ts b/packages/web/src/utils/build-angular/utils/run-module-as-observable-fork.ts index ab13efdb0e..57947ce1ea 100644 --- a/packages/web/src/utils/build-angular/utils/run-module-as-observable-fork.ts +++ b/packages/web/src/utils/build-angular/utils/run-module-as-observable-fork.ts @@ -11,27 +11,26 @@ import { resolve } from 'path'; import { Observable } from 'rxjs'; const treeKill = require('tree-kill'); - export function runModuleAsObservableFork( cwd: string, modulePath: string, exportName: string | undefined, // tslint:disable-next-line:no-any - args: any[], + args: any[] ): Observable { return new Observable(obs => { const workerPath: string = resolve(__dirname, './run-module-worker.js'); const debugArgRegex = /--inspect(?:-brk|-port)?|--debug(?:-brk|-port)/; - const execArgv = process.execArgv.filter((arg) => { + const execArgv = process.execArgv.filter(arg => { // Remove debug args. // Workaround for https://github.com/nodejs/node/issues/9435 return !debugArgRegex.test(arg); }); - const forkOptions: ForkOptions = { + const forkOptions: ForkOptions = ({ cwd, - execArgv, - } as {} as ForkOptions; + execArgv + } as {}) as ForkOptions; // TODO: support passing in a logger to use as stdio streams // if (logger) { @@ -77,7 +76,7 @@ export function runModuleAsObservableFork( hash: '5d4b9a5c0a4e0f9977598437b0e85bcc', modulePath, exportName, - args, + args }); // Teardown logic. When unsubscribing, kill the forked process. diff --git a/packages/web/src/utils/build-angular/utils/run-module-worker.js b/packages/web/src/utils/build-angular/utils/run-module-worker.js index 2370abead7..21033a3186 100644 --- a/packages/web/src/utils/build-angular/utils/run-module-worker.js +++ b/packages/web/src/utils/build-angular/utils/run-module-worker.js @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -process.on('message', (message) => { +process.on('message', message => { // Only process messages with the hash in 'run-module-as-observable-fork.ts'. if (message.hash === '5d4b9a5c0a4e0f9977598437b0e85bcc') { const requiredModule = require(message.modulePath); @@ -17,4 +17,3 @@ process.on('message', (message) => { } } }); - diff --git a/packages/web/src/utils/build-angular/utils/version.ts b/packages/web/src/utils/build-angular/utils/version.ts index 3a1b47c943..657d755138 100644 --- a/packages/web/src/utils/build-angular/utils/version.ts +++ b/packages/web/src/utils/build-angular/utils/version.ts @@ -8,15 +8,24 @@ import { logging, tags } from '@angular-devkit/core'; import { SemVer, gte, satisfies } from 'semver'; -export function assertCompatibleAngularVersion(projectRoot: string, logger: logging.LoggerApi) { +export function assertCompatibleAngularVersion( + projectRoot: string, + logger: logging.LoggerApi +) { let angularCliPkgJson; let angularPkgJson; let rxjsPkgJson; const resolveOptions = { paths: [projectRoot] }; try { - const angularPackagePath = require.resolve('@angular/core/package.json', resolveOptions); - const rxjsPackagePath = require.resolve('rxjs/package.json', resolveOptions); + const angularPackagePath = require.resolve( + '@angular/core/package.json', + resolveOptions + ); + const rxjsPackagePath = require.resolve( + 'rxjs/package.json', + resolveOptions + ); angularPkgJson = require(angularPackagePath); rxjsPkgJson = require(rxjsPackagePath); @@ -28,7 +37,14 @@ export function assertCompatibleAngularVersion(projectRoot: string, logger: logg process.exit(2); } - if (!(angularPkgJson && angularPkgJson['version'] && rxjsPkgJson && rxjsPkgJson['version'])) { + if ( + !( + angularPkgJson && + angularPkgJson['version'] && + rxjsPkgJson && + rxjsPkgJson['version'] + ) + ) { logger.error(tags.stripIndents` Cannot determine versions of "@angular/core" and/or "rxjs". This likely means your local installation is broken. Please reinstall your packages. @@ -38,7 +54,10 @@ export function assertCompatibleAngularVersion(projectRoot: string, logger: logg } try { - const angularCliPkgPath = require.resolve('@angular/cli/package.json', resolveOptions); + const angularCliPkgPath = require.resolve( + '@angular/cli/package.json', + resolveOptions + ); angularCliPkgJson = require(angularCliPkgPath); if (!(angularCliPkgJson && angularCliPkgJson['version'])) { throw new Error(); @@ -66,7 +85,11 @@ export function assertCompatibleAngularVersion(projectRoot: string, logger: logg const angularVersion = new SemVer(angularPkgJson['version']); const rxjsVersion = new SemVer(rxjsPkgJson['version']); - if (!satisfies(angularVersion, supportedAngularSemver, { includePrerelease: true })) { + if ( + !satisfies(angularVersion, supportedAngularSemver, { + includePrerelease: true + }) + ) { logger.error( tags.stripIndents` This version of CLI is only compatible with Angular versions ${supportedAngularSemver}, @@ -74,7 +97,7 @@ export function assertCompatibleAngularVersion(projectRoot: string, logger: logg Please visit the link below to find instructions on how to update Angular. https://angular-update-guide.firebaseapp.com/ - ` + '\n', + ` + '\n' ); process.exit(3); @@ -90,18 +113,21 @@ export function assertCompatibleAngularVersion(projectRoot: string, logger: logg Please visit the link below to find instructions on how to update RxJs. https://docs.google.com/document/d/12nlLt71VLKb-z3YaSGzUfx6mJbc34nsMXtByPUN35cg/edit# - ` + '\n', + ` + '\n' ); process.exit(3); - } else if (gte(angularVersion, '6.0.0-rc.0') && !gte(rxjsVersion, '6.0.0-beta.0')) { + } else if ( + gte(angularVersion, '6.0.0-rc.0') && + !gte(rxjsVersion, '6.0.0-beta.0') + ) { logger.warn( tags.stripIndents` This project uses a temporary compatibility version of RxJs (${rxjsVersion}). Please visit the link below to find instructions on how to update RxJs. https://docs.google.com/document/d/12nlLt71VLKb-z3YaSGzUfx6mJbc34nsMXtByPUN35cg/edit# - ` + '\n', + ` + '\n' ); } } diff --git a/packages/web/src/utils/build-angular/utils/webpack-browser-config.ts b/packages/web/src/utils/build-angular/utils/webpack-browser-config.ts index c3682dac18..102fdf1e61 100644 --- a/packages/web/src/utils/build-angular/utils/webpack-browser-config.ts +++ b/packages/web/src/utils/build-angular/utils/webpack-browser-config.ts @@ -13,7 +13,7 @@ import { normalize, resolve, schema, - virtualFs, + virtualFs } from '@angular-devkit/core'; import { NodeJsSyncHost } from '@angular-devkit/core/node'; import * as fs from 'fs'; @@ -27,14 +27,16 @@ import { NormalizedBrowserBuilderSchema, defaultProgress, fullDifferential, - normalizeBrowserSchema, + normalizeBrowserSchema } from '../utils'; import { BuildBrowserFeatures } from './build-browser-features'; const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); const webpackMerge = require('webpack-merge'); -type BrowserWebpackConfigOptions = WebpackConfigOptions; +type BrowserWebpackConfigOptions = WebpackConfigOptions< + NormalizedBrowserBuilderSchema +>; export async function generateWebpackConfig( context: BuilderContext, @@ -42,12 +44,16 @@ export async function generateWebpackConfig( projectRoot: string, sourceRoot: string | undefined, options: NormalizedBrowserBuilderSchema, - webpackPartialGenerator: (wco: BrowserWebpackConfigOptions) => webpack.Configuration[], - logger: logging.LoggerApi, + webpackPartialGenerator: ( + wco: BrowserWebpackConfigOptions + ) => webpack.Configuration[], + logger: logging.LoggerApi ): Promise { // Ensure Build Optimizer is only used with AOT. if (options.buildOptimizer && !options.aot) { - throw new Error(`The 'buildOptimizer' option cannot be used without 'aot'.`); + throw new Error( + `The 'buildOptimizer' option cannot be used without 'aot'.` + ); } const tsConfigPath = path.resolve(workspaceRoot, options.tsConfig); @@ -59,10 +65,14 @@ export async function generateWebpackConfig( // At the moment, only the browser builder supports differential loading // However this config generation is used by multiple builders such as dev-server const scriptTarget = tsConfig.options.target || ts.ScriptTarget.ES5; - const buildBrowserFeatures = new BuildBrowserFeatures(projectRoot, scriptTarget); - const differentialLoading = context.builder.builderName === 'browser' - && !options.watch - && buildBrowserFeatures.isDifferentialLoadingNeeded(); + const buildBrowserFeatures = new BuildBrowserFeatures( + projectRoot, + scriptTarget + ); + const differentialLoading = + context.builder.builderName === 'browser' && + !options.watch && + buildBrowserFeatures.isDifferentialLoadingNeeded(); const scriptTargets = [scriptTarget]; @@ -73,29 +83,33 @@ export async function generateWebpackConfig( // For differential loading, we can have several targets return scriptTargets.map(scriptTarget => { let buildOptions: NormalizedBrowserBuilderSchema = { ...options }; - const supportES2015 - = scriptTarget !== ts.ScriptTarget.ES3 && scriptTarget !== ts.ScriptTarget.ES5; + const supportES2015 = + scriptTarget !== ts.ScriptTarget.ES3 && + scriptTarget !== ts.ScriptTarget.ES5; if (differentialLoading && fullDifferential) { buildOptions = { ...options, - ...( - // FIXME: we do create better webpack config composition to achieve the below - // When DL is enabled and supportES2015 is true it means that we are on the second build - // This also means that we don't need to include styles and assets multiple times - supportES2015 - ? {} - : { + ...// FIXME: we do create better webpack config composition to achieve the below + // When DL is enabled and supportES2015 is true it means that we are on the second build + // This also means that we don't need to include styles and assets multiple times + (supportES2015 + ? {} + : { styles: options.extractCss ? [] : options.styles, - assets: [], - } - ), + assets: [] + }), es5BrowserSupport: undefined, esVersionInFileName: true, - scriptTargetOverride: scriptTarget, + scriptTargetOverride: scriptTarget }; } else if (differentialLoading && !fullDifferential) { - buildOptions = { ...options, esVersionInFileName: true, scriptTargetOverride: ts.ScriptTarget.ES5, es5BrowserSupport: undefined }; + buildOptions = { + ...options, + esVersionInFileName: true, + scriptTargetOverride: ts.ScriptTarget.ES5, + es5BrowserSupport: undefined + }; } const wco: BrowserWebpackConfigOptions = { @@ -106,7 +120,7 @@ export async function generateWebpackConfig( buildOptions, tsConfig, tsConfigPath, - supportES2015, + supportES2015 }; wco.buildOptions.progress = defaultProgress(wco.buildOptions.progress); @@ -121,21 +135,24 @@ export async function generateWebpackConfig( if (!webpackConfig.resolve.alias) { webpackConfig.resolve.alias = {}; } - webpackConfig.resolve.alias['zone.js/dist/zone'] = 'zone.js/dist/zone-evergreen'; + webpackConfig.resolve.alias['zone.js/dist/zone'] = + 'zone.js/dist/zone-evergreen'; } if (options.profile || process.env['NG_BUILD_PROFILING']) { const esVersionInFileName = getEsVersionForFileName( - fullDifferential ? buildOptions.scriptTargetOverride : tsConfig.options.target, - wco.buildOptions.esVersionInFileName, + fullDifferential + ? buildOptions.scriptTargetOverride + : tsConfig.options.target, + wco.buildOptions.esVersionInFileName ); const smp = new SpeedMeasurePlugin({ outputFormat: 'json', outputTarget: path.resolve( workspaceRoot, - `speed-measure-plugin${esVersionInFileName}.json`, - ), + `speed-measure-plugin${esVersionInFileName}.json` + ) }); return smp.wrap(webpackConfig); @@ -145,18 +162,22 @@ export async function generateWebpackConfig( }); } - export async function generateBrowserWebpackConfigFromWorkspace( options: BrowserBuilderSchema, context: BuilderContext, projectName: string, workspace: experimental.workspace.Workspace, host: virtualFs.Host, - webpackPartialGenerator: (wco: BrowserWebpackConfigOptions) => webpack.Configuration[], - logger: logging.LoggerApi, + webpackPartialGenerator: ( + wco: BrowserWebpackConfigOptions + ) => webpack.Configuration[], + logger: logging.LoggerApi ): Promise { // TODO: Use a better interface for workspace access. - const projectRoot = resolve(workspace.root, normalize(workspace.getProject(projectName).root)); + const projectRoot = resolve( + workspace.root, + normalize(workspace.getProject(projectName).root) + ); const projectSourceRoot = workspace.getProject(projectName).sourceRoot; const sourceRoot = projectSourceRoot ? resolve(workspace.root, normalize(projectSourceRoot)) @@ -167,7 +188,7 @@ export async function generateBrowserWebpackConfigFromWorkspace( workspace.root, projectRoot, sourceRoot, - options, + options ); return generateWebpackConfig( @@ -177,30 +198,38 @@ export async function generateBrowserWebpackConfigFromWorkspace( sourceRoot && getSystemPath(sourceRoot), normalizedOptions, webpackPartialGenerator, - logger, + logger ); } - export async function generateBrowserWebpackConfigFromContext( options: BrowserBuilderSchema, context: BuilderContext, - webpackPartialGenerator: (wco: BrowserWebpackConfigOptions) => webpack.Configuration[], - host: virtualFs.Host = new NodeJsSyncHost(), -): Promise<{ workspace: experimental.workspace.Workspace, config: webpack.Configuration[] }> { + webpackPartialGenerator: ( + wco: BrowserWebpackConfigOptions + ) => webpack.Configuration[], + host: virtualFs.Host = new NodeJsSyncHost() +): Promise<{ + workspace: experimental.workspace.Workspace; + config: webpack.Configuration[]; +}> { const registry = new schema.CoreSchemaRegistry(); registry.addPostTransform(schema.transforms.addUndefinedDefaults); const workspace = await experimental.workspace.Workspace.fromPath( host, normalize(context.workspaceRoot), - registry, + registry ); - const projectName = context.target ? context.target.project : workspace.getDefaultProjectName(); + const projectName = context.target + ? context.target.project + : workspace.getDefaultProjectName(); if (!projectName) { - throw new Error('Must either have a target from the context or a default project.'); + throw new Error( + 'Must either have a target from the context or a default project.' + ); } const config = await generateBrowserWebpackConfigFromWorkspace( @@ -210,7 +239,7 @@ export async function generateBrowserWebpackConfigFromContext( workspace, host, webpackPartialGenerator, - context.logger, + context.logger ); return { workspace, config }; diff --git a/packages/web/src/utils/config.ts b/packages/web/src/utils/config.ts index e1c6e93251..56b1c01441 100644 --- a/packages/web/src/utils/config.ts +++ b/packages/web/src/utils/config.ts @@ -95,16 +95,14 @@ export function getBaseWebpackPartial( } if (options.extractLicenses) { - extraPlugins.push( - new LicenseWebpackPlugin({ - stats: { - warnings: false, - errors: false, - }, - perChunkOutput: false, - outputFilename: `3rdpartylicenses.txt`, - }) as any - ); + extraPlugins.push(new LicenseWebpackPlugin({ + stats: { + warnings: false, + errors: false + }, + perChunkOutput: false, + outputFilename: `3rdpartylicenses.txt` + }) as any); } // process asset entries