Show More
Commit Description:
fix wrong merge
Commit Description:
fix wrong merge
References:
File last commit:
Show/Diff file:
Action:
node_modules/static-module/index.js
| 434 lines
| 15.7 KiB
| application/javascript
| JavascriptLexer
|
r789 | var through = require('through2'); | |||
var Readable = require('readable-stream').Readable; | ||||
var concat = require('concat-stream'); | ||||
var duplexer = require('duplexer2'); | ||||
var acorn = require('acorn-node'); | ||||
var walkAst = require('acorn-node/walk').full; | ||||
var scan = require('scope-analyzer'); | ||||
var unparse = require('escodegen').generate; | ||||
var inspect = require('object-inspect'); | ||||
var evaluate = require('static-eval'); | ||||
var copy = require('shallow-copy'); | ||||
var has = require('has'); | ||||
var MagicString = require('magic-string'); | ||||
var convertSourceMap = require('convert-source-map'); | ||||
var mergeSourceMap = require('merge-source-map'); | ||||
module.exports = function parse (modules, opts) { | ||||
if (!opts) opts = {}; | ||||
var vars = opts.vars || {}; | ||||
var varModules = opts.varModules || {}; | ||||
var parserOpts = copy(opts.parserOpts || {}); | ||||
var updates = []; | ||||
var moduleBindings = []; | ||||
var sourcemapper; | ||||
var inputMap; | ||||
var output = through(); | ||||
var body, ast; | ||||
return duplexer(concat({ encoding: 'buffer' }, function (buf) { | ||||
try { | ||||
body = buf.toString('utf8').replace(/^#!/, '//#!'); | ||||
var matches = false; | ||||
for (var key in modules) { | ||||
if (body.indexOf(key) !== -1) { | ||||
matches = true; | ||||
break; | ||||
} | ||||
} | ||||
if (!matches) { | ||||
// just pass it through | ||||
output.end(buf); | ||||
return; | ||||
} | ||||
if (opts.sourceMap) { | ||||
inputMap = convertSourceMap.fromSource(body); | ||||
if (inputMap) inputMap = inputMap.toObject(); | ||||
body = convertSourceMap.removeComments(body); | ||||
sourcemapper = new MagicString(body); | ||||
} | ||||
ast = acorn.parse(body, parserOpts); | ||||
// scan.crawl does .parent tracking, so we can use acorn's builtin walker. | ||||
scan.crawl(ast); | ||||
walkAst(ast, walk); | ||||
} | ||||
catch (err) { return error(err) } | ||||
finish(body); | ||||
}), output); | ||||
function finish (src) { | ||||
var pos = 0; | ||||
src = String(src); | ||||
moduleBindings.forEach(function (binding) { | ||||
if (binding.isReferenced()) { | ||||
return; | ||||
} | ||||
var node = binding.initializer; | ||||
if (node.type === 'VariableDeclarator') { | ||||
var i = node.parent.declarations.indexOf(node); | ||||
if (node.parent.declarations.length === 1) { | ||||
// remove the entire declaration | ||||
updates.push({ | ||||
start: node.parent.start, | ||||
offset: node.parent.end - node.parent.start, | ||||
stream: st() | ||||
}); | ||||
} else if (i === node.parent.declarations.length - 1) { | ||||
updates.push({ | ||||
// remove ", a = 1" | ||||
start: node.parent.declarations[i - 1].end, | ||||
offset: node.end - node.parent.declarations[i - 1].end, | ||||
stream: st() | ||||
}); | ||||
} else { | ||||
updates.push({ | ||||
// remove "a = 1, " | ||||
start: node.start, | ||||
offset: node.parent.declarations[i + 1].start - node.start, | ||||
stream: st() | ||||
}); | ||||
} | ||||
} else if (node.parent.type === 'SequenceExpression' && node.parent.expressions.length > 1) { | ||||
var i = node.parent.expressions.indexOf(node); | ||||
if (i === node.parent.expressions.length - 1) { | ||||
updates.push({ | ||||
// remove ", a = 1" | ||||
start: node.parent.expressions[i - 1].end, | ||||
offset: node.end - node.parent.expressions[i - 1].end, | ||||
stream: st() | ||||
}); | ||||
} else { | ||||
updates.push({ | ||||
// remove "a = 1, " | ||||
start: node.start, | ||||
offset: node.parent.expressions[i + 1].start - node.start, | ||||
stream: st() | ||||
}); | ||||
} | ||||
} else { | ||||
if (node.parent.type === 'ExpressionStatement') node = node.parent; | ||||
updates.push({ | ||||
start: node.start, | ||||
offset: node.end - node.start, | ||||
stream: st() | ||||
}); | ||||
} | ||||
}); | ||||
updates.sort(function(a, b) { return a.start - b.start; }); | ||||
(function next () { | ||||
if (updates.length === 0) return done(); | ||||
var s = updates.shift(); | ||||
output.write(src.slice(pos, s.start)); | ||||
pos = s.start + s.offset; | ||||
s.stream.pipe(output, { end: false }); | ||||
if (opts.sourceMap) { | ||||
s.stream.pipe(concat({ encoding: 'string' }, function (chunk) { | ||||
// We have to give magic-string the replacement string, | ||||
// so it can calculate the amount of lines and columns. | ||||
if (s.offset === 0) { | ||||
sourcemapper.appendRight(s.start, chunk); | ||||
} else { | ||||
sourcemapper.overwrite(s.start, s.start + s.offset, chunk); | ||||
} | ||||
})).on('finish', next); | ||||
} else { | ||||
s.stream.on('end', next); | ||||
} | ||||
})(); | ||||
function done () { | ||||
output.write(src.slice(pos)); | ||||
if (opts.sourceMap) { | ||||
var map = sourcemapper.generateMap({ | ||||
source: opts.inputFilename || 'input.js', | ||||
includeContent: true | ||||
}); | ||||
if (inputMap) { | ||||
var merged = mergeSourceMap(inputMap, map); | ||||
output.write('\n' + convertSourceMap.fromObject(merged).toComment() + '\n'); | ||||
} else { | ||||
output.write('\n//# sourceMappingURL=' + map.toUrl() + '\n'); | ||||
} | ||||
} | ||||
output.end(); | ||||
} | ||||
} | ||||
function error (msg) { | ||||
var err = typeof msg === 'string' ? new Error(msg) : msg; | ||||
output.emit('error', err); | ||||
} | ||||
function walk (node) { | ||||
if (opts.sourceMap) { | ||||
sourcemapper.addSourcemapLocation(node.start); | ||||
sourcemapper.addSourcemapLocation(node.end); | ||||
} | ||||
var isreq = isRequire(node); | ||||
var isreqm = false, isreqv = false, reqid; | ||||
if (isreq) { | ||||
reqid = node.arguments[0].value; | ||||
isreqm = has(modules, reqid); | ||||
isreqv = has(varModules, reqid); | ||||
} | ||||
if (isreqv && node.parent.type === 'VariableDeclarator' | ||||
&& node.parent.id.type === 'Identifier') { | ||||
var binding = scan.getBinding(node.parent.id); | ||||
if (binding) binding.value = varModules[reqid]; | ||||
} | ||||
else if (isreqv && node.parent.type === 'AssignmentExpression' | ||||
&& node.parent.left.type === 'Identifier') { | ||||
var binding = scan.getBinding(node.parent.left); | ||||
if (binding) binding.value = varModules[reqid]; | ||||
} | ||||
else if (isreqv && node.parent.type === 'MemberExpression' | ||||
&& isStaticProperty(node.parent.property) | ||||
&& node.parent.parent.type === 'VariableDeclarator' | ||||
&& node.parent.parent.id.type === 'Identifier') { | ||||
var binding = scan.getBinding(node.parent.parent.id); | ||||
var v = varModules[reqid][resolveProperty(node.parent.property)]; | ||||
if (binding) binding.value = v; | ||||
} | ||||
else if (isreqv && node.parent.type === 'MemberExpression' | ||||
&& node.parent.property.type === 'Identifier') { | ||||
//vars[node.parent.parent.id.name] = varModules[reqid]; | ||||
} | ||||
else if (isreqv && node.parent.type === 'CallExpression') { | ||||
// | ||||
} | ||||
if (isreqm && node.parent.type === 'VariableDeclarator' | ||||
&& node.parent.id.type === 'Identifier') { | ||||
var binding = scan.getBinding(node.parent.id); | ||||
if (binding) { | ||||
binding.module = modules[reqid]; | ||||
binding.initializer = node.parent; | ||||
binding.remove(node.parent.id); | ||||
moduleBindings.push(binding); | ||||
} | ||||
} | ||||
else if (isreqm && node.parent.type === 'AssignmentExpression' | ||||
&& node.parent.left.type === 'Identifier') { | ||||
var binding = scan.getBinding(node.parent.left); | ||||
if (binding) { | ||||
binding.module = modules[reqid]; | ||||
binding.initializer = node.parent; | ||||
binding.remove(node.parent.left); | ||||
moduleBindings.push(binding); | ||||
} | ||||
} | ||||
else if (isreqm && node.parent.type === 'MemberExpression' | ||||
&& isStaticProperty(node.parent.property) | ||||
&& node.parent.parent.type === 'VariableDeclarator' | ||||
&& node.parent.parent.id.type === 'Identifier') { | ||||
var binding = scan.getBinding(node.parent.parent.id); | ||||
if (binding) { | ||||
binding.module = modules[reqid][resolveProperty(node.parent.property)]; | ||||
binding.initializer = node.parent.parent; | ||||
binding.remove(node.parent.parent.id); | ||||
moduleBindings.push(binding); | ||||
} | ||||
} | ||||
else if (isreqm && node.parent.type === 'MemberExpression' | ||||
&& isStaticProperty(node.parent.property)) { | ||||
var name = resolveProperty(node.parent.property); | ||||
var cur = copy(node.parent.parent); | ||||
cur.callee = copy(node.parent.property); | ||||
cur.callee.parent = cur; | ||||
traverse(cur.callee, modules[reqid][name]); | ||||
} | ||||
else if (isreqm && node.parent.type === 'CallExpression') { | ||||
var cur = copy(node.parent); | ||||
var iname = Math.pow(16,8) * Math.random(); | ||||
cur.callee = { | ||||
type: 'Identifier', | ||||
name: '_' + Math.floor(iname).toString(16), | ||||
parent: cur | ||||
}; | ||||
traverse(cur.callee, modules[reqid]); | ||||
} | ||||
if (node.type === 'Identifier') { | ||||
var binding = scan.getBinding(node) | ||||
if (binding && binding.module) traverse(node, binding.module, binding); | ||||
} | ||||
} | ||||
function traverse (node, val, binding) { | ||||
for (var p = node; p; p = p.parent) { | ||||
if (p.start === undefined || p.end === undefined) continue; | ||||
} | ||||
if (node.parent.type === 'CallExpression') { | ||||
if (typeof val !== 'function') { | ||||
return error( | ||||
'tried to statically call ' + inspect(val) | ||||
+ ' as a function' | ||||
); | ||||
} | ||||
var xvars = getVars(node.parent, vars); | ||||
xvars[node.name] = val; | ||||
var res = evaluate(node.parent, xvars); | ||||
if (res !== undefined) { | ||||
if (binding) binding.remove(node) | ||||
updates.push({ | ||||
start: node.parent.start, | ||||
offset: node.parent.end - node.parent.start, | ||||
stream: isStream(res) ? wrapStream(res) : st(String(res)) | ||||
}); | ||||
} | ||||
} | ||||
else if (node.parent.type === 'MemberExpression') { | ||||
if (!isStaticProperty(node.parent.property)) { | ||||
return error( | ||||
'dynamic property in member expression: ' | ||||
+ body.slice(node.parent.start, node.parent.end) | ||||
); | ||||
} | ||||
var cur = node.parent.parent; | ||||
if (cur.type === 'MemberExpression') { | ||||
cur = cur.parent; | ||||
if (cur.type !== 'CallExpression' | ||||
&& cur.parent.type === 'CallExpression') { | ||||
cur = cur.parent; | ||||
} | ||||
} | ||||
if (node.parent.type === 'MemberExpression' | ||||
&& (cur.type !== 'CallExpression' | ||||
&& cur.type !== 'MemberExpression')) { | ||||
cur = node.parent; | ||||
} | ||||
var xvars = getVars(cur, vars); | ||||
xvars[node.name] = val; | ||||
var res = evaluate(cur, xvars); | ||||
if (res === undefined && cur.type === 'CallExpression') { | ||||
// static-eval can't safely evaluate code with callbacks, so do it manually in a safe way | ||||
var callee = evaluate(cur.callee, xvars); | ||||
var args = cur.arguments.map(function (arg) { | ||||
// Return a function stub for callbacks so that `static-module` users | ||||
// can do `callback.toString()` and get the original source | ||||
if (arg.type === 'FunctionExpression' || arg.type === 'ArrowFunctionExpression') { | ||||
var fn = function () { | ||||
throw new Error('static-module: cannot call callbacks defined inside source code'); | ||||
}; | ||||
fn.toString = function () { | ||||
return body.slice(arg.start, arg.end); | ||||
}; | ||||
return fn; | ||||
} | ||||
return evaluate(arg, xvars); | ||||
}); | ||||
if (callee !== undefined) { | ||||
try { | ||||
res = callee.apply(null, args); | ||||
} catch (err) { | ||||
// Evaluate to undefined | ||||
} | ||||
} | ||||
} | ||||
if (res !== undefined) { | ||||
if (binding) binding.remove(node) | ||||
updates.push({ | ||||
start: cur.start, | ||||
offset: cur.end - cur.start, | ||||
stream: isStream(res) ? wrapStream(res) : st(String(res)) | ||||
}); | ||||
} | ||||
} | ||||
else if (node.parent.type === 'UnaryExpression') { | ||||
var xvars = getVars(node.parent, vars); | ||||
xvars[node.name] = val; | ||||
var res = evaluate(node.parent, xvars); | ||||
if (res !== undefined) { | ||||
if (binding) binding.remove(node) | ||||
updates.push({ | ||||
start: node.parent.start, | ||||
offset: node.parent.end - node.parent.start, | ||||
stream: isStream(res) ? wrapStream(res) : st(String(res)) | ||||
}); | ||||
} else { | ||||
output.emit('error', new Error( | ||||
'unsupported unary operator: ' + node.parent.operator | ||||
)); | ||||
} | ||||
} | ||||
else { | ||||
output.emit('error', new Error( | ||||
'unsupported type for static module: ' + node.parent.type | ||||
+ '\nat expression:\n\n ' + unparse(node.parent) + '\n' | ||||
)); | ||||
} | ||||
} | ||||
} | ||||
function isRequire (node) { | ||||
var c = node.callee; | ||||
return c | ||||
&& node.type === 'CallExpression' | ||||
&& c.type === 'Identifier' | ||||
&& c.name === 'require' | ||||
; | ||||
} | ||||
function isStream (s) { | ||||
return s && typeof s === 'object' && typeof s.pipe === 'function'; | ||||
} | ||||
function wrapStream (s) { | ||||
if (typeof s.read === 'function') return s | ||||
else return (new Readable).wrap(s) | ||||
} | ||||
function isStaticProperty(node) { | ||||
return node.type === 'Identifier' || node.type === 'Literal'; | ||||
} | ||||
function resolveProperty(node) { | ||||
return node.type === 'Identifier' ? node.name : node.value; | ||||
} | ||||
function st (msg) { | ||||
var r = new Readable; | ||||
r._read = function () {}; | ||||
if (msg != null) r.push(msg); | ||||
r.push(null); | ||||
return r; | ||||
} | ||||
function nearestScope(node) { | ||||
do { | ||||
var scope = scan.scope(node); | ||||
if (scope) return scope; | ||||
} while ((node = node.parent)); | ||||
} | ||||
function getVars(node, vars) { | ||||
var xvars = copy(vars || {}); | ||||
var scope = nearestScope(node); | ||||
if (scope) { | ||||
scope.forEachAvailable(function (binding, name) { | ||||
if (binding.hasOwnProperty('value')) xvars[name] = binding.value; | ||||
}); | ||||
} | ||||
return xvars; | ||||
} | ||||