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 = debug('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: options.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('/'));
|
||
});
|
||
}
|