214 lines
6.2 KiB
JavaScript
214 lines
6.2 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 && node.value.type === 'Identifier') {
|
|||
|
|
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 { inject as default };
|
|||
|
|
//# sourceMappingURL=index.js.map
|