node-uglifier-es
Version:
Fully auto merging and uglifying a whole NodeJs project into one file with external files option. Recompiled from Zsolt Istvan Szabo's work with uglify-es instead of uglify-js-harmony.
258 lines (199 loc) • 8.77 kB
text/coffeescript
#
#/*!
# * node-uglifier
# * Copyright (c) 2014 Zsolt Szabo Istvan
# * MIT Licensed
# *
# */
#
fsExtra = require('fs-extra');
fs = require('fs');
UglifyJS = require('uglify-es')
path = require('path')
_ = require('underscore')
packageUtils = module.exports
# Check if `module` is a native module (like `net` or `tty`).
packageUtils.isNative = (module)->
try
return require.resolve(module) == module;
catch err
return false;
packageUtils.readFile = (pathAbs, encoding = 'utf8')->
options = {encoding}
return fs.readFileSync(pathAbs, options)
packageUtils.getAst = (code)->
return UglifyJS.parse(code)
#absolulte file path
packageUtils.getMatchingFiles = (rootPath, dirAndFileArray)->
r = []
rootDir = if fs.lstatSync(rootPath).isDirectory() then path.resolve(rootPath) else path.dirname(path.resolve(rootPath))
for dirOrFile in dirAndFileArray
destination = path.resolve(rootDir, dirOrFile)
try
filestats = fs.lstatSync(destination)
catch me
#probably missing extension, file not found
filestats = null
if filestats and filestats.isDirectory()
#we have directory
fs.readdirSync(destination).reduce(((prev, curr)-> prev.push(path.join(destination, curr));return prev), r)
else
if path.extname(destination) == ""
fileName = path.basename(destination)
fs.readdirSync(path.dirname(destination)).filter((fileNameLoc)-> fileNameLoc.indexOf(fileName) != -1).reduce(((prev, curr)-> prev.push(path.join(destination,
curr)); return prev), r)
else
r.push(destination)
return r
packageUtils.getIfNonNativeNotFilteredNonNpm = (fileAbs, filters, possibleExtensions)->
#if path can be resolved and it is file than it is non native, non npm
r = null
if path.extname(fileAbs) == ""
existingExtensions = possibleExtensions.filter((ext)->return fs.existsSync(fileAbs + "." + ext))
if existingExtensions.length > 1 then throw new Error(" multiple matching extensions problem for " + fileAbs)
r = if existingExtensions.length == 1 then fileAbs + "." + existingExtensions[0] else null
else
r = if fs.existsSync(fileAbs) then fileAbs else null
if r
if filters.filter((fFile)->return path.normalize(fFile) == path.normalize(r)).length > 0
r = null;
console.log(fileAbs + " was filtered ")
return r
packageUtils.walkExpressions=(astNode,parentNode,depth)->
if depth>5 then return null
if astNode.name=="require"
return parentNode?.args || astNode.args
else if astNode.expression?
return packageUtils.walkExpressions(astNode.expression,astNode,depth+1)
#assume no directory is native module
#returns the file path of modules if file exists
#if no extension specified first existing possibleExtensions is used
packageUtils.getRequireStatements = (ast, file, possibleExtensions = ["js", "coffee"], packNodeModules = false)->
r = []
fileDir = path.dirname(file)
handleRequireNode = (text, args)->
pathOfModuleRaw = args[0].value
if !pathOfModuleRaw? then throw new Error("probably dynamic")
#has / or \ in the string
hasPathInIt = (!_.isEmpty(pathOfModuleRaw.match("/")) || !_.isEmpty(pathOfModuleRaw.match(/\\/)))
if hasPathInIt
#it is not a module, do nothing
else if packNodeModules
#find node_module directory, than main in package json if no index.js found
pathOfModuleRaw=require.resolve(pathOfModuleRaw)
else
#it is module and not packed
return false
pathOfModuleLoc = path.resolve(fileDir, pathOfModuleRaw)
try
pathOfModuleLocStats =fs.lstatSync(pathOfModuleLoc)
catch me;
if (pathOfModuleLocStats and pathOfModuleLocStats.isDirectory())
pathOfModuleLoc=path.resolve(pathOfModuleLoc, "index")
#if path can be resolved and it is file than it is non native, non npm
pathOfModule = packageUtils.getIfNonNativeNotFilteredNonNpm(pathOfModuleLoc, [], possibleExtensions)
rs = {text, path: pathOfModule}
if pathOfModule
r.push(rs)
ast.walk(new UglifyJS.TreeWalker(
(node)->
if (node instanceof UglifyJS.AST_Call) && (node.start.value == 'require' || (node.start.value == 'new' and node.expression.print_to_string() == "require"))
text = node.print_to_string({beautify: false})
# console.log(text)
#expression argument takes precedence over the first argument of require
requireArgs = node?.expression?.args
walkedArgs=packageUtils.walkExpressions(node,null,1)
if _.isEmpty(requireArgs)
requireArgs = node.args
try
# if args.length != 1 then
# throw new Error ("in file: " + file + " require supposed to have 1 argument: " + text)
if requireArgs.length != 1 or !handleRequireNode(text, requireArgs) and !_.isEmpty(walkedArgs)
text2="require('#{walkedArgs[0].value}')"
handleRequireNode(text2, walkedArgs)
catch me
console.log("Warning!:")
console.log("unhandled require type in file: " + file + " the problematic statement: " + text + " probably something fancy going on! " + " the error: " + me.message)
return true
else if (node instanceof UglifyJS.AST_Call) && (node.start.value == 'new' and node.expression.start.value == "(" and node.expression.print_to_string().indexOf("require") != -1)
args = node.expression.args
text = "require" + "('" + args[0].value + "')"
# console.log(text)
handleRequireNode(text, args)
console.log("second " + text)
return true
else
return
)
)
return r
strEscapeMap = {
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
};
packageUtils.hexifyString = (str)->
r = ""
if !str.length > 0 then return r
for i in [0..str.length - 1]
char = str[i];
if (strEscapeMap[char])
r += r[char];
else if ('\\' == char)
r += '\\' + str[++i]
else
r += '\\x' + str.charCodeAt(i).toString(16);
return r;
packageUtils.deHexifyString = (str)->
return str.toString()
packageUtils.getSourceHexified = (ast)->
hexify = (node)->
if (node instanceof UglifyJS.AST_String)
text = node.getValue()
hex = packageUtils.hexifyString(text);
obj = _.extend({}, node);
obj.value = hex
return new UglifyJS.AST_String(obj);
else
return
transformer = new UglifyJS.TreeTransformer(null, hexify);
stream = new UglifyJS.OutputStream;
stream.print_string = (str)->
return this.print('"' + str + '"')
ast = ast.transform(transformer);
ast.print(stream);
return stream.toString()
#packageUtils.replaceAll=(find, replace, str)->
# return str.replace(new RegExp(find, 'g'), replace);
packageUtils.replaceRequireStatement = (textIn, orig, replacement)->
text = textIn
isReplaced = false
text = text.replace(orig, (token)-> (isReplaced = true;return replacement))
if !isReplaced
withTheOtherQuotation = orig
if withTheOtherQuotation.indexOf("'") != -1
withTheOtherQuotation = withTheOtherQuotation.replace(/[']/ig, '"')
else
withTheOtherQuotation = withTheOtherQuotation.replace(/["]/ig, "'")
text = text.replace(withTheOtherQuotation, (token)-> (isReplaced = true;return replacement))
if !isReplaced
throw new Error(orig + " was not replaced with " + replacement)
return text
packageUtils.countWords = (sentence)->
index = {}
words = sentence
.replace(/[.,?!;()"'-]/g, " ")
.replace(/\s+/g, " ")
.toLowerCase()
.split(" ");
words.forEach((word)->
if (!(index.hasOwnProperty(word)))
index[word] = 0;
index[word]++;
)
return index
# console.log([text,path.resolve(fileDir,pathOfModule),packageUtils.isNative(pathOfModule)].join(" | "))
#print_to_string({ beautify: false }).replace(/-/g, "_") AST_Assign node.left,right
# return true; no descend