Skip to content

Commit e8f27f0

Browse files
filipesilvaBrocco
authored andcommitted
feat(@angular/cli): support sourcemaps and minification in scripts
Adds sourcemap and minification to javascript added via the `scripts` array in `.angular-cli.json`. `script-loader` is no longer used, which should help with CSP since it used `eval`. Scripts will no longer appear in the console output for `ng build`, as they are now assets instead of webpack entry points. It's no longer possible to have the `output` property of both a `scripts` and a `styles` entry pointing to the same file. This wasn't officially supported or listed in the docs, but used to be possible. Fix #2796 Fix #7226 Fix #7290 Related to #6872
1 parent b9a62e0 commit e8f27f0

File tree

12 files changed

+202
-67
lines changed

12 files changed

+202
-67
lines changed

package-lock.json

+72-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@
8282
"resolve": "^1.1.7",
8383
"rxjs": "^5.4.2",
8484
"sass-loader": "^6.0.3",
85-
"script-loader": "^0.7.0",
8685
"semver": "^5.3.0",
8786
"silent-error": "^1.0.0",
8887
"source-map": "^0.5.6",
@@ -94,6 +93,7 @@
9493
"typescript": "~2.4.2",
9594
"url-loader": "^0.5.7",
9695
"webpack": "~3.4.1",
96+
"webpack-concat-plugin": "1.4.0",
9797
"webpack-dev-middleware": "^1.11.0",
9898
"webpack-dev-server": "~2.5.1",
9999
"webpack-merge": "^4.1.0",

packages/@angular/cli/models/webpack-configs/common.ts

+35-6
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import * as webpack from 'webpack';
22
import * as path from 'path';
33
import { GlobCopyWebpackPlugin } from '../../plugins/glob-copy-webpack-plugin';
44
import { NamedLazyChunksWebpackPlugin } from '../../plugins/named-lazy-chunks-webpack-plugin';
5+
import { InsertConcatAssetsWebpackPlugin } from '../../plugins/insert-concat-assets-webpack-plugin';
56
import { extraEntryParser, getOutputHashFormat } from './utils';
67
import { WebpackConfigOptions } from '../webpack-config';
78

9+
const ConcatPlugin = require('webpack-concat-plugin');
810
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
911
const CircularDependencyPlugin = require('circular-dependency-plugin');
1012

@@ -15,7 +17,6 @@ const CircularDependencyPlugin = require('circular-dependency-plugin');
1517
*
1618
* require('source-map-loader')
1719
* require('raw-loader')
18-
* require('script-loader')
1920
* require('url-loader')
2021
* require('file-loader')
2122
* require('@angular-devkit/build-optimizer')
@@ -45,12 +46,40 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
4546
// process global scripts
4647
if (appConfig.scripts.length > 0) {
4748
const globalScripts = extraEntryParser(appConfig.scripts, appRoot, 'scripts');
48-
49-
// add entry points and lazy chunks
50-
globalScripts.forEach(script => {
51-
let scriptPath = `script-loader!${script.path}`;
52-
entryPoints[script.entry] = (entryPoints[script.entry] || []).concat(scriptPath);
49+
const globalScriptsByEntry = globalScripts
50+
.reduce((prev: { entry: string, paths: string[], lazy: boolean }[], curr) => {
51+
52+
let existingEntry = prev.find((el) => el.entry === curr.entry);
53+
if (existingEntry) {
54+
existingEntry.paths.push(curr.path);
55+
// All entries have to be lazy for the bundle to be lazy.
56+
existingEntry.lazy = existingEntry.lazy && curr.lazy;
57+
} else {
58+
prev.push({ entry: curr.entry, paths: [curr.path], lazy: curr.lazy });
59+
}
60+
return prev;
61+
}, []);
62+
63+
64+
// Add a new asset for each entry.
65+
globalScriptsByEntry.forEach((script) => {
66+
const hash = hashFormat.chunk !== '' && !script.lazy ? '.[hash]' : '';
67+
extraPlugins.push(new ConcatPlugin({
68+
uglify: buildOptions.target === 'production' ? { sourceMapIncludeSources: true } : false,
69+
sourceMap: buildOptions.sourcemaps,
70+
name: script.entry,
71+
// Lazy scripts don't get a hash, otherwise they can't be loaded by name.
72+
fileName: `[name]${script.lazy ? '' : hash}.bundle.js`,
73+
filesToConcat: script.paths
74+
}));
5375
});
76+
77+
// Insert all the assets created by ConcatPlugin in the right place in index.html.
78+
extraPlugins.push(new InsertConcatAssetsWebpackPlugin(
79+
globalScriptsByEntry
80+
.filter((el) => !el.lazy)
81+
.map((el) => el.entry)
82+
));
5483
}
5584

5685
// process asset entries

packages/@angular/cli/models/webpack-configs/utils.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,16 @@ export interface HashFormat {
7373
chunk: string;
7474
extract: string;
7575
file: string;
76+
script: string;
7677
}
7778

7879
export function getOutputHashFormat(option: string, length = 20): HashFormat {
7980
/* tslint:disable:max-line-length */
8081
const hashFormats: { [option: string]: HashFormat } = {
81-
none: { chunk: '', extract: '', file: '' },
82-
media: { chunk: '', extract: '', file: `.[hash:${length}]` },
83-
bundles: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: '' },
84-
all: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: `.[hash:${length}]` },
82+
none: { chunk: '', extract: '', file: '' , script: '' },
83+
media: { chunk: '', extract: '', file: `.[hash:${length}]`, script: '' },
84+
bundles: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: '' , script: '.[hash]' },
85+
all: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: `.[hash:${length}]`, script: '.[hash]' },
8586
};
8687
/* tslint:enable:max-line-length */
8788
return hashFormats[option] || hashFormats['none'];

