123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- var undeclaredIdentifiers = require('undeclared-identifiers');
- var through = require('through2');
- var merge = require('xtend');
- var parse = require('acorn-node').parse;
- var path = require('path');
- var isAbsolute = path.isAbsolute || require('path-is-absolute');
- var processPath = require.resolve('process/browser.js');
- var isbufferPath = require.resolve('is-buffer')
- var combineSourceMap = require('combine-source-map');
- function getRelativeRequirePath(fullPath, fromPath) {
- var relpath = path.relative(path.dirname(fromPath), fullPath);
- // If fullPath is in the same directory or a subdirectory of fromPath,
- // relpath will result in something like "index.js", "src/abc.js".
- // require() needs "./" prepended to these paths.
- if (!/^\./.test(relpath) && !isAbsolute(relpath)) {
- relpath = "./" + relpath;
- }
- // On Windows: Convert path separators to what require() expects
- if (path.sep === '\\') {
- relpath = relpath.replace(/\\/g, '/');
- }
- return relpath;
- }
- var defaultVars = {
- process: function (file) {
- var relpath = getRelativeRequirePath(processPath, file);
- return 'require(' + JSON.stringify(relpath) + ')';
- },
- global: function () {
- return 'typeof global !== "undefined" ? global : '
- + 'typeof self !== "undefined" ? self : '
- + 'typeof window !== "undefined" ? window : {}'
- ;
- },
- 'Buffer.isBuffer': function (file) {
- var relpath = getRelativeRequirePath(isbufferPath, file);
- return 'require(' + JSON.stringify(relpath) + ')';
- },
- Buffer: function () {
- return 'require("buffer").Buffer';
- },
- setImmediate: function () {
- return 'require("timers").setImmediate';
- },
- clearImmediate: function () {
- return 'require("timers").clearImmediate';
- },
- __filename: function (file, basedir) {
- var relpath = path.relative(basedir, file);
- // standardize path separators, use slash in Windows too
- if ( path.sep === '\\' ) {
- relpath = relpath.replace(/\\/g, '/');
- }
- var filename = '/' + relpath;
- return JSON.stringify(filename);
- },
- __dirname: function (file, basedir) {
- var relpath = path.relative(basedir, file);
- // standardize path separators, use slash in Windows too
- if ( path.sep === '\\' ) {
- relpath = relpath.replace(/\\/g, '/');
- }
- var dir = path.dirname('/' + relpath );
- return JSON.stringify(dir);
- }
- };
- module.exports = function (file, opts) {
- if (/\.json$/i.test(file)) return through();
- if (!opts) opts = {};
-
- var basedir = opts.basedir || '/';
- var vars = merge(defaultVars, opts.vars);
- var varNames = Object.keys(vars).filter(function(name) {
- return typeof vars[name] === 'function';
- });
-
- var quick = RegExp(varNames.map(function (name) {
- return '\\b' + name + '\\b';
- }).join('|'));
-
- var chunks = [];
-
- return through(write, end);
-
- function write (chunk, enc, next) { chunks.push(chunk); next() }
-
- function end () {
- var self = this;
- var source = Buffer.isBuffer(chunks[0])
- ? Buffer.concat(chunks).toString('utf8')
- : chunks.join('')
- ;
- source = source
- .replace(/^\ufeff/, '')
- .replace(/^#![^\n]*\n/, '\n');
-
- if (opts.always !== true && !quick.test(source)) {
- this.push(source);
- this.push(null);
- return;
- }
-
- try {
- var undeclared = opts.always
- ? { identifiers: varNames, properties: [] }
- : undeclaredIdentifiers(parse(source), { wildcard: true })
- ;
- }
- catch (err) {
- var e = new SyntaxError(
- (err.message || err) + ' while parsing ' + file
- );
- e.type = 'syntax';
- e.filename = file;
- return this.emit('error', e);
- }
-
- var globals = {};
-
- varNames.forEach(function (name) {
- if (!/\./.test(name)) return;
- var parts = name.split('.')
- var prop = undeclared.properties.indexOf(name)
- if (prop === -1 || countprops(undeclared.properties, parts[0]) > 1) return;
- var value = vars[name](file, basedir);
- if (!value) return;
- globals[parts[0]] = '{'
- + JSON.stringify(parts[1]) + ':' + value + '}';
- self.emit('global', name);
- });
- varNames.forEach(function (name) {
- if (/\./.test(name)) return;
- if (globals[name]) return;
- if (undeclared.identifiers.indexOf(name) < 0) return;
- var value = vars[name](file, basedir);
- if (!value) return;
- globals[name] = value;
- self.emit('global', name);
- });
-
- this.push(closeOver(globals, source, file, opts));
- this.push(null);
- }
- };
- module.exports.vars = defaultVars;
- function closeOver (globals, src, file, opts) {
- var keys = Object.keys(globals);
- if (keys.length === 0) return src;
- var values = keys.map(function (key) { return globals[key] });
-
- // we double-wrap the source in IIFEs to prevent code like
- // (function(Buffer){ const Buffer = null }())
- // which causes a parse error.
- var wrappedSource = '(function (){\n' + src + '\n}).call(this)';
- if (keys.length <= 3) {
- wrappedSource = '(function (' + keys.join(',') + '){'
- + wrappedSource + '}).call(this,' + values.join(',') + ')'
- ;
- }
- else {
- // necessary to make arguments[3..6] still work for workerify etc
- // a,b,c,arguments[3..6],d,e,f...
- var extra = [ '__argument0', '__argument1', '__argument2', '__argument3' ];
- var names = keys.slice(0,3).concat(extra).concat(keys.slice(3));
- values.splice(3, 0,
- 'arguments[3]','arguments[4]',
- 'arguments[5]','arguments[6]'
- );
- wrappedSource = '(function (' + names.join(',') + '){'
- + wrappedSource + '}).call(this,' + values.join(',') + ')';
- }
- // Generate source maps if wanted. Including the right offset for
- // the wrapped source.
- if (!opts.debug) {
- return wrappedSource;
- }
- var sourceFile = path.relative(opts.basedir, file)
- .replace(/\\/g, '/');
- var sourceMap = combineSourceMap.create().addFile(
- { sourceFile: sourceFile, source: src},
- { line: 1 });
- return combineSourceMap.removeComments(wrappedSource) + "\n"
- + sourceMap.comment();
- }
- function countprops (props, name) {
- return props.filter(function (prop) {
- return prop.slice(0, name.length + 1) === name + '.';
- }).length;
- }
|