vite-require
Version:
Like Webpack's require
264 lines (263 loc) • 13.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamicRequire = void 0;
const path_1 = __importDefault(require("path"));
const fast_glob_1 = __importDefault(require("fast-glob"));
const analyze_1 = require("./analyze");
const vite_plugin_dynamic_import_1 = require("vite-plugin-dynamic-import");
const utils_1 = require("./utils");
/**
* ```
* At present, divide `require(id: Literal)` into three cases
* 目前,将 `require(id: Literal)` 分为三种情况
*
* ①(🎯)
* In the top-level scope and can be converted to `import` directly
* 在顶层作用域,并且直接转换成 import
*
* ②(🚧)
* If the `id` in `require(id: Literal)` is a literal string, the `require` statement will be promoted to the top-level scope and become an `import` statement
* 如果 require(id: Literal) 中的 id 是字面量字符串,require 语句将会被提升到顶级作用域,变成 import 语句
*
* ③(🚧)
* If the `id` in `require(dynamic-id)` is a dynamic-id, the `require` statement will be converted to `__matchRequireRuntime` function
* 如果 require(dynamic-id) 中的 id 动态 id,require 语句将会被转换成 __matchRequireRuntime 函数
* ```
*/
class DynamicRequire {
constructor(options, config, resolve = new vite_plugin_dynamic_import_1.Resolve(config)) {
this.options = options;
this.config = config;
this.resolve = resolve;
this.EXT = '.extension';
}
async transform(analyzed, importer) {
var _a, _b;
const { code, require: statements } = analyzed;
const ms = new utils_1.MagicString(code);
const promotionImports = [];
const runtimeFunctions = [];
const importCache = new Map( /* import-id, import-name */);
let counter = 0;
for (const statement of statements) {
const { node, ancestors, dynamic, topScopeNode, } = statement;
counter++;
const require2import = `__require2import__${counter}__`;
let requireId;
const requireIdNode = node.arguments[0];
if (!requireIdNode)
continue; // Not value - require()
if (requireIdNode.type === 'Literal') {
requireId = requireIdNode.value;
}
else if (dynamic === 'Literal') {
requireId = requireIdNode.quasis[0].value.raw;
}
if (utils_1.builtins.includes(requireId))
continue;
if (!requireId && dynamic !== 'dynamic') {
const codeSnippets = analyzed.code.slice(node.start, node.end);
throw new Error(`The following require statement cannot be converted.
-> ${codeSnippets}
${'^'.repeat(codeSnippets.length)}`);
}
if (topScopeNode) {
// ①(🎯)
let imptStatement = '';
let declaration = ''; // `declaration` used to merge import
switch (topScopeNode.type) {
case analyze_1.TopScopeType.ExpressionStatement:
// TODO: with members
imptStatement = `import '${requireId}';`;
break;
case analyze_1.TopScopeType.VariableDeclaration:
// TODO: Multiple declaration
const VariableDeclarator = topScopeNode.declarations[0];
const { /* L-V */ id, /* R-V */ init } = VariableDeclarator;
// Left value
let LV;
if (id.type === 'Identifier') {
LV = id.name;
}
else if (id.type === 'ObjectPattern') {
LV = [];
for (const { key, value } of id.properties) {
LV.push({ key: key.name, value: value.name });
}
}
else {
throw new Error(`Unknown VariableDeclarator.id.type(L-V): ${id.type}`);
}
const LV_str = (spe) => typeof LV === 'object'
? LV.map(e => e.key === e.value ? e.key : `${e.key} ${spe} ${e.value}`).join(', ')
: '';
// Right value
if (init.type === 'CallExpression') {
if (typeof LV === 'string') {
// const acorn = require('acorn')
imptStatement = this.generatedImportAs(LV, requireId); // `import * as ${LV} from '${requireId}'`
}
else {
// const { parse } = require('acorn')
imptStatement = `import { ${LV_str('as')} } from '${requireId}'`;
}
}
else if (init.type === 'MemberExpression') {
const onlyOneMember = ancestors.find(an => an.type === 'MemberExpression').property.name;
const importDefault = onlyOneMember === 'default';
if (typeof LV === 'string') {
if (importDefault) {
// const foo = require('foo').default
imptStatement = `import ${LV} from '${requireId}'`;
}
else {
imptStatement = onlyOneMember === LV
// const bar = require('foo').bar
? `import { ${LV} } from '${requireId}'`
// const barAlias = require('foo').bar
: `import { ${onlyOneMember} as ${LV} } from '${requireId}'`;
}
}
else {
if (importDefault) {
// const { member1, member2 } = require('foo').default
imptStatement = `import ${require2import} from '${requireId}'`;
}
else {
// const { member1, member2 } = require('foo').bar
imptStatement = `import { ${onlyOneMember} as ${require2import} } from '${requireId}'`;
}
declaration = `const { ${LV_str(':')} } = ${require2import}`;
}
}
else {
throw new Error(`Unknown VariableDeclarator.init.type(R-V): ${id.init}`);
}
ms.overwrite(topScopeNode.start, topScopeNode.end, imptStatement + declaration);
break;
default:
throw new Error(`Unknown TopScopeType: ${topScopeNode}`);
}
}
else if (dynamic === 'dynamic') {
// ③(🚧)
let resolved;
const PAHT_FILL = '####/';
const EXT_FILL = '.extension';
let globRaw;
let glob = await (0, vite_plugin_dynamic_import_1.dynamicImportToGlob)(
// `require` should have only one parameter
node.arguments[0], code.slice(node.start, node.end), async (raw) => {
globRaw = raw;
resolved = await this.resolve.tryResolve(raw, importer);
if (resolved) {
raw = resolved.import.resolved;
}
if (!path_1.default.extname(raw)) {
// Bypass extension restrict
raw = raw + EXT_FILL;
}
if (/^\.\/\*\.\w+$/.test(raw)) {
// Bypass ownDirectoryStarExtension (./*.ext)
raw = raw.replace('./*', `./${PAHT_FILL}*`);
}
return raw;
});
if (!glob) {
// TODO: normallyImporteeRE
// if (normallyImporteeRE.test(globRaw)) {
// normally = globRaw
// return { normally }
// }
return;
}
if (((_a = this.options.dynamic) === null || _a === void 0 ? void 0 : _a.loose) !== false) {
const tmp = (0, vite_plugin_dynamic_import_1.toLooseGlob)(glob);
// TODO: support Array(vite-plugin-dynamic-import)
glob = Array.isArray(tmp) ? tmp[0] : tmp;
}
const globs = [].concat(((_b = this.options.dynamic) === null || _b === void 0 ? void 0 : _b.loose) !== false ? (0, vite_plugin_dynamic_import_1.toLooseGlob)(glob) : glob)
.map(g => {
g.includes(PAHT_FILL) && (g = g.replace(PAHT_FILL, ''));
g.endsWith(EXT_FILL) && (g = g.replace(EXT_FILL, ''));
return g;
});
const fileGlobs = globs
.map(g => path_1.default.extname(g)
? g
// If not ext is not specified, fill necessary extensions
// e.g.
// `./foo/*` -> `./foo/*.{js,ts,vue,...}`
: g + `.{${this.options.extensions.map(e => e.replace(/^\./, '')).join(',')}}`);
const result = fast_glob_1.default.sync(fileGlobs, { cwd: path_1.default.dirname(importer) });
let files = result.map(file => !file.startsWith('.') ? `./${file}` : file);
// skip itself
files = files.filter(f => path_1.default.join(path_1.default.dirname(importer), f) !== importer);
// TODO: execute the Options.onFiles
if (!files.length)
continue;
const maps = (0, vite_plugin_dynamic_import_1.mappingPath)(files, resolved);
const runtimeFnName = `__matchRequireRuntime${counter}__`;
let counter2 = 0;
const cases = [];
for (const [localFile, importeeList] of Object.entries(maps)) {
let dynamic_require2import;
const cache = importCache.get(localFile);
if (cache) {
dynamic_require2import = cache;
}
else {
dynamic_require2import = `__dynamic_require2import__${counter}__${counter2++}`;
importCache.set(localFile, dynamic_require2import);
promotionImports.push(this.generatedImportAs(dynamic_require2import, localFile));
}
cases.push(importeeList
.map(importee => ` case '${importee}':`)
.concat(` return ${dynamic_require2import};`)
.join('\n'));
}
ms.overwrite(node.callee.start, node.callee.end, runtimeFnName);
runtimeFunctions.push(`function ${runtimeFnName}(path) {
switch(path) {
${cases.join('\n')}
default: throw new Error("Cann't found module: " + path);
}
}`);
}
else {
// ②(🚧)
promotionImports.push(this.generatedImportAs(require2import, requireId));
ms.overwrite(node.start, node.end, require2import);
}
}
if (promotionImports.length) {
ms.prepend([
'/* import-promotion-S */',
...promotionImports.map(i => i + ';'),
'/* import-promotion-E */',
].join(' '));
}
if (runtimeFunctions.length) {
ms.append([
'// ---- dynamic require runtime functions --S--',
...runtimeFunctions,
'// ---- dynamic require runtime functions --E--',
].join('\n'));
}
const str = ms.toString();
return str === code ? null : str;
}
/**
* If importee ends in a asset file, it might be better to just import the default module.
*/
generatedImportAs(moduleName, importee) {
if (utils_1.KNOWN_ASSET_TYPES.concat(utils_1.KNOWN_CSS_TYPES).find(e => importee.endsWith(e))) {
return `import ${moduleName} from '${importee}'`;
}
return `import * as ${moduleName} from '${importee}'`;
}
}
exports.DynamicRequire = DynamicRequire;