UNPKG

@vaadin/hilla-file-router

Version:

Hilla file-based router

103 lines 3.88 kB
import { basename } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { generateRuntimeFiles } from "./vite-plugin/generateRuntimeFiles.js"; const INJECTION = "if (Object.keys(nextExports).length === 2 && 'default' in nextExports && 'config' in nextExports) {nextExports = { ...nextExports, config: currentExports.config };}"; /** * A Vite plugin that generates a router from the files in the specific directory. * * @param options - The plugin options. * @returns A Vite plugin. */ export default function vitePluginFileSystemRouter({ viewsDir = "frontend/views/", generatedDir = "frontend/generated/", extensions = [".tsx", ".jsx"], isDevMode = false, debug = false } = {}) { let _viewsDir; let _outDir; let _logger; let runtimeUrls; let _generateRuntimeFiles; let _viewsDirPosix; return { name: "vite-plugin-file-router", enforce: "pre", configResolved({ logger, root, build: { outDir } }) { const _root = pathToFileURL(root); const _generatedDir = new URL(generatedDir, _root); _viewsDir = new URL(viewsDir, _root); _viewsDirPosix = fileURLToPath(_viewsDir).replaceAll("\\", "/"); _outDir = pathToFileURL(outDir); _logger = logger; if (debug) { _logger.info(`The directory of route files: ${String(_viewsDir)}`); _logger.info(`The directory of generated files: ${String(_generatedDir)}`); _logger.info(`The output directory: ${String(_outDir)}`); } runtimeUrls = { json: new URL("file-routes.json", isDevMode ? _generatedDir : _outDir), code: new URL("file-routes.ts", _generatedDir), layouts: new URL("layouts.json", _generatedDir) }; _generateRuntimeFiles = async () => { try { return await generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger, debug); } catch (e) { _logger.error(String(e)); return true; } }; }, async buildStart() { await _generateRuntimeFiles(); }, async hotUpdate({ file, modules }) { const fileUrlString = String(pathToFileURL(file)); if (fileUrlString === String(runtimeUrls.json)) { this.environment.hot.send({ type: "custom", event: "fs-route-update" }); return []; } if (fileUrlString === String(runtimeUrls.code)) { return []; } if (!file.startsWith(_viewsDirPosix)) { if (fileUrlString !== String(runtimeUrls.layouts)) { return; } } if (await _generateRuntimeFiles()) { const mg = this.environment.moduleGraph; const fileRoutesModules = mg.getModulesByFile(fileURLToPath(runtimeUrls.code).replaceAll("\\", "/")); await Promise.all(Array.from(fileRoutesModules, async (fileRouteModule) => { mg.invalidateModule(fileRouteModule); await this.environment.fetchModule(fileRouteModule.id, undefined, { cached: false }); })); const neverImported = modules.every((module) => module.importers.size === 0); if (neverImported) { return [...fileRoutesModules]; } return [...fileRoutesModules, ...modules]; } return undefined; }, transform(code, id) { let modifiedCode = code; if (id.startsWith(_viewsDirPosix) && !basename(id).startsWith("_")) { if (isDevMode) { const injectionPattern = /import\.meta\.hot\.accept[\s\S]+if\s\(!nextExports\)\s+return;/gu; if (injectionPattern.test(modifiedCode)) { modifiedCode = `${modifiedCode.substring(0, injectionPattern.lastIndex)}${INJECTION}${modifiedCode.substring(injectionPattern.lastIndex)}`; } } else { const functionNames = /export\s+default\s+(?:function\s+)?(\w+)/u.exec(modifiedCode); if (functionNames?.length) { const [, functionName] = functionNames; modifiedCode += `Object.defineProperty(${functionName}, 'name', { value: '${functionName}' });\n`; } } return { code: modifiedCode }; } return undefined; } }; } //# sourceMappingURL=./vite-plugin.js.map