UNPKG

@titovdima/vite-plugin-import-map

Version:

A simple and flexible Vite plugin to inject and watch import maps with support for JSON files. Supports inline definitions and external configuration.

159 lines (156 loc) 5.35 kB
// src/index.ts import path2 from "path"; import fs2 from "fs"; // src/utils/updateTsConfig.ts import fs from "fs"; import path from "path"; import * as commentJson from "comment-json"; function makeRelativePath(p) { if (p.startsWith("/src")) { const trimmed = p.replace(/^\/+/, ""); const normalized = trimmed.replace(/\\/g, "/"); return normalized.endsWith("/") ? `${normalized}*` : `${normalized}/*`; } const relative = path.relative(process.cwd(), p).replace(/\\/g, "/"); return relative.endsWith("/") ? `${relative}*` : `${relative}/*`; } function updateTsConfig(imports, tsconfigPath) { const fullPath = path.resolve(process.cwd(), tsconfigPath); if (!fs.existsSync(fullPath)) { console.warn( `[vite-plugin-import-map] tsconfig file not found: ${fullPath}` ); return; } try { const raw = fs.readFileSync(fullPath, "utf-8"); const tsconfig = commentJson.parse(raw); tsconfig.compilerOptions = tsconfig.compilerOptions || {}; tsconfig.compilerOptions.paths = tsconfig.compilerOptions.paths || {}; if (!tsconfig.compilerOptions.baseUrl) { tsconfig.compilerOptions.baseUrl = "."; console.log( `[vite-plugin-import-map] Setting baseUrl to "." in ${tsconfigPath}` ); } const newKeys = Object.keys(imports).map( (k) => (k.endsWith("/") ? k : `${k}/`) + "*" ); const paths = tsconfig.compilerOptions.paths; Object.keys(paths).forEach((existingKey) => { const isGenerated = existingKey.endsWith("/*") && existingKey.includes("/"); const isObsolete = !newKeys.includes(existingKey); if (isGenerated && isObsolete) { delete paths[existingKey]; } }); Object.entries(imports).forEach(([key, value]) => { const aliasKey = key.endsWith("/") ? `${key}*` : `${key}/*`; const normalizedPath = makeRelativePath(value); paths[aliasKey] = [normalizedPath]; }); fs.writeFileSync(fullPath, commentJson.stringify(tsconfig, null, 2)); console.log( `[vite-plugin-import-map] Updated ${tsconfigPath} with import aliases` ); console.log( `[vite-plugin-import-map] To apply updated TypeScript paths, restart the TypeScript server in your editor.` ); } catch (err) { console.error( `[vite-plugin-import-map] Failed to update ${tsconfigPath}:`, err ); } } // src/index.ts function importMapPlugin(options) { const { imports, importMapPath, autoRestart = false, tsconfigPath } = options; let resolvedImports = imports ?? {}; return { name: "vite-plugin-import-map", enforce: "pre", config() { if (importMapPath) { try { const resolvedPath = path2.resolve(process.cwd(), importMapPath); const fileContents = fs2.readFileSync(resolvedPath, "utf-8"); const parsedData = JSON.parse(fileContents); if (parsedData.imports) { resolvedImports = parsedData.imports; console.log("Import aliases:", resolvedImports); } } catch (error) { console.error("Failed to load import-map.json:", error); } } resolvedImports = Object.fromEntries( Object.entries(resolvedImports).map(([key, value]) => { if (!value.startsWith("/")) { value = path2.resolve(process.cwd(), value); } return [key, value]; }) ); console.log( `[vite-plugin-import-map] Updated import map with aliases:`, resolvedImports ); if (tsconfigPath) { updateTsConfig(resolvedImports, tsconfigPath); } return { resolve: { alias: Object.entries(resolvedImports).map(([key, value]) => { const adjustedKey = new RegExp(`^${key}`); return { find: adjustedKey, replacement: value }; }) } }; }, transformIndexHtml(html) { const importMapJson = JSON.stringify({ imports: resolvedImports }); const scriptTag = `<script type="importmap">${importMapJson}</script>`; return html.replace("</head>", `${scriptTag}</head>`); }, handleHotUpdate({ file, server }) { if (importMapPath && path2.resolve(file) === path2.resolve(process.cwd(), importMapPath)) { try { const fileContents = fs2.readFileSync(file, "utf-8"); const parsedData = JSON.parse(fileContents); if (parsedData.imports) { resolvedImports = parsedData.imports; console.log( "[vite-plugin-import-map] Updated import map on the fly:", resolvedImports ); } if (autoRestart) { console.log( "[vite-plugin-import-map] Restarting Vite server to apply changes..." ); server.restart(); } else { console.log( "[vite-plugin-import-map] Import map updated. Please restart Vite manually to apply changes." ); } server.ws.send({ type: "full-reload" }); } catch (error) { console.error( "[vite-plugin-import-map] Error reading or parsing import-map.json:", error ); } } } }; } export { importMapPlugin as default };