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.
typesetting/pitfall/pdfkit/node_modules/brfs/index.js

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;
}