1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 |
|
7 | var _crypto = require('crypto');
|
8 |
|
9 | var _crypto2 = _interopRequireDefault(_crypto);
|
10 |
|
11 | var _path = require('path');
|
12 |
|
13 | var _path2 = _interopRequireDefault(_path);
|
14 |
|
15 | var _sourceMap = require('source-map');
|
16 |
|
17 | var _webpackSources = require('webpack-sources');
|
18 |
|
19 | var _RequestShortener = require('webpack/lib/RequestShortener');
|
20 |
|
21 | var _RequestShortener2 = _interopRequireDefault(_RequestShortener);
|
22 |
|
23 | var _ModuleFilenameHelpers = require('webpack/lib/ModuleFilenameHelpers');
|
24 |
|
25 | var _ModuleFilenameHelpers2 = _interopRequireDefault(_ModuleFilenameHelpers);
|
26 |
|
27 | var _schemaUtils = require('schema-utils');
|
28 |
|
29 | var _schemaUtils2 = _interopRequireDefault(_schemaUtils);
|
30 |
|
31 | var _options = require('./options.json');
|
32 |
|
33 | var _options2 = _interopRequireDefault(_options);
|
34 |
|
35 | var _TaskRunner = require('./TaskRunner');
|
36 |
|
37 | var _TaskRunner2 = _interopRequireDefault(_TaskRunner);
|
38 |
|
39 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
40 |
|
41 | const warningRegex = /\[.+:([0-9]+),([0-9]+)\]/; |
42 |
|
43 |
|
44 |
|
45 |
|
46 | class UglifyJsPlugin {
|
47 | constructor(options = {}) {
|
48 | (0, _schemaUtils2.default)(_options2.default, options, 'UglifyJs Plugin');
|
49 |
|
50 | const {
|
51 | minify,
|
52 | uglifyOptions = {},
|
53 | test = /\.js(\?.*)?$/i,
|
54 | warningsFilter = () => true,
|
55 | extractComments = false,
|
56 | sourceMap = false,
|
57 | cache = false,
|
58 | cacheKeys = defaultCacheKeys => defaultCacheKeys,
|
59 | parallel = false,
|
60 | include,
|
61 | exclude
|
62 | } = options;
|
63 |
|
64 | this.options = {
|
65 | test,
|
66 | warningsFilter,
|
67 | extractComments,
|
68 | sourceMap,
|
69 | cache,
|
70 | cacheKeys,
|
71 | parallel,
|
72 | include,
|
73 | exclude,
|
74 | minify,
|
75 | uglifyOptions: Object.assign({
|
76 | output: {
|
77 | comments: extractComments ? false : /^\**!|@preserve|@license|@cc_on/i
|
78 | }
|
79 | }, uglifyOptions)
|
80 | };
|
81 | }
|
82 |
|
83 | static isSourceMap(input) {
|
84 |
|
85 |
|
86 | return Boolean(input && input.version && input.sources && Array.isArray(input.sources) && typeof input.mappings === 'string');
|
87 | }
|
88 |
|
89 | static buildSourceMap(inputSourceMap) {
|
90 | if (!inputSourceMap || !UglifyJsPlugin.isSourceMap(inputSourceMap)) {
|
91 | return null;
|
92 | }
|
93 |
|
94 | return new _sourceMap.SourceMapConsumer(inputSourceMap);
|
95 | }
|
96 |
|
97 | static buildError(err, file, sourceMap, requestShortener) {
|
98 |
|
99 | if (err.line) {
|
100 | const original = sourceMap && sourceMap.originalPositionFor({
|
101 | line: err.line,
|
102 | column: err.col
|
103 | });
|
104 |
|
105 | if (original && original.source && requestShortener) {
|
106 | return new Error(`${file} from UglifyJs\n${err.message} [${requestShortener.shorten(original.source)}:${original.line},${original.column}][${file}:${err.line},${err.col}]`);
|
107 | }
|
108 |
|
109 | return new Error(`${file} from UglifyJs\n${err.message} [${file}:${err.line},${err.col}]`);
|
110 | } else if (err.stack) {
|
111 | return new Error(`${file} from UglifyJs\n${err.stack}`);
|
112 | }
|
113 |
|
114 | return new Error(`${file} from UglifyJs\n${err.message}`);
|
115 | }
|
116 |
|
117 | static buildWarning(warning, file, sourceMap, requestShortener, warningsFilter) {
|
118 | let warningMessage = warning;
|
119 | let locationMessage = '';
|
120 | let source = null;
|
121 |
|
122 | if (sourceMap) {
|
123 | const match = warningRegex.exec(warning);
|
124 |
|
125 | if (match) {
|
126 | const line = +match[1];
|
127 | const column = +match[2];
|
128 | const original = sourceMap.originalPositionFor({
|
129 | line,
|
130 | column
|
131 | });
|
132 |
|
133 | if (original && original.source && original.source !== file && requestShortener) {
|
134 | ({ source } = original);
|
135 | warningMessage = `${warningMessage.replace(warningRegex, '')}`;
|
136 |
|
137 | locationMessage = `[${requestShortener.shorten(original.source)}:${original.line},${original.column}]`;
|
138 | }
|
139 | }
|
140 | }
|
141 |
|
142 | if (warningsFilter && !warningsFilter(warning, source)) {
|
143 | return null;
|
144 | }
|
145 |
|
146 | return `UglifyJs Plugin: ${warningMessage}${locationMessage}`;
|
147 | }
|
148 |
|
149 | apply(compiler) {
|
150 | const buildModuleFn = moduleArg => {
|
151 |
|
152 | moduleArg.useSourceMap = true;
|
153 | };
|
154 |
|
155 | const optimizeFn = (compilation, chunks, callback) => {
|
156 | const taskRunner = new _TaskRunner2.default({
|
157 | cache: this.options.cache,
|
158 | parallel: this.options.parallel
|
159 | });
|
160 |
|
161 | const processedAssets = new WeakSet();
|
162 | const tasks = [];
|
163 |
|
164 | chunks.reduce((acc, chunk) => acc.concat(chunk.files || []), []).concat(compilation.additionalChunkAssets || []).filter(_ModuleFilenameHelpers2.default.matchObject.bind(null, this.options)).forEach(file => {
|
165 | let inputSourceMap;
|
166 |
|
167 | const asset = compilation.assets[file];
|
168 |
|
169 | if (processedAssets.has(asset)) {
|
170 | return;
|
171 | }
|
172 |
|
173 | try {
|
174 | let input;
|
175 |
|
176 | if (this.options.sourceMap && asset.sourceAndMap) {
|
177 | const { source, map } = asset.sourceAndMap();
|
178 |
|
179 | input = source;
|
180 |
|
181 | if (UglifyJsPlugin.isSourceMap(map)) {
|
182 | inputSourceMap = map;
|
183 | } else {
|
184 | inputSourceMap = map;
|
185 |
|
186 | compilation.warnings.push(new Error(`${file} contains invalid source map`));
|
187 | }
|
188 | } else {
|
189 | input = asset.source();
|
190 | inputSourceMap = null;
|
191 | }
|
192 |
|
193 |
|
194 | let commentsFile = false;
|
195 |
|
196 | if (this.options.extractComments) {
|
197 | commentsFile = this.options.extractComments.filename || `${file}.LICENSE`;
|
198 |
|
199 | if (typeof commentsFile === 'function') {
|
200 | commentsFile = commentsFile(file);
|
201 | }
|
202 | }
|
203 |
|
204 | const task = {
|
205 | file,
|
206 | input,
|
207 | inputSourceMap,
|
208 | commentsFile,
|
209 | extractComments: this.options.extractComments,
|
210 | uglifyOptions: this.options.uglifyOptions,
|
211 | minify: this.options.minify
|
212 | };
|
213 |
|
214 | if (this.options.cache) {
|
215 | const { outputPath } = compiler;
|
216 | const defaultCacheKeys = {
|
217 |
|
218 | 'uglify-js': require('uglify-js/package.json').version,
|
219 |
|
220 | 'uglifyjs-webpack-plugin': require('../package.json').version,
|
221 | 'uglifyjs-webpack-plugin-options': this.options,
|
222 | path: `${outputPath ? `${outputPath}/` : ''}${file}`,
|
223 | hash: _crypto2.default.createHash('md4').update(input).digest('hex')
|
224 | };
|
225 |
|
226 | task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file);
|
227 | }
|
228 |
|
229 | tasks.push(task);
|
230 | } catch (error) {
|
231 | compilation.errors.push(UglifyJsPlugin.buildError(error, file, UglifyJsPlugin.buildSourceMap(inputSourceMap), new _RequestShortener2.default(compiler.context)));
|
232 | }
|
233 | });
|
234 |
|
235 | taskRunner.run(tasks, (tasksError, results) => {
|
236 | if (tasksError) {
|
237 | compilation.errors.push(tasksError);
|
238 |
|
239 | return;
|
240 | }
|
241 |
|
242 | results.forEach((data, index) => {
|
243 | const { file, input, inputSourceMap, commentsFile } = tasks[index];
|
244 | const { error, map, code, warnings, extractedComments } = data;
|
245 |
|
246 | let sourceMap = null;
|
247 |
|
248 | if (error || warnings && warnings.length > 0) {
|
249 | sourceMap = UglifyJsPlugin.buildSourceMap(inputSourceMap);
|
250 | }
|
251 |
|
252 |
|
253 |
|
254 | if (error) {
|
255 | compilation.errors.push(UglifyJsPlugin.buildError(error, file, sourceMap, new _RequestShortener2.default(compiler.context)));
|
256 |
|
257 | return;
|
258 | }
|
259 |
|
260 | let outputSource;
|
261 |
|
262 | if (map) {
|
263 | outputSource = new _webpackSources.SourceMapSource(code, file, JSON.parse(map), input, inputSourceMap);
|
264 | } else {
|
265 | outputSource = new _webpackSources.RawSource(code);
|
266 | }
|
267 |
|
268 |
|
269 | if (commentsFile && extractedComments.length > 0) {
|
270 |
|
271 | if (this.options.extractComments.banner !== false) {
|
272 | let banner = this.options.extractComments.banner || `For license information please see ${_path2.default.posix.basename(commentsFile)}`;
|
273 |
|
274 | if (typeof banner === 'function') {
|
275 | banner = banner(commentsFile);
|
276 | }
|
277 |
|
278 | if (banner) {
|
279 | outputSource = new _webpackSources.ConcatSource(`/*! ${banner} */\n`, outputSource);
|
280 | }
|
281 | }
|
282 |
|
283 | const commentsSource = new _webpackSources.RawSource(`${extractedComments.join('\n\n')}\n`);
|
284 |
|
285 | if (commentsFile in compilation.assets) {
|
286 |
|
287 | if (compilation.assets[commentsFile] instanceof _webpackSources.ConcatSource) {
|
288 | compilation.assets[commentsFile].add('\n');
|
289 | compilation.assets[commentsFile].add(commentsSource);
|
290 | } else {
|
291 | compilation.assets[commentsFile] = new _webpackSources.ConcatSource(compilation.assets[commentsFile], '\n', commentsSource);
|
292 | }
|
293 | } else {
|
294 | compilation.assets[commentsFile] = commentsSource;
|
295 | }
|
296 | }
|
297 |
|
298 |
|
299 | processedAssets.add(compilation.assets[file] = outputSource);
|
300 |
|
301 |
|
302 | if (warnings && warnings.length > 0) {
|
303 | warnings.forEach(warning => {
|
304 | const builtWarning = UglifyJsPlugin.buildWarning(warning, file, sourceMap, new _RequestShortener2.default(compiler.context), this.options.warningsFilter);
|
305 |
|
306 | if (builtWarning) {
|
307 | compilation.warnings.push(builtWarning);
|
308 | }
|
309 | });
|
310 | }
|
311 | });
|
312 |
|
313 | taskRunner.exit();
|
314 |
|
315 | callback();
|
316 | });
|
317 | };
|
318 |
|
319 | const plugin = { name: this.constructor.name };
|
320 |
|
321 | compiler.hooks.compilation.tap(plugin, compilation => {
|
322 | if (this.options.sourceMap) {
|
323 | compilation.hooks.buildModule.tap(plugin, buildModuleFn);
|
324 | }
|
325 |
|
326 | compilation.hooks.optimizeChunkAssets.tapAsync(plugin, optimizeFn.bind(this, compilation));
|
327 | });
|
328 | }
|
329 | }
|
330 |
|
331 | exports.default = UglifyJsPlugin; |
\ | No newline at end of file |