@vaadin/hilla-file-router 24.7.3 → 24.7.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/hilla-file-router",
3
- "version": "24.7.3",
3
+ "version": "24.7.4",
4
4
  "description": "Hilla file-based router",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -61,9 +61,9 @@
61
61
  "react-router": "7"
62
62
  },
63
63
  "dependencies": {
64
- "@vaadin/hilla-generator-utils": "24.7.3",
65
- "@vaadin/hilla-react-auth": "24.7.3",
66
- "@vaadin/hilla-react-signals": "24.7.3",
64
+ "@vaadin/hilla-generator-utils": "24.7.4",
65
+ "@vaadin/hilla-react-auth": "24.7.4",
66
+ "@vaadin/hilla-react-signals": "24.7.4",
67
67
  "typescript": "5.7.3"
68
68
  }
69
69
  }
@@ -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: "24.7.3"
22
+ version: "24.7.4"
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,kBAAuC;AAGrD,EAAC,CAAC,SAAS,YAAa,OAAO,WAAW,CAAE,MAAM;AAClD,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;CACrB,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/1af72d8adc613024/hilla/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(): readonly MenuItem[] {\n // @ts-expect-error: esbuild injection\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n ((feature, vaadinObj = (window.Vaadin ??= {})) => {\n vaadinObj.registrations ??= [];\n vaadinObj.registrations.push({\n is: feature ? `@vaadin/hilla-file-router/${feature}` : '@vaadin/hilla-file-router',\n version: '24.7.3',\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 }))\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
+ {"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,kBAAuC;AAGrD,EAAC,CAAC,SAAS,YAAa,OAAO,WAAW,CAAE,MAAM;AAClD,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;CACrB,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/1af72d8adc613024/hilla/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(): readonly MenuItem[] {\n // @ts-expect-error: esbuild injection\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n ((feature, vaadinObj = (window.Vaadin ??= {})) => {\n vaadinObj.registrations ??= [];\n vaadinObj.registrations.push({\n is: feature ? `@vaadin/hilla-file-router/${feature}` : '@vaadin/hilla-file-router',\n version: '24.7.4',\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 }))\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}
@@ -54,9 +54,7 @@ export default async function createViewConfigJson(views) {
54
54
  waitingForIdentifier = false;
55
55
  }
56
56
  }
57
- if (config === undefined) {
58
- config = { flowLayout: flowLayout ?? false };
59
- }
57
+ config ??= { flowLayout: flowLayout ?? false };
60
58
  let title;
61
59
  if (config.title) {
62
60
  ({title} = config);
@@ -1 +1 @@
1
- {"mappings":"AAAA,SAAS,kCAAmC;AAC5C,SAAS,uBAAwB;AACjC,OAAO,oBAAoC;AAC3C,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;;;;;;;AAQD,eAAe,eAAe,qBAAqBC,OAA8C;CAC/F,MAAM,MAAM,MAAM,cAChB,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,GAAG,KAAK;AACnC,cAAS,OAAO,kBAAkB;AAClC,SAAI,OAAO,eAAe,WAAW;MACnC,MAAM,OAAO,KAAK,MAAM,KAAK,UAAU,OAAO,CAAC;AAE/C,WAAK,aAAa,cAAc;AAChC,eAAS;KACV;IACF;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,MAAI,WAAW,WAAW;AACxB,YAAS,EAAE,YAAY,cAAc,MAAO;EAC7C;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;AAED,QAAO,KAAK,UAAU,KAAK,WAAW,EAAE;AACzC","names":["node: Node","views: readonly RouteMeta[]","config: ViewConfig | undefined","componentName: string | undefined","title: string"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/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';\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 */\nexport default async function createViewConfigJson(views: readonly RouteMeta[]): Promise<string> {\n const res = 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(`(${code})`);\n config = script.runInThisContext() as ViewConfig;\n if (config.flowLayout === undefined) {\n const copy = JSON.parse(JSON.stringify(config));\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n copy.flowLayout = flowLayout ?? false;\n config = copy;\n }\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 if (config === undefined) {\n 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 return JSON.stringify(res, undefined, 2);\n}\n"],"version":3}
1
+ {"mappings":"AAAA,SAAS,kCAAmC;AAC5C,SAAS,uBAAwB;AACjC,OAAO,oBAAoC;AAC3C,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;;;;;;;AAQD,eAAe,eAAe,qBAAqBC,OAA8C;CAC/F,MAAM,MAAM,MAAM,cAChB,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,GAAG,KAAK;AACnC,cAAS,OAAO,kBAAkB;AAClC,SAAI,OAAO,eAAe,WAAW;MACnC,MAAM,OAAO,KAAK,MAAM,KAAK,UAAU,OAAO,CAAC;AAE/C,WAAK,aAAa,cAAc;AAChC,eAAS;KACV;IACF;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,aAAW,EAAE,YAAY,cAAc,MAAO;EAE9C,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;AAED,QAAO,KAAK,UAAU,KAAK,WAAW,EAAE;AACzC","names":["node: Node","views: readonly RouteMeta[]","config: ViewConfig | undefined","componentName: string | undefined","title: string"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/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';\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 */\nexport default async function createViewConfigJson(views: readonly RouteMeta[]): Promise<string> {\n const res = 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(`(${code})`);\n config = script.runInThisContext() as ViewConfig;\n if (config.flowLayout === undefined) {\n const copy = JSON.parse(JSON.stringify(config));\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n copy.flowLayout = flowLayout ?? false;\n config = copy;\n }\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 ??= { flowLayout: flowLayout ?? false };\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 return JSON.stringify(res, undefined, 2);\n}\n"],"version":3}