-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Gregory Waxman
committed
Jan 4, 2017
1 parent
6406da7
commit 75dfe7e
Showing
4 changed files
with
395 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
/* | ||
MIT License http://www.opensource.org/licenses/mit-license.php | ||
Author Tobias Koppers @sokra | ||
*/ | ||
"use strict"; | ||
|
||
const SourceMapConsumer = require("source-map").SourceMapConsumer; | ||
const SourceMapSource = require("webpack-sources").SourceMapSource; | ||
const RawSource = require("webpack-sources").RawSource; | ||
const RequestShortener = require("./lib/RequestShortener"); | ||
const ModuleFilenameHelpers = require("./lib/ModuleFilenameHelpers"); | ||
const uglify = require("uglify-js"); | ||
|
||
class UglifyJsPlugin { | ||
constructor(options) { | ||
if(typeof options !== "object" || Array.isArray(options)) options = {}; | ||
if(typeof options.compressor !== "undefined") options.compress = options.compressor; | ||
this.options = options; | ||
} | ||
|
||
apply(compiler) { | ||
let options = this.options; | ||
options.test = options.test || /\.js($|\?)/i; | ||
|
||
let requestShortener = new RequestShortener(compiler.context); | ||
compiler.plugin("compilation", (compilation) => { | ||
if(options.sourceMap) { | ||
compilation.plugin("build-module", (module) => { | ||
// to get detailed location info about errors | ||
module.useSourceMap = true; | ||
}); | ||
} | ||
compilation.plugin("optimize-chunk-assets", (chunks, callback) => { | ||
let files = []; | ||
chunks.forEach((chunk) => files.push.apply(files, chunk.files)); | ||
files.push.apply(files, compilation.additionalChunkAssets); | ||
files = files.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options)); | ||
files.forEach((file) => { | ||
let oldWarnFunction = uglify.AST_Node.warn_function; | ||
let warnings = []; | ||
let sourceMap; | ||
try { | ||
let asset = compilation.assets[file]; | ||
if(asset.__UglifyJsPlugin) { | ||
compilation.assets[file] = asset.__UglifyJsPlugin; | ||
return; | ||
} | ||
let input; | ||
let inputSourceMap; | ||
if(options.sourceMap) { | ||
if(asset.sourceAndMap) { | ||
let sourceAndMap = asset.sourceAndMap(); | ||
inputSourceMap = sourceAndMap.map; | ||
input = sourceAndMap.source; | ||
} else { | ||
inputSourceMap = asset.map(); | ||
input = asset.source(); | ||
} | ||
sourceMap = new SourceMapConsumer(inputSourceMap); | ||
uglify.AST_Node.warn_function = (warning) => { // eslint-disable-line camelcase | ||
let match = /\[.+:([0-9]+),([0-9]+)\]/.exec(warning); | ||
let line = +match[1]; | ||
let column = +match[2]; | ||
let original = sourceMap.originalPositionFor({ | ||
line: line, | ||
column: column | ||
}); | ||
if(!original || !original.source || original.source === file) return; | ||
warnings.push(warning.replace(/\[.+:([0-9]+),([0-9]+)\]/, "") + | ||
"[" + requestShortener.shorten(original.source) + ":" + original.line + "," + original.column + "]"); | ||
}; | ||
} else { | ||
input = asset.source(); | ||
uglify.AST_Node.warn_function = (warning) => { // eslint-disable-line camelcase | ||
warnings.push(warning); | ||
}; | ||
} | ||
uglify.base54.reset(); | ||
let ast = uglify.parse(input, { | ||
filename: file | ||
}); | ||
if(options.compress !== false) { | ||
ast.figure_out_scope(); | ||
let compress = uglify.Compressor(options.compress || { | ||
warnings: false | ||
}); // eslint-disable-line new-cap | ||
ast = ast.transform(compress); | ||
} | ||
if(options.mangle !== false) { | ||
ast.figure_out_scope(options.mangle || {}); | ||
ast.compute_char_frequency(options.mangle || {}); | ||
ast.mangle_names(options.mangle || {}); | ||
if(options.mangle && options.mangle.props) { | ||
uglify.mangle_properties(ast, options.mangle.props); | ||
} | ||
} | ||
let output = {}; | ||
output.comments = Object.prototype.hasOwnProperty.call(options, "comments") ? options.comments : /^\**!|@preserve|@license/; | ||
output.beautify = options.beautify; | ||
for(let k in options.output) { | ||
output[k] = options.output[k]; | ||
} | ||
let map; | ||
if(options.sourceMap) { | ||
map = uglify.SourceMap({ // eslint-disable-line new-cap | ||
file: file, | ||
root: "" | ||
}); | ||
output.source_map = map; // eslint-disable-line camelcase | ||
} | ||
let stream = uglify.OutputStream(output); // eslint-disable-line new-cap | ||
ast.print(stream); | ||
if(map) map = map + ""; | ||
stream = stream + ""; | ||
asset.__UglifyJsPlugin = compilation.assets[file] = (map ? | ||
new SourceMapSource(stream, file, JSON.parse(map), input, inputSourceMap) : | ||
new RawSource(stream)); | ||
if(warnings.length > 0) { | ||
compilation.warnings.push(new Error(file + " from UglifyJs\n" + warnings.join("\n"))); | ||
} | ||
} catch(err) { | ||
if(err.line) { | ||
let original = sourceMap && sourceMap.originalPositionFor({ | ||
line: err.line, | ||
column: err.col | ||
}); | ||
if(original && original.source) { | ||
compilation.errors.push(new Error(file + " from UglifyJs\n" + err.message + " [" + requestShortener.shorten(original.source) + ":" + original.line + "," + original.column + "][" + file + ":" + err.line + "," + err.col + "]")); | ||
} else { | ||
compilation.errors.push(new Error(file + " from UglifyJs\n" + err.message + " [" + file + ":" + err.line + "," + err.col + "]")); | ||
} | ||
} else if(err.msg) { | ||
compilation.errors.push(new Error(file + " from UglifyJs\n" + err.msg)); | ||
} else | ||
compilation.errors.push(new Error(file + " from UglifyJs\n" + err.stack)); | ||
} finally { | ||
uglify.AST_Node.warn_function = oldWarnFunction; // eslint-disable-line camelcase | ||
} | ||
}); | ||
callback(); | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
module.exports = UglifyJsPlugin; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* | ||
MIT License http://www.opensource.org/licenses/mit-license.php | ||
Author Tobias Koppers @sokra | ||
*/ | ||
var ModuleFilenameHelpers = exports; | ||
|
||
ModuleFilenameHelpers.ALL_LOADERS_RESOURCE = "[all-loaders][resource]"; | ||
ModuleFilenameHelpers.REGEXP_ALL_LOADERS_RESOURCE = /\[all-?loaders\]\[resource\]/gi; | ||
ModuleFilenameHelpers.LOADERS_RESOURCE = "[loaders][resource]"; | ||
ModuleFilenameHelpers.REGEXP_LOADERS_RESOURCE = /\[loaders\]\[resource\]/gi; | ||
ModuleFilenameHelpers.RESOURCE = "[resource]"; | ||
ModuleFilenameHelpers.REGEXP_RESOURCE = /\[resource\]/gi; | ||
ModuleFilenameHelpers.ABSOLUTE_RESOURCE_PATH = "[absolute-resource-path]"; | ||
ModuleFilenameHelpers.REGEXP_ABSOLUTE_RESOURCE_PATH = /\[abs(olute)?-?resource-?path\]/gi; | ||
ModuleFilenameHelpers.RESOURCE_PATH = "[resource-path]"; | ||
ModuleFilenameHelpers.REGEXP_RESOURCE_PATH = /\[resource-?path\]/gi; | ||
ModuleFilenameHelpers.ALL_LOADERS = "[all-loaders]"; | ||
ModuleFilenameHelpers.REGEXP_ALL_LOADERS = /\[all-?loaders\]/gi; | ||
ModuleFilenameHelpers.LOADERS = "[loaders]"; | ||
ModuleFilenameHelpers.REGEXP_LOADERS = /\[loaders\]/gi; | ||
ModuleFilenameHelpers.QUERY = "[query]"; | ||
ModuleFilenameHelpers.REGEXP_QUERY = /\[query\]/gi; | ||
ModuleFilenameHelpers.ID = "[id]"; | ||
ModuleFilenameHelpers.REGEXP_ID = /\[id\]/gi; | ||
ModuleFilenameHelpers.HASH = "[hash]"; | ||
ModuleFilenameHelpers.REGEXP_HASH = /\[hash\]/gi; | ||
|
||
function getAfter(str, token) { | ||
var idx = str.indexOf(token); | ||
return idx < 0 ? "" : str.substr(idx); | ||
} | ||
|
||
function getBefore(str, token) { | ||
var idx = str.lastIndexOf(token); | ||
return idx < 0 ? "" : str.substr(0, idx); | ||
} | ||
|
||
function getHash(str) { | ||
var hash = require("crypto").createHash("md5"); | ||
hash.update(str); | ||
return hash.digest("hex").substr(0, 4); | ||
} | ||
|
||
function asRegExp(test) { | ||
if(typeof test === "string") test = new RegExp("^" + test.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")); | ||
return test; | ||
} | ||
|
||
ModuleFilenameHelpers.createFilename = function createFilename(module, moduleFilenameTemplate, requestShortener) { | ||
var absoluteResourcePath; | ||
var hash; | ||
var identifier; | ||
var moduleId; | ||
var shortIdentifier; | ||
if(!module) module = ""; | ||
if(typeof module === "string") { | ||
shortIdentifier = requestShortener.shorten(module); | ||
identifier = shortIdentifier; | ||
moduleId = ""; | ||
absoluteResourcePath = module.split("!").pop(); | ||
hash = getHash(identifier); | ||
} else { | ||
shortIdentifier = module.readableIdentifier(requestShortener); | ||
identifier = requestShortener.shorten(module.identifier()); | ||
moduleId = module.id; | ||
absoluteResourcePath = module.resourcePath || module.identifier().split("!").pop(); | ||
hash = getHash(identifier); | ||
} | ||
var resource = shortIdentifier.split("!").pop(); | ||
var loaders = getBefore(shortIdentifier, "!"); | ||
var allLoaders = getBefore(identifier, "!"); | ||
var query = getAfter(resource, "?"); | ||
var resourcePath = resource.substr(0, resource.length - query.length); | ||
if(typeof moduleFilenameTemplate === "function") { | ||
return moduleFilenameTemplate({ | ||
identifier: identifier, | ||
shortIdentifier: shortIdentifier, | ||
resource: resource, | ||
resourcePath: resourcePath, | ||
absoluteResourcePath: absoluteResourcePath, | ||
allLoaders: allLoaders, | ||
query: query, | ||
moduleId: moduleId, | ||
hash: hash | ||
}); | ||
} | ||
return moduleFilenameTemplate | ||
.replace(ModuleFilenameHelpers.REGEXP_ALL_LOADERS_RESOURCE, identifier) | ||
.replace(ModuleFilenameHelpers.REGEXP_LOADERS_RESOURCE, shortIdentifier) | ||
.replace(ModuleFilenameHelpers.REGEXP_RESOURCE, resource) | ||
.replace(ModuleFilenameHelpers.REGEXP_RESOURCE_PATH, resourcePath) | ||
.replace(ModuleFilenameHelpers.REGEXP_ABSOLUTE_RESOURCE_PATH, absoluteResourcePath) | ||
.replace(ModuleFilenameHelpers.REGEXP_ALL_LOADERS, allLoaders) | ||
.replace(ModuleFilenameHelpers.REGEXP_LOADERS, loaders) | ||
.replace(ModuleFilenameHelpers.REGEXP_QUERY, query) | ||
.replace(ModuleFilenameHelpers.REGEXP_ID, moduleId) | ||
.replace(ModuleFilenameHelpers.REGEXP_HASH, hash); | ||
}; | ||
|
||
ModuleFilenameHelpers.createFooter = function createFooter(module, requestShortener) { | ||
if(!module) module = ""; | ||
if(typeof module === "string") { | ||
return [ | ||
"// WEBPACK FOOTER //", | ||
"// " + requestShortener.shorten(module) | ||
].join("\n"); | ||
} else { | ||
return [ | ||
"//////////////////", | ||
"// WEBPACK FOOTER", | ||
"// " + module.readableIdentifier(requestShortener), | ||
"// module id = " + module.id, | ||
"// module chunks = " + module.chunks.map(function(c) { | ||
return c.id; | ||
}).join(" ") | ||
].join("\n"); | ||
} | ||
}; | ||
|
||
ModuleFilenameHelpers.replaceDuplicates = function replaceDuplicates(array, fn, comparator) { | ||
var countMap = {}; | ||
var posMap = {}; | ||
array.forEach(function(item, idx) { | ||
countMap[item] = (countMap[item] || []); | ||
countMap[item].push(idx); | ||
posMap[item] = 0; | ||
}); | ||
if(comparator) { | ||
Object.keys(countMap).forEach(function(item) { | ||
countMap[item].sort(comparator); | ||
}); | ||
} | ||
return array.map(function(item, i) { | ||
if(countMap[item].length > 1) { | ||
if(comparator && countMap[item][0] === i) | ||
return item; | ||
return fn(item, i, posMap[item]++); | ||
} else return item; | ||
}); | ||
}; | ||
|
||
ModuleFilenameHelpers.matchPart = function matchPart(str, test) { | ||
if(!test) return true; | ||
test = asRegExp(test); | ||
if(Array.isArray(test)) { | ||
return test.map(asRegExp).filter(function(regExp) { | ||
return regExp.test(str); | ||
}).length > 0; | ||
} else { | ||
return test.test(str); | ||
} | ||
}; | ||
|
||
ModuleFilenameHelpers.matchObject = function matchObject(obj, str) { | ||
if(obj.test) | ||
if(!ModuleFilenameHelpers.matchPart(str, obj.test)) return false; | ||
if(obj.include) | ||
if(!ModuleFilenameHelpers.matchPart(str, obj.include)) return false; | ||
if(obj.exclude) | ||
if(ModuleFilenameHelpers.matchPart(str, obj.exclude)) return false; | ||
return true; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
MIT License http://www.opensource.org/licenses/mit-license.php | ||
Author Tobias Koppers @sokra | ||
*/ | ||
var path = require("path"); | ||
|
||
function RequestShortener(directory) { | ||
directory = directory.replace(/\\/g, "/"); | ||
var parentDirectory = path.dirname(directory); | ||
if(/[\/\\]$/.test(directory)) directory = directory.substr(0, directory.length - 1); | ||
if(directory) { | ||
var currentDirectoryRegExp = directory.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); | ||
currentDirectoryRegExp = new RegExp("^" + currentDirectoryRegExp + "|(!)" + currentDirectoryRegExp, "g"); | ||
|
||
this.currentDirectoryRegExp = currentDirectoryRegExp; | ||
} | ||
|
||
if(/[\/\\]$/.test(parentDirectory)) parentDirectory = parentDirectory.substr(0, parentDirectory.length - 1); | ||
if(parentDirectory && parentDirectory !== directory) { | ||
var parentDirectoryRegExp = parentDirectory.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); | ||
parentDirectoryRegExp = new RegExp("^" + parentDirectoryRegExp + "|(!)" + parentDirectoryRegExp, "g"); | ||
|
||
this.parentDirectoryRegExp = parentDirectoryRegExp; | ||
} | ||
|
||
if(__dirname.length >= 2) { | ||
var buildins = path.join(__dirname, "..").replace(/\\/g, "/"); | ||
var buildinsAsModule = currentDirectoryRegExp && currentDirectoryRegExp.test(buildins); | ||
var buildinsRegExp = buildins.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); | ||
buildinsRegExp = new RegExp("^" + buildinsRegExp + "|(!)" + buildinsRegExp, "g"); | ||
|
||
this.buildinsAsModule = buildinsAsModule; | ||
this.buildinsRegExp = buildinsRegExp; | ||
} | ||
|
||
this.nodeModulesRegExp = /\/node_modules\//g; | ||
this.indexJsRegExp = /\/index.js(!|\?|\(query\))/g; | ||
} | ||
module.exports = RequestShortener; | ||
|
||
RequestShortener.prototype.shorten = function(request) { | ||
if(!request) | ||
return request; | ||
request = request.replace(/\\/g, "/"); | ||
if(this.buildinsAsModule && this.buildinsRegExp) | ||
request = request.replace(this.buildinsRegExp, "!(webpack)"); | ||
if(this.currentDirectoryRegExp) | ||
request = request.replace(this.currentDirectoryRegExp, "!."); | ||
if(this.parentDirectoryRegExp) | ||
request = request.replace(this.parentDirectoryRegExp, "!.."); | ||
if(!this.buildinsAsModule && this.buildinsRegExp) | ||
request = request.replace(this.buildinsRegExp, "!(webpack)"); | ||
request = request.replace(this.nodeModulesRegExp, "/~/"); | ||
request = request.replace(this.indexJsRegExp, "$1"); | ||
return request.replace(/^!|!$/, ""); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "webpack-uglify-harmony", | ||
"version": "1.0.0", | ||
"description": "A replacement for Webpack's UglifyJSPlugin to use UglifyJS Harmony", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/Akkuma/webpack-uglify-harmony.git" | ||
}, | ||
"keywords": [ | ||
"webpack", | ||
"uglifyjs", | ||
"harmony", | ||
"uglify", | ||
"plugin" | ||
], | ||
"author": "Gregory Waxman", | ||
"license": "BSD-3-Clause", | ||
"bugs": { | ||
"url": "https://github.com/Akkuma/webpack-uglify-harmony/issues" | ||
}, | ||
"homepage": "https://github.com/Akkuma/webpack-uglify-harmony#readme", | ||
"dependencies": { | ||
"source-map": "^0.5.6", | ||
"uglify-js": "github:mishoo/UglifyJS2#harmony", | ||
"webpack-sources": "^0.1.3" | ||
} | ||
} |