@vaadin/hilla-file-router 24.4.0-alpha21 → 24.4.0-alpha23

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.
Files changed (44) hide show
  1. package/package.json +3 -3
  2. package/runtime/RouterConfigurationBuilder.d.ts +14 -4
  3. package/runtime/RouterConfigurationBuilder.d.ts.map +1 -1
  4. package/runtime/RouterConfigurationBuilder.js +86 -55
  5. package/runtime/RouterConfigurationBuilder.js.map +2 -2
  6. package/runtime/createMenuItems.d.ts +3 -1
  7. package/runtime/createMenuItems.d.ts.map +1 -1
  8. package/runtime/createMenuItems.js +7 -2
  9. package/runtime/createMenuItems.js.map +2 -2
  10. package/runtime.d.ts +0 -1
  11. package/runtime.d.ts.map +1 -1
  12. package/runtime.js +0 -1
  13. package/runtime.js.map +2 -2
  14. package/shared/convertComponentNameToTitle.d.ts +1 -1
  15. package/shared/convertComponentNameToTitle.d.ts.map +1 -1
  16. package/shared/convertComponentNameToTitle.js +4 -7
  17. package/shared/convertComponentNameToTitle.js.map +2 -2
  18. package/shared/transformTree.d.ts +1 -11
  19. package/shared/transformTree.d.ts.map +1 -1
  20. package/shared/transformTree.js +3 -15
  21. package/shared/transformTree.js.map +2 -2
  22. package/types.d.ts +3 -3
  23. package/vite-plugin/collectRoutesFromFS.d.ts +3 -3
  24. package/vite-plugin/collectRoutesFromFS.d.ts.map +1 -1
  25. package/vite-plugin/collectRoutesFromFS.js +46 -34
  26. package/vite-plugin/collectRoutesFromFS.js.map +2 -2
  27. package/vite-plugin/createRoutesFromMeta.d.ts +1 -1
  28. package/vite-plugin/createRoutesFromMeta.d.ts.map +1 -1
  29. package/vite-plugin/createRoutesFromMeta.js +30 -25
  30. package/vite-plugin/createRoutesFromMeta.js.map +2 -2
  31. package/vite-plugin/createViewConfigJson.d.ts +1 -1
  32. package/vite-plugin/createViewConfigJson.d.ts.map +1 -1
  33. package/vite-plugin/createViewConfigJson.js +51 -38
  34. package/vite-plugin/createViewConfigJson.js.map +2 -2
  35. package/vite-plugin/generateRuntimeFiles.d.ts.map +1 -1
  36. package/vite-plugin/generateRuntimeFiles.js +0 -5
  37. package/vite-plugin/generateRuntimeFiles.js.map +2 -2
  38. package/vite-plugin.d.ts.map +1 -1
  39. package/vite-plugin.js +17 -6
  40. package/vite-plugin.js.map +2 -2
  41. package/runtime/toReactRouter.d.ts +0 -11
  42. package/runtime/toReactRouter.d.ts.map +0 -1
  43. package/runtime/toReactRouter.js +0 -31
  44. package/runtime/toReactRouter.js.map +0 -7
