UNPKG

8.49 kBJavaScriptView Raw
1/*
2 Incremental Uglify JS for webpack
3*/
4var path = require('path');
5var fs = require('fs');
6var SourceMapConsumer = require("webpack-core/lib/source-map").SourceMapConsumer;
7var SourceMapSource = require("webpack-core/lib/SourceMapSource");
8var RawSource = require("webpack-core/lib/RawSource");
9var RequestShortener = require("./utils/RequestShortener");
10var ModuleFilenameHelpers = require("./utils/ModuleFilenameHelpers");
11var uglify = require("uglify-js");
12var fileUtils = require('./utils/file');
13
14
15var M_TIME_FILE = 'm_time_record.json';
16var CACHED_UGLIFY_JS_FOLDER = 'cached_uglify';
17
18function 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}
30module.exports = UglifyJsPlugin;
31
32UglifyJsPlugin.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 // to get detailed location info about errors
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 // cacheUglifyFiles = fileUtils.getFilesInDirectory(cacheUglifyFolder);
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) { // eslint-disable-line camelcase
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) { // eslint-disable-line camelcase
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); // eslint-disable-line new-cap
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({ // eslint-disable-line new-cap
165 file: file,
166 root: ""
167 });
168 output.source_map = map; // eslint-disable-line camelcase
169 }
170 var stream = uglify.OutputStream(output); // eslint-disable-line new-cap
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; // eslint-disable-line camelcase
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
211function 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}