UNPKG

@vaadin/hilla-file-router

Version:

Hilla file-based router

80 lines 2.91 kB
import { readFile } from "node:fs/promises"; import { Script } from "node:vm"; import ts from "typescript"; import { convertComponentNameToTitle } from "../shared/convertComponentNameToTitle.js"; import { transformTree } from "../shared/transformTree.js"; import { convertFSRouteSegmentToURLPatternFormat, extractParameterFromRouteSegment } from "./utils.js"; /** * Walks the TypeScript AST using the deep-first search algorithm. * * @param node - The node to walk. */ function* walkAST(node) { yield node; for (const child of node.getChildren()) { yield* walkAST(child); } } /** * Creates a map of all leaf routes to their configuration. This file is used by the server to provide server-side * routes along with managing the client-side routes. * * @param views - The route metadata tree. * @returns A view configuration tree. */ export default async function createViewConfigJson(views) { return await transformTree(views, null, async (routes, next) => await Promise.all(routes.map(async ({ path, file, layout, children, flowLayout }) => { const newChildren = children ? await next(children) : undefined; if (!file && !layout) { return { route: convertFSRouteSegmentToURLPatternFormat(path), params: extractParameterFromRouteSegment(path), children: newChildren }; } const sourceFile = ts.createSourceFile("f.ts", await readFile(file ?? layout, "utf8"), ts.ScriptTarget.ESNext, true); let config; let waitingForIdentifier = false; let componentName; for (const node of walkAST(sourceFile)) { if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === "config") { if (node.initializer && ts.isObjectLiteralExpression(node.initializer)) { const code = node.initializer.getText(sourceFile); const script = new Script(` function key(x) { return x[0]; } (${code}) `); config = script.runInThisContext(); } } else if (node.getText(sourceFile).startsWith("export default")) { waitingForIdentifier = true; } else if (waitingForIdentifier && ts.isIdentifier(node)) { componentName = node.text; waitingForIdentifier = false; } } config = { ...config ?? {}, flowLayout: config?.flowLayout ?? flowLayout ?? false }; let title; if (config.title) { ({title} = config); } else { if (!componentName) { throw new Error(`The file "${String(file ?? layout)}" must contain a default export of a component whose name will be used as title by default`); } title = convertComponentNameToTitle(componentName); } return { route: convertFSRouteSegmentToURLPatternFormat(path), ...config, params: extractParameterFromRouteSegment(config.route ?? path), title, children: newChildren ?? (layout ? [] : undefined) }; }))); } //# sourceMappingURL=./createViewConfigJson.js.map