packages/@angular/cli/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
"resolve": "^1.1.7",
6868
"rxjs": "^5.4.2",
6969
"sass-loader": "^6.0.3",
70-
"script-loader": "^0.7.0",
7170
"semver": "^5.1.0",
7271
"silent-error": "^1.0.0",
7372
"source-map-loader": "^0.2.0",
@@ -79,6 +78,7 @@
7978
"typescript": ">=2.0.0 <2.5.0",
8079
"url-loader": "^0.5.7",
8180
"webpack": "~3.4.1",
81+
"webpack-concat-plugin": "1.4.0",
8282
"webpack-dev-middleware": "^1.11.0",
8383
"webpack-dev-server": "~2.5.1",
8484
"webpack-merge": "^4.1.0",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Add assets from `ConcatPlugin` to index.html.
2+
3+
export class InsertConcatAssetsWebpackPlugin {
4+
// Priority list of where to insert asset.
5+
private insertAfter = [
6+
/polyfills(\.[0-9a-f]{20})?\.bundle\.js/,
7+
/inline(\.[0-9a-f]{20})?\.bundle\.js/,
8+
];
9+
10+
constructor(private entryNames: string[]) { }
11+
12+
apply(compiler: any): void {
13+
compiler.plugin('compilation', (compilation: any) => {
14+
compilation.plugin('html-webpack-plugin-before-html-generation',
15+
(htmlPluginData: any, callback: any) => {
16+
17+
const fileNames = this.entryNames.map((entryName) => {
18+
const fileName = htmlPluginData.assets.webpackConcat
19+
&& htmlPluginData.assets.webpackConcat[entryName];
20+
21+
if (!fileName) {
22+
// Something went wrong and the asset was not correctly added.
23+
throw new Error(`Cannot find file for ${entryName} script.`);
24+
}
25+
26+
return fileName;
27+
});
28+
29+
let insertAt = 0;
30+
31+
// TODO: try to figure out if there are duplicate bundle names when adding and throw
32+
for (let el of this.insertAfter) {
33+
const jsIdx = htmlPluginData.assets.js.findIndex((js: string) => js.match(el));
34+
if (jsIdx !== -1) {
35+
insertAt = jsIdx + 1;
36+
break;
37+
}
38+
}
39+
40+
htmlPluginData.assets.js.splice(insertAt, 0, ...fileNames);
41+
callback(null, htmlPluginData);
42+
});
43+
});
44+
}
45+
}

packages/@angular/cli/plugins/webpack.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ module.exports = {
88
require('../plugins/suppress-entry-chunks-webpack-plugin')
99
.SuppressExtractedTextChunksWebpackPlugin,
1010
NamedLazyChunksWebpackPlugin:
11-
require('../plugins/named-lazy-chunks-webpack-plugin').NamedLazyChunksWebpackPlugin
11+
require('../plugins/named-lazy-chunks-webpack-plugin').NamedLazyChunksWebpackPlugin,
12+
InsertConcatAssetsWebpackPlugin:
13+
require('../plugins/insert-concat-assets-webpack-plugin').InsertConcatAssetsWebpackPlugin
1214
};

