213 lines
6.1 KiB
JavaScript
213 lines
6.1 KiB
JavaScript
import { sep } from 'path';
|
||
import { createFilter, attachScopes, makeLegalIdentifier } from '@rollup/pluginutils';
|
||
import { walk } from 'estree-walker';
|
||
import MagicString from 'magic-string';
|
||
|
||
var escape = function (str) { return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); };
|
||
|
||
var isReference = function (node, parent) {
|
||
if (node.type === 'MemberExpression') {
|
||
return !node.computed && isReference(node.object, node);
|
||
}
|
||
|
||
if (node.type === 'Identifier') {
|
||
// TODO is this right?
|
||
if (parent.type === 'MemberExpression') { return parent.computed || node === parent.object; }
|
||
|
||
// disregard the `bar` in { bar: foo }
|
||
if (parent.type === 'Property' && node !== parent.value) { return false; }
|
||
|
||
// disregard the `bar` in `class Foo { bar () {...} }`
|
||
if (parent.type === 'MethodDefinition') { return false; }
|
||
|
||
// disregard the `bar` in `export { foo as bar }`
|
||
if (parent.type === 'ExportSpecifier' && node !== parent.local) { return false; }
|
||
|
||
// disregard the `bar` in `import { bar as foo }`
|
||
if (parent.type === 'ImportSpecifier' && node === parent.imported) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
var flatten = function (startNode) {
|
||
var parts = [];
|
||
var node = startNode;
|
||
|
||
while (node.type === 'MemberExpression') {
|
||
parts.unshift(node.property.name);
|
||
node = node.object;
|
||
}
|
||
|
||
var name = node.name;
|
||
parts.unshift(name);
|
||
|
||
return { name: name, keypath: parts.join('.') };
|
||
};
|
||
|
||
function inject(options) {
|
||
if (!options) { throw new Error('Missing options'); }
|
||
|
||
var filter = createFilter(options.include, options.exclude);
|
||
|
||
var modules = options.modules;
|
||
|
||
if (!modules) {
|
||
modules = Object.assign({}, options);
|
||
delete modules.include;
|
||
delete modules.exclude;
|
||
delete modules.sourceMap;
|
||
delete modules.sourcemap;
|
||
}
|
||
|
||
var modulesMap = new Map(Object.entries(modules));
|
||
|
||
// Fix paths on Windows
|
||
if (sep !== '/') {
|
||
modulesMap.forEach(function (mod, key) {
|
||
modulesMap.set(
|
||
key,
|
||
Array.isArray(mod) ? [mod[0].split(sep).join('/'), mod[1]] : mod.split(sep).join('/')
|
||
);
|
||
});
|
||
}
|
||
|
||
var firstpass = new RegExp(("(?:" + (Array.from(modulesMap.keys()).map(escape).join('|')) + ")"), 'g');
|
||
var sourceMap = options.sourceMap !== false && options.sourcemap !== false;
|
||
|
||
return {
|
||
name: 'inject',
|
||
|
||
transform: function transform(code, id) {
|
||
if (!filter(id)) { return null; }
|
||
if (code.search(firstpass) === -1) { return null; }
|
||
|
||
if (sep !== '/') { id = id.split(sep).join('/'); } // eslint-disable-line no-param-reassign
|
||
|
||
var ast = null;
|
||
try {
|
||
ast = this.parse(code);
|
||
} catch (err) {
|
||
this.warn({
|
||
code: 'PARSE_ERROR',
|
||
message: ("rollup-plugin-inject: failed to parse " + id + ". Consider restricting the plugin to particular files via options.include")
|
||
});
|
||
}
|
||
if (!ast) {
|
||
return null;
|
||
}
|
||
|
||
var imports = new Set();
|
||
ast.body.forEach(function (node) {
|
||
if (node.type === 'ImportDeclaration') {
|
||
node.specifiers.forEach(function (specifier) {
|
||
imports.add(specifier.local.name);
|
||
});
|
||
}
|
||
});
|
||
|
||
// analyse scopes
|
||
var scope = attachScopes(ast, 'scope');
|
||
|
||
var magicString = new MagicString(code);
|
||
|
||
var newImports = new Map();
|
||
|
||
function handleReference(node, name, keypath) {
|
||
var mod = modulesMap.get(keypath);
|
||
if (mod && !imports.has(name) && !scope.contains(name)) {
|
||
if (typeof mod === 'string') { mod = [mod, 'default']; }
|
||
|
||
// prevent module from importing itself
|
||
if (mod[0] === id) { return false; }
|
||
|
||
var hash = keypath + ":" + (mod[0]) + ":" + (mod[1]);
|
||
|
||
var importLocalName =
|
||
name === keypath ? name : makeLegalIdentifier(("$inject_" + keypath));
|
||
|
||
if (!newImports.has(hash)) {
|
||
// escape apostrophes and backslashes for use in single-quoted string literal
|
||
var modName = mod[0].replace(/[''\\]/g, '\\$&');
|
||
if (mod[1] === '*') {
|
||
newImports.set(hash, ("import * as " + importLocalName + " from '" + modName + "';"));
|
||
} else {
|
||
newImports.set(hash, ("import { " + (mod[1]) + " as " + importLocalName + " } from '" + modName + "';"));
|
||
}
|
||
}
|
||
|
||
if (name !== keypath) {
|
||
magicString.overwrite(node.start, node.end, importLocalName, {
|
||
storeName: true
|
||
});
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
walk(ast, {
|
||
enter: function enter(node, parent) {
|
||
if (sourceMap) {
|
||
magicString.addSourcemapLocation(node.start);
|
||
magicString.addSourcemapLocation(node.end);
|
||
}
|
||
|
||
if (node.scope) {
|
||
scope = node.scope; // eslint-disable-line prefer-destructuring
|
||
}
|
||
|
||
// special case – shorthand properties. because node.key === node.value,
|
||
// we can't differentiate once we've descended into the node
|
||
if (node.type === 'Property' && node.shorthand) {
|
||
var ref = node.key;
|
||
var name = ref.name;
|
||
handleReference(node, name, name);
|
||
this.skip();
|
||
return;
|
||
}
|
||
|
||
if (isReference(node, parent)) {
|
||
var ref$1 = flatten(node);
|
||
var name$1 = ref$1.name;
|
||
var keypath = ref$1.keypath;
|
||
var handled = handleReference(node, name$1, keypath);
|
||
if (handled) {
|
||
this.skip();
|
||
}
|
||
}
|
||
},
|
||
leave: function leave(node) {
|
||
if (node.scope) {
|
||
scope = scope.parent;
|
||
}
|
||
}
|
||
});
|
||
|
||
if (newImports.size === 0) {
|
||
return {
|
||
code: code,
|
||
ast: ast,
|
||
map: sourceMap ? magicString.generateMap({ hires: true }) : null
|
||
};
|
||
}
|
||
var importBlock = Array.from(newImports.values()).join('\n\n');
|
||
|
||
magicString.prepend((importBlock + "\n\n"));
|
||
|
||
return {
|
||
code: magicString.toString(),
|
||
map: sourceMap ? magicString.generateMap({ hires: true }) : null
|
||
};
|
||
}
|
||
};
|
||
}
|
||
|
||
export default inject;
|