@vaadin/hilla-file-router 25.3.0-alpha8 → 25.3.0-alpha9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/runtime/createMenuItems.js +1 -1
- package/runtime/createMenuItems.js.map +1 -1
- package/vite-plugin/createRoutesFromMeta.js +1 -1
- package/vite-plugin/createRoutesFromMeta.js.map +1 -1
- package/vite-plugin/createViewConfigJson.js +1 -1
- package/vite-plugin/createViewConfigJson.js.map +1 -1
- package/vite-plugin.js +55 -6
- package/vite-plugin.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/hilla-file-router",
|
|
3
|
-
"version": "25.3.0-
|
|
3
|
+
"version": "25.3.0-alpha9",
|
|
4
4
|
"description": "Hilla file-based router",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.js",
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
"react-router": "7"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
+
"@typescript/typescript6": "6.0.2",
|
|
64
65
|
"@ungap/with-resolvers": "0.1.0",
|
|
65
|
-
"@vaadin/hilla-generator-utils": "25.3.0-
|
|
66
|
-
"@vaadin/hilla-react-auth": "25.3.0-
|
|
67
|
-
"@vaadin/hilla-react-signals": "25.3.0-
|
|
68
|
-
"typescript": "npm:@typescript/typescript6@6.0.2"
|
|
66
|
+
"@vaadin/hilla-generator-utils": "25.3.0-alpha9",
|
|
67
|
+
"@vaadin/hilla-react-auth": "25.3.0-alpha9",
|
|
68
|
+
"@vaadin/hilla-react-signals": "25.3.0-alpha9"
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -19,7 +19,7 @@ export function createMenuItems() {
|
|
|
19
19
|
vaadinObj.registrations ??= [];
|
|
20
20
|
vaadinObj.registrations.push({
|
|
21
21
|
is: feature ? `@vaadin/hilla-file-router/${feature}` : "@vaadin/hilla-file-router",
|
|
22
|
-
version: "25.3.0-
|
|
22
|
+
version: "25.3.0-alpha9"
|
|
23
23
|
});
|
|
24
24
|
})("createMenuItems", window.Vaadin);
|
|
25
25
|
const collator = new Intl.Collator("en-US");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AACA,SAAsB,2CAA4C;AAIlE,OAAO,MAAMA,cAAkF,OAC5F,OAAwB,QAAQ,MAClC;AAED,SAAS,WAAWC,OAA4B;AAC9C,UAAS,MAAM,MAAM;AACtB;AAED,SAAS,uBAAuBC,MAAuB;AACrD,QAAO,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,QAAQ,WAAW,IAAI,CAAC;AAClE;;;;;;;;;AAUD,OAAO,SAAS,kBAA2D;AAGzE,EAAC,CAAC,SAAS,YAAa,WAAW,WAAW,CAAE,MAAM;AACtD,YAAU,kBAAkB,CAAE;AAC9B,YAAU,cAAc,KAAK;GAC3B,IAAI,WAAW,4BAA4B,QAAQ,IAAI;GACvD,SAAS;EACV,EAAC;CACH,GAAE,mBAAoB,OAAwB,OAAO;CACpD,MAAM,WAAW,IAAI,KAAK,SAAS;AACnC,MAAK,YAAY,OAAO;AACtB,SAAO,CAAE;CACV;CAED,MAAM,QAAQ,OAAO,QAAQ,YAAY,MAAM;AAE/C,QACE,MAEG,OAAO,CAAC,CAAC,MAAM,MAAM,MAAM,WAAW,MAAM,KAAK,uBAAuB,KAAK,CAAC,CAE9E,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM;EACxB,IAAI;EACJ,MAAM,OAAO,MAAM;EACnB,OAAO,OAAO,MAAM,SAAS,OAAO;EACpC,OAAO,OAAO,MAAM;EACpB,QAAQ,OAAO;CAChB,GAAE,CAEF,KAAK,CAAC,OAAO,UAAU;EACtB,MAAM,cAAc,MAAM,SAAS,OAAO,cAAc,MAAM,SAAS,OAAO;AAC9E,SAAO,eAAe,IAAI,aAAa,SAAS,QAAQ,MAAM,IAAI,MAAM,GAAG;CAC5E,EAAC;AAEP;AAED,IAAI,OAAO,KAAK,KAAK;AACnB,QAAO,KAAK,IAAI,GAAG,mBAAmB,MAAM;AAC1C,QAAM,iBAAiB,CACpB,KAAK,OAAO,SAAS,KAAK,MAAM,CAAC,CACjC,KAAK,CAAC,SAAS;AACd,eAAY,QAAQ;EACrB,EAAC,CACD,MAAM,CAACC,MAAe;AACrB,WAAQ,MAAM,8BAA8B,EAAE;EAC/C,EAAC;CACL,EAAC;AACH","names":["viewsSignal: Signal<Readonly<Record<string, Readonly<ViewConfig>>> | undefined>","value: ViewConfig","path: string","e: unknown"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/file-router/src/runtime/createMenuItems.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport { type Signal, signal } from '@vaadin/hilla-react-signals';\nimport type { VaadinWindow } from '../shared/internal.js';\nimport type { MenuItem, ViewConfig } from '../types.js';\n\nexport const viewsSignal: Signal<Readonly<Record<string, Readonly<ViewConfig>>> | undefined> = signal(\n (window as VaadinWindow).Vaadin?.views,\n);\n\nfunction isExcluded(value: ViewConfig): boolean {\n return !!value.menu?.exclude;\n}\n\nfunction hasVariablePathSegment(path: string): boolean {\n return path.split('/').some((segment) => segment.startsWith(':'));\n}\n\n/**\n * Creates menu items from the views provided by the server. The views are sorted according to the\n * {@link ViewConfig.menu.order}, filtered out if they are explicitly excluded via {@link ViewConfig.menu.exclude}.\n * Note that views with no order are put below views with an order. Ties are resolved based on the path string\n * comparison.\n *\n * @returns A list of menu items.\n */\nexport function createMenuItems<T = unknown>(): ReadonlyArray<MenuItem<T>> {\n // @ts-expect-error: esbuild injection\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n ((feature, vaadinObj = (globalThis.Vaadin ??= {})) => {\n vaadinObj.registrations ??= [];\n vaadinObj.registrations.push({\n is: feature ? `@vaadin/hilla-file-router/${feature}` : '@vaadin/hilla-file-router',\n version: '25.3.0-
|
|
1
|
+
{"mappings":"AACA,SAAsB,2CAA4C;AAIlE,OAAO,MAAMA,cAAkF,OAC5F,OAAwB,QAAQ,MAClC;AAED,SAAS,WAAWC,OAA4B;AAC9C,UAAS,MAAM,MAAM;AACtB;AAED,SAAS,uBAAuBC,MAAuB;AACrD,QAAO,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,QAAQ,WAAW,IAAI,CAAC;AAClE;;;;;;;;;AAUD,OAAO,SAAS,kBAA2D;AAGzE,EAAC,CAAC,SAAS,YAAa,WAAW,WAAW,CAAE,MAAM;AACtD,YAAU,kBAAkB,CAAE;AAC9B,YAAU,cAAc,KAAK;GAC3B,IAAI,WAAW,4BAA4B,QAAQ,IAAI;GACvD,SAAS;EACV,EAAC;CACH,GAAE,mBAAoB,OAAwB,OAAO;CACpD,MAAM,WAAW,IAAI,KAAK,SAAS;AACnC,MAAK,YAAY,OAAO;AACtB,SAAO,CAAE;CACV;CAED,MAAM,QAAQ,OAAO,QAAQ,YAAY,MAAM;AAE/C,QACE,MAEG,OAAO,CAAC,CAAC,MAAM,MAAM,MAAM,WAAW,MAAM,KAAK,uBAAuB,KAAK,CAAC,CAE9E,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM;EACxB,IAAI;EACJ,MAAM,OAAO,MAAM;EACnB,OAAO,OAAO,MAAM,SAAS,OAAO;EACpC,OAAO,OAAO,MAAM;EACpB,QAAQ,OAAO;CAChB,GAAE,CAEF,KAAK,CAAC,OAAO,UAAU;EACtB,MAAM,cAAc,MAAM,SAAS,OAAO,cAAc,MAAM,SAAS,OAAO;AAC9E,SAAO,eAAe,IAAI,aAAa,SAAS,QAAQ,MAAM,IAAI,MAAM,GAAG;CAC5E,EAAC;AAEP;AAED,IAAI,OAAO,KAAK,KAAK;AACnB,QAAO,KAAK,IAAI,GAAG,mBAAmB,MAAM;AAC1C,QAAM,iBAAiB,CACpB,KAAK,OAAO,SAAS,KAAK,MAAM,CAAC,CACjC,KAAK,CAAC,SAAS;AACd,eAAY,QAAQ;EACrB,EAAC,CACD,MAAM,CAACC,MAAe;AACrB,WAAQ,MAAM,8BAA8B,EAAE;EAC/C,EAAC;CACL,EAAC;AACH","names":["viewsSignal: Signal<Readonly<Record<string, Readonly<ViewConfig>>> | undefined>","value: ViewConfig","path: string","e: unknown"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/file-router/src/runtime/createMenuItems.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport { type Signal, signal } from '@vaadin/hilla-react-signals';\nimport type { VaadinWindow } from '../shared/internal.js';\nimport type { MenuItem, ViewConfig } from '../types.js';\n\nexport const viewsSignal: Signal<Readonly<Record<string, Readonly<ViewConfig>>> | undefined> = signal(\n (window as VaadinWindow).Vaadin?.views,\n);\n\nfunction isExcluded(value: ViewConfig): boolean {\n return !!value.menu?.exclude;\n}\n\nfunction hasVariablePathSegment(path: string): boolean {\n return path.split('/').some((segment) => segment.startsWith(':'));\n}\n\n/**\n * Creates menu items from the views provided by the server. The views are sorted according to the\n * {@link ViewConfig.menu.order}, filtered out if they are explicitly excluded via {@link ViewConfig.menu.exclude}.\n * Note that views with no order are put below views with an order. Ties are resolved based on the path string\n * comparison.\n *\n * @returns A list of menu items.\n */\nexport function createMenuItems<T = unknown>(): ReadonlyArray<MenuItem<T>> {\n // @ts-expect-error: esbuild injection\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n ((feature, vaadinObj = (globalThis.Vaadin ??= {})) => {\n vaadinObj.registrations ??= [];\n vaadinObj.registrations.push({\n is: feature ? `@vaadin/hilla-file-router/${feature}` : '@vaadin/hilla-file-router',\n version: '25.3.0-alpha9',\n });\n})('createMenuItems', (window as VaadinWindow).Vaadin);\n const collator = new Intl.Collator('en-US');\n if (!viewsSignal.value) {\n return [];\n }\n\n const views = Object.entries(viewsSignal.value);\n\n return (\n views\n // Filter out the views that are explicitly excluded from the menu.\n .filter(([path, value]) => !isExcluded(value) && !hasVariablePathSegment(path))\n // Map the views to menu items.\n .map(([path, config]) => ({\n to: path,\n icon: config.menu?.icon,\n title: config.menu?.title ?? config.title,\n order: config.menu?.order,\n detail: config.detail as T | undefined,\n }))\n // Sort views according to the order specified in the view configuration.\n .sort((menuA, menuB) => {\n const ordersDiff = (menuA.order ?? Number.MAX_VALUE) - (menuB.order ?? Number.MAX_VALUE);\n return ordersDiff !== 0 ? ordersDiff : collator.compare(menuA.to, menuB.to);\n })\n );\n}\n\nif (import.meta.hot) {\n import.meta.hot.on('fs-route-update', () => {\n fetch('?v-r=routeinfo')\n .then(async (resp) => resp.json())\n .then((json) => {\n viewsSignal.value = json;\n })\n .catch((e: unknown) => {\n console.error('Failed to fetch route info', e);\n });\n });\n}\n"],"version":3}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { createPrinter, factory, NewLineKind } from "@typescript/typescript6";
|
|
1
2
|
import createSourceFile from "@vaadin/hilla-generator-utils/createSourceFile.js";
|
|
2
3
|
import DependencyManager from "@vaadin/hilla-generator-utils/dependencies/DependencyManager.js";
|
|
3
4
|
import PathManager from "@vaadin/hilla-generator-utils/dependencies/PathManager.js";
|
|
4
5
|
import ast from "@vaadin/hilla-generator-utils/tsc-template.js";
|
|
5
|
-
import { createPrinter, factory, NewLineKind } from "typescript";
|
|
6
6
|
import { transformTree } from "../shared/transformTree.js";
|
|
7
7
|
import { convertFSRouteSegmentToURLPatternFormat } from "./utils.js";
|
|
8
8
|
const printer = createPrinter({ newLine: NewLineKind.LineFeed });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","names":["mod: LazyModule | RegularModule | undefined","pathContext: readonly string[]","path: string","config: Readonly<ServerViewConfig>","#manager","#views","#configs","views: readonly RouteMeta[]","viewConfigs: readonly ServerViewConfig[]","errors: string[]","_children: readonly CallExpression[] | undefined","module: LazyModule | RegularModule | undefined","relativePath: string | undefined","fileName: string | undefined","#createRouteData","children: readonly CallExpression[] | undefined","component: PropertyAccessExpression | CallExpression | undefined","config: PropertyAccessExpression | ObjectLiteralExpression | string | undefined","urls: RuntimeFileUrls"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/file-router/src/vite-plugin/createRoutesFromMeta.ts"],"sourcesContent":["import createSourceFile from '@vaadin/hilla-generator-utils/createSourceFile.js';\nimport DependencyManager from '@vaadin/hilla-generator-utils/dependencies/DependencyManager.js';\nimport PathManager from '@vaadin/hilla-generator-utils/dependencies/PathManager.js';\nimport ast from '@vaadin/hilla-generator-utils/tsc-template.js';\nimport {\n type CallExpression,\n createPrinter,\n factory,\n type Identifier,\n NewLineKind,\n type ObjectLiteralExpression,\n type PropertyAccessExpression,\n} from 'typescript';\nimport type { ServerViewConfig } from '../shared/internal.js';\nimport { transformTree } from '../shared/transformTree.js';\nimport type { RouteMeta } from './collectRoutesFromFS.js';\nimport type { RuntimeFileUrls } from './generateRuntimeFiles.js';\nimport { convertFSRouteSegmentToURLPatternFormat } from './utils.js';\n\nconst printer = createPrinter({ newLine: NewLineKind.LineFeed });\n\nconst fileExtensions = ['.ts', '.tsx', '.js', '.jsx'];\n\nconst HILLA_FILE_ROUTER = '@vaadin/hilla-file-router';\nconst HILLA_FILE_ROUTER_RUNTIME = `${HILLA_FILE_ROUTER}/runtime.js`;\nconst HILLA_FILE_ROUTER_TYPES = `${HILLA_FILE_ROUTER}/types.js`;\n\ntype LazyModule = Readonly<{\n path: string;\n config: ServerViewConfig;\n lazyId: Identifier;\n}>;\n\ntype RegularModule = Readonly<{\n componentId: PropertyAccessExpression;\n configId: PropertyAccessExpression;\n flowLayout?: boolean;\n}>;\n\nfunction isLazyModule(mod: LazyModule | RegularModule | undefined): mod is LazyModule {\n return !!mod && 'path' in mod;\n}\n\nfunction isLazyRoute(pathContext: readonly string[], path: string, config: Readonly<ServerViewConfig>): boolean {\n if (config.lazy !== undefined) {\n return config.lazy;\n }\n\n // \"/\" and \"/login\" are eager by default\n const rootContext = !pathContext.some(Boolean);\n const eagerDefault = rootContext && (path === '' || path === 'login');\n return !eagerDefault;\n}\n\nclass RouteFromMetaProcessor {\n readonly #manager: DependencyManager;\n readonly #views: readonly RouteMeta[];\n readonly #configs: readonly ServerViewConfig[];\n\n constructor(\n views: readonly RouteMeta[],\n viewConfigs: readonly ServerViewConfig[],\n { code: codeFile }: RuntimeFileUrls,\n ) {\n this.#views = views;\n this.#configs = viewConfigs;\n\n const codeDir = new URL('./', codeFile);\n this.#manager = new DependencyManager(new PathManager({ extension: '.js', relativeTo: codeDir }));\n }\n\n /**\n * Loads all the files from the received metadata and creates a framework-agnostic route tree.\n */\n process(): string {\n const {\n paths,\n imports: { namespace, named },\n } = this.#manager;\n const errors: string[] = [];\n\n const routes = transformTree<\n [pathContext: readonly string[], metas: readonly RouteMeta[], configs: readonly ServerViewConfig[]],\n readonly CallExpression[]\n >([[], this.#views, this.#configs], null, ([pathContext, metas, configs], next) => {\n errors.push(\n ...metas\n .map((route) => route.path)\n .filter((item, index, arr) => arr.indexOf(item) !== index)\n .map((dup) => `console.error(\"Two views share the same path: ${dup}\");`),\n );\n\n return metas.map(({ file, layout, path, children }, index) => {\n const config = configs[index]!;\n let _children: readonly CallExpression[] | undefined;\n\n if (children) {\n // Assuming that if we have `children` in the route, we also have\n // `children` in the config.\n _children = next([[...pathContext, path], children, config.children!]);\n }\n\n let module: LazyModule | RegularModule | undefined;\n\n let relativePath: string | undefined;\n let fileName: string | undefined;\n\n if (file) {\n const fileExt = fileExtensions.find((ext) => file.pathname.endsWith(ext));\n relativePath = paths.createRelativePath(file, fileExt);\n fileName = 'Page';\n } else if (layout) {\n const fileExt = fileExtensions.find((ext) => layout.pathname.endsWith(ext));\n relativePath = paths.createRelativePath(layout, fileExt);\n fileName = 'Layout';\n }\n\n if (relativePath && fileName) {\n if (isLazyRoute(pathContext, path, config)) {\n module = {\n path: relativePath,\n config,\n lazyId: named.getIdentifier('react', 'lazy') ?? named.add('react', 'lazy'),\n } satisfies LazyModule;\n } else {\n const mod = namespace.add(relativePath, fileName);\n const reactModuleType =\n named.getIdentifier(HILLA_FILE_ROUTER_TYPES, 'RouteModule') ??\n named.add(HILLA_FILE_ROUTER_TYPES, 'RouteModule', true);\n\n module = {\n componentId: ast`${mod}.default`.node as PropertyAccessExpression,\n configId: ast`(${mod} as ${reactModuleType}).config`.node as PropertyAccessExpression,\n flowLayout: config.flowLayout,\n } satisfies RegularModule;\n }\n }\n\n return this.#createRouteData(convertFSRouteSegmentToURLPatternFormat(path), module, _children);\n });\n });\n\n const agnosticRouteId =\n named.getIdentifier(HILLA_FILE_ROUTER_TYPES, 'AgnosticRoute') ??\n named.add(HILLA_FILE_ROUTER_TYPES, 'AgnosticRoute', true);\n const routeDeclaration = [\n ...this.#manager.imports.toCode(),\n ...ast`${errors.join('\\n')}\nconst routes: readonly ${agnosticRouteId}[] = ${factory.createArrayLiteralExpression(routes, true)};\nexport default routes;`.source.statements,\n ];\n\n const file = createSourceFile(routeDeclaration, 'file-routes.ts');\n return printer.printFile(file);\n }\n\n /**\n * Create an abstract route creation function call. The nested function calls\n * create a route tree.\n *\n * @param path - The path of the route.\n * @param module - The module to build route from.\n * @param children - The list of child route call expressions.\n */\n #createRouteData(\n path: string,\n module: LazyModule | RegularModule | undefined,\n children: readonly CallExpression[] | undefined,\n ): CallExpression {\n const { named } = this.#manager.imports;\n\n const createRouteId =\n named.getIdentifier(HILLA_FILE_ROUTER_RUNTIME, 'createRoute') ??\n named.add(HILLA_FILE_ROUTER_RUNTIME, 'createRoute');\n\n let component: PropertyAccessExpression | CallExpression | undefined;\n let config: PropertyAccessExpression | ObjectLiteralExpression | string | undefined;\n\n if (isLazyModule(module)) {\n const { children: _c, params: _p, ...viewConfig } = module.config;\n\n component = ast`${module.lazyId}(() => import('${module.path}')`.node as CallExpression;\n config = Object.keys(viewConfig).length > 0 ? JSON.stringify(viewConfig) : '';\n } else if (module) {\n component = module.componentId;\n config = module.flowLayout\n ? (ast`const a = %{ { ...${module.configId}, flowLayout: ${module.flowLayout.toString()} } }%`\n .node as ObjectLiteralExpression)\n : module.configId;\n }\n\n const _children = children ? factory.createArrayLiteralExpression(children, true) : '';\n\n // ```ts\n // createRoute(\"grandparent\", {...grandparentConfig, flowLayout: true}, [\n // createRoute(\"parent\", ParentPage, parentConfig, [\n // createRoute(\"child\", lazy(() => import('../ChildPage.js')), { title: 'Child Page' }),\n // ]),\n // ]),\n // ```\n return ast`${createRouteId}(\"${path}\", ${component ?? ''}, ${config ?? ''}, ${_children})`.node as CallExpression;\n }\n}\n\nexport default function createRoutesFromMeta(\n views: readonly RouteMeta[],\n viewConfigs: readonly ServerViewConfig[],\n urls: RuntimeFileUrls,\n): string {\n return new RouteFromMetaProcessor(views, viewConfigs, urls).process();\n}\n"],"version":3}
|
|
1
|
+
{"mappings":"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","names":["mod: LazyModule | RegularModule | undefined","pathContext: readonly string[]","path: string","config: Readonly<ServerViewConfig>","#manager","#views","#configs","views: readonly RouteMeta[]","viewConfigs: readonly ServerViewConfig[]","errors: string[]","_children: readonly CallExpression[] | undefined","module: LazyModule | RegularModule | undefined","relativePath: string | undefined","fileName: string | undefined","#createRouteData","children: readonly CallExpression[] | undefined","component: PropertyAccessExpression | CallExpression | undefined","config: PropertyAccessExpression | ObjectLiteralExpression | string | undefined","urls: RuntimeFileUrls"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/file-router/src/vite-plugin/createRoutesFromMeta.ts"],"sourcesContent":["import {\n type CallExpression,\n createPrinter,\n factory,\n type Identifier,\n NewLineKind,\n type ObjectLiteralExpression,\n type PropertyAccessExpression,\n} from '@typescript/typescript6';\nimport createSourceFile from '@vaadin/hilla-generator-utils/createSourceFile.js';\nimport DependencyManager from '@vaadin/hilla-generator-utils/dependencies/DependencyManager.js';\nimport PathManager from '@vaadin/hilla-generator-utils/dependencies/PathManager.js';\nimport ast from '@vaadin/hilla-generator-utils/tsc-template.js';\nimport type { ServerViewConfig } from '../shared/internal.js';\nimport { transformTree } from '../shared/transformTree.js';\nimport type { RouteMeta } from './collectRoutesFromFS.js';\nimport type { RuntimeFileUrls } from './generateRuntimeFiles.js';\nimport { convertFSRouteSegmentToURLPatternFormat } from './utils.js';\n\nconst printer = createPrinter({ newLine: NewLineKind.LineFeed });\n\nconst fileExtensions = ['.ts', '.tsx', '.js', '.jsx'];\n\nconst HILLA_FILE_ROUTER = '@vaadin/hilla-file-router';\nconst HILLA_FILE_ROUTER_RUNTIME = `${HILLA_FILE_ROUTER}/runtime.js`;\nconst HILLA_FILE_ROUTER_TYPES = `${HILLA_FILE_ROUTER}/types.js`;\n\ntype LazyModule = Readonly<{\n path: string;\n config: ServerViewConfig;\n lazyId: Identifier;\n}>;\n\ntype RegularModule = Readonly<{\n componentId: PropertyAccessExpression;\n configId: PropertyAccessExpression;\n flowLayout?: boolean;\n}>;\n\nfunction isLazyModule(mod: LazyModule | RegularModule | undefined): mod is LazyModule {\n return !!mod && 'path' in mod;\n}\n\nfunction isLazyRoute(pathContext: readonly string[], path: string, config: Readonly<ServerViewConfig>): boolean {\n if (config.lazy !== undefined) {\n return config.lazy;\n }\n\n // \"/\" and \"/login\" are eager by default\n const rootContext = !pathContext.some(Boolean);\n const eagerDefault = rootContext && (path === '' || path === 'login');\n return !eagerDefault;\n}\n\nclass RouteFromMetaProcessor {\n readonly #manager: DependencyManager;\n readonly #views: readonly RouteMeta[];\n readonly #configs: readonly ServerViewConfig[];\n\n constructor(\n views: readonly RouteMeta[],\n viewConfigs: readonly ServerViewConfig[],\n { code: codeFile }: RuntimeFileUrls,\n ) {\n this.#views = views;\n this.#configs = viewConfigs;\n\n const codeDir = new URL('./', codeFile);\n this.#manager = new DependencyManager(new PathManager({ extension: '.js', relativeTo: codeDir }));\n }\n\n /**\n * Loads all the files from the received metadata and creates a framework-agnostic route tree.\n */\n process(): string {\n const {\n paths,\n imports: { namespace, named },\n } = this.#manager;\n const errors: string[] = [];\n\n const routes = transformTree<\n [pathContext: readonly string[], metas: readonly RouteMeta[], configs: readonly ServerViewConfig[]],\n readonly CallExpression[]\n >([[], this.#views, this.#configs], null, ([pathContext, metas, configs], next) => {\n errors.push(\n ...metas\n .map((route) => route.path)\n .filter((item, index, arr) => arr.indexOf(item) !== index)\n .map((dup) => `console.error(\"Two views share the same path: ${dup}\");`),\n );\n\n return metas.map(({ file, layout, path, children }, index) => {\n const config = configs[index]!;\n let _children: readonly CallExpression[] | undefined;\n\n if (children) {\n // Assuming that if we have `children` in the route, we also have\n // `children` in the config.\n _children = next([[...pathContext, path], children, config.children!]);\n }\n\n let module: LazyModule | RegularModule | undefined;\n\n let relativePath: string | undefined;\n let fileName: string | undefined;\n\n if (file) {\n const fileExt = fileExtensions.find((ext) => file.pathname.endsWith(ext));\n relativePath = paths.createRelativePath(file, fileExt);\n fileName = 'Page';\n } else if (layout) {\n const fileExt = fileExtensions.find((ext) => layout.pathname.endsWith(ext));\n relativePath = paths.createRelativePath(layout, fileExt);\n fileName = 'Layout';\n }\n\n if (relativePath && fileName) {\n if (isLazyRoute(pathContext, path, config)) {\n module = {\n path: relativePath,\n config,\n lazyId: named.getIdentifier('react', 'lazy') ?? named.add('react', 'lazy'),\n } satisfies LazyModule;\n } else {\n const mod = namespace.add(relativePath, fileName);\n const reactModuleType =\n named.getIdentifier(HILLA_FILE_ROUTER_TYPES, 'RouteModule') ??\n named.add(HILLA_FILE_ROUTER_TYPES, 'RouteModule', true);\n\n module = {\n componentId: ast`${mod}.default`.node as PropertyAccessExpression,\n configId: ast`(${mod} as ${reactModuleType}).config`.node as PropertyAccessExpression,\n flowLayout: config.flowLayout,\n } satisfies RegularModule;\n }\n }\n\n return this.#createRouteData(convertFSRouteSegmentToURLPatternFormat(path), module, _children);\n });\n });\n\n const agnosticRouteId =\n named.getIdentifier(HILLA_FILE_ROUTER_TYPES, 'AgnosticRoute') ??\n named.add(HILLA_FILE_ROUTER_TYPES, 'AgnosticRoute', true);\n const routeDeclaration = [\n ...this.#manager.imports.toCode(),\n ...ast`${errors.join('\\n')}\nconst routes: readonly ${agnosticRouteId}[] = ${factory.createArrayLiteralExpression(routes, true)};\nexport default routes;`.source.statements,\n ];\n\n const file = createSourceFile(routeDeclaration, 'file-routes.ts');\n return printer.printFile(file);\n }\n\n /**\n * Create an abstract route creation function call. The nested function calls\n * create a route tree.\n *\n * @param path - The path of the route.\n * @param module - The module to build route from.\n * @param children - The list of child route call expressions.\n */\n #createRouteData(\n path: string,\n module: LazyModule | RegularModule | undefined,\n children: readonly CallExpression[] | undefined,\n ): CallExpression {\n const { named } = this.#manager.imports;\n\n const createRouteId =\n named.getIdentifier(HILLA_FILE_ROUTER_RUNTIME, 'createRoute') ??\n named.add(HILLA_FILE_ROUTER_RUNTIME, 'createRoute');\n\n let component: PropertyAccessExpression | CallExpression | undefined;\n let config: PropertyAccessExpression | ObjectLiteralExpression | string | undefined;\n\n if (isLazyModule(module)) {\n const { children: _c, params: _p, ...viewConfig } = module.config;\n\n component = ast`${module.lazyId}(() => import('${module.path}')`.node as CallExpression;\n config = Object.keys(viewConfig).length > 0 ? JSON.stringify(viewConfig) : '';\n } else if (module) {\n component = module.componentId;\n config = module.flowLayout\n ? (ast`const a = %{ { ...${module.configId}, flowLayout: ${module.flowLayout.toString()} } }%`\n .node as ObjectLiteralExpression)\n : module.configId;\n }\n\n const _children = children ? factory.createArrayLiteralExpression(children, true) : '';\n\n // ```ts\n // createRoute(\"grandparent\", {...grandparentConfig, flowLayout: true}, [\n // createRoute(\"parent\", ParentPage, parentConfig, [\n // createRoute(\"child\", lazy(() => import('../ChildPage.js')), { title: 'Child Page' }),\n // ]),\n // ]),\n // ```\n return ast`${createRouteId}(\"${path}\", ${component ?? ''}, ${config ?? ''}, ${_children})`.node as CallExpression;\n }\n}\n\nexport default function createRoutesFromMeta(\n views: readonly RouteMeta[],\n viewConfigs: readonly ServerViewConfig[],\n urls: RuntimeFileUrls,\n): string {\n return new RouteFromMetaProcessor(views, viewConfigs, urls).process();\n}\n"],"version":3}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { Script } from "node:vm";
|
|
3
|
-
import ts from "typescript";
|
|
3
|
+
import ts from "@typescript/typescript6";
|
|
4
4
|
import { convertComponentNameToTitle } from "../shared/convertComponentNameToTitle.js";
|
|
5
5
|
import { transformTree } from "../shared/transformTree.js";
|
|
6
6
|
import { convertFSRouteSegmentToURLPatternFormat, extractParameterFromRouteSegment } from "./utils.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,SAAS,kCAAmC;AAC5C,SAAS,uBAAwB;AACjC,OAAO,
|
|
1
|
+
{"mappings":"AAAA,SAAS,kCAAmC;AAC5C,SAAS,uBAAwB;AACjC,OAAO,iCAAiD;AACxD,SAAS,6EAA8E;AAEvF,SAAS,iDAAkD;AAG3D,SAAS,yCAAyC,oDAAqD;;;;;;AAOvG,UAAU,QAAQA,MAA6B;AAC7C,OAAM;AAEN,MAAK,MAAM,SAAS,KAAK,aAAa,EAAE;AACtC,SAAO,QAAQ,MAAM;CACtB;AACF;;;;;;;;AASD,eAAe,eAAe,qBAAqBC,OAAmE;AACpH,QAAO,MAAM,cACX,OACA,MACA,OAAO,QAAQ,SACb,MAAM,QAAQ,IACZ,OAAO,IAAI,OAAO,EAAE,MAAM,MAAM,QAAQ,UAAU,YAAY,KAAK;EACjE,MAAM,cAAc,WAAW,MAAM,KAAK,SAAS,GAAG;AAEtD,OAAK,SAAS,QAAQ;AACpB,UAAO;IACL,OAAO,wCAAwC,KAAK;IACpD,QAAQ,iCAAiC,KAAK;IAC9C,UAAU;GACX;EACF;EAED,MAAM,aAAa,GAAG,iBACpB,QACA,MAAM,SAAS,QAAQ,QAAS,OAAO,EACvC,GAAG,aAAa,QAChB,KACD;EACD,IAAIC;EACJ,IAAI,uBAAuB;EAC3B,IAAIC;AAEJ,OAAK,MAAM,QAAQ,QAAQ,WAAW,EAAE;AACtC,OAAI,GAAG,sBAAsB,KAAK,IAAI,GAAG,aAAa,KAAK,KAAK,IAAI,KAAK,KAAK,SAAS,UAAU;AAC/F,QAAI,KAAK,eAAe,GAAG,0BAA0B,KAAK,YAAY,EAAE;KACtE,MAAM,OAAO,KAAK,YAAY,QAAQ,WAAW;KACjD,MAAM,SAAS,IAAI,QAAQ;;;;qBAItB,KAAK;;AAEV,cAAS,OAAO,kBAAkB;IACnC;GACF,WAAU,KAAK,QAAQ,WAAW,CAAC,WAAW,iBAAiB,EAAE;AAChE,2BAAuB;GACxB,WAAU,wBAAwB,GAAG,aAAa,KAAK,EAAE;AACxD,oBAAgB,KAAK;AACrB,2BAAuB;GACxB;EACF;AAED,WAAS;GACP,GAAI,UAAU,CAAE;GAChB,YAAY,QAAQ,cAAc,cAAc;EACjD;EAED,IAAIC;AAEJ,MAAI,OAAO,OAAO;AAChB,IAAC,CAAE,MAAO,GAAG;EACd,OAAM;AACL,QAAK,eAAe;AAClB,UAAM,IAAI,OACP,YAAY,OAAO,QAAQ,OAAQ,CAAC;GAExC;AAED,WAAQ,4BAA4B,cAAc;EACnD;AAED,SAAO;GACL,OAAO,wCAAwC,KAAK;GACpD,GAAG;GACH,QAAQ,iCAAiC,OAAO,SAAS,KAAK;GAC9D;GACA,UAAU,gBAAgB,SAAS,CAAE,IAAG;EACzC;CACF,EAAC,CACH,CACJ;AACF","names":["node: Node","views: readonly RouteMeta[]","config: ViewConfig | undefined","componentName: string | undefined","title: string"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/file-router/src/vite-plugin/createViewConfigJson.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { Script } from 'node:vm';\nimport ts, { type Node } from '@typescript/typescript6';\nimport { convertComponentNameToTitle } from '../shared/convertComponentNameToTitle.js';\nimport type { ServerViewConfig } from '../shared/internal.js';\nimport { transformTree } from '../shared/transformTree.js';\nimport type { ViewConfig } from '../types.js';\nimport type { RouteMeta } from './collectRoutesFromFS.js';\nimport { convertFSRouteSegmentToURLPatternFormat, extractParameterFromRouteSegment } from './utils.js';\n\n/**\n * Walks the TypeScript AST using the deep-first search algorithm.\n *\n * @param node - The node to walk.\n */\nfunction* walkAST(node: Node): Generator<Node> {\n yield node;\n\n for (const child of node.getChildren()) {\n yield* walkAST(child);\n }\n}\n\n/**\n * Creates a map of all leaf routes to their configuration. This file is used by the server to provide server-side\n * routes along with managing the client-side routes.\n *\n * @param views - The route metadata tree.\n * @returns A view configuration tree.\n */\nexport default async function createViewConfigJson(views: readonly RouteMeta[]): Promise<readonly ServerViewConfig[]> {\n return await transformTree<readonly RouteMeta[], Promise<readonly ServerViewConfig[]>>(\n views,\n null,\n async (routes, next) =>\n await Promise.all(\n routes.map(async ({ path, file, layout, children, flowLayout }) => {\n const newChildren = children ? await next(children) : undefined;\n\n if (!file && !layout) {\n return {\n route: convertFSRouteSegmentToURLPatternFormat(path),\n params: extractParameterFromRouteSegment(path),\n children: newChildren,\n } satisfies ServerViewConfig;\n }\n\n const sourceFile = ts.createSourceFile(\n 'f.ts',\n await readFile(file ?? layout!, 'utf8'),\n ts.ScriptTarget.ESNext,\n true,\n );\n let config: ViewConfig | undefined;\n let waitingForIdentifier = false;\n let componentName: string | undefined;\n\n for (const node of walkAST(sourceFile)) {\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === 'config') {\n if (node.initializer && ts.isObjectLiteralExpression(node.initializer)) {\n const code = node.initializer.getText(sourceFile);\n const script = new Script(`\n function key(x) {\n return x[0];\n }\n (${code})\n `);\n config = script.runInThisContext() as ViewConfig;\n }\n } else if (node.getText(sourceFile).startsWith('export default')) {\n waitingForIdentifier = true;\n } else if (waitingForIdentifier && ts.isIdentifier(node)) {\n componentName = node.text;\n waitingForIdentifier = false;\n }\n }\n\n config = {\n ...(config ?? {}),\n flowLayout: config?.flowLayout ?? flowLayout ?? false,\n };\n\n let title: string;\n\n if (config.title) {\n ({ title } = config);\n } else {\n if (!componentName) {\n throw new Error(\n `The file \"${String(file ?? layout!)}\" must contain a default export of a component whose name will be used as title by default`,\n );\n }\n\n title = convertComponentNameToTitle(componentName);\n }\n\n return {\n route: convertFSRouteSegmentToURLPatternFormat(path),\n ...config,\n params: extractParameterFromRouteSegment(config.route ?? path),\n title,\n children: newChildren ?? (layout ? [] : undefined),\n } satisfies ServerViewConfig;\n }),\n ),\n );\n}\n"],"version":3}
|
package/vite-plugin.js
CHANGED
|
@@ -1,7 +1,48 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
3
|
import { generateRuntimeFiles } from "./vite-plugin/generateRuntimeFiles.js";
|
|
4
|
-
const
|
|
4
|
+
const REFRESH_IGNORE_VIRTUAL_ID = "virtual:hilla-file-router-refresh-ignore";
|
|
5
|
+
const RESOLVED_REFRESH_IGNORE_VIRTUAL_ID = `\0${REFRESH_IGNORE_VIRTUAL_ID}`;
|
|
6
|
+
/**
|
|
7
|
+
* Installs the `__getReactRefreshIgnoredExports` hook of the React Refresh
|
|
8
|
+
* runtime.
|
|
9
|
+
*
|
|
10
|
+
* React Fast Refresh (the HMR mechanism of `@vitejs/plugin-react`) requires
|
|
11
|
+
* all exports of a module to be React components; otherwise, an update
|
|
12
|
+
* invalidates the module and causes a full page reload. Route files, however,
|
|
13
|
+
* are allowed to export a static `config` object in addition to the React
|
|
14
|
+
* component. To keep Fast Refresh working for them, each route file is
|
|
15
|
+
* registered in `window.__HILLA_FILE_ROUTER_VIEWS__`, and the hook excludes
|
|
16
|
+
* the `config` export of registered modules from the refresh boundary
|
|
17
|
+
* validation. Updates to the `config` contents are propagated separately
|
|
18
|
+
* through the regenerated `file-routes.ts` module.
|
|
19
|
+
*
|
|
20
|
+
* The hook is delivered as a virtual module imported by every route file, so
|
|
21
|
+
* Vite evaluates it once per application boot.
|
|
22
|
+
*/
|
|
23
|
+
const REFRESH_IGNORE_INSTALLER = `if (typeof window !== 'undefined') {
|
|
24
|
+
const previousHook = window.__getReactRefreshIgnoredExports;
|
|
25
|
+
window.__getReactRefreshIgnoredExports = ({ id }) => {
|
|
26
|
+
const ignoredExports = previousHook?.({ id }) ?? [];
|
|
27
|
+
return window.__HILLA_FILE_ROUTER_VIEWS__?.has(id) ? [...ignoredExports, 'config'] : ignoredExports;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
/**
|
|
32
|
+
* Creates a code snippet that registers a route file for the exclusion of its
|
|
33
|
+
* `config` export from the React Fast Refresh boundary validation.
|
|
34
|
+
*
|
|
35
|
+
* @param id - The module id of the route file, as seen by the Vite transform
|
|
36
|
+
* pipeline. The React Refresh runtime receives the same id at runtime.
|
|
37
|
+
*/
|
|
38
|
+
function createReactRefreshConfigExclusion(id) {
|
|
39
|
+
return `
|
|
40
|
+
;import ${JSON.stringify(REFRESH_IGNORE_VIRTUAL_ID)};
|
|
41
|
+
if (typeof window !== 'undefined') {
|
|
42
|
+
(window.__HILLA_FILE_ROUTER_VIEWS__ ??= new Set()).add(${JSON.stringify(id)});
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
5
46
|
/**
|
|
6
47
|
* A Vite plugin that generates a router from the files in the specific directory.
|
|
7
48
|
*
|
|
@@ -47,6 +88,12 @@ export default function vitePluginFileSystemRouter({ viewsDir = "frontend/views/
|
|
|
47
88
|
async buildStart() {
|
|
48
89
|
await _generateRuntimeFiles();
|
|
49
90
|
},
|
|
91
|
+
resolveId(source) {
|
|
92
|
+
return source === REFRESH_IGNORE_VIRTUAL_ID ? RESOLVED_REFRESH_IGNORE_VIRTUAL_ID : undefined;
|
|
93
|
+
},
|
|
94
|
+
load(id) {
|
|
95
|
+
return id === RESOLVED_REFRESH_IGNORE_VIRTUAL_ID ? REFRESH_IGNORE_INSTALLER : undefined;
|
|
96
|
+
},
|
|
50
97
|
async hotUpdate({ file, modules }) {
|
|
51
98
|
const fileUrlString = String(pathToFileURL(file));
|
|
52
99
|
if (fileUrlString === String(runtimeUrls.json)) {
|
|
@@ -83,15 +130,17 @@ export default function vitePluginFileSystemRouter({ viewsDir = "frontend/views/
|
|
|
83
130
|
let modifiedCode = code;
|
|
84
131
|
if (id.startsWith(_viewsDirPosix) && !basename(id).startsWith("_")) {
|
|
85
132
|
if (isDevMode) {
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
88
|
-
modifiedCode
|
|
133
|
+
const [path] = id.split("?");
|
|
134
|
+
if (extensions.some((ext) => path.endsWith(ext))) {
|
|
135
|
+
modifiedCode += createReactRefreshConfigExclusion(id);
|
|
89
136
|
}
|
|
90
137
|
} else {
|
|
91
|
-
const functionNames = /export\s+default\s+(?:function\s+)?(\w+)/u.exec(modifiedCode);
|
|
138
|
+
const functionNames = /export\s+default\s+(?:(?:function|class)\s+)?(\w+)/u.exec(modifiedCode);
|
|
92
139
|
if (functionNames?.length) {
|
|
93
140
|
const [, functionName] = functionNames;
|
|
94
|
-
|
|
141
|
+
if (functionName !== "function" && functionName !== "class") {
|
|
142
|
+
modifiedCode += `Object.defineProperty(${functionName}, 'name', { value: '${functionName}' });\n`;
|
|
143
|
+
}
|
|
95
144
|
}
|
|
96
145
|
}
|
|
97
146
|
return { code: modifiedCode };
|
package/vite-plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","names":["_viewsDir: URL","_outDir: URL","_logger: Logger","runtimeUrls: RuntimeFileUrls","_generateRuntimeFiles: () => Promise<boolean>","_viewsDirPosix: string","e: unknown"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/file-router/src/vite-plugin.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { TransformResult } from 'rollup';\nimport type { EnvironmentModuleNode, HotUpdateOptions, Logger, Plugin } from 'vite';\nimport { generateRuntimeFiles, type RuntimeFileUrls } from './vite-plugin/generateRuntimeFiles.js';\n\nconst INJECTION =\n \"if (Object.keys(nextExports).length === 2 && 'default' in nextExports && 'config' in nextExports) {nextExports = { ...nextExports, config: currentExports.config };}\";\n\n/**\n * The options for the Vite file-based router plugin.\n */\nexport type PluginOptions = Readonly<{\n /**\n * The base directory for the router. The folders and files in this directory\n * will be used as route paths.\n *\n * @defaultValue `frontend/views`\n */\n viewsDir?: URL | string;\n /**\n * The directory where the generated view file will be stored.\n *\n * @defaultValue `frontend/generated`\n */\n generatedDir?: URL | string;\n /**\n * The list of extensions that will be collected as routes of the file-based\n * router.\n *\n * @defaultValue `['.tsx', '.jsx']`\n */\n extensions?: readonly string[];\n /**\n * The flag to indicate whether the plugin is running in development mode.\n *\n * @defaultValue `false`\n */\n isDevMode?: boolean;\n /**\n * The flag to indicate whether to output debug information\n *\n * @defaultValue `false`\n */\n debug?: boolean;\n}>;\n\n/**\n * A Vite plugin that generates a router from the files in the specific directory.\n *\n * @param options - The plugin options.\n * @returns A Vite plugin.\n */\nexport default function vitePluginFileSystemRouter({\n viewsDir = 'frontend/views/',\n generatedDir = 'frontend/generated/',\n extensions = ['.tsx', '.jsx'],\n isDevMode = false,\n debug = false,\n}: PluginOptions = {}): Plugin {\n let _viewsDir: URL;\n let _outDir: URL;\n let _logger: Logger;\n let runtimeUrls: RuntimeFileUrls;\n let _generateRuntimeFiles: () => Promise<boolean>;\n let _viewsDirPosix: string;\n\n return {\n name: 'vite-plugin-file-router',\n enforce: 'pre',\n configResolved({ logger, root, build: { outDir } }) {\n const _root = pathToFileURL(root);\n const _generatedDir = new URL(generatedDir, _root);\n\n _viewsDir = new URL(viewsDir, _root);\n _viewsDirPosix = fileURLToPath(_viewsDir).replaceAll('\\\\', '/');\n _outDir = pathToFileURL(outDir);\n _logger = logger;\n\n if (debug) {\n _logger.info(`The directory of route files: ${String(_viewsDir)}`);\n _logger.info(`The directory of generated files: ${String(_generatedDir)}`);\n _logger.info(`The output directory: ${String(_outDir)}`);\n }\n runtimeUrls = {\n json: new URL('file-routes.json', isDevMode ? _generatedDir : _outDir),\n code: new URL('file-routes.ts', _generatedDir),\n layouts: new URL('layouts.json', _generatedDir),\n };\n _generateRuntimeFiles = async (): Promise<boolean> => {\n try {\n return await generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger, debug);\n } catch (e: unknown) {\n _logger.error(String(e));\n return true;\n }\n };\n },\n async buildStart() {\n await _generateRuntimeFiles();\n },\n async hotUpdate({ file, modules }: HotUpdateOptions): Promise<void | EnvironmentModuleNode[]> {\n const fileUrlString = String(pathToFileURL(file));\n\n if (fileUrlString === String(runtimeUrls.json)) {\n // Reload file routes JSON with a custom HMR event.\n this.environment.hot.send({ type: 'custom', event: 'fs-route-update' });\n return [];\n }\n\n if (fileUrlString === String(runtimeUrls.code)) {\n // Skip HMR for file routes from Vite builtin file change listener,\n // as we have our own HMR handling below.\n return [];\n }\n\n if (!file.startsWith(_viewsDirPosix)) {\n // Outside views folder, only changes to layouts file should trigger\n // files generation.\n if (fileUrlString !== String(runtimeUrls.layouts)) {\n return;\n }\n }\n\n // Check and update file routes if needed.\n if (await _generateRuntimeFiles()) {\n // The \"file-routes.ts\" file was changed at this point, so it should\n // be considered within the HMR update that caused the routes update.\n\n const mg = this.environment.moduleGraph;\n const fileRoutesModules = mg.getModulesByFile(fileURLToPath(runtimeUrls.code).replaceAll('\\\\', '/'))!;\n\n // Make eager update for file routes in Vite module graph\n // for consistency with the generated file contents.\n await Promise.all(\n Array.from(fileRoutesModules, async (fileRouteModule) => {\n mg.invalidateModule(fileRouteModule);\n await this.environment.fetchModule(fileRouteModule.id!, undefined, { cached: false });\n }),\n );\n\n const neverImported = modules.every((module) => module.importers.size === 0);\n if (neverImported) {\n // The current file is a not imported anywhere, however it caused\n // \"file-routes.ts\" update: possibly a route for this file\n // was removed, or we're processing \"layouts.json\".\n\n // Default Vite HMR behavior for not imported modules is a full page\n // reload. However, in this case, we could avoid it and only do HMR\n // for the file routes instead.\n return [...fileRoutesModules];\n }\n\n // Add \"file-routes.ts\" to current HMR module update list to reload\n // both at the same time.\n return [...fileRoutesModules, ...modules];\n }\n\n return undefined;\n },\n transform(code, id): Promise<TransformResult> | TransformResult {\n let modifiedCode = code;\n if (id.startsWith(_viewsDirPosix) && !basename(id).startsWith('_')) {\n if (isDevMode) {\n // To enable HMR for route files with exported configurations, we need\n // to address a limitation in `react-refresh`. This library requires\n // strict equality (`===`) for non-component exports. However, the\n // dynamic nature of HMR makes maintaining this equality between object\n // literals challenging.\n //\n // To work around this, we implement a strategy that preserves the\n // reference to the original configuration object (`currentExports.config`),\n // replacing any newly created configuration objects (`nextExports.config`)\n // with it. This ensures that the HMR mechanism perceives the\n // configuration as unchanged.\n const injectionPattern = /import\\.meta\\.hot\\.accept[\\s\\S]+if\\s\\(!nextExports\\)\\s+return;/gu;\n if (injectionPattern.test(modifiedCode)) {\n modifiedCode = `${modifiedCode.substring(0, injectionPattern.lastIndex)}${INJECTION}${modifiedCode.substring(\n injectionPattern.lastIndex,\n )}`;\n }\n } else {\n // In production mode, the function name is assigned as name to the function itself to avoid minification\n const functionNames = /export\\s+default\\s+(?:function\\s+)?(\\w+)/u.exec(modifiedCode);\n\n if (functionNames?.length) {\n const [, functionName] = functionNames;\n modifiedCode += `Object.defineProperty(${functionName}, 'name', { value: '${functionName}' });\\n`;\n }\n }\n\n return {\n code: modifiedCode,\n };\n }\n\n return undefined;\n },\n };\n}\n"],"version":3}
|
|
1
|
+
{"mappings":"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","names":["id: string","_viewsDir: URL","_outDir: URL","_logger: Logger","runtimeUrls: RuntimeFileUrls","_generateRuntimeFiles: () => Promise<boolean>","_viewsDirPosix: string","e: unknown"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/file-router/src/vite-plugin.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { TransformResult } from 'rollup';\nimport type { EnvironmentModuleNode, HotUpdateOptions, Logger, Plugin } from 'vite';\nimport { generateRuntimeFiles, type RuntimeFileUrls } from './vite-plugin/generateRuntimeFiles.js';\n\nconst REFRESH_IGNORE_VIRTUAL_ID = 'virtual:hilla-file-router-refresh-ignore';\nconst RESOLVED_REFRESH_IGNORE_VIRTUAL_ID = `\\0${REFRESH_IGNORE_VIRTUAL_ID}`;\n\n/**\n * Installs the `__getReactRefreshIgnoredExports` hook of the React Refresh\n * runtime.\n *\n * React Fast Refresh (the HMR mechanism of `@vitejs/plugin-react`) requires\n * all exports of a module to be React components; otherwise, an update\n * invalidates the module and causes a full page reload. Route files, however,\n * are allowed to export a static `config` object in addition to the React\n * component. To keep Fast Refresh working for them, each route file is\n * registered in `window.__HILLA_FILE_ROUTER_VIEWS__`, and the hook excludes\n * the `config` export of registered modules from the refresh boundary\n * validation. Updates to the `config` contents are propagated separately\n * through the regenerated `file-routes.ts` module.\n *\n * The hook is delivered as a virtual module imported by every route file, so\n * Vite evaluates it once per application boot.\n */\nconst REFRESH_IGNORE_INSTALLER = `if (typeof window !== 'undefined') {\n const previousHook = window.__getReactRefreshIgnoredExports;\n window.__getReactRefreshIgnoredExports = ({ id }) => {\n const ignoredExports = previousHook?.({ id }) ?? [];\n return window.__HILLA_FILE_ROUTER_VIEWS__?.has(id) ? [...ignoredExports, 'config'] : ignoredExports;\n };\n}\n`;\n\n/**\n * Creates a code snippet that registers a route file for the exclusion of its\n * `config` export from the React Fast Refresh boundary validation.\n *\n * @param id - The module id of the route file, as seen by the Vite transform\n * pipeline. The React Refresh runtime receives the same id at runtime.\n */\nfunction createReactRefreshConfigExclusion(id: string): string {\n return `\n;import ${JSON.stringify(REFRESH_IGNORE_VIRTUAL_ID)};\nif (typeof window !== 'undefined') {\n (window.__HILLA_FILE_ROUTER_VIEWS__ ??= new Set()).add(${JSON.stringify(id)});\n}\n`;\n}\n\n/**\n * The options for the Vite file-based router plugin.\n */\nexport type PluginOptions = Readonly<{\n /**\n * The base directory for the router. The folders and files in this directory\n * will be used as route paths.\n *\n * @defaultValue `frontend/views`\n */\n viewsDir?: URL | string;\n /**\n * The directory where the generated view file will be stored.\n *\n * @defaultValue `frontend/generated`\n */\n generatedDir?: URL | string;\n /**\n * The list of extensions that will be collected as routes of the file-based\n * router.\n *\n * @defaultValue `['.tsx', '.jsx']`\n */\n extensions?: readonly string[];\n /**\n * The flag to indicate whether the plugin is running in development mode.\n *\n * @defaultValue `false`\n */\n isDevMode?: boolean;\n /**\n * The flag to indicate whether to output debug information\n *\n * @defaultValue `false`\n */\n debug?: boolean;\n}>;\n\n/**\n * A Vite plugin that generates a router from the files in the specific directory.\n *\n * @param options - The plugin options.\n * @returns A Vite plugin.\n */\nexport default function vitePluginFileSystemRouter({\n viewsDir = 'frontend/views/',\n generatedDir = 'frontend/generated/',\n extensions = ['.tsx', '.jsx'],\n isDevMode = false,\n debug = false,\n}: PluginOptions = {}): Plugin {\n let _viewsDir: URL;\n let _outDir: URL;\n let _logger: Logger;\n let runtimeUrls: RuntimeFileUrls;\n let _generateRuntimeFiles: () => Promise<boolean>;\n let _viewsDirPosix: string;\n\n return {\n name: 'vite-plugin-file-router',\n enforce: 'pre',\n configResolved({ logger, root, build: { outDir } }) {\n const _root = pathToFileURL(root);\n const _generatedDir = new URL(generatedDir, _root);\n\n _viewsDir = new URL(viewsDir, _root);\n _viewsDirPosix = fileURLToPath(_viewsDir).replaceAll('\\\\', '/');\n _outDir = pathToFileURL(outDir);\n _logger = logger;\n\n if (debug) {\n _logger.info(`The directory of route files: ${String(_viewsDir)}`);\n _logger.info(`The directory of generated files: ${String(_generatedDir)}`);\n _logger.info(`The output directory: ${String(_outDir)}`);\n }\n runtimeUrls = {\n json: new URL('file-routes.json', isDevMode ? _generatedDir : _outDir),\n code: new URL('file-routes.ts', _generatedDir),\n layouts: new URL('layouts.json', _generatedDir),\n };\n _generateRuntimeFiles = async (): Promise<boolean> => {\n try {\n return await generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger, debug);\n } catch (e: unknown) {\n _logger.error(String(e));\n return true;\n }\n };\n },\n async buildStart() {\n await _generateRuntimeFiles();\n },\n resolveId(source) {\n return source === REFRESH_IGNORE_VIRTUAL_ID ? RESOLVED_REFRESH_IGNORE_VIRTUAL_ID : undefined;\n },\n load(id) {\n return id === RESOLVED_REFRESH_IGNORE_VIRTUAL_ID ? REFRESH_IGNORE_INSTALLER : undefined;\n },\n async hotUpdate({ file, modules }: HotUpdateOptions): Promise<void | EnvironmentModuleNode[]> {\n const fileUrlString = String(pathToFileURL(file));\n\n if (fileUrlString === String(runtimeUrls.json)) {\n // Reload file routes JSON with a custom HMR event.\n this.environment.hot.send({ type: 'custom', event: 'fs-route-update' });\n return [];\n }\n\n if (fileUrlString === String(runtimeUrls.code)) {\n // Skip HMR for file routes from Vite builtin file change listener,\n // as we have our own HMR handling below.\n return [];\n }\n\n if (!file.startsWith(_viewsDirPosix)) {\n // Outside views folder, only changes to layouts file should trigger\n // files generation.\n if (fileUrlString !== String(runtimeUrls.layouts)) {\n return;\n }\n }\n\n // Check and update file routes if needed.\n if (await _generateRuntimeFiles()) {\n // The \"file-routes.ts\" file was changed at this point, so it should\n // be considered within the HMR update that caused the routes update.\n\n const mg = this.environment.moduleGraph;\n const fileRoutesModules = mg.getModulesByFile(fileURLToPath(runtimeUrls.code).replaceAll('\\\\', '/'))!;\n\n // Make eager update for file routes in Vite module graph\n // for consistency with the generated file contents.\n await Promise.all(\n Array.from(fileRoutesModules, async (fileRouteModule) => {\n mg.invalidateModule(fileRouteModule);\n await this.environment.fetchModule(fileRouteModule.id!, undefined, { cached: false });\n }),\n );\n\n const neverImported = modules.every((module) => module.importers.size === 0);\n if (neverImported) {\n // The current file is a not imported anywhere, however it caused\n // \"file-routes.ts\" update: possibly a route for this file\n // was removed, or we're processing \"layouts.json\".\n\n // Default Vite HMR behavior for not imported modules is a full page\n // reload. However, in this case, we could avoid it and only do HMR\n // for the file routes instead.\n return [...fileRoutesModules];\n }\n\n // Add \"file-routes.ts\" to current HMR module update list to reload\n // both at the same time.\n return [...fileRoutesModules, ...modules];\n }\n\n return undefined;\n },\n transform(code, id): Promise<TransformResult> | TransformResult {\n let modifiedCode = code;\n if (id.startsWith(_viewsDirPosix) && !basename(id).startsWith('_')) {\n if (isDevMode) {\n const [path] = id.split('?');\n if (extensions.some((ext) => path!.endsWith(ext))) {\n modifiedCode += createReactRefreshConfigExclusion(id);\n }\n } else {\n // In production mode, the function name is assigned as name to the function itself to avoid minification\n const functionNames = /export\\s+default\\s+(?:(?:function|class)\\s+)?(\\w+)/u.exec(modifiedCode);\n\n if (functionNames?.length) {\n const [, functionName] = functionNames;\n // For anonymous default exports, the keyword itself is captured\n // instead of a name, so there is nothing to preserve.\n if (functionName !== 'function' && functionName !== 'class') {\n modifiedCode += `Object.defineProperty(${functionName}, 'name', { value: '${functionName}' });\\n`;\n }\n }\n }\n\n return {\n code: modifiedCode,\n };\n }\n\n return undefined;\n },\n };\n}\n"],"version":3}
|