mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-22 15:02:11 +08:00
215 lines
7.1 KiB
TypeScript
215 lines
7.1 KiB
TypeScript
import * as webpack from 'webpack';
|
|
import * as path from 'path';
|
|
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
|
|
import { NamedLazyChunksWebpackPlugin } from '../../plugins/named-lazy-chunks-webpack-plugin';
|
|
import { InsertConcatAssetsWebpackPlugin } from '../../plugins/insert-concat-assets-webpack-plugin';
|
|
import { extraEntryParser, getOutputHashFormat, AssetPattern } from './utils';
|
|
import { isDirectory } from '../../utilities/is-directory';
|
|
import { requireProjectModule } from '../../utilities/require-project-module';
|
|
import { WebpackConfigOptions } from '../webpack-config';
|
|
|
|
const ConcatPlugin = require('webpack-concat-plugin');
|
|
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
|
|
const CircularDependencyPlugin = require('circular-dependency-plugin');
|
|
const SilentError = require('silent-error');
|
|
|
|
/**
|
|
* Enumerate loaders and their dependencies from this file to let the dependency validator
|
|
* know they are used.
|
|
*
|
|
* require('source-map-loader')
|
|
* require('raw-loader')
|
|
* require('url-loader')
|
|
* require('file-loader')
|
|
* require('@angular-devkit/build-optimizer')
|
|
*/
|
|
|
|
export function getCommonConfig(wco: WebpackConfigOptions) {
|
|
const { projectRoot, buildOptions, appConfig } = wco;
|
|
|
|
const appRoot = path.resolve(projectRoot, appConfig.root);
|
|
const nodeModules = path.resolve(projectRoot, 'node_modules');
|
|
|
|
let extraPlugins: any[] = [];
|
|
let extraRules: any[] = [];
|
|
let entryPoints: { [key: string]: string[] } = {};
|
|
|
|
if (appConfig.main) {
|
|
entryPoints['main'] = [path.resolve(appRoot, appConfig.main)];
|
|
}
|
|
|
|
if (appConfig.polyfills) {
|
|
entryPoints['polyfills'] = [path.resolve(appRoot, appConfig.polyfills)];
|
|
}
|
|
|
|
// determine hashing format
|
|
const hashFormat = getOutputHashFormat(buildOptions.outputHashing);
|
|
|
|
// process global scripts
|
|
if (appConfig.scripts.length > 0) {
|
|
const globalScripts = extraEntryParser(appConfig.scripts, appRoot, 'scripts');
|
|
const globalScriptsByEntry = globalScripts
|
|
.reduce((prev: { entry: string, paths: string[], lazy: boolean }[], curr) => {
|
|
|
|
let existingEntry = prev.find((el) => el.entry === curr.entry);
|
|
if (existingEntry) {
|
|
existingEntry.paths.push(curr.path);
|
|
// All entries have to be lazy for the bundle to be lazy.
|
|
existingEntry.lazy = existingEntry.lazy && curr.lazy;
|
|
} else {
|
|
prev.push({ entry: curr.entry, paths: [curr.path], lazy: curr.lazy });
|
|
}
|
|
return prev;
|
|
}, []);
|
|
|
|
|
|
// Add a new asset for each entry.
|
|
globalScriptsByEntry.forEach((script) => {
|
|
const hash = hashFormat.chunk !== '' && !script.lazy ? '.[hash]' : '';
|
|
extraPlugins.push(new ConcatPlugin({
|
|
uglify: buildOptions.target === 'production' ? { sourceMapIncludeSources: true } : false,
|
|
sourceMap: buildOptions.sourcemaps,
|
|
name: script.entry,
|
|
// Lazy scripts don't get a hash, otherwise they can't be loaded by name.
|
|
fileName: `[name]${script.lazy ? '' : hash}.bundle.js`,
|
|
filesToConcat: script.paths
|
|
}));
|
|
});
|
|
|
|
// Insert all the assets created by ConcatPlugin in the right place in index.html.
|
|
extraPlugins.push(new InsertConcatAssetsWebpackPlugin(
|
|
globalScriptsByEntry
|
|
.filter((el) => !el.lazy)
|
|
.map((el) => el.entry)
|
|
));
|
|
}
|
|
|
|
// process asset entries
|
|
if (appConfig.assets) {
|
|
const copyWebpackPluginPatterns = appConfig.assets.map((asset: string | AssetPattern) => {
|
|
// Convert all string assets to object notation.
|
|
asset = typeof asset === 'string' ? { glob: asset } : asset;
|
|
// Add defaults.
|
|
// Input is always resolved relative to the appRoot.
|
|
asset.input = path.resolve(appRoot, asset.input || '');
|
|
asset.output = asset.output || '';
|
|
asset.glob = asset.glob || '';
|
|
|
|
// Prevent asset configurations from writing outside of the output path
|
|
const fullOutputPath = path.resolve(buildOptions.outputPath, asset.output);
|
|
if (!fullOutputPath.startsWith(path.resolve(buildOptions.outputPath))) {
|
|
const message = 'An asset cannot be written to a location outside of the output path.';
|
|
throw new SilentError(message);
|
|
}
|
|
|
|
// Ensure trailing slash.
|
|
if (isDirectory(path.resolve(asset.input))) {
|
|
asset.input += '/';
|
|
}
|
|
|
|
// Convert dir patterns to globs.
|
|
if (isDirectory(path.resolve(asset.input, asset.glob))) {
|
|
asset.glob = asset.glob + '/**/*';
|
|
}
|
|
|
|
return {
|
|
context: asset.input,
|
|
to: asset.output,
|
|
from: {
|
|
glob: asset.glob,
|
|
dot: true
|
|
}
|
|
};
|
|
});
|
|
const copyWebpackPluginOptions = { ignore: ['.gitkeep'] };
|
|
|
|
const copyWebpackPluginInstance = new CopyWebpackPlugin(copyWebpackPluginPatterns,
|
|
copyWebpackPluginOptions);
|
|
|
|
// Save options so we can use them in eject.
|
|
(copyWebpackPluginInstance as any)['copyWebpackPluginPatterns'] = copyWebpackPluginPatterns;
|
|
(copyWebpackPluginInstance as any)['copyWebpackPluginOptions'] = copyWebpackPluginOptions;
|
|
|
|
extraPlugins.push(copyWebpackPluginInstance);
|
|
}
|
|
|
|
if (buildOptions.progress) {
|
|
extraPlugins.push(new ProgressPlugin({ profile: buildOptions.verbose, colors: true }));
|
|
}
|
|
|
|
if (buildOptions.showCircularDependencies) {
|
|
extraPlugins.push(new CircularDependencyPlugin({
|
|
exclude: /(\\|\/)node_modules(\\|\/)/
|
|
}));
|
|
}
|
|
|
|
if (buildOptions.buildOptimizer) {
|
|
extraRules.push({
|
|
test: /\.js$/,
|
|
use: [{
|
|
loader: '@angular-devkit/build-optimizer/webpack-loader',
|
|
options: { sourceMap: buildOptions.sourcemaps }
|
|
}]
|
|
});
|
|
}
|
|
|
|
if (buildOptions.namedChunks) {
|
|
extraPlugins.push(new NamedLazyChunksWebpackPlugin());
|
|
}
|
|
|
|
// Load rxjs path aliases.
|
|
// https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#build-and-treeshaking
|
|
let alias = {};
|
|
try {
|
|
const rxjsPathMappingImport = wco.supportES2015
|
|
? 'rxjs/_esm2015/path-mapping'
|
|
: 'rxjs/_esm5/path-mapping';
|
|
const rxPaths = requireProjectModule(projectRoot, rxjsPathMappingImport);
|
|
alias = rxPaths(nodeModules);
|
|
} catch (e) { }
|
|
|
|
return {
|
|
resolve: {
|
|
extensions: ['.ts', '.js'],
|
|
modules: ['node_modules', nodeModules],
|
|
symlinks: !buildOptions.preserveSymlinks,
|
|
alias
|
|
},
|
|
resolveLoader: {
|
|
modules: [nodeModules, 'node_modules']
|
|
},
|
|
context: __dirname,
|
|
entry: entryPoints,
|
|
output: {
|
|
path: path.resolve(buildOptions.outputPath),
|
|
publicPath: buildOptions.deployUrl,
|
|
filename: `[name]${hashFormat.chunk}.bundle.js`,
|
|
chunkFilename: `[id]${hashFormat.chunk}.chunk.js`
|
|
},
|
|
module: {
|
|
rules: [
|
|
{ test: /\.html$/, loader: 'raw-loader' },
|
|
{
|
|
test: /\.(eot|svg|cur)$/,
|
|
loader: 'file-loader',
|
|
options: {
|
|
name: `[name]${hashFormat.file}.[ext]`,
|
|
limit: 10000
|
|
}
|
|
},
|
|
{
|
|
test: /\.(jpg|png|webp|gif|otf|ttf|woff|woff2|ani)$/,
|
|
loader: 'url-loader',
|
|
options: {
|
|
name: `[name]${hashFormat.file}.[ext]`,
|
|
limit: 10000
|
|
}
|
|
}
|
|
].concat(extraRules)
|
|
},
|
|
plugins: [
|
|
new webpack.NoEmitOnErrorsPlugin()
|
|
].concat(extraPlugins)
|
|
};
|
|
}
|