1 |
|
2 |
|
3 |
|
4 | var path = require('path');
|
5 | var fs = require('fs');
|
6 | var SourceMapConsumer = require("webpack-core/lib/source-map").SourceMapConsumer;
|
7 | var SourceMapSource = require("webpack-core/lib/SourceMapSource");
|
8 | var RawSource = require("webpack-core/lib/RawSource");
|
9 | var RequestShortener = require("./utils/RequestShortener");
|
10 | var ModuleFilenameHelpers = require("./utils/ModuleFilenameHelpers");
|
11 | var uglify = require("uglify-js");
|
12 | var fileUtils = require('./utils/file');
|
13 |
|
14 |
|
15 | var M_TIME_FILE = 'm_time_record.json';
|
16 | var CACHED_UGLIFY_JS_FOLDER = 'cached_uglify';
|
17 |
|
18 | function UglifyJsPlugin(options) {
|
19 | if(typeof options !== "object") options = {};
|
20 |
|
21 | if(options.cacheFolder === undefined) {
|
22 | return;
|
23 | }
|
24 |
|
25 | if(typeof options.compressor !== "undefined") {
|
26 | options.compress = options.compressor;
|
27 | }
|
28 | this.options = options;
|
29 | }
|
30 | module.exports = UglifyJsPlugin;
|
31 |
|
32 | UglifyJsPlugin.prototype.apply = function(compiler) {
|
33 | var options = this.options;
|
34 | options.test = options.test || /\.js($|\?)/i;
|
35 |
|
36 | var requestShortener = new RequestShortener(compiler.context);
|
37 | compiler.plugin("compilation", function(compilation) {
|
38 | if(options.sourceMap !== false) {
|
39 | compilation.plugin("build-module", function(module) {
|
40 |
|
41 | module.useSourceMap = true;
|
42 | });
|
43 | }
|
44 | compilation.plugin("optimize-chunk-assets", function(chunks, callback) {
|
45 | var cacheUglifyFolder = path.resolve(__dirname, options.cacheFolder+'/'+CACHED_UGLIFY_JS_FOLDER),
|
46 | mTimeRecordsFile = path.resolve(__dirname, options.cacheFolder+'/'+M_TIME_FILE);
|
47 | var mTimeRecords, cacheUglifyFiles = [],
|
48 | changedChunks = [], changedFiles = [],
|
49 | unChangedChunks = [], unChangedFiles = [];
|
50 |
|
51 | if(fileUtils.exists(cacheUglifyFolder) && fileUtils.exists(mTimeRecordsFile)) {
|
52 |
|
53 | try {
|
54 | mTimeRecords = JSON.parse(fileUtils.read(mTimeRecordsFile));
|
55 | }catch(e) {
|
56 | mTimeRecords = {};
|
57 | }
|
58 |
|
59 | if(Object.keys(mTimeRecords).length > 0) {
|
60 | chunks.forEach(function(chunk) {
|
61 | var _modules = chunk.modules,
|
62 | isChanged = false;
|
63 | _modules.forEach(function(_module) {
|
64 | var modulePath = _module.resource,
|
65 | moduleStat = fs.statSync(modulePath),
|
66 | moduleStatMTime = moduleStat.mtime;
|
67 | if(moduleStatMTime !== mTimeRecords[modulePath]) {
|
68 | isChanged = true;
|
69 | mTimeRecords[modulePath] = moduleStatMTime
|
70 | }
|
71 | });
|
72 |
|
73 | if(isChanged) {
|
74 | changedChunks.push(chunk);
|
75 | } else {
|
76 | unChangedChunks.push(chunk);
|
77 | }
|
78 | });
|
79 |
|
80 | fileUtils.write(mTimeRecords);
|
81 | }
|
82 | }
|
83 |
|
84 | var files = getFilesFromChunks(changedChunks);
|
85 |
|
86 | if(unChangedChunks.length > 0) {
|
87 | unChangedFiles = getFilesFromChunks(unChangedChunks);
|
88 | unChangedFiles.forEach(function(unChangedFile) {
|
89 | var unChangedFilePath = cacheUglifyFolder + '/' + unChangedFile.replace(compilation.hash, '');
|
90 | if(fileUtils.exists(unChangedFilePath)) {
|
91 | compilation[unChangedFile] = new RawSource(fileUtils.read(unChangedFilePath));
|
92 | }else {
|
93 | files.push(unChangedFile);
|
94 | }
|
95 | });
|
96 | }
|
97 |
|
98 |
|
99 | compilation.additionalChunkAssets.forEach(function(file) {
|
100 | files.push(file);
|
101 | });
|
102 | files = files.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options));
|
103 | files.forEach(function(file) {
|
104 | var oldWarnFunction = uglify.AST_Node.warn_function;
|
105 | var warnings = [];
|
106 | try {
|
107 | var asset = compilation.assets[file];
|
108 | if(asset.__UglifyJsPlugin) {
|
109 | compilation.assets[file] = asset.__UglifyJsPlugin;
|
110 | return;
|
111 | }
|
112 | if(options.sourceMap !== false) {
|
113 | if(asset.sourceAndMap) {
|
114 | var sourceAndMap = asset.sourceAndMap();
|
115 | var inputSourceMap = sourceAndMap.map;
|
116 | var input = sourceAndMap.source;
|
117 | } else {
|
118 | var inputSourceMap = asset.map();
|
119 | var input = asset.source();
|
120 | }
|
121 | var sourceMap = new SourceMapConsumer(inputSourceMap);
|
122 | uglify.AST_Node.warn_function = function(warning) {
|
123 | var match = /\[.+:([0-9]+),([0-9]+)\]/.exec(warning);
|
124 | var line = +match[1];
|
125 | var column = +match[2];
|
126 | var original = sourceMap.originalPositionFor({
|
127 | line: line,
|
128 | column: column
|
129 | });
|
130 | if(!original || !original.source || original.source === file) return;
|
131 | warnings.push(warning.replace(/\[.+:([0-9]+),([0-9]+)\]/, "") +
|
132 | "[" + requestShortener.shorten(original.source) + ":" + original.line + "," + original.column + "]");
|
133 | };
|
134 | } else {
|
135 | var input = asset.source();
|
136 | uglify.AST_Node.warn_function = function(warning) {
|
137 | warnings.push(warning);
|
138 | };
|
139 | }
|
140 | uglify.base54.reset();
|
141 | var ast = uglify.parse(input, {
|
142 | filename: file
|
143 | });
|
144 | if(options.compress !== false) {
|
145 | ast.figure_out_scope();
|
146 | var compress = uglify.Compressor(options.compress);
|
147 | ast = ast.transform(compress);
|
148 | }
|
149 | if(options.mangle !== false) {
|
150 | ast.figure_out_scope();
|
151 | ast.compute_char_frequency(options.mangle || {});
|
152 | ast.mangle_names(options.mangle || {});
|
153 | if(options.mangle && options.mangle.props) {
|
154 | uglify.mangle_properties(ast, options.mangle.props);
|
155 | }
|
156 | }
|
157 | var output = {};
|
158 | output.comments = Object.prototype.hasOwnProperty.call(options, "comments") ? options.comments : /^\**!|@preserve|@license/;
|
159 | output.beautify = options.beautify;
|
160 | for(var k in options.output) {
|
161 | output[k] = options.output[k];
|
162 | }
|
163 | if(options.sourceMap !== false) {
|
164 | var map = uglify.SourceMap({
|
165 | file: file,
|
166 | root: ""
|
167 | });
|
168 | output.source_map = map;
|
169 | }
|
170 | var stream = uglify.OutputStream(output);
|
171 | ast.print(stream);
|
172 | if(map) map = map + "";
|
173 | stream = stream + "";
|
174 | asset.__UglifyJsPlugin = compilation.assets[file] = (map ?
|
175 | new SourceMapSource(stream, file, JSON.parse(map), input, inputSourceMap) :
|
176 | new RawSource(stream));
|
177 | if(warnings.length > 0) {
|
178 | compilation.warnings.push(new Error(file + " from UglifyJs\n" + warnings.join("\n")));
|
179 | }
|
180 | } catch(err) {
|
181 | if(err.line) {
|
182 | var original = sourceMap && sourceMap.originalPositionFor({
|
183 | line: err.line,
|
184 | column: err.col
|
185 | });
|
186 | if(original && original.source) {
|
187 | compilation.errors.push(new Error(file + " from UglifyJs\n" + err.message + " [" + requestShortener.shorten(original.source) + ":" + original.line + "," + original.column + "]"));
|
188 | } else {
|
189 | compilation.errors.push(new Error(file + " from UglifyJs\n" + err.message + " [" + file + ":" + err.line + "," + err.col + "]"));
|
190 | }
|
191 | } else if(err.msg) {
|
192 | compilation.errors.push(new Error(file + " from UglifyJs\n" + err.msg));
|
193 | } else
|
194 | compilation.errors.push(new Error(file + " from UglifyJs\n" + err.stack));
|
195 | } finally {
|
196 | uglify.AST_Node.warn_function = oldWarnFunction;
|
197 |
|
198 | var toBeCachedJS = file.replace(compilation.hash, '');
|
199 | fileUtils.write(cacheUglifyFolder + '/' + toBeCachedJS, asset.__UglifyJsPlugin._value);
|
200 | }
|
201 | });
|
202 | callback();
|
203 | });
|
204 | compilation.plugin("normal-module-loader", function(context) {
|
205 | context.minimize = true;
|
206 | });
|
207 | });
|
208 | };
|
209 |
|
210 |
|
211 | function getFilesFromChunks(chunks) {
|
212 | var files = [];
|
213 | chunks.forEach(function(chunk) {
|
214 | chunk.files.forEach(function(file) {
|
215 | files.push(file);
|
216 | });
|
217 | });
|
218 | return files;
|
219 | }
|