196 lines
8.1 KiB
JavaScript
196 lines
8.1 KiB
JavaScript
|
"use strict";
|
|||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|||
|
};
|
|||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|||
|
exports.uniViteInjectPlugin = void 0;
|
|||
|
const path_1 = require("path");
|
|||
|
const debug_1 = __importDefault(require("debug"));
|
|||
|
const pluginutils_1 = require("@rollup/pluginutils");
|
|||
|
const estree_walker_1 = require("estree-walker");
|
|||
|
const shared_1 = require("@vue/shared");
|
|||
|
const magic_string_1 = __importDefault(require("magic-string"));
|
|||
|
const utils_1 = require("../utils");
|
|||
|
const debugInject = (0, debug_1.default)('uni:inject');
|
|||
|
const debugInjectTry = (0, debug_1.default)('uni:inject-try');
|
|||
|
function uniViteInjectPlugin(name, options) {
|
|||
|
if (!options)
|
|||
|
throw new Error('Missing options');
|
|||
|
const filter = (0, pluginutils_1.createFilter)(options.include, options.exclude);
|
|||
|
const modules = (0, shared_1.extend)({}, options);
|
|||
|
delete modules.include;
|
|||
|
delete modules.exclude;
|
|||
|
delete modules.sourceMap;
|
|||
|
delete modules.callback;
|
|||
|
const reassignments = new Set();
|
|||
|
const modulesMap = new Map();
|
|||
|
const namespaceModulesMap = new Map();
|
|||
|
Object.keys(modules).forEach((name) => {
|
|||
|
if (name.endsWith('.')) {
|
|||
|
namespaceModulesMap.set(name, modules[name]);
|
|||
|
}
|
|||
|
modulesMap.set(name, modules[name]);
|
|||
|
});
|
|||
|
const hasNamespace = namespaceModulesMap.size > 0;
|
|||
|
// Fix paths on Windows
|
|||
|
if (path_1.sep !== '/') {
|
|||
|
normalizeModulesMap(modulesMap);
|
|||
|
normalizeModulesMap(namespaceModulesMap);
|
|||
|
}
|
|||
|
const firstpass = new RegExp(`(?:${Array.from(modulesMap.keys()).map(escape).join('|')})`, 'g');
|
|||
|
const sourceMap = options.sourceMap !== false;
|
|||
|
const callback = options.callback;
|
|||
|
return {
|
|||
|
name,
|
|||
|
// 确保在 commonjs 之后,否则会混合 es6 module 与 cjs 的代码,导致 commonjs 失效
|
|||
|
enforce: 'post',
|
|||
|
transform(code, id) {
|
|||
|
if (!filter(id))
|
|||
|
return null;
|
|||
|
if (!(0, utils_1.isJsFile)(id))
|
|||
|
return null;
|
|||
|
debugInjectTry(id);
|
|||
|
if (code.search(firstpass) === -1)
|
|||
|
return null;
|
|||
|
if (path_1.sep !== '/')
|
|||
|
id = id.split(path_1.sep).join('/');
|
|||
|
const ast = this.parse(code);
|
|||
|
const imports = new Set();
|
|||
|
ast.body.forEach((node) => {
|
|||
|
if (node.type === 'ImportDeclaration') {
|
|||
|
node.specifiers.forEach((specifier) => {
|
|||
|
imports.add(specifier.local.name);
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
// analyse scopes
|
|||
|
let scope = (0, pluginutils_1.attachScopes)(ast, 'scope');
|
|||
|
const magicString = new magic_string_1.default(code);
|
|||
|
const newImports = new Map();
|
|||
|
function handleReference(node, name, keypath, parent) {
|
|||
|
let mod = modulesMap.get(keypath);
|
|||
|
if (!mod && hasNamespace) {
|
|||
|
const mods = keypath.split('.');
|
|||
|
if (mods.length === 2) {
|
|||
|
mod = namespaceModulesMap.get(mods[0] + '.');
|
|||
|
if (mod) {
|
|||
|
if ((0, shared_1.isArray)(mod)) {
|
|||
|
const testFn = mod[1];
|
|||
|
if (testFn(mods[1])) {
|
|||
|
mod = [mod[0], mods[1]];
|
|||
|
}
|
|||
|
else {
|
|||
|
mod = undefined;
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
mod = [mod, mods[1]];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (mod && !imports.has(name) && !scope.contains(name)) {
|
|||
|
if ((0, shared_1.isString)(mod))
|
|||
|
mod = [mod, 'default'];
|
|||
|
if (mod[0] === id)
|
|||
|
return false;
|
|||
|
const hash = `${keypath}:${mod[0]}:${mod[1]}`;
|
|||
|
// 当 API 被覆盖定义后,不再摇树
|
|||
|
if (reassignments.has(hash)) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (parent &&
|
|||
|
(0, utils_1.isAssignmentExpression)(parent) &&
|
|||
|
parent.left === node) {
|
|||
|
reassignments.add(hash);
|
|||
|
return false;
|
|||
|
}
|
|||
|
const importLocalName = name === keypath ? name : (0, pluginutils_1.makeLegalIdentifier)(`$inject_${keypath}`);
|
|||
|
if (!newImports.has(hash)) {
|
|||
|
if (mod[1] === '*') {
|
|||
|
newImports.set(hash, `import * as ${importLocalName} from '${mod[0]}';`);
|
|||
|
}
|
|||
|
else {
|
|||
|
newImports.set(hash, `import { ${mod[1]} as ${importLocalName} } from '${mod[0]}';`);
|
|||
|
callback && callback(newImports, mod);
|
|||
|
}
|
|||
|
}
|
|||
|
if (name !== keypath) {
|
|||
|
magicString.overwrite(node.start, node.end, importLocalName, {
|
|||
|
storeName: true,
|
|||
|
});
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
(0, estree_walker_1.walk)(ast, {
|
|||
|
enter(node, parent) {
|
|||
|
if (sourceMap) {
|
|||
|
magicString.addSourcemapLocation(node.start);
|
|||
|
magicString.addSourcemapLocation(node.end);
|
|||
|
}
|
|||
|
if (node.scope) {
|
|||
|
scope = node.scope;
|
|||
|
}
|
|||
|
if ((0, utils_1.isProperty)(node) && node.shorthand) {
|
|||
|
const { name } = node.key;
|
|||
|
handleReference(node, name, name);
|
|||
|
this.skip();
|
|||
|
return;
|
|||
|
}
|
|||
|
if ((0, utils_1.isReference)(node, parent)) {
|
|||
|
const { name, keypath } = flatten(node);
|
|||
|
const handled = handleReference(node, name, keypath, parent);
|
|||
|
if (handled) {
|
|||
|
this.skip();
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
leave(node) {
|
|||
|
if (node.scope) {
|
|||
|
scope = scope.parent;
|
|||
|
}
|
|||
|
},
|
|||
|
});
|
|||
|
debugInject(id, newImports.size);
|
|||
|
if (newImports.size === 0) {
|
|||
|
return {
|
|||
|
code,
|
|||
|
// 不能返回 ast ,否则会导致代码不能被再次修改
|
|||
|
// 比如 App.vue 中,console.log('uniCloud') 触发了 inject 检测,检测完,发现不需要
|
|||
|
// 此时返回 ast,会导致 import { setupApp } from '@dcloudio/uni-h5' 不会被编译
|
|||
|
// ast
|
|||
|
map: null,
|
|||
|
};
|
|||
|
}
|
|||
|
const importBlock = Array.from(newImports.values()).join('\n\n');
|
|||
|
magicString.prepend(`${importBlock}\n\n`);
|
|||
|
return {
|
|||
|
code: magicString.toString(),
|
|||
|
map: sourceMap ? magicString.generateMap({ hires: true }) : null,
|
|||
|
};
|
|||
|
},
|
|||
|
};
|
|||
|
}
|
|||
|
exports.uniViteInjectPlugin = uniViteInjectPlugin;
|
|||
|
const escape = (str) => str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
|||
|
const flatten = (startNode) => {
|
|||
|
const parts = [];
|
|||
|
let node = startNode;
|
|||
|
while ((0, utils_1.isMemberExpression)(node)) {
|
|||
|
parts.unshift(node.property.name);
|
|||
|
node = node.object;
|
|||
|
}
|
|||
|
const { name } = node;
|
|||
|
parts.unshift(name);
|
|||
|
return { name, keypath: parts.join('.') };
|
|||
|
};
|
|||
|
function normalizeModulesMap(modulesMap) {
|
|||
|
modulesMap.forEach((mod, key) => {
|
|||
|
modulesMap.set(key, (0, shared_1.isArray)(mod)
|
|||
|
? [mod[0].split(path_1.sep).join('/'), mod[1]]
|
|||
|
: mod.split(path_1.sep).join('/'));
|
|||
|
});
|
|||
|
}
|