diff --git a/packages/angular/cli/lib/config/schema.json b/packages/angular/cli/lib/config/schema.json index cf93bf4e4dad..8a3117b98f1d 100644 --- a/packages/angular/cli/lib/config/schema.json +++ b/packages/angular/cli/lib/config/schema.json @@ -995,6 +995,11 @@ "type": "boolean", "description": "If the bundle will be lazy loaded.", "default": false + }, + "inject": { + "type": "boolean", + "description": "If the bundle will be referenced in the HTML file.", + "default": true } }, "additionalProperties": false, @@ -1516,6 +1521,11 @@ "type": "boolean", "description": "If the bundle will be lazy loaded.", "default": false + }, + "inject": { + "type": "boolean", + "description": "If the bundle will be referenced in the HTML file.", + "default": true } }, "additionalProperties": false, diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts index ec8318348e78..a553b2d98508 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts @@ -72,11 +72,9 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { tsConfig.options.target || ScriptTarget.ES5, ); if ((buildOptions.scriptTargetOverride || tsConfig.options.target) === ScriptTarget.ES5) { - if (buildOptions.es5BrowserSupport || - ( - buildOptions.es5BrowserSupport === undefined && - buildBrowserFeatures.isEs5SupportNeeded() - ) + if ( + buildOptions.es5BrowserSupport || + (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 @@ -91,7 +89,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { : [noModuleScript]; } - // For differential loading we don't need to generate a seperate polyfill file + // For 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 = buildBrowserFeatures.isDifferentialLoadingNeeded() ? 'polyfills' @@ -120,9 +118,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`), - })); + extraPlugins.push( + new debug.ProfilingPlugin({ + outputPath: path.resolve(root, `chrome-profiler-events${targetInFileName}.json`), + }), + ); } // determine hashing format @@ -130,50 +130,54 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { // process global scripts if (buildOptions.scripts.length > 0) { - const globalScriptsByBundleName = normalizeExtraEntryPoints(buildOptions.scripts, 'scripts') - .reduce((prev: { bundleName: string, paths: string[], lazy: 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.lazy && !curr.lazy) { - // All entries have to be lazy for the bundle to be lazy. - throw new Error(`The ${curr.bundleName} bundle is mixing lazy and non-lazy scripts.`); - } - - existingEntry.paths.push(resolvedPath); - } else { - prev.push({ - bundleName, - paths: [resolvedPath], - lazy: curr.lazy || false, - }); + 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.`, + ); } - return prev; - }, []); + existingEntry.paths.push(resolvedPath); + } else { + prev.push({ + bundleName, + paths: [resolvedPath], + inject: curr.inject, + }); + } + return prev; + }, []); // Add a new asset for each entry. - globalScriptsByBundleName.forEach((script) => { + globalScriptsByBundleName.forEach(script => { // Lazy scripts don't get a hash, otherwise they can't be loaded by name. - const hash = script.lazy ? '' : hashFormat.script; + const hash = script.inject ? hashFormat.script : ''; const bundleName = script.bundleName; - extraPlugins.push(new ScriptsWebpackPlugin({ - name: bundleName, - sourceMap: scriptsSourceMap, - filename: `${path.basename(bundleName)}${hash}.js`, - scripts: script.paths, - basePath: projectRoot, - })); + extraPlugins.push( + new ScriptsWebpackPlugin({ + name: bundleName, + sourceMap: scriptsSourceMap, + filename: `${path.basename(bundleName)}${hash}.js`, + scripts: script.paths, + 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 + '/'; @@ -198,8 +202,10 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { const copyWebpackPluginOptions = { ignore: ['.gitkeep', '**/.DS_Store', '**/Thumbs.db'] }; - const copyWebpackPluginInstance = new CopyWebpackPlugin(copyWebpackPluginPatterns, - copyWebpackPluginOptions); + const copyWebpackPluginInstance = new CopyWebpackPlugin( + copyWebpackPluginPatterns, + copyWebpackPluginOptions, + ); extraPlugins.push(copyWebpackPluginInstance); } @@ -208,20 +214,24 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { } if (buildOptions.showCircularDependencies) { - extraPlugins.push(new CircularDependencyPlugin({ - exclude: /([\\\/]node_modules[\\\/])|(ngfactory\.js$)/, - })); + extraPlugins.push( + new CircularDependencyPlugin({ + exclude: /([\\\/]node_modules[\\\/])|(ngfactory\.js$)/, + }), + ); } if (buildOptions.statsJson) { - extraPlugins.push(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); - }); - } - }); + extraPlugins.push( + 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); + }); + } + })(), + ); } if (buildOptions.namedChunks) { @@ -266,7 +276,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { : 'rxjs/_esm5/path-mapping'; const rxPaths = require(require.resolve(rxjsPathMappingImport, { paths: [projectRoot] })); alias = rxPaths(nodeModules); - } catch { } + } catch {} const extraMinimizers = []; if (stylesOptimization) { @@ -274,7 +284,7 @@ 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), }), ); } @@ -307,15 +317,18 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { }, // 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 - compress: (buildOptions.platform == 'server' ? { - global_defs: angularGlobalDefinitions, - } : { - pure_getters: buildOptions.buildOptimizer, - // 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, - }), + compress: + buildOptions.platform == 'server' + ? { + global_defs: angularGlobalDefinitions, + } + : { + pure_getters: buildOptions.buildOptimizer, + // 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, + }, // We also want to avoid mangling on server. ...(buildOptions.platform == 'server' ? { mangle: false } : {}), }; @@ -330,8 +343,10 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { ); } - if (wco.tsConfig.options.target !== undefined && - wco.tsConfig.options.target >= ScriptTarget.ES2017) { + if ( + wco.tsConfig.options.target !== undefined && + wco.tsConfig.options.target >= ScriptTarget.ES2017 + ) { wco.logger.warn(tags.stripIndent` WARNING: Zone.js does not support native async/await in ES2017. These blocks are not intercepted by zone.js and will not triggering change detection. @@ -340,18 +355,13 @@ 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', - ], + modules: [wco.tsConfig.options.baseUrl || projectRoot, 'node_modules'], alias, }, resolveLoader: { diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/styles.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/styles.ts index b7e13bb67203..12d99fbef39a 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/styles.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/styles.ts @@ -34,7 +34,7 @@ const postcssImports = require('postcss-import'); * require('node-sass') * require('sass-loader') */ - +// tslint:disable-next-line:no-big-function export function getStylesConfig(wco: WebpackConfigOptions) { const { root, buildOptions } = wco; const entryPoints: { [key: string]: string[] } = {}; @@ -46,10 +46,10 @@ export function getStylesConfig(wco: WebpackConfigOptions) { // Determine hashing format. const hashFormat = getOutputHashFormat(buildOptions.outputHashing as string); - const postcssPluginCreator = function (loader: webpack.loader.LoaderContext) { + const postcssPluginCreator = function(loader: webpack.loader.LoaderContext) { return [ postcssImports({ - resolve: (url: string) => url.startsWith('~') ? url.substr(1) : url, + resolve: (url: string) => (url.startsWith('~') ? url.substr(1) : url), load: (filename: string) => { return new Promise((resolve, reject) => { loader.fs.readFile(filename, (err: Error, data: Buffer) => { @@ -81,12 +81,14 @@ export function getStylesConfig(wco: WebpackConfigOptions) { const includePaths: string[] = []; let lessPathOptions: { paths?: string[] } = {}; - if (buildOptions.stylePreprocessorOptions - && buildOptions.stylePreprocessorOptions.includePaths - && buildOptions.stylePreprocessorOptions.includePaths.length > 0 + if ( + buildOptions.stylePreprocessorOptions && + buildOptions.stylePreprocessorOptions.includePaths && + buildOptions.stylePreprocessorOptions.includePaths.length > 0 ) { buildOptions.stylePreprocessorOptions.includePaths.forEach((includePath: string) => - includePaths.push(path.resolve(root, includePath))); + includePaths.push(path.resolve(root, includePath)), + ); lessPathOptions = { paths: includePaths, }; @@ -105,8 +107,8 @@ export function getStylesConfig(wco: WebpackConfigOptions) { entryPoints[style.bundleName] = [resolvedPath]; } - // Add lazy styles to the list. - if (style.lazy) { + // Add non injected styles to the list. + if (!style.inject) { chunkNames.push(style.bundleName); } @@ -131,7 +133,7 @@ export function getStylesConfig(wco: WebpackConfigOptions) { try { // tslint:disable-next-line:no-implicit-dependencies fiber = require('fibers'); - } catch { } + } catch {} } // set base rules to derive final rules from @@ -139,38 +141,44 @@ export function getStylesConfig(wco: WebpackConfigOptions) { { test: /\.css$/, use: [] }, { test: /\.scss$|\.sass$/, - use: [{ - loader: 'sass-loader', - options: { - implementation: sassImplementation, - fiber, - sourceMap: cssSourceMap, - // bootstrap-sass requires a minimum precision of 8 - precision: 8, - includePaths, + use: [ + { + loader: 'sass-loader', + options: { + implementation: sassImplementation, + fiber, + sourceMap: cssSourceMap, + // bootstrap-sass requires a minimum precision of 8 + precision: 8, + includePaths, + }, }, - }], + ], }, { test: /\.less$/, - use: [{ - loader: 'less-loader', - options: { - sourceMap: cssSourceMap, - javascriptEnabled: true, - ...lessPathOptions, + use: [ + { + loader: 'less-loader', + options: { + sourceMap: cssSourceMap, + javascriptEnabled: true, + ...lessPathOptions, + }, }, - }], + ], }, { test: /\.styl$/, - use: [{ - loader: 'stylus-loader', - options: { - sourceMap: cssSourceMap, - paths: includePaths, + use: [ + { + loader: 'stylus-loader', + options: { + sourceMap: cssSourceMap, + paths: includePaths, + }, }, - }], + ], }, ]; @@ -194,28 +202,30 @@ export function getStylesConfig(wco: WebpackConfigOptions) { // load global css as css files if (globalStylePaths.length > 0) { - rules.push(...baseRules.map(({ test, use }) => { - return { - include: globalStylePaths, - test, - use: [ - buildOptions.extractCss ? MiniCssExtractPlugin.loader : 'style-loader', - RawCssLoader, - { - loader: 'postcss-loader', - options: { - ident: buildOptions.extractCss ? 'extracted' : 'embedded', - plugins: postcssPluginCreator, - sourceMap: cssSourceMap - && !buildOptions.extractCss - && !buildOptions.sourceMap.hidden - ? 'inline' : cssSourceMap, + rules.push( + ...baseRules.map(({ test, use }) => { + return { + include: globalStylePaths, + test, + use: [ + buildOptions.extractCss ? MiniCssExtractPlugin.loader : 'style-loader', + RawCssLoader, + { + loader: 'postcss-loader', + options: { + ident: buildOptions.extractCss ? 'extracted' : 'embedded', + plugins: postcssPluginCreator, + sourceMap: + cssSourceMap && !buildOptions.extractCss && !buildOptions.sourceMap.hidden + ? 'inline' + : cssSourceMap, + }, }, - }, - ...(use as webpack.Loader[]), - ], - }; - })); + ...(use as webpack.Loader[]), + ], + }; + }), + ); } if (buildOptions.extractCss) { diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/utils.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/utils.ts index e0afd2ad53ef..d9a3e5da7594 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/utils.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/utils.ts @@ -23,32 +23,45 @@ export interface HashFormat { export function getOutputHashFormat(option: string, length = 20): HashFormat { /* tslint:disable:max-line-length */ const hashFormats: { [option: string]: HashFormat } = { - none: { chunk: '', extract: '', file: '' , script: '' }, - media: { chunk: '', extract: '', file: `.[hash:${length}]`, script: '' }, - bundles: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: '' , script: `.[hash:${length}]` }, - all: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: `.[hash:${length}]`, script: `.[hash:${length}]` }, + none: { chunk: '', extract: '', file: '', script: '' }, + media: { chunk: '', extract: '', file: `.[hash:${length}]`, script: '' }, + bundles: { + chunk: `.[chunkhash:${length}]`, + extract: `.[contenthash:${length}]`, + file: '', + script: `.[hash:${length}]`, + }, + all: { + chunk: `.[chunkhash:${length}]`, + extract: `.[contenthash:${length}]`, + file: `.[hash:${length}]`, + script: `.[hash:${length}]`, + }, }; /* tslint:enable:max-line-length */ return hashFormats[option] || hashFormats['none']; } -export type NormalizedEntryPoint = ExtraEntryPointClass & { bundleName: string }; +// todo: replace with Omit when we update to TS 3.5 +type Omit = Pick>; +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, lazy: false, bundleName: defaultBundleName }; + normalizedEntry = { input: entry, inject: true, bundleName: defaultBundleName }; } else { + const { lazy, inject = true, ...newEntry } = entry; + const injectNormalized = entry.lazy !== undefined ? !entry.lazy : inject; let bundleName; if (entry.bundleName) { bundleName = entry.bundleName; - } else if (entry.lazy) { + } 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, '')), @@ -57,11 +70,11 @@ export function normalizeExtraEntryPoints( bundleName = defaultBundleName; } - normalizedEntry = {...entry, bundleName}; + normalizedEntry = { ...newEntry, inject: injectNormalized, bundleName }; } return normalizedEntry; - }) + }); } export function getSourceMapDevTool( @@ -93,8 +106,9 @@ export function getEsVersionForFileName( scriptTargetOverride: ScriptTarget | undefined, esVersionInFileName = false, ): string { - return scriptTargetOverride && esVersionInFileName ? - '-' + ScriptTarget[scriptTargetOverride].toLowerCase() : ''; + return scriptTargetOverride && esVersionInFileName + ? '-' + ScriptTarget[scriptTargetOverride].toLowerCase() + : ''; } export function isPolyfillsEntry(name: string) { diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/utilities/package-chunk-sort.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/utilities/package-chunk-sort.ts index 3b2262575d70..29e5e17ac34e 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/utilities/package-chunk-sort.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/utilities/package-chunk-sort.ts @@ -8,17 +8,17 @@ import { ExtraEntryPoint } from '../../browser/schema'; import { normalizeExtraEntryPoints } from '../models/webpack-configs/utils'; -export function generateEntryPoints( - appConfig: { styles: ExtraEntryPoint[], scripts: ExtraEntryPoint[] }, -) { - +export function generateEntryPoints(appConfig: { + styles: ExtraEntryPoint[]; + scripts: ExtraEntryPoint[]; +}) { // Add all styles/scripts, except lazy-loaded ones. const extraEntryPoints = ( extraEntryPoints: ExtraEntryPoint[], defaultBundleName: string, ): string[] => { const entryPoints = normalizeExtraEntryPoints(extraEntryPoints, defaultBundleName) - .filter(entry => !entry.lazy) + .filter(entry => entry.inject) .map(entry => entry.bundleName); // remove duplicates @@ -35,9 +35,9 @@ export function generateEntryPoints( 'main', ]; - const duplicates = [...new Set( - entryPoints.filter(x => entryPoints.indexOf(x) !== entryPoints.lastIndexOf(x)), - )]; + const duplicates = [ + ...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(`', '`)}'.`); diff --git a/packages/angular_devkit/build_angular/src/browser/schema.json b/packages/angular_devkit/build_angular/src/browser/schema.json index 57122978fea1..12967f11c7d0 100644 --- a/packages/angular_devkit/build_angular/src/browser/schema.json +++ b/packages/angular_devkit/build_angular/src/browser/schema.json @@ -428,7 +428,13 @@ "lazy": { "type": "boolean", "description": "If the bundle will be lazy loaded.", - "default": false + "default": false, + "x-deprecated": "Use 'inject' option with 'false' value instead." + }, + "inject": { + "type": "boolean", + "description": "If the bundle will be referenced in the HTML file.", + "default": true } }, "additionalProperties": false, diff --git a/packages/angular_devkit/build_angular/src/karma/schema.json b/packages/angular_devkit/build_angular/src/karma/schema.json index f5b8b64fddab..9bf96efe307e 100644 --- a/packages/angular_devkit/build_angular/src/karma/schema.json +++ b/packages/angular_devkit/build_angular/src/karma/schema.json @@ -258,7 +258,13 @@ "lazy": { "type": "boolean", "description": "If the bundle will be lazy loaded.", - "default": false + "default": false, + "x-deprecated": "Use 'inject' option with 'false' value instead." + }, + "inject": { + "type": "boolean", + "description": "If the bundle will be referenced in the HTML file.", + "default": true } }, "additionalProperties": false, diff --git a/packages/angular_devkit/build_angular/test/browser/output-hashing_spec_large.ts b/packages/angular_devkit/build_angular/test/browser/output-hashing_spec_large.ts index 4ca1779f3448..c99cad5b62bf 100644 --- a/packages/angular_devkit/build_angular/test/browser/output-hashing_spec_large.ts +++ b/packages/angular_devkit/build_angular/test/browser/output-hashing_spec_large.ts @@ -9,7 +9,11 @@ import { Architect } from '@angular-devkit/architect'; import { normalize } from '@angular-devkit/core'; import { - browserBuild, createArchitect, host, lazyModuleFiles, lazyModuleStringImport, + browserBuild, + createArchitect, + host, + lazyModuleFiles, + lazyModuleStringImport, } from '../utils'; describe('Browser Builder output hashing', () => { @@ -28,13 +32,16 @@ describe('Browser Builder output hashing', () => { function generateFileHashMap(): Map { const hashes = new Map(); - host.scopedSync().list(normalize('./dist')).forEach(name => { - const matches = name.match(OUTPUT_RE); - if (matches) { - const [, module, hash] = matches; - hashes.set(module, hash); - } - }); + host + .scopedSync() + .list(normalize('./dist')) + .forEach(name => { + const matches = name.match(OUTPUT_RE); + if (matches) { + const [, module, hash] = matches; + hashes.set(module, hash); + } + }); return hashes; } @@ -48,7 +55,8 @@ describe('Browser Builder output hashing', () => { if (hash == oldHashes.get(module)) { if (shouldChange.includes(module)) { throw new Error( - `Module "${module}" did not change hash (${hash}), but was expected to.`); + `Module "${module}" did not change hash (${hash}), but was expected to.`, + ); } } else if (!shouldChange.includes(module)) { throw new Error(`Module "${module}" changed hash (${hash}), but was not expected to.`); @@ -153,11 +161,11 @@ describe('Browser Builder output hashing', () => { expect(host.fileMatchExists('dist', /spectrum\.[0-9a-f]{20}\.png/)).toBeFalsy(); }); - it('does not hash lazy styles', async () => { + it('does not hash non injected styles', async () => { const overrides = { outputHashing: 'all', extractCss: true, - styles: [{ input: 'src/styles.css', lazy: true }], + styles: [{ input: 'src/styles.css', inject: false }], }; await browserBuild(architect, host, target, overrides); @@ -168,12 +176,12 @@ describe('Browser Builder output hashing', () => { expect(host.scopedSync().exists(normalize('dist/styles.css.map'))).toBe(true); }); - it('does not hash lazy styles when optimization is enabled', async () => { + it('does not hash non injected styles when optimization is enabled', async () => { const overrides = { outputHashing: 'all', extractCss: true, optimization: true, - styles: [{ input: 'src/styles.css', lazy: true }], + styles: [{ input: 'src/styles.css', inject: false }], }; await browserBuild(architect, host, target, overrides); diff --git a/packages/angular_devkit/build_angular/test/browser/scripts-array_spec_large.ts b/packages/angular_devkit/build_angular/test/browser/scripts-array_spec_large.ts index 48aa593dbdc9..615c3bf8b7c3 100644 --- a/packages/angular_devkit/build_angular/test/browser/scripts-array_spec_large.ts +++ b/packages/angular_devkit/build_angular/test/browser/scripts-array_spec_large.ts @@ -33,9 +33,9 @@ describe('Browser Builder scripts array', () => { 'src/binput-script.js', 'src/ainput-script.js', 'src/cinput-script.js', - { input: 'src/lazy-script.js', bundleName: 'lazy-script', lazy: true }, + { input: 'src/lazy-script.js', bundleName: 'lazy-script', inject: false }, { input: 'src/pre-rename-script.js', bundleName: 'renamed-script' }, - { input: 'src/pre-rename-lazy-script.js', bundleName: 'renamed-lazy-script', lazy: true }, + { input: 'src/pre-rename-lazy-script.js', bundleName: 'renamed-lazy-script', inject: false }, ]; const target = { project: 'app', target: 'build' }; diff --git a/packages/angular_devkit/build_angular/test/browser/styles_spec_large.ts b/packages/angular_devkit/build_angular/test/browser/styles_spec_large.ts index 242bcddcd32a..0bbfd62e544b 100644 --- a/packages/angular_devkit/build_angular/test/browser/styles_spec_large.ts +++ b/packages/angular_devkit/build_angular/test/browser/styles_spec_large.ts @@ -11,7 +11,6 @@ import { Architect } from '@angular-devkit/architect'; import { logging, normalize, tags } from '@angular-devkit/core'; import { browserBuild, createArchitect, host } from '../utils'; - describe('Browser Builder styles', () => { const extensionsWithImportSupport = ['css', 'scss', 'less', 'styl']; const extensionsWithVariableSupport = ['scss', 'less', 'styl']; @@ -32,9 +31,9 @@ describe('Browser Builder styles', () => { it('supports global styles', async () => { const styles = [ 'src/input-style.css', - { input: 'src/lazy-style.css', bundleName: 'lazy-style', lazy: true }, + { input: 'src/lazy-style.css', bundleName: 'lazy-style', inject: false }, { input: 'src/pre-rename-style.css', bundleName: 'renamed-style' }, - { input: 'src/pre-rename-lazy-style.css', bundleName: 'renamed-lazy-style', lazy: true }, + { input: 'src/pre-rename-lazy-style.css', bundleName: 'renamed-lazy-style', inject: false }, ] as {}; const cssMatches: { [path: string]: string } = { 'styles.css': '.input-style', @@ -43,8 +42,9 @@ describe('Browser Builder styles', () => { 'renamed-lazy-style.css': '.pre-rename-lazy-style', }; const cssIndexMatches: { [path: string]: string } = { - 'index.html': '' - + '', + 'index.html': + '' + + '', }; const jsMatches: { [path: string]: string } = { 'styles.js': '.input-style', @@ -53,12 +53,13 @@ describe('Browser Builder styles', () => { 'renamed-lazy-style.js': '.pre-rename-lazy-style', }; const jsIndexMatches: { [path: string]: string } = { - 'index.html': '' - + '' - + '' - + '' - + '' - + '', + 'index.html': + '' + + '' + + '' + + '' + + '' + + '', }; host.writeMultipleFiles({ @@ -144,16 +145,16 @@ describe('Browser Builder styles', () => { const matches: { [path: string]: RegExp } = { 'styles.css': new RegExp( // The global style should be there - /p\s*{\s*background-color: #f00;\s*}(.|\n|\r)*/.source - // The global style via import should be there - + /body\s*{\s*background-color: #00f;\s*}/.source, + /p\s*{\s*background-color: #f00;\s*}(.|\n|\r)*/.source + + // The global style via import should be there + /body\s*{\s*background-color: #00f;\s*}/.source, ), 'styles.css.map': /"mappings":".+"/, 'main.js': new RegExp( // The component style should be there - /h1(.|\n|\r)*background:\s*#000(.|\n|\r)*/.source - // The component style via import should be there - + /.outer(.|\n|\r)*.inner(.|\n|\r)*background:\s*#[fF]+/.source, + /h1(.|\n|\r)*background:\s*#000(.|\n|\r)*/.source + + // The component style via import should be there + /.outer(.|\n|\r)*.inner(.|\n|\r)*background:\s*#[fF]+/.source, ), }; @@ -188,8 +189,11 @@ describe('Browser Builder styles', () => { @import "@angular/material/prebuilt-themes/indigo-pink.css"; `, }); - host.replaceInFile('src/app/app.component.ts', './app.component.css', - `./app.component.${ext}`); + host.replaceInFile( + 'src/app/app.component.ts', + './app.component.css', + `./app.component.${ext}`, + ); const overrides = { extractCss: true, @@ -213,7 +217,6 @@ describe('Browser Builder styles', () => { extensionsWithVariableSupport.forEach(ext => { it(`supports ${ext} includePaths`, async () => { - let variableAssignment = ''; let variablereference = ''; if (ext === 'scss') { @@ -244,8 +247,11 @@ describe('Browser Builder styles', () => { 'main.js': /h2.*{.*color: #f00;.*}/, }; - host.replaceInFile('src/app/app.component.ts', './app.component.css', - `./app.component.${ext}`); + host.replaceInFile( + 'src/app/app.component.ts', + './app.component.css', + `./app.component.${ext}`, + ); const overrides = { extractCss: true, @@ -291,7 +297,7 @@ describe('Browser Builder styles', () => { /* normal-comment */ /*! important-comment */ div { flex: 1 }`, - 'browserslist': 'IE 10', + browserslist: 'IE 10', }); const overrides = { extractCss: true, optimization: false }; @@ -299,8 +305,7 @@ describe('Browser Builder styles', () => { expect(await files['styles.css']).toContain(tags.stripIndents` /* normal-comment */ /*! important-comment */ - div { -ms-flex: 1; flex: 1 }`, - ); + div { -ms-flex: 1; flex: 1 }`); }); it(`minimizes css`, async () => { @@ -345,14 +350,12 @@ describe('Browser Builder styles', () => { expect(main).toContain(`url('/assets/component-img-absolute.svg')`); expect(main).toContain(`url('component-img-relative.png')`); - expect(host.scopedSync().exists(normalize('dist/assets/global-img-absolute.svg'))) - .toBe(true); - expect(host.scopedSync().exists(normalize('dist/global-img-relative.png'))) - .toBe(true); - expect(host.scopedSync().exists(normalize('dist/assets/component-img-absolute.svg'))) - .toBe(true); - expect(host.scopedSync().exists(normalize('dist/component-img-relative.png'))) - .toBe(true); + expect(host.scopedSync().exists(normalize('dist/assets/global-img-absolute.svg'))).toBe(true); + expect(host.scopedSync().exists(normalize('dist/global-img-relative.png'))).toBe(true); + expect(host.scopedSync().exists(normalize('dist/assets/component-img-absolute.svg'))).toBe( + true, + ); + expect(host.scopedSync().exists(normalize('dist/component-img-relative.png'))).toBe(true); // Check urls with deploy-url scheme are used as is. files = (await browserBuild(architect, host, target, { @@ -456,14 +459,12 @@ describe('Browser Builder styles', () => { expect(styles).toContain(`url('global-img-relative.png')`); expect(main).toContain(`url('/assets/component-img-absolute.svg')`); expect(main).toContain(`url('component-img-relative.png')`); - expect(host.scopedSync().exists(normalize('dist/assets/global-img-absolute.svg'))) - .toBe(true); - expect(host.scopedSync().exists(normalize('dist/global-img-relative.png'))) - .toBe(true); - expect(host.scopedSync().exists(normalize('dist/assets/component-img-absolute.svg'))) - .toBe(true); - expect(host.scopedSync().exists(normalize('dist/component-img-relative.png'))) - .toBe(true); + expect(host.scopedSync().exists(normalize('dist/assets/global-img-absolute.svg'))).toBe(true); + expect(host.scopedSync().exists(normalize('dist/global-img-relative.png'))).toBe(true); + expect(host.scopedSync().exists(normalize('dist/assets/component-img-absolute.svg'))).toBe( + true, + ); + expect(host.scopedSync().exists(normalize('dist/component-img-relative.png'))).toBe(true); // Check urls with deploy-url scheme are used as is. files = (await browserBuild(architect, host, target, { diff --git a/packages/schematics/angular/migrations/migration-collection.json b/packages/schematics/angular/migrations/migration-collection.json index 3cd1b2c82c79..f2f7a02f7551 100644 --- a/packages/schematics/angular/migrations/migration-collection.json +++ b/packages/schematics/angular/migrations/migration-collection.json @@ -39,6 +39,11 @@ "version": "8.0.0-beta.14", "factory": "./update-8/#updateLazyModulePaths", "description": "Update an Angular CLI project to version 8." + }, + "migration-09": { + "version": "9.0.0-beta.0", + "factory": "./update-9", + "description": "Update an Angular CLI project to version 9." } } } diff --git a/packages/schematics/angular/migrations/update-9/index.ts b/packages/schematics/angular/migrations/update-9/index.ts new file mode 100644 index 000000000000..eb6d7869a4d2 --- /dev/null +++ b/packages/schematics/angular/migrations/update-9/index.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { Rule, chain } from '@angular-devkit/schematics'; +import { UpdateWorkspaceConfig } from './update-workspace-config'; + +export default function(): Rule { + return () => { + return chain([ + UpdateWorkspaceConfig(), + ]); + }; +} diff --git a/packages/schematics/angular/migrations/update-9/update-workspace-config.ts b/packages/schematics/angular/migrations/update-9/update-workspace-config.ts new file mode 100644 index 000000000000..3dc9f637ce53 --- /dev/null +++ b/packages/schematics/angular/migrations/update-9/update-workspace-config.ts @@ -0,0 +1,123 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { + JsonAstObject, + JsonParseMode, + parseJsonAst, +} from '@angular-devkit/core'; +import { Rule, Tree, UpdateRecorder } from '@angular-devkit/schematics'; +import { + findPropertyInAstObject, + insertPropertyInAstObjectInOrder, + removePropertyInAstObject, +} from '../../utility/json-utils'; + +export function UpdateWorkspaceConfig(): Rule { + return (tree: Tree) => { + let workspaceConfigPath = 'angular.json'; + let angularConfigContent = tree.read(workspaceConfigPath); + + if (!angularConfigContent) { + workspaceConfigPath = '.angular.json'; + angularConfigContent = tree.read(workspaceConfigPath); + + if (!angularConfigContent) { + return; + } + } + + const angularJson = parseJsonAst(angularConfigContent.toString(), JsonParseMode.Loose); + if (angularJson.kind !== 'object') { + return; + } + + const projects = findPropertyInAstObject(angularJson, 'projects'); + if (!projects || projects.kind !== 'object') { + return; + } + + // For all projects + const recorder = tree.beginUpdate(workspaceConfigPath); + for (const project of projects.properties) { + const projectConfig = project.value; + if (projectConfig.kind !== 'object') { + break; + } + + const architect = findPropertyInAstObject(projectConfig, 'architect'); + if (!architect || architect.kind !== 'object') { + break; + } + + const buildTarget = findPropertyInAstObject(architect, 'build'); + if (buildTarget && buildTarget.kind === 'object') { + const builder = findPropertyInAstObject(buildTarget, 'builder'); + // Projects who's build builder is not build-angular:browser + if (builder && builder.kind === 'string' && builder.value === '@angular-devkit/build-angular:browser') { + updateOption('styles', recorder, buildTarget); + updateOption('scripts', recorder, buildTarget); + } + } + + const testTarget = findPropertyInAstObject(architect, 'test'); + if (testTarget && testTarget.kind === 'object') { + const builder = findPropertyInAstObject(testTarget, 'builder'); + // Projects who's build builder is not build-angular:browser + if (builder && builder.kind === 'string' && builder.value === '@angular-devkit/build-angular:karma') { + updateOption('styles', recorder, testTarget); + updateOption('scripts', recorder, testTarget); + } + } + } + + tree.commitUpdate(recorder); + + return tree; + }; +} + +/** + * Helper to retreive all the options in various configurations + */ +function getAllOptions(builderConfig: JsonAstObject): JsonAstObject[] { + const options = []; + const configurations = findPropertyInAstObject(builderConfig, 'configurations'); + if (configurations && configurations.kind === 'object') { + options.push(...configurations.properties.map(x => x.value)); + } + + options.push(findPropertyInAstObject(builderConfig, 'options')); + + return options.filter(o => o && o.kind === 'object') as JsonAstObject[]; +} + +function updateOption(property: 'scripts' | 'styles', recorder: UpdateRecorder, builderConfig: JsonAstObject) { + const options = getAllOptions(builderConfig); + + for (const option of options) { + const propertyOption = findPropertyInAstObject(option, property); + if (!propertyOption || propertyOption.kind !== 'array') { + continue; + } + + for (const node of propertyOption.elements) { + if (!node || node.kind !== 'object') { + // skip non complex objects + continue; + } + + const lazy = findPropertyInAstObject(node, 'lazy'); + removePropertyInAstObject(recorder, node, 'lazy'); + + // if lazy was not true, it is redundant hence, don't add it + if (lazy && lazy.kind === 'true') { + insertPropertyInAstObjectInOrder(recorder, node, 'inject', false, 0); + } + } + } +} diff --git a/packages/schematics/angular/migrations/update-9/update-workspace-config_spec.ts b/packages/schematics/angular/migrations/update-9/update-workspace-config_spec.ts new file mode 100644 index 000000000000..64865e83c83b --- /dev/null +++ b/packages/schematics/angular/migrations/update-9/update-workspace-config_spec.ts @@ -0,0 +1,128 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { EmptyTree } from '@angular-devkit/schematics'; +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; + +function readWorkspaceConfig(tree: UnitTestTree) { + return JSON.parse(tree.readContent('/angular.json')); +} + +const scriptsWithLazy = [ + { bundleName: 'one', input: 'one.js', lazy: false }, + { bundleName: 'two', input: 'two.js', lazy: true }, + { bundleName: 'tree', input: 'tree.js' }, + 'four.js', +] + +const scriptsExpectWithLazy = [ + { bundleName: 'one', input: 'one.js' }, + { bundleName: 'two', inject: false, input: 'two.js' }, + { bundleName: 'tree', input: 'tree.js' }, + 'four.js', +] + +const stylesWithLazy = [ + { bundleName: 'one', input: 'one.css', lazy: false }, + { bundleName: 'two', input: 'two.css', lazy: true }, + { bundleName: 'tree', input: 'tree.css' }, + 'four.css', +] + +const stylesExpectWithLazy = [ + { bundleName: 'one', input: 'one.css' }, + { bundleName: 'two', inject: false, input: 'two.css' }, + { bundleName: 'tree', input: 'tree.css' }, + 'four.css', +] + +const workspacePath = '/angular.json'; + +// tslint:disable:no-big-function +describe('Migration to version 9', () => { + describe('Migrate workspace config', () => { + const schematicRunner = new SchematicTestRunner( + 'migrations', + require.resolve('../migration-collection.json'), + ); + + let tree: UnitTestTree; + + beforeEach(async () => { + tree = new UnitTestTree(new EmptyTree()); + tree = await schematicRunner + .runExternalSchematicAsync( + require.resolve('../../collection.json'), + 'ng-new', + { + name: 'migration-test', + version: '1.2.3', + directory: '.', + }, + tree, + ) + .toPromise(); + }); + + it('should update scripts in build target', () => { + let config = readWorkspaceConfig(tree); + let build = config.projects['migration-test'].architect.build; + build.options.scripts = scriptsWithLazy; + build.configurations.production.scripts = scriptsWithLazy; + + tree.overwrite(workspacePath, JSON.stringify(config)); + const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch()); + config = readWorkspaceConfig(tree2); + build = config.projects['migration-test'].architect.build; + expect(build.options.scripts).toEqual(scriptsExpectWithLazy); + expect(build.configurations.production.scripts).toEqual(scriptsExpectWithLazy); + }); + + it('should update styles in build target', () => { + let config = readWorkspaceConfig(tree); + let build = config.projects['migration-test'].architect.build; + build.options.styles = stylesWithLazy; + build.configurations.production.styles = stylesWithLazy; + + tree.overwrite(workspacePath, JSON.stringify(config)); + const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch()); + config = readWorkspaceConfig(tree2); + build = config.projects['migration-test'].architect.build; + expect(build.options.styles).toEqual(stylesExpectWithLazy); + expect(build.configurations.production.styles).toEqual(stylesExpectWithLazy); + }); + + it('should update scripts in test target', () => { + let config = readWorkspaceConfig(tree); + let test = config.projects['migration-test'].architect.test; + test.options.scripts = scriptsWithLazy; + test.configurations = { production: { scripts: scriptsWithLazy } }; + + tree.overwrite(workspacePath, JSON.stringify(config)); + const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch()); + config = readWorkspaceConfig(tree2); + test = config.projects['migration-test'].architect.test; + expect(test.options.scripts).toEqual(scriptsExpectWithLazy); + expect(test.configurations.production.scripts).toEqual(scriptsExpectWithLazy); + }); + + it('should update styles in test target', () => { + let config = readWorkspaceConfig(tree); + let test = config.projects['migration-test'].architect.test; + test.options.styles = stylesWithLazy; + test.configurations = { production: { styles: stylesWithLazy } }; + + tree.overwrite(workspacePath, JSON.stringify(config)); + const tree2 = schematicRunner.runSchematic('migration-09', {}, tree.branch()); + config = readWorkspaceConfig(tree2); + test = config.projects['migration-test'].architect.test; + expect(test.options.styles).toEqual(stylesExpectWithLazy); + expect(test.configurations.production.styles).toEqual(stylesExpectWithLazy); + }); + }); +}); diff --git a/tests/legacy-cli/e2e/tests/basic/scripts-array.ts b/tests/legacy-cli/e2e/tests/basic/scripts-array.ts index 061b36f38d03..a3a59832068f 100644 --- a/tests/legacy-cli/e2e/tests/basic/scripts-array.ts +++ b/tests/legacy-cli/e2e/tests/basic/scripts-array.ts @@ -4,7 +4,7 @@ import { writeMultipleFiles, expectFileToMatch, appendToFile, - expectFileMatchToExist + expectFileMatchToExist, } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; @@ -13,46 +13,58 @@ import * as fs from 'fs'; import * as path from 'path'; // tslint:disable:max-line-length -export default function () { - return writeMultipleFiles({ - 'src/string-script.js': 'console.log(\'string-script\'); var number = 1+1;', - 'src/zstring-script.js': 'console.log(\'zstring-script\');', - 'src/fstring-script.js': 'console.log(\'fstring-script\');', - 'src/ustring-script.js': 'console.log(\'ustring-script\');', - 'src/bstring-script.js': 'console.log(\'bstring-script\');', - 'src/astring-script.js': 'console.log(\'astring-script\');', - 'src/cstring-script.js': 'console.log(\'cstring-script\');', - 'src/input-script.js': 'console.log(\'input-script\');', - 'src/lazy-script.js': 'console.log(\'lazy-script\');', - 'src/pre-rename-script.js': 'console.log(\'pre-rename-script\');', - 'src/pre-rename-lazy-script.js': 'console.log(\'pre-rename-lazy-script\');', - }) - .then(() => appendToFile('src/main.ts', 'import \'./string-script.js\';')) - .then(() => updateJsonFile('angular.json', configJson => { - const appArchitect = configJson.projects['test-project'].architect; - appArchitect.build.options.scripts = [ - { input: 'src/string-script.js' }, - { input: 'src/zstring-script.js' }, - { input: 'src/fstring-script.js' }, - { input: 'src/ustring-script.js' }, - { input: 'src/bstring-script.js' }, - { input: 'src/astring-script.js' }, - { input: 'src/cstring-script.js' }, - { input: 'src/input-script.js' }, - { input: 'src/lazy-script.js', lazy: true }, - { input: 'src/pre-rename-script.js', bundleName: 'renamed-script' }, - { input: 'src/pre-rename-lazy-script.js', bundleName: 'renamed-lazy-script', lazy: true } - ]; - })) - .then(() => ng('build', '--extract-css')) - // files were created successfully - .then(() => expectFileToMatch('dist/test-project/scripts.js', 'string-script')) - .then(() => expectFileToMatch('dist/test-project/scripts.js', 'input-script')) - .then(() => expectFileToMatch('dist/test-project/lazy-script.js', 'lazy-script')) - .then(() => expectFileToMatch('dist/test-project/renamed-script.js', 'pre-rename-script')) - .then(() => expectFileToMatch('dist/test-project/renamed-lazy-script.js', 'pre-rename-lazy-script')) - // index.html lists the right bundles - .then(() => expectFileToMatch('dist/test-project/index.html', oneLineTrim` +export default function() { + return ( + writeMultipleFiles({ + 'src/string-script.js': "console.log('string-script'); var number = 1+1;", + 'src/zstring-script.js': "console.log('zstring-script');", + 'src/fstring-script.js': "console.log('fstring-script');", + 'src/ustring-script.js': "console.log('ustring-script');", + 'src/bstring-script.js': "console.log('bstring-script');", + 'src/astring-script.js': "console.log('astring-script');", + 'src/cstring-script.js': "console.log('cstring-script');", + 'src/input-script.js': "console.log('input-script');", + 'src/lazy-script.js': "console.log('lazy-script');", + 'src/pre-rename-script.js': "console.log('pre-rename-script');", + 'src/pre-rename-lazy-script.js': "console.log('pre-rename-lazy-script');", + }) + .then(() => appendToFile('src/main.ts', "import './string-script.js';")) + .then(() => + updateJsonFile('angular.json', configJson => { + const appArchitect = configJson.projects['test-project'].architect; + appArchitect.build.options.scripts = [ + { input: 'src/string-script.js' }, + { input: 'src/zstring-script.js' }, + { input: 'src/fstring-script.js' }, + { input: 'src/ustring-script.js' }, + { input: 'src/bstring-script.js' }, + { input: 'src/astring-script.js' }, + { input: 'src/cstring-script.js' }, + { input: 'src/input-script.js' }, + { input: 'src/lazy-script.js', inject: false }, + { input: 'src/pre-rename-script.js', bundleName: 'renamed-script' }, + { + input: 'src/pre-rename-lazy-script.js', + bundleName: 'renamed-lazy-script', + inject: false, + }, + ]; + }), + ) + .then(() => ng('build', '--extract-css')) + // files were created successfully + .then(() => expectFileToMatch('dist/test-project/scripts.js', 'string-script')) + .then(() => expectFileToMatch('dist/test-project/scripts.js', 'input-script')) + .then(() => expectFileToMatch('dist/test-project/lazy-script.js', 'lazy-script')) + .then(() => expectFileToMatch('dist/test-project/renamed-script.js', 'pre-rename-script')) + .then(() => + expectFileToMatch('dist/test-project/renamed-lazy-script.js', 'pre-rename-lazy-script'), + ) + // index.html lists the right bundles + .then(() => + expectFileToMatch( + 'dist/test-project/index.html', + oneLineTrim` @@ -63,37 +75,44 @@ export default function () { - `)) - // Ensure scripts can be separately imported from the app. - .then(() => expectFileToMatch('dist/test-project/main-es5.js', 'console.log(\'string-script\');')) - .then(() => expectFileToMatch('dist/test-project/main-es2015.js', 'console.log(\'string-script\');')); - // TODO(architect): disabled until --prod is added. - // Verify uglify, sourcemaps and hashes. Lazy scripts should not get hashes. - // .then(() => ng('build', '--prod', '--source-map')) - // .then(() => expectFileMatchToExist('dist', /scripts\.[0-9a-f]{20}\.js/)) - // .then(fileName => expectFileToMatch(`dist/${fileName}`, 'var number=2;')) - // .then(() => expectFileMatchToExist('dist', /scripts\.[0-9a-f]{20}\.js\.map/)) - // .then(() => expectFileMatchToExist('dist', /renamed-script\.[0-9a-f]{20}\.js/)) - // .then(() => expectFileMatchToExist('dist', /renamed-script\.[0-9a-f]{20}\.js.map/)) - // .then(() => expectFileToMatch('dist/test-project/lazy-script.js', 'lazy-script')) - // .then(() => expectFileToMatch('dist/test-project/enamed-lazy-script.js', 'pre-rename-lazy-script')) + `, + ), + ) + // Ensure scripts can be separately imported from the app. + .then(() => + expectFileToMatch('dist/test-project/main-es5.js', "console.log('string-script');"), + ) + .then(() => + expectFileToMatch('dist/test-project/main-es2015.js', "console.log('string-script');"), + ) + ); + // TODO(architect): disabled until --prod is added. + // Verify uglify, sourcemaps and hashes. Lazy scripts should not get hashes. + // .then(() => ng('build', '--prod', '--source-map')) + // .then(() => expectFileMatchToExist('dist', /scripts\.[0-9a-f]{20}\.js/)) + // .then(fileName => expectFileToMatch(`dist/${fileName}`, 'var number=2;')) + // .then(() => expectFileMatchToExist('dist', /scripts\.[0-9a-f]{20}\.js\.map/)) + // .then(() => expectFileMatchToExist('dist', /renamed-script\.[0-9a-f]{20}\.js/)) + // .then(() => expectFileMatchToExist('dist', /renamed-script\.[0-9a-f]{20}\.js.map/)) + // .then(() => expectFileToMatch('dist/test-project/lazy-script.js', 'lazy-script')) + // .then(() => expectFileToMatch('dist/test-project/enamed-lazy-script.js', 'pre-rename-lazy-script')) - // // Expect order to be preserved. - // .then(() => { - // const [fileName] = fs.readdirSync('dist') - // .filter(name => name.match(/^scripts\..*\.js$/)); + // // Expect order to be preserved. + // .then(() => { + // const [fileName] = fs.readdirSync('dist') + // .filter(name => name.match(/^scripts\..*\.js$/)); - // const content = fs.readFileSync(path.join('dist', fileName), 'utf-8'); - // const re = new RegExp(/['"]string-script['"].*/.source - // + /['"]zstring-script['"].*/.source - // + /['"]fstring-script['"].*/.source - // + /['"]ustring-script['"].*/.source - // + /['"]bstring-script['"].*/.source - // + /['"]astring-script['"].*/.source - // + /['"]cstring-script['"].*/.source - // + /['"]input-script['"]/.source); - // if (!content.match(re)) { - // throw new Error('Scripts are not included in order.'); - // } - // }); + // const content = fs.readFileSync(path.join('dist', fileName), 'utf-8'); + // const re = new RegExp(/['"]string-script['"].*/.source + // + /['"]zstring-script['"].*/.source + // + /['"]fstring-script['"].*/.source + // + /['"]ustring-script['"].*/.source + // + /['"]bstring-script['"].*/.source + // + /['"]astring-script['"].*/.source + // + /['"]cstring-script['"].*/.source + // + /['"]input-script['"]/.source); + // if (!content.match(re)) { + // throw new Error('Scripts are not included in order.'); + // } + // }); } diff --git a/tests/legacy-cli/e2e/tests/basic/styles-array.ts b/tests/legacy-cli/e2e/tests/basic/styles-array.ts index 0261b23de5bd..93565e3c1fca 100644 --- a/tests/legacy-cli/e2e/tests/basic/styles-array.ts +++ b/tests/legacy-cli/e2e/tests/basic/styles-array.ts @@ -1,47 +1,58 @@ // TODO(architect): edit the architect config instead of the cli config. -import { - writeMultipleFiles, - expectFileToMatch -} from '../../utils/fs'; +import { writeMultipleFiles, expectFileToMatch } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; import { oneLineTrim } from 'common-tags'; -export default function () { - return writeMultipleFiles({ - 'src/string-style.css': '.string-style { color: red }', - 'src/input-style.css': '.input-style { color: red }', - 'src/lazy-style.css': '.lazy-style { color: red }', - 'src/pre-rename-style.css': '.pre-rename-style { color: red }', - 'src/pre-rename-lazy-style.css': '.pre-rename-lazy-style { color: red }', - }) - .then(() => updateJsonFile('angular.json', workspaceJson => { - const appArchitect = workspaceJson.projects['test-project'].architect; - appArchitect.build.options.styles = [ - { input: 'src/string-style.css' }, - { input: 'src/input-style.css' }, - { input: 'src/lazy-style.css', lazy: true }, - { input: 'src/pre-rename-style.css', bundleName: 'renamed-style' }, - { input: 'src/pre-rename-lazy-style.css', bundleName: 'renamed-lazy-style', lazy: true } - ]; - })) - .then(() => ng('build', '--extract-css')) - // files were created successfully - .then(() => expectFileToMatch('dist/test-project/styles.css', '.string-style')) - .then(() => expectFileToMatch('dist/test-project/styles.css', '.input-style')) - .then(() => expectFileToMatch('dist/test-project/lazy-style.css', '.lazy-style')) - .then(() => expectFileToMatch('dist/test-project/renamed-style.css', '.pre-rename-style')) - .then(() => expectFileToMatch( - 'dist/test-project/renamed-lazy-style.css', - '.pre-rename-lazy-style', - )) - // index.html lists the right bundles - .then(() => expectFileToMatch('dist/test-project/index.html', oneLineTrim` +export default function() { + return ( + writeMultipleFiles({ + 'src/string-style.css': '.string-style { color: red }', + 'src/input-style.css': '.input-style { color: red }', + 'src/lazy-style.css': '.lazy-style { color: red }', + 'src/pre-rename-style.css': '.pre-rename-style { color: red }', + 'src/pre-rename-lazy-style.css': '.pre-rename-lazy-style { color: red }', + }) + .then(() => + updateJsonFile('angular.json', workspaceJson => { + const appArchitect = workspaceJson.projects['test-project'].architect; + appArchitect.build.options.styles = [ + { input: 'src/string-style.css' }, + { input: 'src/input-style.css' }, + { input: 'src/lazy-style.css', inject: false }, + { input: 'src/pre-rename-style.css', bundleName: 'renamed-style' }, + { + input: 'src/pre-rename-lazy-style.css', + bundleName: 'renamed-lazy-style', + inject: false, + }, + ]; + }), + ) + .then(() => ng('build', '--extract-css')) + // files were created successfully + .then(() => expectFileToMatch('dist/test-project/styles.css', '.string-style')) + .then(() => expectFileToMatch('dist/test-project/styles.css', '.input-style')) + .then(() => expectFileToMatch('dist/test-project/lazy-style.css', '.lazy-style')) + .then(() => expectFileToMatch('dist/test-project/renamed-style.css', '.pre-rename-style')) + .then(() => + expectFileToMatch('dist/test-project/renamed-lazy-style.css', '.pre-rename-lazy-style'), + ) + // index.html lists the right bundles + .then(() => + expectFileToMatch( + 'dist/test-project/index.html', + oneLineTrim` - `)) - .then(() => expectFileToMatch('dist/test-project/index.html', oneLineTrim` + `, + ), + ) + .then(() => + expectFileToMatch( + 'dist/test-project/index.html', + oneLineTrim` @@ -50,5 +61,8 @@ export default function () { - `)); + `, + ), + ) + ); } diff --git a/tests/legacy-cli/e2e/tests/build/styles/extract-css.ts b/tests/legacy-cli/e2e/tests/build/styles/extract-css.ts index bfb970699528..89c213820aba 100644 --- a/tests/legacy-cli/e2e/tests/build/styles/extract-css.ts +++ b/tests/legacy-cli/e2e/tests/build/styles/extract-css.ts @@ -1,56 +1,77 @@ -import { - writeMultipleFiles, - expectFileToExist, - expectFileToMatch -} from '../../../utils/fs'; +import { writeMultipleFiles, expectFileToExist, expectFileToMatch } from '../../../utils/fs'; import { ng } from '../../../utils/process'; import { updateJsonFile } from '../../../utils/project'; import { expectToFail } from '../../../utils/utils'; import { oneLineTrim } from 'common-tags'; -export default function () { +export default function() { // TODO(architect): Delete this test. It is now in devkit/build-angular. - return Promise.resolve() - .then(() => writeMultipleFiles({ - 'src/string-style.css': '.string-style { color: red }', - 'src/input-style.css': '.input-style { color: red }', - 'src/lazy-style.css': '.lazy-style { color: red }', - 'src/pre-rename-style.css': '.pre-rename-style { color: red }', - 'src/pre-rename-lazy-style.css': '.pre-rename-lazy-style { color: red }', - })) - .then(() => updateJsonFile('angular.json', workspaceJson => { - const appArchitect = workspaceJson.projects['test-project'].architect; - appArchitect.build.options.styles = [ - { input: 'src/string-style.css' }, - { input: 'src/input-style.css' }, - { input: 'src/lazy-style.css', lazy: true }, - { input: 'src/pre-rename-style.css', bundleName: 'renamed-style' }, - { input: 'src/pre-rename-lazy-style.css', bundleName: 'renamed-lazy-style', lazy: true } - ]; - })) - .then(() => ng('build', '--extract-css')) - // files were created successfully - .then(() => expectFileToMatch('dist/test-project/styles.css', '.string-style')) - .then(() => expectFileToMatch('dist/test-project/styles.css', '.input-style')) - .then(() => expectFileToMatch('dist/test-project/lazy-style.css', '.lazy-style')) - .then(() => expectFileToMatch('dist/test-project/renamed-style.css', '.pre-rename-style')) - .then(() => expectFileToMatch('dist/test-project/renamed-lazy-style.css', '.pre-rename-lazy-style')) - // there are no js entry points for css only bundles - .then(() => expectToFail(() => expectFileToExist('dist/test-project/style-es5.js'))) - .then(() => expectToFail(() => expectFileToExist('dist/test-project/lazy-style-es5.js'))) - .then(() => expectToFail(() => expectFileToExist('dist/test-project/renamed-style-es5.js'))) - .then(() => expectToFail(() => expectFileToExist('dist/test-project/renamed-lazy-style-es5.js'))) - .then(() => expectToFail(() => expectFileToExist('dist/test-project/style-es2015.js'))) - .then(() => expectToFail(() => expectFileToExist('dist/test-project/lazy-style-es2015.js'))) - .then(() => expectToFail(() => expectFileToExist('dist/test-project/renamed-style-es2015.js'))) - .then(() => expectToFail(() => expectFileToExist('dist/test-project/renamed-lazy-style-es2015.js'))) - // index.html lists the right bundles - .then(() => expectFileToMatch('dist/test-project/index.html', new RegExp(oneLineTrim` + return ( + Promise.resolve() + .then(() => + writeMultipleFiles({ + 'src/string-style.css': '.string-style { color: red }', + 'src/input-style.css': '.input-style { color: red }', + 'src/lazy-style.css': '.lazy-style { color: red }', + 'src/pre-rename-style.css': '.pre-rename-style { color: red }', + 'src/pre-rename-lazy-style.css': '.pre-rename-lazy-style { color: red }', + }), + ) + .then(() => + updateJsonFile('angular.json', workspaceJson => { + const appArchitect = workspaceJson.projects['test-project'].architect; + appArchitect.build.options.styles = [ + { input: 'src/string-style.css' }, + { input: 'src/input-style.css' }, + { input: 'src/lazy-style.css', inject: false }, + { input: 'src/pre-rename-style.css', bundleName: 'renamed-style' }, + { + input: 'src/pre-rename-lazy-style.css', + bundleName: 'renamed-lazy-style', + inject: false, + }, + ]; + }), + ) + .then(() => ng('build', '--extract-css')) + // files were created successfully + .then(() => expectFileToMatch('dist/test-project/styles.css', '.string-style')) + .then(() => expectFileToMatch('dist/test-project/styles.css', '.input-style')) + .then(() => expectFileToMatch('dist/test-project/lazy-style.css', '.lazy-style')) + .then(() => expectFileToMatch('dist/test-project/renamed-style.css', '.pre-rename-style')) + .then(() => + expectFileToMatch('dist/test-project/renamed-lazy-style.css', '.pre-rename-lazy-style'), + ) + // there are no js entry points for css only bundles + .then(() => expectToFail(() => expectFileToExist('dist/test-project/style-es5.js'))) + .then(() => expectToFail(() => expectFileToExist('dist/test-project/lazy-style-es5.js'))) + .then(() => expectToFail(() => expectFileToExist('dist/test-project/renamed-style-es5.js'))) + .then(() => + expectToFail(() => expectFileToExist('dist/test-project/renamed-lazy-style-es5.js')), + ) + .then(() => expectToFail(() => expectFileToExist('dist/test-project/style-es2015.js'))) + .then(() => expectToFail(() => expectFileToExist('dist/test-project/lazy-style-es2015.js'))) + .then(() => + expectToFail(() => expectFileToExist('dist/test-project/renamed-style-es2015.js')), + ) + .then(() => + expectToFail(() => expectFileToExist('dist/test-project/renamed-lazy-style-es2015.js')), + ) + // index.html lists the right bundles + .then(() => + expectFileToMatch( + 'dist/test-project/index.html', + new RegExp(oneLineTrim` - `))) - .then(() => expectFileToMatch('dist/test-project/index.html', oneLineTrim` + `), + ), + ) + .then(() => + expectFileToMatch( + 'dist/test-project/index.html', + oneLineTrim` @@ -59,22 +80,36 @@ export default function () { - `)) - // also check when css isn't extracted - .then(() => ng('build', '--no-extract-css')) - // files were created successfully - .then(() => expectFileToMatch('dist/test-project/styles-es5.js', '.string-style')) - .then(() => expectFileToMatch('dist/test-project/styles-es5.js', '.input-style')) - .then(() => expectFileToMatch('dist/test-project/lazy-style-es5.js', '.lazy-style')) - .then(() => expectFileToMatch('dist/test-project/renamed-style-es5.js', '.pre-rename-style')) - .then(() => expectFileToMatch('dist/test-project/renamed-lazy-style-es5.js', '.pre-rename-lazy-style')) - .then(() => expectFileToMatch('dist/test-project/styles-es2015.js', '.string-style')) - .then(() => expectFileToMatch('dist/test-project/styles-es2015.js', '.input-style')) - .then(() => expectFileToMatch('dist/test-project/lazy-style-es2015.js', '.lazy-style')) - .then(() => expectFileToMatch('dist/test-project/renamed-style-es2015.js', '.pre-rename-style')) - .then(() => expectFileToMatch('dist/test-project/renamed-lazy-style-es2015.js', '.pre-rename-lazy-style')) - // index.html lists the right bundles - .then(() => expectFileToMatch('dist/test-project/index.html', oneLineTrim` + `, + ), + ) + // also check when css isn't extracted + .then(() => ng('build', '--no-extract-css')) + // files were created successfully + .then(() => expectFileToMatch('dist/test-project/styles-es5.js', '.string-style')) + .then(() => expectFileToMatch('dist/test-project/styles-es5.js', '.input-style')) + .then(() => expectFileToMatch('dist/test-project/lazy-style-es5.js', '.lazy-style')) + .then(() => expectFileToMatch('dist/test-project/renamed-style-es5.js', '.pre-rename-style')) + .then(() => + expectFileToMatch('dist/test-project/renamed-lazy-style-es5.js', '.pre-rename-lazy-style'), + ) + .then(() => expectFileToMatch('dist/test-project/styles-es2015.js', '.string-style')) + .then(() => expectFileToMatch('dist/test-project/styles-es2015.js', '.input-style')) + .then(() => expectFileToMatch('dist/test-project/lazy-style-es2015.js', '.lazy-style')) + .then(() => + expectFileToMatch('dist/test-project/renamed-style-es2015.js', '.pre-rename-style'), + ) + .then(() => + expectFileToMatch( + 'dist/test-project/renamed-lazy-style-es2015.js', + '.pre-rename-lazy-style', + ), + ) + // index.html lists the right bundles + .then(() => + expectFileToMatch( + 'dist/test-project/index.html', + oneLineTrim` @@ -87,5 +122,8 @@ export default function () { - `)); + `, + ), + ) + ); }