This repository has been archived on 2024-08-27. You can view files and clone it, but cannot push or open issues or pull requests.
negromate_origins/web/static/js/libjass/build/uglify.js

448 lines
13 KiB
JavaScript
Raw Normal View History

2018-10-12 23:00:20 +02:00
/**
* libjass
*
* https://github.com/Arnavion/libjass
*
* Copyright 2013 Arnav Singh
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var fs = require("fs");
var path = require("path");
var UglifyJS = require("uglify-js");
var FileTransform = require("async-build").FileTransform;
var Run = (function () {
function Run(outputLibraryName, unusedVarsToIgnore) {
this._outputLibraryName = outputLibraryName;
this._unusedVarsToIgnore = unusedVarsToIgnore;
this._root = UglifyJS.parse(fs.readFileSync(path.resolve(__filename, "..", "umd-wrapper.js"), "utf8"));
this._root.figure_out_scope({ screw_ie8: true });
this._toInsert = null;
this._rootSourceMap = null;
}
Run.prototype.addFile = function (file) {
switch (path.extname(file.path)) {
case ".js":
try {
this._toInsert = UglifyJS.parse(file.contents.toString(), {
filename: path.basename(file.path),
toplevel: null,
}).body;
}
catch (ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
throw new Error("UglifyJS parse error: " + ex.toString() + "\n");
}
throw ex;
}
break;
case ".map":
var rawSourceMap = JSON.parse(file.contents.toString());
this._rootSourceMap = UglifyJS.SourceMap({
file: this._outputLibraryName + ".js",
root: "",
orig: rawSourceMap,
});
var generator = this._rootSourceMap.get();
rawSourceMap.sources.forEach(function (sourceRelativePath, index) {
generator.setSourceContent(sourceRelativePath, rawSourceMap.sourcesContent[index]);
});
break;
}
};
Run.prototype.build = function (outputStream) {
var _this = this;
// Splice in the TS output into the UMD wrapper.
var insertionParent = this._root.body[0].body.args[1].body;
this._toInsert.reverse();
for (var i = this._toInsert.length - 1; i >= 0; i--) {
var node = this._toInsert[i];
if (node instanceof UglifyJS.AST_Var) {
for (var j = 0; j < node.definitions.length; j++) {
var definition = node.definitions[j];
if (definition.name.name === "__extends" || definition.name.name === "__decorate") {
definition.value = definition.value.right;
}
}
insertionParent.splice(-1, 0, node);
this._toInsert.splice(i, 1);
}
}
insertionParent.splice.apply(insertionParent, [-1, 0].concat(this._toInsert));
// Fixups
for (var i = 0; i < this._toInsert.length; i++) {
var node = this._toInsert[i];
if (node instanceof UglifyJS.AST_Statement && node.body instanceof UglifyJS.AST_Call && node.body.expression.name === "define") {
var defineCall = node.body;
defineCall.expression.name = "def";
if (defineCall.args[1].elements[0].value !== "require") {
throw new Error("Expected first dep to be require");
}
defineCall.args[1].elements.shift();
defineCall.args[2].argnames.shift();
if (defineCall.args[1].elements[0].value !== "exports") {
throw new Error("Expected second dep to be exports");
}
defineCall.args[1].elements.shift();
defineCall.args[2].argnames.push(defineCall.args[2].argnames.shift());
}
}
// Remove all license headers except the one from the UMD wrapper
var firstLicenseHeader = null;
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
if (node.start) {
(node.start.comments_before || []).some(function (comment, i) {
if (comment.value.indexOf("Copyright") !== -1) {
if (firstLicenseHeader === null) {
firstLicenseHeader = comment;
}
else if (comment !== firstLicenseHeader) {
node.start.comments_before.splice(i, 1);
}
return true;
}
return false;
});
}
}));
// Fixup anonymous functions to print a space after "function"
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
if (node instanceof UglifyJS.AST_Lambda && !node.name) {
node.name = Object.create(UglifyJS.AST_Node.prototype);
node.name.print = function () { };
}
}));
// Fix alignment of multi-line block comments
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
if (node.start && node.start.comments_before) {
node.start.comments_before.forEach(function (comment) {
if (comment.value.indexOf("Copyright") !== -1) {
return;
}
var lines = comment.value.split("\n");
if (lines.length < 2) {
return;
}
var indent = " "; // 5 spaces
for (var i = 0; i < comment.col; i++) {
indent += " ";
}
lines[lines.length - 1] = lines[lines.length - 1].replace(/\s+$/, indent);
comment.value = [lines[0]].concat(lines.slice(1).map(function (line) { return line.replace(/^\s+/, indent); })).join("\n");
});
}
}));
this._root.figure_out_scope({ screw_ie8: true });
// Remove some things from the AST
var nodesToRemove = [];
// Set if there are any unused variables apart from the ones in unusedVarsToIgnore
var haveUnusedVars = false;
// Repeat because removing some declarations may make others unreferenced
for (;;) {
this._root.figure_out_scope({ screw_ie8: true });
// Unreferenced variable and function declarations, and unreferenced terminal function arguments
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
if (node instanceof UglifyJS.AST_SymbolDeclaration && node.unreferenced()) {
if (node instanceof UglifyJS.AST_SymbolFunarg) {
if (this.parent().argnames.indexOf(node) === this.parent().argnames.length - 1) {
nodesToRemove.push({ node: node, parent: this.parent().argnames });
}
}
else if (node instanceof UglifyJS.AST_SymbolVar) {
if (_this._unusedVarsToIgnore.indexOf(node.name) !== -1) {
nodesToRemove.push({ node: this.parent(), parent: this.parent(1).definitions });
if (this.parent(1).definitions.length === 1) {
nodesToRemove.push({ node: this.parent(1), parent: this.parent(2).body });
}
}
else {
haveUnusedVars = true;
}
}
else if (node instanceof UglifyJS.AST_SymbolDefun) {
nodesToRemove.push({ node: this.parent(), parent: this.parent(1).body });
}
}
}));
if (nodesToRemove.length === 0) {
break;
}
nodesToRemove.forEach(function (tuple) {
tuple.parent.splice(tuple.parent.indexOf(tuple.node), 1);
});
nodesToRemove = [];
}
// Move var statements at the end of blocks (generated by TS for rest parameters) to the start of the block.
// This is needed to prevent unreachable-code warnings from UJS
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
if (
node instanceof UglifyJS.AST_Block &&
node.body[node.body.length - 1] instanceof UglifyJS.AST_Var
) {
node.body.unshift(node.body.pop());
}
}));
// Split multiple vars per declaration into one var per declaration
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
if (
node instanceof UglifyJS.AST_Var &&
node.definitions.length > 1 &&
this.parent() instanceof UglifyJS.AST_Block
) {
var parent = this.parent().body;
parent.splice.apply(parent, [parent.indexOf(node), 1].concat(node.definitions.map(function (definition) {
return new UglifyJS.AST_Var({ start: node.start, end: node.end, definitions: [definition] });
})));
}
}));
// Rename all function arguments that begin with _ to not have the _.
// This converts the TypeScript syntax of declaring private members in the constructor declaration `function Color(private _red: number, ...)` to `function Color(red, ...)`
// so that it matches the JSDoc (and looks nicer).
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
if (
node instanceof UglifyJS.AST_SymbolFunarg &&
node.thedef.name[0] === "_" &&
node.thedef.name[1] !== "_" &&
node.thedef.name !== "_super" // Don't rename _super (used in TypeScript's inheritance shim) to super. super is a reserved word.
) {
node.thedef.name = node.thedef.name.slice(1);
}
}));
// Output
var output = {
source_map: this._rootSourceMap,
ascii_only: true,
beautify: true,
comments: function (node, comment) {
return comment.value.indexOf("tslint") === -1;
},
};
var stream = UglifyJS.OutputStream(output);
this._root.print(stream);
outputStream.push({
path: this._outputLibraryName + ".js",
contents: Buffer.concat([new Buffer(stream.toString()), new Buffer("\n//# sourceMappingURL=" + this._outputLibraryName + ".js.map")])
});
outputStream.push({
path: this._outputLibraryName + ".js.map",
contents: new Buffer(this._rootSourceMap.get().toString())
});
// Print unused variables
if (haveUnusedVars) {
this._root.walk(new UglifyJS.TreeWalker(function (node, descend) {
if (node instanceof UglifyJS.AST_SymbolVar && node.unreferenced()) {
if (_this._unusedVarsToIgnore.indexOf(node.name) === -1) {
console.warn("Unused variable %s at %s:%s:%s", node.name, node.start.file, node.start.line, node.start.col);
}
}
}));
}
};
return Run;
})();
module.exports = {
build: function (outputLibraryName, unusedVarsToIgnore) {
var run = new Run(outputLibraryName, unusedVarsToIgnore);
return new FileTransform(function (file) {
run.addFile(file);
}, function () {
run.build(this);
});
},
watch: function (outputLibraryName, unusedVarsToIgnore) {
var files = Object.create(null);
return new FileTransform(function (file) {
if (file.path !== "END") {
files[file.path] = file;
}
else {
var run = new Run(outputLibraryName, unusedVarsToIgnore);
Object.keys(files).forEach(function (filename) {
run.addFile(files[filename]);
});
run.build(this);
}
});
},
minify: function () {
var codeFile = null;
var sourceMapFile = null;
return new FileTransform(function (file) {
switch (path.extname(file.path)) {
case ".js":
codeFile = file;
break;
case ".map":
sourceMapFile = file;
break;
}
if (codeFile !== null && sourceMapFile !== null) {
UglifyJS.base54.reset();
// Parse
var root = null;
root = UglifyJS.parse(codeFile.contents.toString(), {
filename: path.basename(codeFile.path),
toplevel: root
});
root.figure_out_scope({ screw_ie8: true });
// Warnings
root.scope_warnings({
func_arguments: false
});
// Compress
var compressor = UglifyJS.Compressor({
warnings: true,
screw_ie8: true
});
root = root.transform(compressor);
// Mangle
root.figure_out_scope({ screw_ie8: true });
root.compute_char_frequency();
root.mangle_names({ screw_ie8: true });
root = UglifyJS.mangle_properties(root, {
regex: /^_/
});
// Output
var firstLicenseHeaderFound = false; // To detect and preserve the first license header
var output = {
source_map: UglifyJS.SourceMap({
file: path.basename(sourceMapFile.path),
orig: sourceMapFile.contents.toString()
}),
ascii_only: true,
comments: function (node, comment) {
if (!firstLicenseHeaderFound && comment.value.indexOf("Copyright") !== -1) {
firstLicenseHeaderFound = true;
return true;
}
return false;
},
screw_ie8: true
};
var stream = UglifyJS.OutputStream(output);
root.print(stream);
codeFile.path = codeFile.path.replace(/\.js$/, ".min.js");
sourceMapFile.path = sourceMapFile.path.replace(/\.js\.map$/, ".min.js.map");
codeFile.contents = Buffer.concat([new Buffer(stream.toString()), new Buffer("\n//# sourceMappingURL="), new Buffer(sourceMapFile.path)]);
this.push(codeFile);
var inputSourceMapObject = JSON.parse(sourceMapFile.contents.toString());
var outputSourceMapObject = output.source_map.get();
outputSourceMapObject._sources.toArray().forEach(function (filename, i) {
outputSourceMapObject.setSourceContent(filename, inputSourceMapObject.sourcesContent[i]);
});
sourceMapFile.contents = new Buffer(output.source_map.toString());
this.push(sourceMapFile);
codeFile = null;
sourceMapFile = null;
}
});
}
};
var originalSymbolUnreferenced = UglifyJS.AST_Symbol.prototype.unreferenced;
// Workaround for https://github.com/mishoo/UglifyJS2/issues/789 - Nodes explicitly marked with ujs:unreferenced will not be warned for.
UglifyJS.AST_Symbol.prototype.unreferenced = function () {
if (this.start.comments_before.length > 0 && this.start.comments_before[0].value.trim() === "ujs:unreferenced") {
return false;
}
return originalSymbolUnreferenced.call(this);
};