packages/@angular/cli/tasks/eject.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin');
2323
const HtmlWebpackPlugin = require('html-webpack-plugin');
2424
const SilentError = require('silent-error');
2525
const CircularDependencyPlugin = require('circular-dependency-plugin');
26+
const ConcatPlugin = require('webpack-concat-plugin');
2627
const Task = require('../ember-cli/lib/models/task');
2728

2829
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
@@ -85,6 +86,10 @@ class JsonWebpackSerializer {
8586
};
8687
}
8788

89+
private _insertConcatAssetsWebpackPluginSerialize(value: any): any {
90+
return value.entryNames;
91+
}
92+
8893
private _commonsChunkPluginSerialize(value: any): any {
8994
let minChunks = value.minChunks;
9095
switch (typeof minChunks) {
@@ -149,6 +154,10 @@ class JsonWebpackSerializer {
149154
return plugin.options;
150155
}
151156

157+
private _concatPlugin(plugin: any) {
158+
return plugin.settings;
159+
}
160+
152161
private _pluginsReplacer(plugins: any[]) {
153162
return plugins.map(plugin => {
154163
let args = plugin.options || undefined;
@@ -184,6 +193,10 @@ class JsonWebpackSerializer {
184193
args = this._globCopyWebpackPluginSerialize(plugin);
185194
this._addImport('@angular/cli/plugins/webpack', 'GlobCopyWebpackPlugin');
186195
break;
196+
case angularCliPlugins.InsertConcatAssetsWebpackPlugin:
197+
args = this._insertConcatAssetsWebpackPluginSerialize(plugin);
198+
this._addImport('@angular/cli/plugins/webpack', 'InsertConcatAssetsWebpackPlugin');
199+
break;
187200
case webpack.optimize.CommonsChunkPlugin:
188201
args = this._commonsChunkPluginSerialize(plugin);
189202
this._addImport('webpack.optimize', 'CommonsChunkPlugin');
@@ -210,6 +223,11 @@ class JsonWebpackSerializer {
210223
case LicenseWebpackPlugin:
211224
args = this._licenseWebpackPlugin(plugin);
212225
this._addImport('license-webpack-plugin', 'LicenseWebpackPlugin');
226+
break;
227+
case ConcatPlugin:
228+
args = this._concatPlugin(plugin);
229+
this.variableImports['webpack-concat-plugin'] = 'ConcatPlugin';
230+
break;
213231
default:
214232
if (plugin.constructor.name == 'AngularServiceWorkerPlugin') {
215233
this._addImport('@angular/service-worker/build/webpack', plugin.constructor.name);
@@ -513,13 +531,13 @@ export default Task.extend({
513531
'postcss-url',
514532
'raw-loader',
515533
'sass-loader',
516-
'script-loader',
517534
'source-map-loader',
518535
'istanbul-instrumenter-loader',
519536
'style-loader',
520537
'stylus-loader',
521538
'url-loader',
522539
'circular-dependency-plugin',
540+
'webpack-concat-plugin',
523541
].forEach((packageName: string) => {
524542
packageJson['devDependencies'][packageName] = ourPackageJson['dependencies'][packageName];
525543
});

packages/@angular/cli/utilities/package-chunk-sort.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ExtraEntry, extraEntryParser } from '../models/webpack-configs/utils';
22

33
// Sort chunks according to a predefined order:
4-
// inline, polyfills, all scripts, all styles, vendor, main
4+
// inline, polyfills, all styles, vendor, main
55
export function packageChunkSort(appConfig: any) {
66
let entryPoints = ['inline', 'polyfills', 'sw-register'];
77

@@ -11,10 +11,6 @@ export function packageChunkSort(appConfig: any) {
1111
}
1212
};
1313

14-
if (appConfig.scripts) {
15-
extraEntryParser(appConfig.scripts, './', 'scripts').forEach(pushExtraEntries);
16-
}
17-
1814
if (appConfig.styles) {
1915
extraEntryParser(appConfig.styles, './', 'styles').forEach(pushExtraEntries);
2016
}

0 commit comments

Comments
 (0)