import path from 'node:path'; import { init, parse } from 'es-module-lexer'; import MagicString from 'magic-string'; import { BARE_IMPORT_RE, CLIENT_PUBLIC_PATH, PRE_BUNDLE_DIR } from '../constants'; import { cleanUrl, getShortName, isInternalRequest, isJSRequest } from '../utils'; import type { Plugin } from '../plugin'; import type { ServerContext } from '../server/index'; export function importAnalysisPlugin(): Plugin { let serverContext: ServerContext; return { name: 'ranite:import-analysis', configureServer(s) { serverContext = s; }, async transform(code: string, id: string) { if (!isJSRequest(id) || isInternalRequest(id)) { return null; } await init; const importedModules = new Set(); const [imports] = parse(code); const ms = new MagicString(code); const { moduleGraph } = serverContext; const curMod = moduleGraph.getModuleById(id)!; const resolve = async (id: string, importer?: string) => { const resolved = await serverContext.pluginContainer.resolveId(id, importer); if (!resolved) { return; } const cleanedId = cleanUrl(resolved.id); const mod = moduleGraph.getModuleById(cleanedId); const resolvedId = `/${getShortName(resolved.id, serverContext.root)}`; if (mod && mod.lastHMRTimestamp > 0) { // resolvedId += "?t=" + mod.lastHMRTimestamp; } return resolvedId; }; for (const importInfo of imports) { const { s: modStart, e: modEnd, n: modSource } = importInfo; if (!modSource || isInternalRequest(modSource)) continue; // 静态资源 if (modSource.endsWith('.svg')) { // 加上 ?import 后缀 const resolvedUrl = path.join(path.dirname(id), modSource); ms.overwrite(modStart, modEnd, `${resolvedUrl}?import`); continue; } // 第三方库: 路径重写到预构建产物的路径 if (BARE_IMPORT_RE.test(modSource)) { const bundlePath = path.join(serverContext.root, PRE_BUNDLE_DIR, `${modSource}.js`); ms.overwrite(modStart, modEnd, bundlePath); importedModules.add(bundlePath); } else if (modSource.startsWith('.') || modSource.startsWith('/')) { const resolved = await resolve(modSource, id); if (resolved) { ms.overwrite(modStart, modEnd, resolved); importedModules.add(resolved); } } } // 只对业务源码注入 if (!id.includes('node_modules')) { // 注入 HMR 相关的工具函数 ms.prepend( `import { createHotContext as __ranite__createHotContext } from "${CLIENT_PUBLIC_PATH}";` + `import.meta.hot = __ranite__createHotContext(${JSON.stringify(cleanUrl(curMod.url))});`, ); } moduleGraph.updateModuleInfo(curMod, importedModules); return { code: ms.toString(), map: ms.generateMap(), }; }, }; }