You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
8.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"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 uts_1 = require("../../uts");
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) && !(0, uts_1.isUniHelpers)(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('/'));
});
}