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