UNPKG

@vaadin/hilla-file-router

Version:

Hilla file-based router

95 lines (93 loc) 4.67 kB
import { template, transform as transformer } from "@vaadin/hilla-generator-utils/ast.js"; import createSourceFile from "@vaadin/hilla-generator-utils/createSourceFile.js"; import DependencyManager from "@vaadin/hilla-generator-utils/dependencies/DependencyManager.js"; import PathManager from "@vaadin/hilla-generator-utils/dependencies/PathManager.js"; import ts from "typescript"; import { transformTree } from "../shared/transformTree.js"; import { convertFSRouteSegmentToURLPatternFormat } from "./utils.js"; const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); const fileExtensions = [ ".ts", ".tsx", ".js", ".jsx" ]; class RouteFromMetaProcessor { #manager; #views; constructor(views, { code: codeFile }) { this.#views = views; const codeDir = new URL("./", codeFile); this.#manager = new DependencyManager(new PathManager({ extension: ".js", relativeTo: codeDir })); } /** * Loads all the files from the received metadata and creates a framework-agnostic route tree. * * @param views - The abstract route tree. * @param generatedDir - The directory where the generated view file will be stored. */ process() { const { paths, imports: { named, namespace } } = this.#manager; const errors = []; const routes = transformTree(this.#views, null, (metas, next) => { errors.push(...metas.map((route) => route.path).filter((item, index, arr) => arr.indexOf(item) !== index).map((dup) => `console.error("Two views share the same path: ${dup}");`)); return metas.map(({ file, layout, path, children, flowLayout }) => { let _children; if (children) { _children = next(children); } let mod; if (file) { const fileExt = fileExtensions.find((ext) => file.pathname.endsWith(ext)); mod = namespace.add(paths.createRelativePath(file, fileExt), `Page`); } else if (layout) { const fileExt = fileExtensions.find((ext) => layout.pathname.endsWith(ext)); mod = namespace.add(paths.createRelativePath(layout, fileExt), `Layout`); } const moduleExtension = flowLayout ? { flowLayout } : undefined; return this.#createRouteData(convertFSRouteSegmentToURLPatternFormat(path), mod, moduleExtension, _children); }); }); const agnosticRouteId = named.getIdentifier("@vaadin/hilla-file-router/types.js", "AgnosticRoute") ?? named.add("@vaadin/hilla-file-router/types.js", "AgnosticRoute", true); let routeDeclaration = template(`${errors.join("\n")} const routes: readonly AGNOSTIC_ROUTE[] = ROUTE; export default routes; `, [transformer((node) => ts.isIdentifier(node) && node.text === "ROUTE" ? ts.factory.createArrayLiteralExpression(routes, true) : node), transformer((node) => ts.isIdentifier(node) && node.text === "AGNOSTIC_ROUTE" ? agnosticRouteId : node)]); routeDeclaration = [...this.#manager.imports.toCode(), ...routeDeclaration]; const file = createSourceFile(routeDeclaration, "file-routes.ts"); return printer.printFile(file); } /** * Create an abstract route creation function call. The nested function calls * create a route tree. * * @param path - The path of the route. * @param mod - The name of the route module imported as a namespace. * @param children - The list of child route call expressions. */ #createRouteData(path, mod, extension, children) { const { named } = this.#manager.imports; let extendModuleId; let modNode = ""; if (extension) { extendModuleId = named.getIdentifier("@vaadin/hilla-file-router/runtime.js", "extendModule") ?? named.add("@vaadin/hilla-file-router/runtime.js", "extendModule"); modNode = `, EXTEND_MODULE(MOD, ${JSON.stringify(extension)})`; } else if (mod) { modNode = `, MOD`; } const createRouteId = named.getIdentifier("@vaadin/hilla-file-router/runtime.js", "createRoute") ?? named.add("@vaadin/hilla-file-router/runtime.js", "createRoute"); return template(`const route = CREATE_ROUTE("${path}", ${modNode}${children ? `, CHILDREN` : ""})`, ([statement]) => statement.declarationList.declarations[0].initializer, [ transformer((node) => ts.isIdentifier(node) && node.text === "CHILDREN" ? ts.factory.createArrayLiteralExpression(children, true) : node), transformer((node) => ts.isIdentifier(node) && node.text === "MOD" ? mod ?? ts.factory.createNull() : node), transformer((node) => ts.isIdentifier(node) && node.text === "EXTEND_MODULE" ? extendModuleId : node), transformer((node) => ts.isIdentifier(node) && node.text === "CREATE_ROUTE" ? createRouteId : node) ]); } } export default function createRoutesFromMeta(views, urls) { return new RouteFromMetaProcessor(views, urls).process(); } //# sourceMappingURL=./createRoutesFromMeta.js.map