You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
4.6 KiB
JavaScript
151 lines
4.6 KiB
JavaScript
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
var through = require('through');
|
|
var falafel = require('falafel');
|
|
var unparse = require('escodegen').generate;
|
|
|
|
module.exports = function (file) {
|
|
if (/\.json$/.test(file)) return through();
|
|
var data = '';
|
|
var fsNames = {};
|
|
var vars = [ '__filename', '__dirname' ];
|
|
var dirname = path.dirname(file);
|
|
var pending = 0;
|
|
|
|
var tr = through(write, end);
|
|
return tr;
|
|
|
|
function containsUndefinedVariable (node) {
|
|
if (node.type === 'Identifier') {
|
|
if (vars.indexOf(node.name) === -1) {
|
|
return true;
|
|
}
|
|
}
|
|
else if (node.type === 'BinaryExpression') {
|
|
return containsUndefinedVariable(node.left)
|
|
|| containsUndefinedVariable(node.right)
|
|
;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
function write (buf) { data += buf }
|
|
function end () {
|
|
try { var output = parse() }
|
|
catch (err) {
|
|
this.emit('error', new Error(
|
|
err.toString().replace('Error: ', '') + ' (' + file + ')')
|
|
);
|
|
}
|
|
|
|
if (pending === 0) finish(output);
|
|
}
|
|
|
|
function finish (output) {
|
|
tr.queue(String(output));
|
|
tr.queue(null);
|
|
}
|
|
|
|
function parse () {
|
|
var output = falafel(data, function (node) {
|
|
if (isRequire(node) && node.arguments[0].value === 'fs'
|
|
&& node.parent.type === 'VariableDeclarator'
|
|
&& node.parent.id.type === 'Identifier') {
|
|
fsNames[node.parent.id.name] = true;
|
|
}
|
|
if (isRequire(node) && node.arguments[0].value === 'fs'
|
|
&& node.parent.type === 'AssignmentExpression'
|
|
&& node.parent.left.type === 'Identifier') {
|
|
fsNames[node.parent.left.name] = true;
|
|
}
|
|
|
|
if (node.type !== 'CallExpression' || !isFs(node.callee)) return;
|
|
|
|
var type;
|
|
if (isRFS(node.callee.property)) type = 'sync';
|
|
else if (isRF(node.callee.property)) type = 'async';
|
|
if (!type) return;
|
|
|
|
var args = node.arguments;
|
|
var canBeInlined = !containsUndefinedVariable(args[0]);
|
|
if (!canBeInlined) return;
|
|
|
|
var t = 'return ' + unparse(args[0]);
|
|
var fpath = Function(vars, t)(file, dirname);
|
|
|
|
var enc = null;
|
|
if (args[1] && !/^Function/.test(args[1].type)) {
|
|
enc = Function('return ' + unparse(args[1]))()
|
|
}
|
|
|
|
++ pending;
|
|
if (enc && typeof enc === 'object' && enc.encoding) {
|
|
enc = enc.encoding;
|
|
}
|
|
|
|
var isBuffer = false;
|
|
if (enc === null || enc === undefined) {
|
|
isBuffer = true;
|
|
enc = 'base64';
|
|
}
|
|
fs.readFile(fpath, enc, function (err, src) {
|
|
if (err) return tr.emit('error', errorWithFile(file, err));
|
|
var code = isBuffer
|
|
? 'Buffer(' + JSON.stringify(src) + ',"base64")'
|
|
: JSON.stringify(src)
|
|
;
|
|
if (type === 'sync') {
|
|
node.update(code);
|
|
}
|
|
else if (type === 'async') {
|
|
var cb = args[2] || args[1];
|
|
if (!cb) return;
|
|
node.update(
|
|
'process.nextTick(function () {'
|
|
+ '(' + cb.source() + ')'
|
|
+ '(null,' + code + ')'
|
|
+ '})'
|
|
);
|
|
}
|
|
tr.emit('file', fpath);
|
|
if (--pending === 0) finish(output);
|
|
});
|
|
});
|
|
return output;
|
|
}
|
|
|
|
function isFs (p) {
|
|
if (!p) return false;
|
|
if (p.type !== 'MemberExpression') return false;
|
|
return (p.object.type === 'Identifier' && fsNames[p.object.name])
|
|
|| isRequire(p.object)
|
|
;
|
|
}
|
|
};
|
|
|
|
function isRFS (node) {
|
|
return node.type === 'Identifier' && node.name === 'readFileSync';
|
|
}
|
|
|
|
function isRF (node) {
|
|
return node.type === 'Identifier' && node.name === 'readFile';
|
|
}
|
|
|
|
function isRequire (node) {
|
|
var c = node.callee;
|
|
return c
|
|
&& node.type === 'CallExpression'
|
|
&& c.type === 'Identifier'
|
|
&& c.name === 'require'
|
|
;
|
|
}
|
|
|
|
function errorWithFile (file, err) {
|
|
var e = new Error(err.message + '\n while running brfs on ' + file);
|
|
e.file = file;
|
|
return e;
|
|
}
|