@@ -21,38 +21,53 @@ async function collectRoutesFromFS(dir, { extensions, logger, parent = dir }) {
21
21
  let children = [];
22
22
  let layout;
23
23
  for await (const d of await opendir(dir)) {
24
- if (d.isDirectory() && !d.name.startsWith("_")) {
25
- children.push(await collectRoutesFromFS(new URL(`${d.name}/`, dir), { extensions, logger, parent: dir }));
26
- } else if (d.isFile() && extensions.includes(extname(d.name))) {
27
- const file = new URL(d.name, dir);
28
- const url = await checkFile(file, logger);
29
- if (url === void 0) {
30
- continue;
24
+ if (d.name.startsWith("_")) {
25
+ continue;
26
+ }
27
+ if (d.isDirectory()) {
28
+ const directoryRoutes = await collectRoutesFromFS(new URL(`${d.name}/`, dir), {
29
+ extensions,
30
+ logger,
31
+ parent: dir
32
+ });
33
+ if (directoryRoutes.length === 1 && directoryRoutes[0].layout) {
34
+ const [layoutRoute] = directoryRoutes;
35
+ children.push(layoutRoute);
36
+ } else if (directoryRoutes.length > 0) {
37
+ children.push({ path: d.name, children: directoryRoutes });
31
38
  }
32
- const name = basename(d.name, extname(d.name));
33
- if (name.startsWith("@")) {
34
- if (name === "@layout") {
35
- layout = file;
36
- } else if (name === "@index") {
37
- children.push({
38
- path: "",
39
- file
40
- });
41
- } else {
42
- throw new Error(
43
- 'Symbol "@" is reserved for special directories and files; only "@layout" and "@index" are allowed'
44
- );
45
- }
46
- } else if (!name.startsWith("_")) {
47
- children.push({
48
- path: name,
49
- file
50
- });
39
+ continue;
40
+ }
41
+ if (!extensions.includes(extname(d.name))) {
42
+ if (warningFor.includes(extname(d.name))) {
43
+ logger.warn(
44
+ `File System based router expects only JSX files in 'Frontend/views/' directory, such as '*.tsx' and '*.jsx'. The file '${d.name}' will be ignored by router, as it doesn't match this convention. Please consider storing it in another directory.`
45
+ );
51
46
  }
52
- } else if (d.isFile() && !d.name.startsWith("_") && warningFor.includes(extname(d.name))) {
53
- logger.warn(
54
- `File System based router expects only JSX files in 'Frontend/views/' directory, such as '*.tsx' and '*.jsx'. The file '${d.name}' will be ignored by router, as it doesn't match this convention. Please consider storing it in another directory.`
47
+ continue;
48
+ }
49
+ const file = new URL(d.name, dir);
50
+ const url = await checkFile(file, logger);
51
+ if (url === void 0) {
52
+ continue;
53
+ }
54
+ const name = basename(d.name, extname(d.name));
55
+ if (name === "@layout") {
56
+ layout = file;
57
+ } else if (name === "@index") {
58
+ children.push({
59
+ path: "",
60
+ file
61
+ });
62
+ } else if (name.startsWith("@")) {
63
+ throw new Error(
64
+ 'Symbol "@" is reserved for special directories and files; only "@layout" and "@index" are allowed'
55
65
  );
66
+ } else {
67
+ children.push({
68
+ path: name,
69
+ file
70
+ });
56
71
  }
57
72
  }
58
73
  [children, layout] = await Promise.all([
@@ -65,11 +80,8 @@ async function collectRoutesFromFS(dir, { extensions, logger, parent = dir }) {
65
80
  ),
66
81
  checkFile(layout, logger)
67
82
  ]);
68
- return {
69
- path,
70
- layout,
71
- children: children.sort(({ path: a }, { path: b }) => collator.compare(cleanUp(a), cleanUp(b)))
72
- };
83
+ children = children.sort(({ path: a }, { path: b }) => collator.compare(cleanUp(a), cleanUp(b)));
84
+ return layout ? [{ path, layout, children }] : children;
73
85
  }
74
86
  export {
75
87
  collectRoutesFromFS as default
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/vite-plugin/collectRoutesFromFS.ts"],
4
- "sourcesContent": ["import { opendir, readFile } from 'node:fs/promises';\nimport { basename, extname, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Logger } from 'vite';\nimport { cleanUp } from './utils.js';\n\nexport type RouteMeta = Readonly<{\n path: string;\n file?: URL;\n layout?: URL;\n children?: RouteMeta[];\n}>;\n\n/**\n * Routes collector options.\n */\nexport type CollectRoutesOptions = Readonly<{\n /**\n * The list of extensions for files that will be collected as routes.\n */\n extensions: readonly string[];\n /**\n * The parent directory of the current directory. This is a\n * nested parameter used inside the function only.\n */\n parent?: URL;\n /**\n * The Vite logger instance.\n */\n logger: Logger;\n}>;\n\nasync function checkFile(url: URL | undefined, logger: Logger): Promise<URL | undefined> {\n if (url) {\n const contents = await readFile(url, 'utf-8');\n if (contents.trim() === '') {\n return undefined;\n } else if (!contents.includes('export default')) {\n logger.error(`The file \"${String(url)}\" should contain a default export of a component`);\n return undefined;\n }\n }\n\n return url;\n}\n\nconst collator = new Intl.Collator('en-US');\n\nconst warningFor = ['.ts', '.js'];\n\n/**\n * Collect route metadata from the file system and build a route tree.\n *\n * It accepts files that start with `@` as special files.\n * - `@layout` contains a component that wraps the child components.\n * - `@index` contains a component that will be used as the index page of the directory.\n *\n * It accepts files that start with `_` as private files. They will be ignored.\n *\n * @param dir - The directory to collect routes from.\n * @param options - The options object.\n *\n * @returns The route metadata tree.\n */\nexport default async function collectRoutesFromFS(\n dir: URL,\n { extensions, logger, parent = dir }: CollectRoutesOptions,\n): Promise<RouteMeta> {\n const path = relative(fileURLToPath(parent), fileURLToPath(dir));\n let children: RouteMeta[] = [];\n let layout: URL | undefined;\n\n for await (const d of await opendir(dir)) {\n if (d.isDirectory() && !d.name.startsWith('_')) {\n children.push(await collectRoutesFromFS(new URL(`${d.name}/`, dir), { extensions, logger, parent: dir }));\n } else if (d.isFile() && extensions.includes(extname(d.name))) {\n const file = new URL(d.name, dir);\n const url = await checkFile(file, logger);\n if (url === undefined) {\n continue;\n }\n const name = basename(d.name, extname(d.name));\n\n if (name.startsWith('@')) {\n if (name === '@layout') {\n layout = file;\n } else if (name === '@index') {\n children.push({\n path: '',\n file,\n });\n } else {\n throw new Error(\n 'Symbol \"@\" is reserved for special directories and files; only \"@layout\" and \"@index\" are allowed',\n );\n }\n } else if (!name.startsWith('_')) {\n children.push({\n path: name,\n file,\n });\n }\n } else if (d.isFile() && !d.name.startsWith('_') && warningFor.includes(extname(d.name))) {\n logger.warn(\n `File System based router expects only JSX files in 'Frontend/views/' directory, such as '*.tsx' and '*.jsx'. The file '${d.name}' will be ignored by router, as it doesn't match this convention. Please consider storing it in another directory.`,\n );\n }\n }\n\n [children, layout] = await Promise.all([\n Promise.all(\n children.map(async (child) => ({\n ...child,\n file: child.file,\n layout: await checkFile(child.layout, logger),\n })),\n ),\n checkFile(layout, logger),\n ]);\n\n return {\n path,\n layout,\n children: children.sort(({ path: a }, { path: b }) => collator.compare(cleanUp(a), cleanUp(b))),\n };\n}\n"],
5
- "mappings": "AAAA,SAAS,SAAS,gBAAgB;AAClC,SAAS,UAAU,SAAS,gBAAgB;AAC5C,SAAS,qBAAqB;AAE9B,SAAS,eAAe;AA4BxB,eAAe,UAAU,KAAsB,QAA0C;AACvF,MAAI,KAAK;AACP,UAAM,WAAW,MAAM,SAAS,KAAK,OAAO;AAC5C,QAAI,SAAS,KAAK,MAAM,IAAI;AAC1B,aAAO;AAAA,IACT,WAAW,CAAC,SAAS,SAAS,gBAAgB,GAAG;AAC/C,aAAO,MAAM,aAAa,OAAO,GAAG,CAAC,kDAAkD;AACvF,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,MAAM,WAAW,IAAI,KAAK,SAAS,OAAO;AAE1C,MAAM,aAAa,CAAC,OAAO,KAAK;AAgBhC,eAAO,oBACL,KACA,EAAE,YAAY,QAAQ,SAAS,IAAI,GACf;AACpB,QAAM,OAAO,SAAS,cAAc,MAAM,GAAG,cAAc,GAAG,CAAC;AAC/D,MAAI,WAAwB,CAAC;AAC7B,MAAI;AAEJ,mBAAiB,KAAK,MAAM,QAAQ,GAAG,GAAG;AACxC,QAAI,EAAE,YAAY,KAAK,CAAC,EAAE,KAAK,WAAW,GAAG,GAAG;AAC9C,eAAS,KAAK,MAAM,oBAAoB,IAAI,IAAI,GAAG,EAAE,IAAI,KAAK,GAAG,GAAG,EAAE,YAAY,QAAQ,QAAQ,IAAI,CAAC,CAAC;AAAA,IAC1G,WAAW,EAAE,OAAO,KAAK,WAAW,SAAS,QAAQ,EAAE,IAAI,CAAC,GAAG;AAC7D,YAAM,OAAO,IAAI,IAAI,EAAE,MAAM,GAAG;AAChC,YAAM,MAAM,MAAM,UAAU,MAAM,MAAM;AACxC,UAAI,QAAQ,QAAW;AACrB;AAAA,MACF;AACA,YAAM,OAAO,SAAS,EAAE,MAAM,QAAQ,EAAE,IAAI,CAAC;AAE7C,UAAI,KAAK,WAAW,GAAG,GAAG;AACxB,YAAI,SAAS,WAAW;AACtB,mBAAS;AAAA,QACX,WAAW,SAAS,UAAU;AAC5B,mBAAS,KAAK;AAAA,YACZ,MAAM;AAAA,YACN;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AACL,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF,WAAW,CAAC,KAAK,WAAW,GAAG,GAAG;AAChC,iBAAS,KAAK;AAAA,UACZ,MAAM;AAAA,UACN;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,WAAW,EAAE,OAAO,KAAK,CAAC,EAAE,KAAK,WAAW,GAAG,KAAK,WAAW,SAAS,QAAQ,EAAE,IAAI,CAAC,GAAG;AACxF,aAAO;AAAA,QACL,0HAA0H,EAAE,IAAI;AAAA,MAClI;AAAA,IACF;AAAA,EACF;AAEA,GAAC,UAAU,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,IACrC,QAAQ;AAAA,MACN,SAAS,IAAI,OAAO,WAAW;AAAA,QAC7B,GAAG;AAAA,QACH,MAAM,MAAM;AAAA,QACZ,QAAQ,MAAM,UAAU,MAAM,QAAQ,MAAM;AAAA,MAC9C,EAAE;AAAA,IACJ;AAAA,IACA,UAAU,QAAQ,MAAM;AAAA,EAC1B,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,UAAU,SAAS,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,SAAS,QAAQ,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;AAAA,EAChG;AACF;",
4
+ "sourcesContent": ["import { opendir, readFile } from 'node:fs/promises';\nimport { basename, extname, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Logger } from 'vite';\nimport { cleanUp } from './utils.js';\n\nexport type RouteMeta = Readonly<{\n path: string;\n file?: URL;\n layout?: URL;\n children?: readonly RouteMeta[];\n}>;\n\n/**\n * Routes collector options.\n */\nexport type CollectRoutesOptions = Readonly<{\n /**\n * The list of extensions for files that will be collected as routes.\n */\n extensions: readonly string[];\n /**\n * The parent directory of the current directory. This is a\n * nested parameter used inside the function only.\n */\n parent?: URL;\n /**\n * The Vite logger instance.\n */\n logger: Logger;\n}>;\n\nasync function checkFile(url: URL | undefined, logger: Logger): Promise<URL | undefined> {\n if (url) {\n const contents = await readFile(url, 'utf-8');\n if (contents.trim() === '') {\n return undefined;\n } else if (!contents.includes('export default')) {\n logger.error(`The file \"${String(url)}\" should contain a default export of a component`);\n return undefined;\n }\n }\n\n return url;\n}\n\nconst collator = new Intl.Collator('en-US');\n\nconst warningFor = ['.ts', '.js'];\n\n/**\n * Collect route metadata from the file system and build a route tree.\n *\n * It accepts files that start with `@` as special files.\n * - `@layout` contains a component that wraps the child components.\n * - `@index` contains a component that will be used as the index page of the directory.\n *\n * It accepts files that start with `_` as private files. They will be ignored.\n *\n * @param dir - The directory to collect routes from.\n * @param options - The options object.\n *\n * @returns The route metadata array.\n */\nexport default async function collectRoutesFromFS(\n dir: URL,\n { extensions, logger, parent = dir }: CollectRoutesOptions,\n): Promise<readonly RouteMeta[]> {\n const path = relative(fileURLToPath(parent), fileURLToPath(dir));\n let children: RouteMeta[] = [];\n let layout: URL | undefined;\n\n for await (const d of await opendir(dir)) {\n if (d.name.startsWith('_')) {\n continue;\n }\n\n if (d.isDirectory()) {\n const directoryRoutes = await collectRoutesFromFS(new URL(`${d.name}/`, dir), {\n extensions,\n logger,\n parent: dir,\n });\n if (directoryRoutes.length === 1 && directoryRoutes[0].layout) {\n const [layoutRoute] = directoryRoutes;\n children.push(layoutRoute);\n } else if (directoryRoutes.length > 0) {\n children.push({ path: d.name, children: directoryRoutes });\n }\n continue;\n }\n\n if (!extensions.includes(extname(d.name))) {\n if (warningFor.includes(extname(d.name))) {\n logger.warn(\n `File System based router expects only JSX files in 'Frontend/views/' directory, such as '*.tsx' and '*.jsx'. The file '${d.name}' will be ignored by router, as it doesn't match this convention. Please consider storing it in another directory.`,\n );\n }\n continue;\n }\n\n const file = new URL(d.name, dir);\n const url = await checkFile(file, logger);\n if (url === undefined) {\n continue;\n }\n const name = basename(d.name, extname(d.name));\n\n if (name === '@layout') {\n layout = file;\n } else if (name === '@index') {\n children.push({\n path: '',\n file,\n });\n } else if (name.startsWith('@')) {\n throw new Error(\n 'Symbol \"@\" is reserved for special directories and files; only \"@layout\" and \"@index\" are allowed',\n );\n } else {\n children.push({\n path: name,\n file,\n });\n }\n }\n\n [children, layout] = await Promise.all([\n Promise.all(\n children.map(async (child) => ({\n ...child,\n file: child.file,\n layout: await checkFile(child.layout, logger),\n })),\n ),\n checkFile(layout, logger),\n ]);\n\n children = children.sort(({ path: a }, { path: b }) => collator.compare(cleanUp(a), cleanUp(b)));\n\n // If a layout was found, wrap the other routes with the layout route.\n return layout ? [{ path, layout, children }] : children;\n}\n"],
5
+ "mappings": "AAAA,SAAS,SAAS,gBAAgB;AAClC,SAAS,UAAU,SAAS,gBAAgB;AAC5C,SAAS,qBAAqB;AAE9B,SAAS,eAAe;AA4BxB,eAAe,UAAU,KAAsB,QAA0C;AACvF,MAAI,KAAK;AACP,UAAM,WAAW,MAAM,SAAS,KAAK,OAAO;AAC5C,QAAI,SAAS,KAAK,MAAM,IAAI;AAC1B,aAAO;AAAA,IACT,WAAW,CAAC,SAAS,SAAS,gBAAgB,GAAG;AAC/C,aAAO,MAAM,aAAa,OAAO,GAAG,CAAC,kDAAkD;AACvF,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,MAAM,WAAW,IAAI,KAAK,SAAS,OAAO;AAE1C,MAAM,aAAa,CAAC,OAAO,KAAK;AAgBhC,eAAO,oBACL,KACA,EAAE,YAAY,QAAQ,SAAS,IAAI,GACJ;AAC/B,QAAM,OAAO,SAAS,cAAc,MAAM,GAAG,cAAc,GAAG,CAAC;AAC/D,MAAI,WAAwB,CAAC;AAC7B,MAAI;AAEJ,mBAAiB,KAAK,MAAM,QAAQ,GAAG,GAAG;AACxC,QAAI,EAAE,KAAK,WAAW,GAAG,GAAG;AAC1B;AAAA,IACF;AAEA,QAAI,EAAE,YAAY,GAAG;AACnB,YAAM,kBAAkB,MAAM,oBAAoB,IAAI,IAAI,GAAG,EAAE,IAAI,KAAK,GAAG,GAAG;AAAA,QAC5E;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AACD,UAAI,gBAAgB,WAAW,KAAK,gBAAgB,CAAC,EAAE,QAAQ;AAC7D,cAAM,CAAC,WAAW,IAAI;AACtB,iBAAS,KAAK,WAAW;AAAA,MAC3B,WAAW,gBAAgB,SAAS,GAAG;AACrC,iBAAS,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,gBAAgB,CAAC;AAAA,MAC3D;AACA;AAAA,IACF;AAEA,QAAI,CAAC,WAAW,SAAS,QAAQ,EAAE,IAAI,CAAC,GAAG;AACzC,UAAI,WAAW,SAAS,QAAQ,EAAE,IAAI,CAAC,GAAG;AACxC,eAAO;AAAA,UACL,0HAA0H,EAAE,IAAI;AAAA,QAClI;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,OAAO,IAAI,IAAI,EAAE,MAAM,GAAG;AAChC,UAAM,MAAM,MAAM,UAAU,MAAM,MAAM;AACxC,QAAI,QAAQ,QAAW;AACrB;AAAA,IACF;AACA,UAAM,OAAO,SAAS,EAAE,MAAM,QAAQ,EAAE,IAAI,CAAC;AAE7C,QAAI,SAAS,WAAW;AACtB,eAAS;AAAA,IACX,WAAW,SAAS,UAAU;AAC5B,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH,WAAW,KAAK,WAAW,GAAG,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF,OAAO;AACL,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,GAAC,UAAU,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,IACrC,QAAQ;AAAA,MACN,SAAS,IAAI,OAAO,WAAW;AAAA,QAC7B,GAAG;AAAA,QACH,MAAM,MAAM;AAAA,QACZ,QAAQ,MAAM,UAAU,MAAM,QAAQ,MAAM;AAAA,MAC9C,EAAE;AAAA,IACJ;AAAA,IACA,UAAU,QAAQ,MAAM;AAAA,EAC1B,CAAC;AAED,aAAW,SAAS,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,SAAS,QAAQ,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;AAG/F,SAAO,SAAS,CAAC,EAAE,MAAM,QAAQ,SAAS,CAAC,IAAI;AACjD;",
6
6
  "names": []
7
7
  }
@@ -6,5 +6,5 @@ import type { RuntimeFileUrls } from './generateRuntimeFiles.js';
6
6
  * @param views - The abstract route tree.
7
7
  * @param generatedDir - The directory where the generated view file will be stored.
8
8
  */
9
- export default function createRoutesFromMeta(views: RouteMeta, { code: codeFile }: RuntimeFileUrls): string;
9
+ export default function createRoutesFromMeta(views: readonly RouteMeta[], { code: codeFile }: RuntimeFileUrls): string;
10
10
  //# sourceMappingURL=createRoutesFromMeta.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createRoutesFromMeta.d.ts","sourceRoot":"","sources":["../src/vite-plugin/createRoutesFromMeta.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAmDjE;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,eAAe,GAAG,MAAM,CAgE1G"}
1
+ {"version":3,"file":"createRoutesFromMeta.d.ts","sourceRoot":"","sources":["../src/vite-plugin/createRoutesFromMeta.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAmDjE;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,eAAe,GAAG,MAAM,CA0ErH"}
@@ -1,10 +1,10 @@
1
- import { sep, relative } from "node:path";
1
+ import { relative, sep } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { template, transform as transformer } from "@vaadin/hilla-generator-utils/ast.js";
4
4
  import createSourceFile from "@vaadin/hilla-generator-utils/createSourceFile.js";
5
5
  import ts, {
6
6
  } from "typescript";
7
- import { transformTreeSync } from "../shared/transformTree.js";
7
+ import { transformTree } from "../shared/transformTree.js";
8
8
  import { convertFSRouteSegmentToURLPatternFormat } from "./utils.js";
9
9
  const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
10
10
  function relativize(url, generatedDir) {
@@ -32,26 +32,18 @@ function createRouteData(path, mod, children) {
32
32
  }
33
33
  function createRoutesFromMeta(views, { code: codeFile }) {
34
34
  const codeDir = new URL("./", codeFile);
35
- const imports = [
36
- template(
37
- 'import { createRoute } from "@vaadin/hilla-file-router/runtime.js";',
38
- ([statement]) => statement
39
- )
40
- ];
35
+ const imports = [];
41
36
  const errors = [];
42
37
  let id = 0;
43
- const routes = transformTreeSync(
44
- views,
45
- (view) => {
46
- if (view.children) {
47
- const paths = view.children.map((c) => c.path);
48
- const uniquePaths = new Set(paths);
49
- paths.filter((p) => !uniquePaths.delete(p)).forEach((p) => errors.push(`console.error("Two views share the same path: ${p}");`));
50
- return view.children.values();
38
+ const routes = transformTree(views, (metas, next) => {
39
+ errors.push(
40
+ ...metas.map((route) => route.path).filter((item, index, arr) => arr.indexOf(item) !== index).map((dup) => `console.error("Two views share the same path: ${dup}");`)
41
+ );
42
+ return metas.map(({ file: file2, layout, path, children }) => {
43
+ let _children;
44
+ if (children) {
45
+ _children = next(...children);
51
46
  }
52
- return void 0;
53
- },
54
- ({ file: file2, layout, path }, children) => {
55
47
  const currentId = id;
56
48
  id += 1;
57
49
  let mod;
@@ -62,15 +54,28 @@ function createRoutesFromMeta(views, { code: codeFile }) {
62
54
  mod = `Layout${currentId}`;
63
55
  imports.push(createImport(mod, relativize(layout, codeDir)));
64
56
  }
65
- return createRouteData(convertFSRouteSegmentToURLPatternFormat(path), mod, children);
66
- }
57
+ return createRouteData(convertFSRouteSegmentToURLPatternFormat(path), mod, _children);
58
+ });
59
+ });
60
+ if (imports.length > 0) {
61
+ const createRouteImport = template(
62
+ 'import { createRoute } from "@vaadin/hilla-file-router/runtime.js";',
63
+ ([statement]) => statement
64
+ );
65
+ imports.unshift(createRouteImport);
66
+ }
67
+ imports.unshift(
68
+ template(
69
+ 'import type { AgnosticRoute } from "@vaadin/hilla-file-router/types.js";',
70
+ ([statement]) => statement
71
+ )
67
72
  );
68
73
  const routeDeclaration = template(
69
74
  `import a from 'IMPORTS';
70
75
 
71
76
  ${errors.join("\n")}
72
77
 
73
- const routes = ROUTE;
78
+ const routes: readonly AgnosticRoute[] = ROUTE;
74
79
 
75
80
  export default routes;
76
81
  `,
@@ -78,12 +83,12 @@ export default routes;
78
83
  transformer(
79
84
  (node) => ts.isImportDeclaration(node) && node.moduleSpecifier.text === "IMPORTS" ? imports : node
80
85
  ),
81
- transformer((node) => ts.isIdentifier(node) && node.text === "ROUTE" ? routes : node)
86
+ transformer(
87
+ (node) => ts.isIdentifier(node) && node.text === "ROUTE" ? ts.factory.createArrayLiteralExpression(routes) : node
88
+ )
82
89
  ]
83
90
  );
84
91
  const file = createSourceFile(routeDeclaration, "file-routes.ts");
85
- const tempFile = createSourceFile(routeDeclaration, "views.ts");
86
- printer.printFile(tempFile);
87
92
  return printer.printFile(file);
88
93
  }
89
94
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/vite-plugin/createRoutesFromMeta.ts"],
4
- "sourcesContent": ["import { sep, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { template, transform as transformer } from '@vaadin/hilla-generator-utils/ast.js';\nimport createSourceFile from '@vaadin/hilla-generator-utils/createSourceFile.js';\nimport ts, {\n type CallExpression,\n type ImportDeclaration,\n type StringLiteral,\n type VariableStatement,\n} from 'typescript';\n\nimport { transformTreeSync } from '../shared/transformTree.js';\nimport type { RouteMeta } from './collectRoutesFromFS.js';\nimport type { RuntimeFileUrls } from './generateRuntimeFiles.js';\nimport { convertFSRouteSegmentToURLPatternFormat } from './utils.js';\n\nconst printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });\n\n/**\n * Convert a file URL to a relative path from the generated directory.\n *\n * @param url - The file URL to convert.\n * @param generatedDir - The directory where the generated view file will be stored.\n */\nfunction relativize(url: URL, generatedDir: URL): string {\n const result = relative(fileURLToPath(generatedDir), fileURLToPath(url)).replaceAll(sep, '/');\n\n if (!result.startsWith('.')) {\n return `./${result}`;\n }\n\n return result;\n}\n\n/**\n * Create an import declaration for a `views` module.\n *\n * @param mod - The name of the route module to import.\n * @param file - The file path of the module.\n */\nfunction createImport(mod: string, file: string): ImportDeclaration {\n const path = `${file.substring(0, file.lastIndexOf('.'))}.js`;\n return template(`import * as ${mod} from '${path}';\\n`, ([statement]) => statement as ts.ImportDeclaration);\n}\n\n/**\n * Create an abstract route creation function call. The nested function calls create a route tree.\n *\n * @param path - The path of the route.\n * @param mod - The name of the route module imported as a namespace.\n * @param children - The list of child route call expressions.\n */\nfunction createRouteData(path: string, mod: string | undefined, children?: readonly CallExpression[]): CallExpression {\n return template(\n `const route = createRoute(\"${path}\"${mod ? `, ${mod}` : ''}${children && children.length > 0 ? `, CHILDREN` : ''})`,\n ([statement]) => (statement as VariableStatement).declarationList.declarations[0].initializer as CallExpression,\n [\n transformer((node) =>\n ts.isIdentifier(node) && node.text === 'CHILDREN' ? ts.factory.createArrayLiteralExpression(children) : node,\n ),\n ],\n );\n}\n\n/**\n * Loads all the files from the received metadata and creates a framework-agnostic route tree.\n *\n * @param views - The abstract route tree.\n * @param generatedDir - The directory where the generated view file will be stored.\n */\nexport default function createRoutesFromMeta(views: RouteMeta, { code: codeFile }: RuntimeFileUrls): string {\n const codeDir = new URL('./', codeFile);\n const imports: ImportDeclaration[] = [\n template(\n 'import { createRoute } from \"@vaadin/hilla-file-router/runtime.js\";',\n ([statement]) => statement as ts.ImportDeclaration,\n ),\n ];\n const errors: string[] = [];\n let id = 0;\n\n const routes = transformTreeSync<RouteMeta, CallExpression>(\n views,\n (view) => {\n if (view.children) {\n const paths = view.children.map((c) => c.path);\n const uniquePaths = new Set(paths);\n paths\n .filter((p) => !uniquePaths.delete(p))\n .forEach((p) => errors.push(`console.error(\"Two views share the same path: ${p}\");`));\n return view.children.values();\n }\n\n return undefined;\n },\n ({ file, layout, path }, children) => {\n const currentId = id;\n id += 1;\n\n let mod: string | undefined;\n if (file) {\n mod = `Page${currentId}`;\n imports.push(createImport(mod, relativize(file, codeDir)));\n } else if (layout) {\n mod = `Layout${currentId}`;\n imports.push(createImport(mod, relativize(layout, codeDir)));\n }\n\n return createRouteData(convertFSRouteSegmentToURLPatternFormat(path), mod, children);\n },\n );\n\n const routeDeclaration = template(\n `import a from 'IMPORTS';\n\n${errors.join('\\n')}\n\nconst routes = ROUTE;\n\nexport default routes;\n`,\n [\n transformer((node) =>\n ts.isImportDeclaration(node) && (node.moduleSpecifier as StringLiteral).text === 'IMPORTS' ? imports : node,\n ),\n transformer((node) => (ts.isIdentifier(node) && node.text === 'ROUTE' ? routes : node)),\n ],\n );\n\n const file = createSourceFile(routeDeclaration, 'file-routes.ts');\n // also keep the old file temporarily for compatibility purposes:\n const tempFile = createSourceFile(routeDeclaration, 'views.ts');\n printer.printFile(tempFile);\n return printer.printFile(file);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { relative, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { template, transform as transformer } from '@vaadin/hilla-generator-utils/ast.js';\nimport createSourceFile from '@vaadin/hilla-generator-utils/createSourceFile.js';\nimport ts, {\n type CallExpression,\n type ImportDeclaration,\n type StringLiteral,\n type VariableStatement,\n} from 'typescript';\n\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 = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });\n\n/**\n * Convert a file URL to a relative path from the generated directory.\n *\n * @param url - The file URL to convert.\n * @param generatedDir - The directory where the generated view file will be stored.\n */\nfunction relativize(url: URL, generatedDir: URL): string {\n const result = relative(fileURLToPath(generatedDir), fileURLToPath(url)).replaceAll(sep, '/');\n\n if (!result.startsWith('.')) {\n return `./${result}`;\n }\n\n return result;\n}\n\n/**\n * Create an import declaration for a `views` module.\n *\n * @param mod - The name of the route module to import.\n * @param file - The file path of the module.\n */\nfunction createImport(mod: string, file: string): ImportDeclaration {\n const path = `${file.substring(0, file.lastIndexOf('.'))}.js`;\n return template(`import * as ${mod} from '${path}';\\n`, ([statement]) => statement as ts.ImportDeclaration);\n}\n\n/**\n * Create an abstract route creation function call. The nested function calls create a route tree.\n *\n * @param path - The path of the route.\n * @param mod - The name of the route module imported as a namespace.\n * @param children - The list of child route call expressions.\n */\nfunction createRouteData(path: string, mod: string | undefined, children?: readonly CallExpression[]): CallExpression {\n return template(\n `const route = createRoute(\"${path}\"${mod ? `, ${mod}` : ''}${children && children.length > 0 ? `, CHILDREN` : ''})`,\n ([statement]) => (statement as VariableStatement).declarationList.declarations[0].initializer as CallExpression,\n [\n transformer((node) =>\n ts.isIdentifier(node) && node.text === 'CHILDREN' ? ts.factory.createArrayLiteralExpression(children) : node,\n ),\n ],\n );\n}\n\n/**\n * Loads all the files from the received metadata and creates a framework-agnostic route tree.\n *\n * @param views - The abstract route tree.\n * @param generatedDir - The directory where the generated view file will be stored.\n */\nexport default function createRoutesFromMeta(views: readonly RouteMeta[], { code: codeFile }: RuntimeFileUrls): string {\n const codeDir = new URL('./', codeFile);\n const imports: ImportDeclaration[] = [];\n const errors: string[] = [];\n let id = 0;\n\n const routes = transformTree<readonly RouteMeta[], readonly CallExpression[]>(views, (metas, 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 }) => {\n let _children: readonly CallExpression[] | undefined;\n\n if (children) {\n _children = next(...children);\n }\n\n const currentId = id;\n id += 1;\n\n let mod: string | undefined;\n if (file) {\n mod = `Page${currentId}`;\n imports.push(createImport(mod, relativize(file, codeDir)));\n } else if (layout) {\n mod = `Layout${currentId}`;\n imports.push(createImport(mod, relativize(layout, codeDir)));\n }\n\n return createRouteData(convertFSRouteSegmentToURLPatternFormat(path), mod, _children);\n });\n });\n\n // Prepend the import for `createRoute` if it was used\n if (imports.length > 0) {\n const createRouteImport = template(\n 'import { createRoute } from \"@vaadin/hilla-file-router/runtime.js\";',\n ([statement]) => statement as ts.ImportDeclaration,\n );\n imports.unshift(createRouteImport);\n }\n\n imports.unshift(\n template(\n 'import type { AgnosticRoute } from \"@vaadin/hilla-file-router/types.js\";',\n ([statement]) => statement as ts.ImportDeclaration,\n ),\n );\n\n const routeDeclaration = template(\n `import a from 'IMPORTS';\n\n${errors.join('\\n')}\n\nconst routes: readonly AgnosticRoute[] = ROUTE;\n\nexport default routes;\n`,\n [\n transformer((node) =>\n ts.isImportDeclaration(node) && (node.moduleSpecifier as StringLiteral).text === 'IMPORTS' ? imports : node,\n ),\n transformer((node) =>\n ts.isIdentifier(node) && node.text === 'ROUTE' ? ts.factory.createArrayLiteralExpression(routes) : node,\n ),\n ],\n );\n\n const file = createSourceFile(routeDeclaration, 'file-routes.ts');\n return printer.printFile(file);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["file"]
7
7
  }
@@ -5,5 +5,5 @@ import type { RouteMeta } from './collectRoutesFromFS.js';
5
5
  *
6
6
  * @param views - The route metadata tree.
7
7
  */
8
- export default function createViewConfigJson(views: RouteMeta): Promise<string>;
8
+ export default function createViewConfigJson(views: readonly RouteMeta[]): Promise<string>;
9
9
  //# sourceMappingURL=createViewConfigJson.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createViewConfigJson.d.ts","sourceRoot":"","sources":["../src/vite-plugin/createViewConfigJson.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAgB1D;;;;;GAKG;AACH,wBAA8B,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAiDpF"}
1
+ {"version":3,"file":"createViewConfigJson.d.ts","sourceRoot":"","sources":["../src/vite-plugin/createViewConfigJson.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAgB1D;;;;;GAKG;AACH,wBAA8B,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAmE/F"}
@@ -13,46 +13,59 @@ function* walkAST(node) {
13
13
  async function createViewConfigJson(views) {
14
14
  const res = await transformTree(
15
15
  views,
16
- (route) => route.children?.values(),
17
- async ({ path, file, layout }, children) => {
18
- if (!file && !layout) {
19
- return {
20
- route: path,
21
- params: extractParameterFromRouteSegment(path),
22
- children
23
- };
24
- }
25
- const sourceFile = ts.createSourceFile(
26
- "f.ts",
27
- await readFile(file ?? layout, "utf8"),
28
- ts.ScriptTarget.ESNext,
29
- true
30
- );
31
- let config;
32
- let waitingForIdentifier = false;
33
- let componentName;
34
- for (const node of walkAST(sourceFile)) {
35
- if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === "config") {
36
- if (node.initializer && ts.isObjectLiteralExpression(node.initializer)) {
37
- const code = node.initializer.getText(sourceFile);
38
- const script = new Script(`(${code})`);
39
- config = script.runInThisContext();
16
+ async (routes, next) => await Promise.all(
17
+ routes.map(async ({ path, file, layout, children }) => {
18
+ const newChildren = children ? await next(...children) : void 0;
19
+ if (!file && !layout) {
20
+ return {
21
+ route: path,
22
+ params: extractParameterFromRouteSegment(path),
23
+ children: newChildren
24
+ };
25
+ }
26
+ const sourceFile = ts.createSourceFile(
27
+ "f.ts",
28
+ await readFile(file ?? layout, "utf8"),
29
+ ts.ScriptTarget.ESNext,
30
+ true
31
+ );
32
+ let config;
33
+ let waitingForIdentifier = false;
34
+ let componentName;
35
+ for (const node of walkAST(sourceFile)) {
36
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === "config") {
37
+ if (node.initializer && ts.isObjectLiteralExpression(node.initializer)) {
38
+ const code = node.initializer.getText(sourceFile);
39
+ const script = new Script(`(${code})`);
40
+ config = script.runInThisContext();
41
+ }
42
+ } else if (node.getText(sourceFile).startsWith("export default")) {
43
+ waitingForIdentifier = true;
44
+ } else if (waitingForIdentifier && ts.isIdentifier(node)) {
45
+ componentName = node.text;
46
+ break;
40
47
  }
41
- } else if (node.getText(sourceFile).startsWith("export default")) {
42
- waitingForIdentifier = true;
43
- } else if (waitingForIdentifier && ts.isIdentifier(node)) {
44
- componentName = node.text;
45
- break;
46
48
  }
47
- }
48
- return {
49
- route: convertFSRouteSegmentToURLPatternFormat(path),
50
- ...config,
51
- params: extractParameterFromRouteSegment(config?.route ?? path),
52
- title: config?.title ?? convertComponentNameToTitle(componentName),
53
- children
54
- };
55
- }
49
+ let title;
50
+ if (config?.title) {
51
+ ({ title } = config);
52
+ } else {
53
+ if (!componentName) {
54
+ throw new Error(
55
+ `The file "${String(file ?? layout)}" must contain a default export of a component whose name will be used as title by default`
56
+ );
57
+ }
58
+ title = convertComponentNameToTitle(componentName);
59
+ }
60
+ return {
61
+ route: convertFSRouteSegmentToURLPatternFormat(path),
62
+ ...config,
63
+ params: extractParameterFromRouteSegment(config?.route ?? path),
64
+ title,
65
+ children: newChildren
66
+ };
67
+ })
68
+ )
56
69
  );
57
70
  return JSON.stringify(res);
58
71
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/vite-plugin/createViewConfigJson.ts"],
4
- "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: RouteMeta): Promise<string> {\n const res = await transformTree<RouteMeta, ServerViewConfig>(\n views,\n (route) => route.children?.values(),\n async ({ path, file, layout }, children) => {\n if (!file && !layout) {\n return {\n route: path,\n params: extractParameterFromRouteSegment(path),\n children,\n } as const;\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 }\n } else if (node.getText(sourceFile).startsWith('export default')) {\n waitingForIdentifier = true;\n } else if (waitingForIdentifier && ts.isIdentifier(node)) {\n componentName = node.text;\n break;\n }\n }\n\n return {\n route: convertFSRouteSegmentToURLPatternFormat(path),\n ...config,\n params: extractParameterFromRouteSegment(config?.route ?? path),\n title: config?.title ?? convertComponentNameToTitle(componentName),\n children,\n } as const;\n },\n );\n\n return JSON.stringify(res);\n}\n"],
5
- "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,OAAO,YAAuB;AAC9B,SAAS,mCAAmC;AAE5C,SAAS,qBAAqB;AAG9B,SAAS,yCAAyC,wCAAwC;AAO1F,UAAU,QAAQ,MAA6B;AAC7C,QAAM;AAEN,aAAW,SAAS,KAAK,YAAY,GAAG;AACtC,WAAO,QAAQ,KAAK;AAAA,EACtB;AACF;AAQA,eAAO,qBAA4C,OAAmC;AACpF,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,UAAU,MAAM,UAAU,OAAO;AAAA,IAClC,OAAO,EAAE,MAAM,MAAM,OAAO,GAAG,aAAa;AAC1C,UAAI,CAAC,QAAQ,CAAC,QAAQ;AACpB,eAAO;AAAA,UACL,OAAO;AAAA,UACP,QAAQ,iCAAiC,IAAI;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AAEA,YAAM,aAAa,GAAG;AAAA,QACpB;AAAA,QACA,MAAM,SAAS,QAAQ,QAAS,MAAM;AAAA,QACtC,GAAG,aAAa;AAAA,QAChB;AAAA,MACF;AACA,UAAI;AACJ,UAAI,uBAAuB;AAC3B,UAAI;AAEJ,iBAAW,QAAQ,QAAQ,UAAU,GAAG;AACtC,YAAI,GAAG,sBAAsB,IAAI,KAAK,GAAG,aAAa,KAAK,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AAC/F,cAAI,KAAK,eAAe,GAAG,0BAA0B,KAAK,WAAW,GAAG;AACtE,kBAAM,OAAO,KAAK,YAAY,QAAQ,UAAU;AAChD,kBAAM,SAAS,IAAI,OAAO,IAAI,IAAI,GAAG;AACrC,qBAAS,OAAO,iBAAiB;AAAA,UACnC;AAAA,QACF,WAAW,KAAK,QAAQ,UAAU,EAAE,WAAW,gBAAgB,GAAG;AAChE,iCAAuB;AAAA,QACzB,WAAW,wBAAwB,GAAG,aAAa,IAAI,GAAG;AACxD,0BAAgB,KAAK;AACrB;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL,OAAO,wCAAwC,IAAI;AAAA,QACnD,GAAG;AAAA,QACH,QAAQ,iCAAiC,QAAQ,SAAS,IAAI;AAAA,QAC9D,OAAO,QAAQ,SAAS,4BAA4B,aAAa;AAAA,QACjE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,KAAK,UAAU,GAAG;AAC3B;",
4
+ "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 async (routes, next) =>\n await Promise.all(\n routes.map(async ({ path, file, layout, children }) => {\n const newChildren = children ? await next(...children) : undefined;\n\n if (!file && !layout) {\n return {\n route: 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 }\n } else if (node.getText(sourceFile).startsWith('export default')) {\n waitingForIdentifier = true;\n } else if (waitingForIdentifier && ts.isIdentifier(node)) {\n componentName = node.text;\n break;\n }\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,\n } satisfies ServerViewConfig;\n }),\n ),\n );\n\n return JSON.stringify(res);\n}\n"],
5
+ "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,OAAO,YAAuB;AAC9B,SAAS,mCAAmC;AAE5C,SAAS,qBAAqB;AAG9B,SAAS,yCAAyC,wCAAwC;AAO1F,UAAU,QAAQ,MAA6B;AAC7C,QAAM;AAEN,aAAW,SAAS,KAAK,YAAY,GAAG;AACtC,WAAO,QAAQ,KAAK;AAAA,EACtB;AACF;AAQA,eAAO,qBAA4C,OAA8C;AAC/F,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,OAAO,QAAQ,SACb,MAAM,QAAQ;AAAA,MACZ,OAAO,IAAI,OAAO,EAAE,MAAM,MAAM,QAAQ,SAAS,MAAM;AACrD,cAAM,cAAc,WAAW,MAAM,KAAK,GAAG,QAAQ,IAAI;AAEzD,YAAI,CAAC,QAAQ,CAAC,QAAQ;AACpB,iBAAO;AAAA,YACL,OAAO;AAAA,YACP,QAAQ,iCAAiC,IAAI;AAAA,YAC7C,UAAU;AAAA,UACZ;AAAA,QACF;AAEA,cAAM,aAAa,GAAG;AAAA,UACpB;AAAA,UACA,MAAM,SAAS,QAAQ,QAAS,MAAM;AAAA,UACtC,GAAG,aAAa;AAAA,UAChB;AAAA,QACF;AACA,YAAI;AACJ,YAAI,uBAAuB;AAC3B,YAAI;AAEJ,mBAAW,QAAQ,QAAQ,UAAU,GAAG;AACtC,cAAI,GAAG,sBAAsB,IAAI,KAAK,GAAG,aAAa,KAAK,IAAI,KAAK,KAAK,KAAK,SAAS,UAAU;AAC/F,gBAAI,KAAK,eAAe,GAAG,0BAA0B,KAAK,WAAW,GAAG;AACtE,oBAAM,OAAO,KAAK,YAAY,QAAQ,UAAU;AAChD,oBAAM,SAAS,IAAI,OAAO,IAAI,IAAI,GAAG;AACrC,uBAAS,OAAO,iBAAiB;AAAA,YACnC;AAAA,UACF,WAAW,KAAK,QAAQ,UAAU,EAAE,WAAW,gBAAgB,GAAG;AAChE,mCAAuB;AAAA,UACzB,WAAW,wBAAwB,GAAG,aAAa,IAAI,GAAG;AACxD,4BAAgB,KAAK;AACrB;AAAA,UACF;AAAA,QACF;AAEA,YAAI;AAEJ,YAAI,QAAQ,OAAO;AACjB,WAAC,EAAE,MAAM,IAAI;AAAA,QACf,OAAO;AACL,cAAI,CAAC,eAAe;AAClB,kBAAM,IAAI;AAAA,cACR,aAAa,OAAO,QAAQ,MAAO,CAAC;AAAA,YACtC;AAAA,UACF;AAEA,kBAAQ,4BAA4B,aAAa;AAAA,QACnD;AAEA,eAAO;AAAA,UACL,OAAO,wCAAwC,IAAI;AAAA,UACnD,GAAG;AAAA,UACH,QAAQ,iCAAiC,QAAQ,SAAS,IAAI;AAAA,UAC9D;AAAA,UACA,UAAU;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACJ;AAEA,SAAO,KAAK,UAAU,GAAG;AAC3B;",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"generateRuntimeFiles.d.ts","sourceRoot":"","sources":["../src/vite-plugin/generateRuntimeFiles.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAKnC;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC;;;OAGG;IACH,IAAI,EAAE,GAAG,CAAC;IACV;;OAEG;IACH,IAAI,EAAE,GAAG,CAAC;CACX,CAAC,CAAC;AAyBH;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,GAAG,EACb,IAAI,EAAE,eAAe,EACrB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAkBf"}
1
+ {"version":3,"file":"generateRuntimeFiles.d.ts","sourceRoot":"","sources":["../src/vite-plugin/generateRuntimeFiles.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAKnC;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC;;;OAGG;IACH,IAAI,EAAE,GAAG,CAAC;IACV;;OAEG;IACH,IAAI,EAAE,GAAG,CAAC;CACX,CAAC,CAAC;AAyBH;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,GAAG,EACb,IAAI,EAAE,eAAe,EACrB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -21,17 +21,12 @@ async function generateRuntimeFiles(viewsDir, urls, extensions, logger) {
21
21
  logger.info("Collected file-based routes");
22
22
  const runtimeRoutesCode = createRoutesFromMeta(routeMeta, urls);
23
23
  const viewConfigJson = await createViewConfigJson(routeMeta);
24
- const tempUrl = new URL("views.ts", urls.code.href);
25
24
  await Promise.all([
26
25
  generateRuntimeFile(urls.json, viewConfigJson).then(
27
26
  () => logger.info(`Frontend route list is generated: ${String(urls.json)}`)
28
27
  ),
29
28
  generateRuntimeFile(urls.code, runtimeRoutesCode).then(
30
29
  () => logger.info(`File Route module is generated: ${String(urls.code)}`)
31
- ),
32
- // also keep the old file temporarily for compatibility purposes:
33
- generateRuntimeFile(tempUrl, runtimeRoutesCode).then(
34
- () => logger.info(`Views module is generated: ${String(tempUrl)}`)
35
30
  )
36
31
  ]);
37
32
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/vite-plugin/generateRuntimeFiles.ts"],
4
- "sourcesContent": ["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport type { Logger } from 'vite';\nimport collectRoutesFromFS from './collectRoutesFromFS.js';\nimport createRoutesFromMeta from './createRoutesFromMeta.js';\nimport createViewConfigJson from './createViewConfigJson.js';\n\n/**\n * The URLs of the files to generate.\n */\nexport type RuntimeFileUrls = Readonly<{\n /**\n * The URL of the JSON file with the leaf routes and their metadata. This file\n * will be processed by the server to provide the final route configuration.\n */\n json: URL;\n /**\n * The URL of the module with the routes tree in a framework-agnostic format.\n */\n code: URL;\n}>;\n\n/**\n * Generates a file conditionally. If the file already exists and its content is the same as the\n * given data, the file will not be overwritten. It is useful to avoid unnecessary server\n * reboot during development.\n *\n * @param url - The URL of the file to generate.\n * @param data - The data to write to the file.\n */\nasync function generateRuntimeFile(url: URL, data: string): Promise<void> {\n await mkdir(new URL('./', url), { recursive: true });\n let contents: string | undefined;\n try {\n contents = await readFile(url, 'utf-8');\n } catch (e: unknown) {\n if (!(e != null && typeof e === 'object' && 'code' in e && e.code === 'ENOENT')) {\n throw e;\n }\n }\n if (contents !== data) {\n await writeFile(url, data, 'utf-8');\n }\n}\n\n/**\n * Collects all file-based routes from the given directory, and based on them generates two files\n * described by {@link RuntimeFileUrls} type.\n * @param viewsDir - The directory that contains file-based routes (views).\n * @param urls - The URLs of the files to generate.\n * @param extensions - The list of extensions that will be collected as routes.\n * @param logger - The Vite logger instance.\n */\nexport async function generateRuntimeFiles(\n viewsDir: URL,\n urls: RuntimeFileUrls,\n extensions: readonly string[],\n logger: Logger,\n): Promise<void> {\n const routeMeta = await collectRoutesFromFS(viewsDir, { extensions, logger });\n logger.info('Collected file-based routes');\n const runtimeRoutesCode = createRoutesFromMeta(routeMeta, urls);\n const viewConfigJson = await createViewConfigJson(routeMeta);\n const tempUrl = new URL('views.ts', urls.code.href);\n await Promise.all([\n generateRuntimeFile(urls.json, viewConfigJson).then(() =>\n logger.info(`Frontend route list is generated: ${String(urls.json)}`),\n ),\n generateRuntimeFile(urls.code, runtimeRoutesCode).then(() =>\n logger.info(`File Route module is generated: ${String(urls.code)}`),\n ),\n // also keep the old file temporarily for compatibility purposes:\n generateRuntimeFile(tempUrl, runtimeRoutesCode).then(() =>\n logger.info(`Views module is generated: ${String(tempUrl)}`),\n ),\n ]);\n}\n"],
5
- "mappings": "AAAA,SAAS,OAAO,UAAU,iBAAiB;AAE3C,OAAO,yBAAyB;AAChC,OAAO,0BAA0B;AACjC,OAAO,0BAA0B;AAyBjC,eAAe,oBAAoB,KAAU,MAA6B;AACxE,QAAM,MAAM,IAAI,IAAI,MAAM,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AACnD,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,SAAS,KAAK,OAAO;AAAA,EACxC,SAAS,GAAY;AACnB,QAAI,EAAE,KAAK,QAAQ,OAAO,MAAM,YAAY,UAAU,KAAK,EAAE,SAAS,WAAW;AAC/E,YAAM;AAAA,IACR;AAAA,EACF;AACA,MAAI,aAAa,MAAM;AACrB,UAAM,UAAU,KAAK,MAAM,OAAO;AAAA,EACpC;AACF;AAUA,eAAsB,qBACpB,UACA,MACA,YACA,QACe;AACf,QAAM,YAAY,MAAM,oBAAoB,UAAU,EAAE,YAAY,OAAO,CAAC;AAC5E,SAAO,KAAK,6BAA6B;AACzC,QAAM,oBAAoB,qBAAqB,WAAW,IAAI;AAC9D,QAAM,iBAAiB,MAAM,qBAAqB,SAAS;AAC3D,QAAM,UAAU,IAAI,IAAI,YAAY,KAAK,KAAK,IAAI;AAClD,QAAM,QAAQ,IAAI;AAAA,IAChB,oBAAoB,KAAK,MAAM,cAAc,EAAE;AAAA,MAAK,MAClD,OAAO,KAAK,qCAAqC,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,IACtE;AAAA,IACA,oBAAoB,KAAK,MAAM,iBAAiB,EAAE;AAAA,MAAK,MACrD,OAAO,KAAK,mCAAmC,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,IACpE;AAAA;AAAA,IAEA,oBAAoB,SAAS,iBAAiB,EAAE;AAAA,MAAK,MACnD,OAAO,KAAK,8BAA8B,OAAO,OAAO,CAAC,EAAE;AAAA,IAC7D;AAAA,EACF,CAAC;AACH;",
4
+ "sourcesContent": ["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport type { Logger } from 'vite';\nimport collectRoutesFromFS from './collectRoutesFromFS.js';\nimport createRoutesFromMeta from './createRoutesFromMeta.js';\nimport createViewConfigJson from './createViewConfigJson.js';\n\n/**\n * The URLs of the files to generate.\n */\nexport type RuntimeFileUrls = Readonly<{\n /**\n * The URL of the JSON file with the leaf routes and their metadata. This file\n * will be processed by the server to provide the final route configuration.\n */\n json: URL;\n /**\n * The URL of the module with the routes tree in a framework-agnostic format.\n */\n code: URL;\n}>;\n\n/**\n * Generates a file conditionally. If the file already exists and its content is the same as the\n * given data, the file will not be overwritten. It is useful to avoid unnecessary server\n * reboot during development.\n *\n * @param url - The URL of the file to generate.\n * @param data - The data to write to the file.\n */\nasync function generateRuntimeFile(url: URL, data: string): Promise<void> {\n await mkdir(new URL('./', url), { recursive: true });\n let contents: string | undefined;\n try {\n contents = await readFile(url, 'utf-8');\n } catch (e: unknown) {\n if (!(e != null && typeof e === 'object' && 'code' in e && e.code === 'ENOENT')) {\n throw e;\n }\n }\n if (contents !== data) {\n await writeFile(url, data, 'utf-8');\n }\n}\n\n/**\n * Collects all file-based routes from the given directory, and based on them generates two files\n * described by {@link RuntimeFileUrls} type.\n * @param viewsDir - The directory that contains file-based routes (views).\n * @param urls - The URLs of the files to generate.\n * @param extensions - The list of extensions that will be collected as routes.\n * @param logger - The Vite logger instance.\n */\nexport async function generateRuntimeFiles(\n viewsDir: URL,\n urls: RuntimeFileUrls,\n extensions: readonly string[],\n logger: Logger,\n): Promise<void> {\n const routeMeta = await collectRoutesFromFS(viewsDir, { extensions, logger });\n logger.info('Collected file-based routes');\n const runtimeRoutesCode = createRoutesFromMeta(routeMeta, urls);\n const viewConfigJson = await createViewConfigJson(routeMeta);\n\n await Promise.all([\n generateRuntimeFile(urls.json, viewConfigJson).then(() =>\n logger.info(`Frontend route list is generated: ${String(urls.json)}`),\n ),\n generateRuntimeFile(urls.code, runtimeRoutesCode).then(() =>\n logger.info(`File Route module is generated: ${String(urls.code)}`),\n ),\n ]);\n}\n"],
5
+ "mappings": "AAAA,SAAS,OAAO,UAAU,iBAAiB;AAE3C,OAAO,yBAAyB;AAChC,OAAO,0BAA0B;AACjC,OAAO,0BAA0B;AAyBjC,eAAe,oBAAoB,KAAU,MAA6B;AACxE,QAAM,MAAM,IAAI,IAAI,MAAM,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AACnD,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,SAAS,KAAK,OAAO;AAAA,EACxC,SAAS,GAAY;AACnB,QAAI,EAAE,KAAK,QAAQ,OAAO,MAAM,YAAY,UAAU,KAAK,EAAE,SAAS,WAAW;AAC/E,YAAM;AAAA,IACR;AAAA,EACF;AACA,MAAI,aAAa,MAAM;AACrB,UAAM,UAAU,KAAK,MAAM,OAAO;AAAA,EACpC;AACF;AAUA,eAAsB,qBACpB,UACA,MACA,YACA,QACe;AACf,QAAM,YAAY,MAAM,oBAAoB,UAAU,EAAE,YAAY,OAAO,CAAC;AAC5E,SAAO,KAAK,6BAA6B;AACzC,QAAM,oBAAoB,qBAAqB,WAAW,IAAI;AAC9D,QAAM,iBAAiB,MAAM,qBAAqB,SAAS;AAE3D,QAAM,QAAQ,IAAI;AAAA,IAChB,oBAAoB,KAAK,MAAM,cAAc,EAAE;AAAA,MAAK,MAClD,OAAO,KAAK,qCAAqC,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,IACtE;AAAA,IACA,oBAAoB,KAAK,MAAM,iBAAiB,EAAE;AAAA,MAAK,MACrD,OAAO,KAAK,mCAAmC,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,IACpE;AAAA,EACF,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["src/vite-plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAG3C;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,0BAA0B,CAAC,EACjD,QAA4B,EAC5B,YAAoC,EACpC,UAA6B,EAC7B,SAAiB,GAClB,GAAE,aAAkB,GAAG,MAAM,CAiF7B"}
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["src/vite-plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAG3C;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,0BAA0B,CAAC,EACjD,QAA4B,EAC5B,YAAoC,EACpC,UAA6B,EAC7B,SAAiB,GAClB,GAAE,aAAkB,GAAG,MAAM,CA8F7B"}
package/vite-plugin.js CHANGED
@@ -53,15 +53,26 @@ function vitePluginFileSystemRouter({
53
53
  server.watcher.on("unlink", changeListener);
54
54
  },
55
55
  transform(code, id) {
56
+ let modifiedCode = code;
56
57
  if (id.startsWith(fileURLToPath(_viewsDir)) && !basename(id).startsWith("_")) {
57
- return {
58
- code: code.replace(
58
+ if (isDevMode) {
59
+ modifiedCode = modifiedCode.replace(
59
60
  hmrInjectionPattern,
60
61
  `if (!nextExports) return;
61
- if (Object.keys(nextExports).length === 2 && 'default' in nextExports && 'config' in nextExports) {
62
- nextExports = { ...nextExports, config: currentExports.config };
63
- }`
64
- )
62
+ if (Object.keys(nextExports).length === 2 && 'default' in nextExports && 'config' in nextExports) {
63
+ nextExports = { ...nextExports, config: currentExports.config };
64
+ }`
65
+ );
66
+ } else {
67
+ const functionNames = /export\s+default\s+(?:function\s+)?(\w+)/u.exec(modifiedCode);
68
+ if (functionNames?.length) {
69
+ const [, functionName] = functionNames;
70
+ modifiedCode += `Object.defineProperty(${functionName}, 'name', { value: '${functionName}' });
71
+ `;
72
+ }
73
+ }
74
+ return {
75
+ code: modifiedCode
65
76
  };
66
77
  }
67
78
  return void 0;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["src/vite-plugin.ts"],
4
- "sourcesContent": ["import { basename } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { TransformResult } from 'rollup';\nimport type { Logger, Plugin } from 'vite';\nimport { generateRuntimeFiles, type RuntimeFileUrls } from './vite-plugin/generateRuntimeFiles.js';\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\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}: PluginOptions = {}): Plugin {\n const hmrInjectionPattern = /(?<=import\\.meta\\.hot\\.accept[\\s\\S]+)if\\s\\(!nextExports\\)\\s+return;/u;\n\n let _viewsDir: URL;\n let _outDir: URL;\n let _logger: Logger;\n let runtimeUrls: RuntimeFileUrls;\n\n return {\n name: 'vite-plugin-file-router',\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 _outDir = pathToFileURL(outDir);\n _logger = logger;\n\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 };\n },\n async buildStart() {\n try {\n await generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger);\n } catch (e: unknown) {\n _logger.error(String(e));\n }\n },\n configureServer(server) {\n const dir = fileURLToPath(_viewsDir);\n\n const changeListener = (file: string): void => {\n if (!file.startsWith(dir)) {\n if (file === fileURLToPath(runtimeUrls.json)) {\n server.hot.send({ type: 'full-reload' });\n }\n return;\n }\n\n generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger).catch((e: unknown) =>\n _logger.error(String(e)),\n );\n };\n\n server.watcher.on('add', changeListener);\n server.watcher.on('change', changeListener);\n server.watcher.on('unlink', changeListener);\n },\n transform(code, id): Promise<TransformResult> | TransformResult {\n if (id.startsWith(fileURLToPath(_viewsDir)) && !basename(id).startsWith('_')) {\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 return {\n code: code.replace(\n hmrInjectionPattern,\n `if (!nextExports) return;\n if (Object.keys(nextExports).length === 2 && 'default' in nextExports && 'config' in nextExports) {\n nextExports = { ...nextExports, config: currentExports.config };\n }`,\n ),\n };\n }\n\n return undefined;\n },\n };\n}\n"],
5
- "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,eAAe,qBAAqB;AAG7C,SAAS,4BAAkD;AAwC5C,SAAR,2BAA4C;AAAA,EACjD,WAAW;AAAA,EACX,eAAe;AAAA,EACf,aAAa,CAAC,QAAQ,MAAM;AAAA,EAC5B,YAAY;AACd,IAAmB,CAAC,GAAW;AAC7B,QAAM,sBAAsB;AAE5B,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,SAAO;AAAA,IACL,MAAM;AAAA,IACN,eAAe,EAAE,QAAQ,MAAM,OAAO,EAAE,OAAO,EAAE,GAAG;AAClD,YAAM,QAAQ,cAAc,IAAI;AAChC,YAAM,gBAAgB,IAAI,IAAI,cAAc,KAAK;AAEjD,kBAAY,IAAI,IAAI,UAAU,KAAK;AACnC,gBAAU,cAAc,MAAM;AAC9B,gBAAU;AAEV,cAAQ,KAAK,iCAAiC,OAAO,SAAS,CAAC,EAAE;AACjE,cAAQ,KAAK,qCAAqC,OAAO,aAAa,CAAC,EAAE;AACzE,cAAQ,KAAK,yBAAyB,OAAO,OAAO,CAAC,EAAE;AAEvD,oBAAc;AAAA,QACZ,MAAM,IAAI,IAAI,oBAAoB,YAAY,gBAAgB,OAAO;AAAA,QACrE,MAAM,IAAI,IAAI,kBAAkB,aAAa;AAAA,MAC/C;AAAA,IACF;AAAA,IACA,MAAM,aAAa;AACjB,UAAI;AACF,cAAM,qBAAqB,WAAW,aAAa,YAAY,OAAO;AAAA,MACxE,SAAS,GAAY;AACnB,gBAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,MACzB;AAAA,IACF;AAAA,IACA,gBAAgB,QAAQ;AACtB,YAAM,MAAM,cAAc,SAAS;AAEnC,YAAM,iBAAiB,CAAC,SAAuB;AAC7C,YAAI,CAAC,KAAK,WAAW,GAAG,GAAG;AACzB,cAAI,SAAS,cAAc,YAAY,IAAI,GAAG;AAC5C,mBAAO,IAAI,KAAK,EAAE,MAAM,cAAc,CAAC;AAAA,UACzC;AACA;AAAA,QACF;AAEA,6BAAqB,WAAW,aAAa,YAAY,OAAO,EAAE;AAAA,UAAM,CAAC,MACvE,QAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,QACzB;AAAA,MACF;AAEA,aAAO,QAAQ,GAAG,OAAO,cAAc;AACvC,aAAO,QAAQ,GAAG,UAAU,cAAc;AAC1C,aAAO,QAAQ,GAAG,UAAU,cAAc;AAAA,IAC5C;AAAA,IACA,UAAU,MAAM,IAAgD;AAC9D,UAAI,GAAG,WAAW,cAAc,SAAS,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,WAAW,GAAG,GAAG;AAY5E,eAAO;AAAA,UACL,MAAM,KAAK;AAAA,YACT;AAAA,YACA;AAAA;AAAA;AAAA;AAAA,UAIF;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { basename } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { TransformResult } from 'rollup';\nimport type { Logger, Plugin } from 'vite';\nimport { generateRuntimeFiles, type RuntimeFileUrls } from './vite-plugin/generateRuntimeFiles.js';\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\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}: PluginOptions = {}): Plugin {\n const hmrInjectionPattern = /(?<=import\\.meta\\.hot\\.accept[\\s\\S]+)if\\s\\(!nextExports\\)\\s+return;/u;\n\n let _viewsDir: URL;\n let _outDir: URL;\n let _logger: Logger;\n let runtimeUrls: RuntimeFileUrls;\n\n return {\n name: 'vite-plugin-file-router',\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 _outDir = pathToFileURL(outDir);\n _logger = logger;\n\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 };\n },\n async buildStart() {\n try {\n await generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger);\n } catch (e: unknown) {\n _logger.error(String(e));\n }\n },\n configureServer(server) {\n const dir = fileURLToPath(_viewsDir);\n\n const changeListener = (file: string): void => {\n if (!file.startsWith(dir)) {\n if (file === fileURLToPath(runtimeUrls.json)) {\n server.hot.send({ type: 'full-reload' });\n }\n return;\n }\n\n generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger).catch((e: unknown) =>\n _logger.error(String(e)),\n );\n };\n\n server.watcher.on('add', changeListener);\n server.watcher.on('change', changeListener);\n server.watcher.on('unlink', changeListener);\n },\n transform(code, id): Promise<TransformResult> | TransformResult {\n let modifiedCode = code;\n if (id.startsWith(fileURLToPath(_viewsDir)) && !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 modifiedCode = modifiedCode.replace(\n hmrInjectionPattern,\n `if (!nextExports) return;\n if (Object.keys(nextExports).length === 2 && 'default' in nextExports && 'config' in nextExports) {\n nextExports = { ...nextExports, config: currentExports.config };\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"],
5
+ "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,eAAe,qBAAqB;AAG7C,SAAS,4BAAkD;AAwC5C,SAAR,2BAA4C;AAAA,EACjD,WAAW;AAAA,EACX,eAAe;AAAA,EACf,aAAa,CAAC,QAAQ,MAAM;AAAA,EAC5B,YAAY;AACd,IAAmB,CAAC,GAAW;AAC7B,QAAM,sBAAsB;AAE5B,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,SAAO;AAAA,IACL,MAAM;AAAA,IACN,eAAe,EAAE,QAAQ,MAAM,OAAO,EAAE,OAAO,EAAE,GAAG;AAClD,YAAM,QAAQ,cAAc,IAAI;AAChC,YAAM,gBAAgB,IAAI,IAAI,cAAc,KAAK;AAEjD,kBAAY,IAAI,IAAI,UAAU,KAAK;AACnC,gBAAU,cAAc,MAAM;AAC9B,gBAAU;AAEV,cAAQ,KAAK,iCAAiC,OAAO,SAAS,CAAC,EAAE;AACjE,cAAQ,KAAK,qCAAqC,OAAO,aAAa,CAAC,EAAE;AACzE,cAAQ,KAAK,yBAAyB,OAAO,OAAO,CAAC,EAAE;AAEvD,oBAAc;AAAA,QACZ,MAAM,IAAI,IAAI,oBAAoB,YAAY,gBAAgB,OAAO;AAAA,QACrE,MAAM,IAAI,IAAI,kBAAkB,aAAa;AAAA,MAC/C;AAAA,IACF;AAAA,IACA,MAAM,aAAa;AACjB,UAAI;AACF,cAAM,qBAAqB,WAAW,aAAa,YAAY,OAAO;AAAA,MACxE,SAAS,GAAY;AACnB,gBAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,MACzB;AAAA,IACF;AAAA,IACA,gBAAgB,QAAQ;AACtB,YAAM,MAAM,cAAc,SAAS;AAEnC,YAAM,iBAAiB,CAAC,SAAuB;AAC7C,YAAI,CAAC,KAAK,WAAW,GAAG,GAAG;AACzB,cAAI,SAAS,cAAc,YAAY,IAAI,GAAG;AAC5C,mBAAO,IAAI,KAAK,EAAE,MAAM,cAAc,CAAC;AAAA,UACzC;AACA;AAAA,QACF;AAEA,6BAAqB,WAAW,aAAa,YAAY,OAAO,EAAE;AAAA,UAAM,CAAC,MACvE,QAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,QACzB;AAAA,MACF;AAEA,aAAO,QAAQ,GAAG,OAAO,cAAc;AACvC,aAAO,QAAQ,GAAG,UAAU,cAAc;AAC1C,aAAO,QAAQ,GAAG,UAAU,cAAc;AAAA,IAC5C;AAAA,IACA,UAAU,MAAM,IAAgD;AAC9D,UAAI,eAAe;AACnB,UAAI,GAAG,WAAW,cAAc,SAAS,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,WAAW,GAAG,GAAG;AAC5E,YAAI,WAAW;AAYb,yBAAe,aAAa;AAAA,YAC1B;AAAA,YACA;AAAA;AAAA;AAAA;AAAA,UAIF;AAAA,QACF,OAAO;AAEL,gBAAM,gBAAgB,4CAA4C,KAAK,YAAY;AAEnF,cAAI,eAAe,QAAQ;AACzB,kBAAM,CAAC,EAAE,YAAY,IAAI;AACzB,4BAAgB,yBAAyB,YAAY,uBAAuB,YAAY;AAAA;AAAA,UAC1F;AAAA,QACF;AAEA,eAAO;AAAA,UACL,MAAM;AAAA,QACR;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,11 +0,0 @@
1
- import type { RouteObject as ReactRouteObject } from 'react-router-dom';
2
- import type { AgnosticRoute } from '../types.js';
3
- /**
4
- * Transforms framework-agnostic route tree into a format that can be used by React Router.
5
- *
6
- * @param routes - Generated routes
7
- *
8
- * @returns The React Router tree.
9
- */
10
- export declare function toReactRouter(routes: AgnosticRoute): ReactRouteObject;
11
- //# sourceMappingURL=toReactRouter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"toReactRouter.d.ts","sourceRoot":"","sources":["../src/runtime/toReactRouter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGxE,OAAO,KAAK,EAAE,aAAa,EAAuB,MAAM,aAAa,CAAC;AAMtE;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,CAsBrE"}