@fluixi/core 1.0.0-alpha.100

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 (253) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +334 -0
  3. package/dist/cdn/core.global.js +2 -0
  4. package/dist/cli/generate.cjs +5639 -0
  5. package/dist/cli/generate.d.ts +7 -0
  6. package/dist/cli/generate.d.ts.map +1 -0
  7. package/dist/cli/generate.js +2580 -0
  8. package/dist/cli/generate.mjs +5644 -0
  9. package/dist/cli/index.cjs +5639 -0
  10. package/dist/cli/index.d.ts +7 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +2580 -0
  13. package/dist/cli/index.mjs +5644 -0
  14. package/dist/cli/run.cjs +64 -0
  15. package/dist/cli/run.d.ts +3 -0
  16. package/dist/cli/run.d.ts.map +1 -0
  17. package/dist/cli/run.js +62 -0
  18. package/dist/cli/run.mjs +40 -0
  19. package/dist/fluixi.d.ts +5 -0
  20. package/dist/src/cdn/chunk-RKBFEFZP.mjs +987 -0
  21. package/dist/src/cdn/core.cjs +1592 -0
  22. package/dist/src/cdn/core.d.ts +16 -0
  23. package/dist/src/cdn/core.d.ts.map +1 -0
  24. package/dist/src/cdn/core.js +15 -0
  25. package/dist/src/cdn/core.mjs +714 -0
  26. package/dist/src/cdn/router-V2THK27H.mjs +74 -0
  27. package/dist/src/index.cjs +1711 -0
  28. package/dist/src/index.d.ts +4 -0
  29. package/dist/src/index.d.ts.map +1 -0
  30. package/dist/src/index.js +11 -0
  31. package/dist/src/index.mjs +1833 -0
  32. package/dist/src/jsx-dev-runtime.cjs +26 -0
  33. package/dist/src/jsx-dev-runtime.d.ts +2 -0
  34. package/dist/src/jsx-dev-runtime.d.ts.map +1 -0
  35. package/dist/src/jsx-dev-runtime.js +1 -0
  36. package/dist/src/jsx-dev-runtime.mjs +24 -0
  37. package/dist/src/jsx-runtime.cjs +26 -0
  38. package/dist/src/jsx-runtime.d.ts +7 -0
  39. package/dist/src/jsx-runtime.d.ts.map +1 -0
  40. package/dist/src/jsx-runtime.js +8 -0
  41. package/dist/src/jsx-runtime.mjs +24 -0
  42. package/dist/src/lib/client/chunk-FDOPDB6Z.mjs +53 -0
  43. package/dist/src/lib/client/index.cjs +1210 -0
  44. package/dist/src/lib/client/index.d.ts +51 -0
  45. package/dist/src/lib/client/index.d.ts.map +1 -0
  46. package/dist/src/lib/client/index.js +174 -0
  47. package/dist/src/lib/client/index.mjs +293 -0
  48. package/dist/src/lib/client/router-4PTBPZLQ.mjs +891 -0
  49. package/dist/src/lib/context/context.cjs +81 -0
  50. package/dist/src/lib/context/context.d.ts +73 -0
  51. package/dist/src/lib/context/context.d.ts.map +1 -0
  52. package/dist/src/lib/context/context.js +93 -0
  53. package/dist/src/lib/context/context.mjs +64 -0
  54. package/dist/src/lib/context/index.cjs +83 -0
  55. package/dist/src/lib/context/index.d.ts +3 -0
  56. package/dist/src/lib/context/index.d.ts.map +1 -0
  57. package/dist/src/lib/context/index.js +1 -0
  58. package/dist/src/lib/context/index.mjs +64 -0
  59. package/dist/src/lib/control-flow.cjs +35 -0
  60. package/dist/src/lib/control-flow.d.ts +4 -0
  61. package/dist/src/lib/control-flow.d.ts.map +1 -0
  62. package/dist/src/lib/control-flow.js +1 -0
  63. package/dist/src/lib/control-flow.mjs +25 -0
  64. package/dist/src/lib/core.cjs +1710 -0
  65. package/dist/src/lib/core.d.ts +91 -0
  66. package/dist/src/lib/core.d.ts.map +1 -0
  67. package/dist/src/lib/core.js +158 -0
  68. package/dist/src/lib/core.mjs +1833 -0
  69. package/dist/src/lib/env/index.cjs +44 -0
  70. package/dist/src/lib/env/index.d.ts +26 -0
  71. package/dist/src/lib/env/index.d.ts.map +1 -0
  72. package/dist/src/lib/env/index.js +31 -0
  73. package/dist/src/lib/env/index.mjs +23 -0
  74. package/dist/src/lib/i18n/index.cjs +123 -0
  75. package/dist/src/lib/i18n/index.d.ts +110 -0
  76. package/dist/src/lib/i18n/index.d.ts.map +1 -0
  77. package/dist/src/lib/i18n/index.js +164 -0
  78. package/dist/src/lib/i18n/index.mjs +103 -0
  79. package/dist/src/lib/index.cjs +598 -0
  80. package/dist/src/lib/index.d.ts +81 -0
  81. package/dist/src/lib/index.d.ts.map +1 -0
  82. package/dist/src/lib/index.js +130 -0
  83. package/dist/src/lib/index.mjs +688 -0
  84. package/dist/src/lib/isomorphic.cjs +90 -0
  85. package/dist/src/lib/isomorphic.d.ts +114 -0
  86. package/dist/src/lib/isomorphic.d.ts.map +1 -0
  87. package/dist/src/lib/isomorphic.js +132 -0
  88. package/dist/src/lib/isomorphic.mjs +69 -0
  89. package/dist/src/lib/jsx-runtime/index.cjs +21 -0
  90. package/dist/src/lib/jsx-runtime/index.d.ts +13 -0
  91. package/dist/src/lib/jsx-runtime/index.d.ts.map +1 -0
  92. package/dist/src/lib/jsx-runtime/index.js +13 -0
  93. package/dist/src/lib/jsx-runtime/index.mjs +4 -0
  94. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +21 -0
  95. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts +2 -0
  96. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts.map +1 -0
  97. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.js +1 -0
  98. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +4 -0
  99. package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +302 -0
  100. package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts +35 -0
  101. package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts.map +1 -0
  102. package/dist/src/lib/plugins/fluixi-routes-plugin.js +118 -0
  103. package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +271 -0
  104. package/dist/src/lib/plugins/index.cjs +311 -0
  105. package/dist/src/lib/plugins/index.d.ts +6 -0
  106. package/dist/src/lib/plugins/index.d.ts.map +1 -0
  107. package/dist/src/lib/plugins/index.js +7 -0
  108. package/dist/src/lib/plugins/index.mjs +279 -0
  109. package/dist/src/lib/plugins/route-codegen.cjs +73 -0
  110. package/dist/src/lib/plugins/route-codegen.d.ts +12 -0
  111. package/dist/src/lib/plugins/route-codegen.d.ts.map +1 -0
  112. package/dist/src/lib/plugins/route-codegen.js +80 -0
  113. package/dist/src/lib/plugins/route-codegen.mjs +52 -0
  114. package/dist/src/lib/plugins/route-scanner.cjs +189 -0
  115. package/dist/src/lib/plugins/route-scanner.d.ts +38 -0
  116. package/dist/src/lib/plugins/route-scanner.d.ts.map +1 -0
  117. package/dist/src/lib/plugins/route-scanner.js +215 -0
  118. package/dist/src/lib/plugins/route-scanner.mjs +158 -0
  119. package/dist/src/lib/plugins/vite.cjs +27 -0
  120. package/dist/src/lib/plugins/vite.d.ts +3 -0
  121. package/dist/src/lib/plugins/vite.d.ts.map +1 -0
  122. package/dist/src/lib/plugins/vite.js +1 -0
  123. package/dist/src/lib/plugins/vite.mjs +6 -0
  124. package/dist/src/lib/render/await.cjs +38 -0
  125. package/dist/src/lib/render/await.d.ts +26 -0
  126. package/dist/src/lib/render/await.d.ts.map +1 -0
  127. package/dist/src/lib/render/await.js +45 -0
  128. package/dist/src/lib/render/await.mjs +17 -0
  129. package/dist/src/lib/render/component.cjs +221 -0
  130. package/dist/src/lib/render/component.d.ts +296 -0
  131. package/dist/src/lib/render/component.d.ts.map +1 -0
  132. package/dist/src/lib/render/component.js +514 -0
  133. package/dist/src/lib/render/component.mjs +214 -0
  134. package/dist/src/lib/render/deferred.cjs +175 -0
  135. package/dist/src/lib/render/deferred.d.ts +36 -0
  136. package/dist/src/lib/render/deferred.d.ts.map +1 -0
  137. package/dist/src/lib/render/deferred.js +141 -0
  138. package/dist/src/lib/render/deferred.mjs +158 -0
  139. package/dist/src/lib/render/index.cjs +573 -0
  140. package/dist/src/lib/render/index.d.ts +98 -0
  141. package/dist/src/lib/render/index.d.ts.map +1 -0
  142. package/dist/src/lib/render/index.js +180 -0
  143. package/dist/src/lib/render/index.mjs +590 -0
  144. package/dist/src/lib/render/lazy.cjs +67 -0
  145. package/dist/src/lib/render/lazy.d.ts +49 -0
  146. package/dist/src/lib/render/lazy.d.ts.map +1 -0
  147. package/dist/src/lib/render/lazy.js +105 -0
  148. package/dist/src/lib/render/lazy.mjs +50 -0
  149. package/dist/src/lib/render/suspense.cjs +141 -0
  150. package/dist/src/lib/render/suspense.d.ts +65 -0
  151. package/dist/src/lib/render/suspense.d.ts.map +1 -0
  152. package/dist/src/lib/render/suspense.js +196 -0
  153. package/dist/src/lib/render/suspense.mjs +136 -0
  154. package/dist/src/lib/render/versions.cjs +35 -0
  155. package/dist/src/lib/render/versions.d.ts +2 -0
  156. package/dist/src/lib/render/versions.d.ts.map +1 -0
  157. package/dist/src/lib/render/versions.js +21 -0
  158. package/dist/src/lib/render/versions.mjs +14 -0
  159. package/dist/src/lib/router/data.cjs +228 -0
  160. package/dist/src/lib/router/data.d.ts +142 -0
  161. package/dist/src/lib/router/data.d.ts.map +1 -0
  162. package/dist/src/lib/router/data.js +258 -0
  163. package/dist/src/lib/router/data.mjs +214 -0
  164. package/dist/src/lib/router/index.cjs +921 -0
  165. package/dist/src/lib/router/index.d.ts +274 -0
  166. package/dist/src/lib/router/index.d.ts.map +1 -0
  167. package/dist/src/lib/router/index.js +904 -0
  168. package/dist/src/lib/router/index.mjs +938 -0
  169. package/dist/src/lib/router/scroll.cjs +79 -0
  170. package/dist/src/lib/router/scroll.d.ts +19 -0
  171. package/dist/src/lib/router/scroll.d.ts.map +1 -0
  172. package/dist/src/lib/router/scroll.js +68 -0
  173. package/dist/src/lib/router/scroll.mjs +58 -0
  174. package/dist/src/lib/router/transition.cjs +75 -0
  175. package/dist/src/lib/router/transition.d.ts +81 -0
  176. package/dist/src/lib/router/transition.d.ts.map +1 -0
  177. package/dist/src/lib/router/transition.js +136 -0
  178. package/dist/src/lib/router/transition.mjs +54 -0
  179. package/dist/src/lib/server/hydration.cjs +169 -0
  180. package/dist/src/lib/server/hydration.d.ts +198 -0
  181. package/dist/src/lib/server/hydration.d.ts.map +1 -0
  182. package/dist/src/lib/server/hydration.js +345 -0
  183. package/dist/src/lib/server/hydration.mjs +148 -0
  184. package/dist/src/lib/server/index.cjs +406 -0
  185. package/dist/src/lib/server/index.d.ts +115 -0
  186. package/dist/src/lib/server/index.d.ts.map +1 -0
  187. package/dist/src/lib/server/index.js +164 -0
  188. package/dist/src/lib/server/index.mjs +384 -0
  189. package/dist/src/lib/server/reactive.cjs +197 -0
  190. package/dist/src/lib/server/reactive.d.ts +239 -0
  191. package/dist/src/lib/server/reactive.d.ts.map +1 -0
  192. package/dist/src/lib/server/reactive.js +413 -0
  193. package/dist/src/lib/server/reactive.mjs +176 -0
  194. package/dist/src/lib/storage/cache.cjs +51 -0
  195. package/dist/src/lib/storage/cache.d.ts +18 -0
  196. package/dist/src/lib/storage/cache.d.ts.map +1 -0
  197. package/dist/src/lib/storage/cache.js +42 -0
  198. package/dist/src/lib/storage/cache.mjs +30 -0
  199. package/dist/src/lib/storage/index.cjs +246 -0
  200. package/dist/src/lib/storage/index.d.ts +83 -0
  201. package/dist/src/lib/storage/index.d.ts.map +1 -0
  202. package/dist/src/lib/storage/index.js +179 -0
  203. package/dist/src/lib/storage/index.mjs +231 -0
  204. package/dist/src/lib/storage/persisted.cjs +91 -0
  205. package/dist/src/lib/storage/persisted.d.ts +53 -0
  206. package/dist/src/lib/storage/persisted.d.ts.map +1 -0
  207. package/dist/src/lib/storage/persisted.js +75 -0
  208. package/dist/src/lib/storage/persisted.mjs +70 -0
  209. package/dist/src/lib/theme/color-scheme.cjs +281 -0
  210. package/dist/src/lib/theme/color-scheme.d.ts +61 -0
  211. package/dist/src/lib/theme/color-scheme.d.ts.map +1 -0
  212. package/dist/src/lib/theme/color-scheme.js +120 -0
  213. package/dist/src/lib/theme/color-scheme.mjs +265 -0
  214. package/dist/src/lib/theme/index.cjs +267 -0
  215. package/dist/src/lib/theme/index.d.ts +78 -0
  216. package/dist/src/lib/theme/index.d.ts.map +1 -0
  217. package/dist/src/lib/theme/index.js +135 -0
  218. package/dist/src/lib/theme/index.mjs +251 -0
  219. package/dist/src/lib/theme/provider.cjs +346 -0
  220. package/dist/src/lib/theme/provider.d.ts +34 -0
  221. package/dist/src/lib/theme/provider.d.ts.map +1 -0
  222. package/dist/src/lib/theme/provider.js +60 -0
  223. package/dist/src/lib/theme/provider.mjs +332 -0
  224. package/dist/src/lib/utils/index.cjs +209 -0
  225. package/dist/src/lib/utils/index.d.ts +286 -0
  226. package/dist/src/lib/utils/index.d.ts.map +1 -0
  227. package/dist/src/lib/utils/index.js +434 -0
  228. package/dist/src/lib/utils/index.mjs +188 -0
  229. package/dist/src/routes.cjs +37 -0
  230. package/dist/src/routes.d.ts +22 -0
  231. package/dist/src/routes.d.ts.map +1 -0
  232. package/dist/src/routes.js +20 -0
  233. package/dist/src/routes.mjs +17 -0
  234. package/dist/src/server.cjs +405 -0
  235. package/dist/src/server.d.ts +63 -0
  236. package/dist/src/server.d.ts.map +1 -0
  237. package/dist/src/server.js +62 -0
  238. package/dist/src/server.mjs +382 -0
  239. package/dist/src/utils.cjs +21 -0
  240. package/dist/src/utils.d.ts +10 -0
  241. package/dist/src/utils.d.ts.map +1 -0
  242. package/dist/src/utils.js +9 -0
  243. package/dist/src/utils.mjs +4 -0
  244. package/dist/src/version.generated.cjs +26 -0
  245. package/dist/src/version.generated.d.ts +3 -0
  246. package/dist/src/version.generated.d.ts.map +1 -0
  247. package/dist/src/version.generated.js +3 -0
  248. package/dist/src/version.generated.mjs +5 -0
  249. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  250. package/package.json +164 -0
  251. package/src/index.ts +19 -0
  252. package/types/fluixi.d.ts +5 -0
  253. package/types/vinxi.d.ts +50 -0
@@ -0,0 +1,279 @@
1
+ /*! @fluixi/core v1.0.0-alpha.100 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
+
3
+ // src/lib/plugins/vite.ts
4
+ import { fluixi, createVitePlugin } from "@fluixi/vite-plugin";
5
+
6
+ // src/lib/plugins/fluixi-routes-plugin.ts
7
+ import * as path2 from "path";
8
+ import { nodePolyfills } from "vite-plugin-node-polyfills";
9
+
10
+ // src/lib/plugins/route-scanner.ts
11
+ import * as fs from "fs";
12
+ import * as path from "path";
13
+ var DEFAULT_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
14
+ function scanRoutes(routesDir, options = {}) {
15
+ const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
16
+ const absDir = path.resolve(routesDir);
17
+ if (!fs.existsSync(absDir)) {
18
+ return [];
19
+ }
20
+ return scanDirectory(absDir, absDir, "", extensions);
21
+ }
22
+ function scanDirectory(dir, rootDir, parentUrlPath, extensions) {
23
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
24
+ const routes = [];
25
+ const layoutRoute = null;
26
+ const files = [];
27
+ const dirs = [];
28
+ for (const entry of entries) {
29
+ if (entry.isDirectory()) {
30
+ dirs.push(entry);
31
+ } else if (entry.isFile() && hasValidExtension(entry.name, extensions)) {
32
+ files.push(entry);
33
+ }
34
+ }
35
+ let layout;
36
+ const nonLayoutFiles = [];
37
+ for (const file of files) {
38
+ const baseName = stripExtension(file.name);
39
+ if (baseName === "layout") {
40
+ layout = buildFileRoute(file.name, dir, rootDir, parentUrlPath, true);
41
+ } else {
42
+ nonLayoutFiles.push(file);
43
+ }
44
+ }
45
+ const fileRoutes = [];
46
+ for (const file of nonLayoutFiles) {
47
+ fileRoutes.push(
48
+ buildFileRoute(file.name, dir, rootDir, parentUrlPath, false)
49
+ );
50
+ }
51
+ const dirRoutes = [];
52
+ for (const d of dirs) {
53
+ const dirName = d.name;
54
+ const isGroup = isRouteGroup(dirName);
55
+ const segment = isGroup ? "" : dirNameToSegment(dirName);
56
+ const childUrlPath = segment ? joinUrlPath(parentUrlPath, segment) : parentUrlPath;
57
+ const children = scanDirectory(
58
+ path.join(dir, dirName),
59
+ rootDir,
60
+ childUrlPath,
61
+ extensions
62
+ );
63
+ if (isGroup) {
64
+ dirRoutes.push(...children);
65
+ } else {
66
+ const childLayout = children.find((c) => c.isLayout && c.urlPath === childUrlPath);
67
+ const childNonLayout = children.filter((c) => !c.isLayout || c.urlPath !== childUrlPath);
68
+ if (childLayout) {
69
+ childLayout.children = [...childLayout.children, ...childNonLayout];
70
+ dirRoutes.push(childLayout);
71
+ } else if (childNonLayout.length > 0) {
72
+ dirRoutes.push(...childNonLayout);
73
+ }
74
+ }
75
+ }
76
+ if (layout) {
77
+ layout.children = [...fileRoutes, ...dirRoutes];
78
+ routes.push(layout);
79
+ } else {
80
+ routes.push(...fileRoutes, ...dirRoutes);
81
+ }
82
+ return sortRoutes(routes);
83
+ }
84
+ function buildFileRoute(fileName, dir, rootDir, parentUrlPath, isLayout) {
85
+ const baseName = stripExtension(fileName);
86
+ const filePath = path.relative(rootDir, path.join(dir, fileName));
87
+ const isIndex = baseName === "index";
88
+ const isCatchAll = baseName.startsWith("[...");
89
+ const isDynamic = !isCatchAll && baseName.startsWith("[") && baseName.endsWith("]");
90
+ let urlSegment;
91
+ if (isLayout) {
92
+ urlSegment = "";
93
+ } else if (isIndex) {
94
+ urlSegment = "/";
95
+ } else if (isCatchAll) {
96
+ const paramName = baseName.slice(4, -1);
97
+ urlSegment = `*${paramName}`;
98
+ } else if (isDynamic) {
99
+ const paramName = baseName.slice(1, -1);
100
+ urlSegment = `:${paramName}`;
101
+ } else {
102
+ urlSegment = baseName;
103
+ }
104
+ const urlPath = isLayout ? parentUrlPath || "/" : isIndex ? parentUrlPath || "/" : joinUrlPath(parentUrlPath, urlSegment);
105
+ const absoluteFilePath = path.join(dir, fileName);
106
+ let hasRouteData = false;
107
+ try {
108
+ const content = fs.readFileSync(absoluteFilePath, "utf-8");
109
+ hasRouteData = /export\s+(const|function|async\s+function)\s+routeData\b/.test(content);
110
+ } catch {
111
+ }
112
+ return {
113
+ filePath: filePath.replace(/\\/g, "/"),
114
+ // normalize to forward slashes
115
+ urlSegment,
116
+ urlPath,
117
+ isLayout,
118
+ isIndex,
119
+ isDynamic,
120
+ isCatchAll,
121
+ isGroup: false,
122
+ children: [],
123
+ parentUrlPath,
124
+ hasRouteData,
125
+ rootDir,
126
+ dir
127
+ };
128
+ }
129
+ function hasValidExtension(fileName, extensions) {
130
+ return extensions.some((ext) => fileName.endsWith(ext));
131
+ }
132
+ function stripExtension(fileName) {
133
+ const lastDot = fileName.lastIndexOf(".");
134
+ return lastDot > 0 ? fileName.slice(0, lastDot) : fileName;
135
+ }
136
+ function isRouteGroup(dirName) {
137
+ return dirName.startsWith("(") && dirName.endsWith(")");
138
+ }
139
+ function dirNameToSegment(dirName) {
140
+ if (dirName.startsWith("[...") && dirName.endsWith("]")) {
141
+ return "*" + dirName.slice(4, -1);
142
+ }
143
+ if (dirName.startsWith("[") && dirName.endsWith("]")) {
144
+ return ":" + dirName.slice(1, -1);
145
+ }
146
+ return dirName;
147
+ }
148
+ function joinUrlPath(parent, segment) {
149
+ const base = parent.endsWith("/") ? parent : parent + "/";
150
+ return base + segment;
151
+ }
152
+ function sortRoutes(routes) {
153
+ return routes.sort((a, b) => {
154
+ if (a.isLayout && !b.isLayout) return -1;
155
+ if (!a.isLayout && b.isLayout) return 1;
156
+ if (a.isIndex && !b.isIndex) return -1;
157
+ if (!a.isIndex && b.isIndex) return 1;
158
+ if (!a.isDynamic && !a.isCatchAll && (b.isDynamic || b.isCatchAll)) return -1;
159
+ if ((a.isDynamic || a.isCatchAll) && !b.isDynamic && !b.isCatchAll) return 1;
160
+ if (a.isDynamic && b.isCatchAll) return -1;
161
+ if (a.isCatchAll && b.isDynamic) return 1;
162
+ return a.urlSegment.localeCompare(b.urlSegment);
163
+ });
164
+ }
165
+
166
+ // src/lib/plugins/route-codegen.ts
167
+ function generateRouteModule(routes, routesDir) {
168
+ const lines = [];
169
+ lines.push(`// Auto-generated by fluixi route plugin`);
170
+ lines.push(`// Do not edit manually`);
171
+ lines.push(``);
172
+ lines.push(`import { lazy } from '@fluixi/core/router';`);
173
+ lines.push(``);
174
+ lines.push(`export const routes = ${generateRouteArray(routes, routesDir, 0)};`);
175
+ lines.push(``);
176
+ return lines.join("\n");
177
+ }
178
+ function generateRouteArray(routes, routesDir, depth) {
179
+ if (routes.length === 0) return "[]";
180
+ const indent = " ".repeat(depth);
181
+ const items = routes.map((route) => generateRouteObject(route, routesDir, depth + 1));
182
+ return `[
183
+ ${items.join(",\n")}
184
+ ${indent}]`;
185
+ }
186
+ function generateRouteObject(route, routesDir, depth) {
187
+ const indent = " ".repeat(depth);
188
+ const props = [];
189
+ const importPath = buildImportPath(route.filePath, routesDir);
190
+ const routePath = computeRoutePath(route);
191
+ props.push(`${indent} path: ${JSON.stringify(routePath)}`);
192
+ props.push(`${indent} component: lazy(() => import(${JSON.stringify(importPath)}))`);
193
+ if (route.hasRouteData) {
194
+ props.push(
195
+ `${indent} data: (args, __owner, __isCancelled, __deps) => import(${JSON.stringify(
196
+ importPath
197
+ )}).then(m => { if (typeof __isCancelled === 'function' && __isCancelled()) return undefined; if (!m.routeData) return undefined; if (typeof m.routeDeps === 'function') { if (typeof __deps === 'function') __deps(m.routeDeps); args = { ...args, deps: m.routeDeps() }; } return m.routeData(args); })`
198
+ );
199
+ }
200
+ if (route.children.length > 0) {
201
+ const childArray = generateRouteArray(route.children, routesDir, depth + 1);
202
+ props.push(`${indent} children: ${childArray}`);
203
+ }
204
+ return `${indent}{
205
+ ${props.join(",\n")}
206
+ ${indent}}`;
207
+ }
208
+ function computeRoutePath(route) {
209
+ return route.urlPath;
210
+ }
211
+ function buildImportPath(filePath, routesDir) {
212
+ const base = routesDir.replace(/\\/g, "/").replace(/\/$/, "");
213
+ return `${base}/${filePath}`;
214
+ }
215
+
216
+ // src/lib/plugins/fluixi-routes-plugin.ts
217
+ var VIRTUAL_MODULE_ID = "virtual:fluixi-routes";
218
+ var RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_MODULE_ID;
219
+ var PUBLIC_MODULE_ID = "@fluixi/core/routes";
220
+ function fluixiRoutesPlugin(options = {}) {
221
+ const routesDir = options.routesDir ?? "src/routes";
222
+ const extensions = options.extensions ?? [".tsx", ".ts", ".jsx", ".js"];
223
+ let projectRoot = "";
224
+ let lastGenerated = null;
225
+ function generateModule(root) {
226
+ const absRoutesDir = path2.resolve(root, routesDir);
227
+ const scanOpts = { extensions };
228
+ const routes = scanRoutes(absRoutesDir, scanOpts);
229
+ return generateRouteModule(routes, "./" + routesDir);
230
+ }
231
+ const usePolyfills = options.polyfills !== false;
232
+ const polyfillsPlugin = usePolyfills ? Object.assign({}, nodePolyfills({ include: ["fs", "path"] }), { apply: "serve" }) : null;
233
+ const routesPlugin = {
234
+ name: "fluixi-routes",
235
+ enforce: "pre",
236
+ configResolved(config) {
237
+ projectRoot = config.root || process.cwd();
238
+ },
239
+ resolveId(id) {
240
+ if (id === VIRTUAL_MODULE_ID || id === PUBLIC_MODULE_ID) {
241
+ return RESOLVED_VIRTUAL_ID;
242
+ }
243
+ return null;
244
+ },
245
+ load(id) {
246
+ if (id !== RESOLVED_VIRTUAL_ID) return null;
247
+ lastGenerated = generateModule(projectRoot || process.cwd());
248
+ return lastGenerated;
249
+ },
250
+ // Vite dev server: watch routes directory for changes
251
+ configureServer(server) {
252
+ const root = projectRoot || process.cwd();
253
+ const absRoutesDir = path2.resolve(root, routesDir);
254
+ server.watcher.add(absRoutesDir);
255
+ server.watcher.on("all", (event, filePath) => {
256
+ if (!filePath.startsWith(absRoutesDir)) return;
257
+ if (!["add", "unlink", "addDir", "unlinkDir", "change"].includes(event)) return;
258
+ if (event !== "addDir" && event !== "unlinkDir" && !extensions.some((ext) => filePath.endsWith(ext))) {
259
+ return;
260
+ }
261
+ const next = generateModule(root);
262
+ if (next === lastGenerated) return;
263
+ lastGenerated = next;
264
+ const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ID);
265
+ if (mod) {
266
+ server.moduleGraph.invalidateModule(mod);
267
+ server.ws.send({ type: "full-reload" });
268
+ }
269
+ });
270
+ }
271
+ };
272
+ return polyfillsPlugin ? [polyfillsPlugin, routesPlugin] : [routesPlugin];
273
+ }
274
+ export {
275
+ createVitePlugin,
276
+ fluixi,
277
+ fluixiRoutesPlugin,
278
+ scanRoutes
279
+ };
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/plugins/route-codegen.ts
21
+ var route_codegen_exports = {};
22
+ __export(route_codegen_exports, {
23
+ generateRouteModule: () => generateRouteModule
24
+ });
25
+ module.exports = __toCommonJS(route_codegen_exports);
26
+ function generateRouteModule(routes, routesDir) {
27
+ const lines = [];
28
+ lines.push(`// Auto-generated by fluixi route plugin`);
29
+ lines.push(`// Do not edit manually`);
30
+ lines.push(``);
31
+ lines.push(`import { lazy } from '@fluixi/core/router';`);
32
+ lines.push(``);
33
+ lines.push(`export const routes = ${generateRouteArray(routes, routesDir, 0)};`);
34
+ lines.push(``);
35
+ return lines.join("\n");
36
+ }
37
+ function generateRouteArray(routes, routesDir, depth) {
38
+ if (routes.length === 0) return "[]";
39
+ const indent = " ".repeat(depth);
40
+ const items = routes.map((route) => generateRouteObject(route, routesDir, depth + 1));
41
+ return `[
42
+ ${items.join(",\n")}
43
+ ${indent}]`;
44
+ }
45
+ function generateRouteObject(route, routesDir, depth) {
46
+ const indent = " ".repeat(depth);
47
+ const props = [];
48
+ const importPath = buildImportPath(route.filePath, routesDir);
49
+ const routePath = computeRoutePath(route);
50
+ props.push(`${indent} path: ${JSON.stringify(routePath)}`);
51
+ props.push(`${indent} component: lazy(() => import(${JSON.stringify(importPath)}))`);
52
+ if (route.hasRouteData) {
53
+ props.push(
54
+ `${indent} data: (args, __owner, __isCancelled, __deps) => import(${JSON.stringify(
55
+ importPath
56
+ )}).then(m => { if (typeof __isCancelled === 'function' && __isCancelled()) return undefined; if (!m.routeData) return undefined; if (typeof m.routeDeps === 'function') { if (typeof __deps === 'function') __deps(m.routeDeps); args = { ...args, deps: m.routeDeps() }; } return m.routeData(args); })`
57
+ );
58
+ }
59
+ if (route.children.length > 0) {
60
+ const childArray = generateRouteArray(route.children, routesDir, depth + 1);
61
+ props.push(`${indent} children: ${childArray}`);
62
+ }
63
+ return `${indent}{
64
+ ${props.join(",\n")}
65
+ ${indent}}`;
66
+ }
67
+ function computeRoutePath(route) {
68
+ return route.urlPath;
69
+ }
70
+ function buildImportPath(filePath, routesDir) {
71
+ const base = routesDir.replace(/\\/g, "/").replace(/\/$/, "");
72
+ return `${base}/${filePath}`;
73
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @module @fluixi/core/plugins/route-codegen
3
+ *
4
+ * Generates an ES module from a ScannedRoute tree.
5
+ * Uses lazy() for code-split component loading with Suspense integration.
6
+ */
7
+ import type { ScannedRoute } from './route-scanner.js';
8
+ /**
9
+ * Generate a virtual ES module string from a scanned route tree.
10
+ */
11
+ export declare function generateRouteModule(routes: ScannedRoute[], routesDir: string): string;
12
+ //# sourceMappingURL=route-codegen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-codegen.d.ts","sourceRoot":"","sources":["../../../../src/lib/plugins/route-codegen.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAevD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EAAE,EACtB,SAAS,EAAE,MAAM,GAChB,MAAM,CAeR"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @module @fluixi/core/plugins/route-codegen
3
+ *
4
+ * Generates an ES module from a ScannedRoute tree.
5
+ * Uses lazy() for code-split component loading with Suspense integration.
6
+ */
7
+ /**
8
+ * Collect all routes into a flat list for import generation.
9
+ */
10
+ function collectRoutes(routes, out = []) {
11
+ for (const route of routes) {
12
+ out.push(route);
13
+ if (route.children.length > 0) {
14
+ collectRoutes(route.children, out);
15
+ }
16
+ }
17
+ return out;
18
+ }
19
+ /**
20
+ * Generate a virtual ES module string from a scanned route tree.
21
+ */
22
+ export function generateRouteModule(routes, routesDir) {
23
+ const lines = [];
24
+ lines.push(`// Auto-generated by fluixi route plugin`);
25
+ lines.push(`// Do not edit manually`);
26
+ lines.push(``);
27
+ // router is lit-free; the old './router' barrel imported @fluixi/lit (extends
28
+ // HTMLElement at module init) which crashed SSR/prod server bundles. (runWithOwner was
29
+ // imported here but never used in the generated output: dropped.)
30
+ lines.push(`import { lazy } from '@fluixi/core/router';`);
31
+ lines.push(``);
32
+ lines.push(`export const routes = ${generateRouteArray(routes, routesDir, 0)};`);
33
+ lines.push(``);
34
+ return lines.join('\n');
35
+ }
36
+ function generateRouteArray(routes, routesDir, depth) {
37
+ if (routes.length === 0)
38
+ return '[]';
39
+ const indent = ' '.repeat(depth);
40
+ const items = routes.map((route) => generateRouteObject(route, routesDir, depth + 1));
41
+ return `[\n${items.join(',\n')}\n${indent}]`;
42
+ }
43
+ function generateRouteObject(route, routesDir, depth) {
44
+ const indent = ' '.repeat(depth);
45
+ const props = [];
46
+ const importPath = buildImportPath(route.filePath, routesDir);
47
+ // Path
48
+ const routePath = computeRoutePath(route);
49
+ props.push(`${indent} path: ${JSON.stringify(routePath)}`);
50
+ // Component: lazy-loaded via dynamic import + Suspense
51
+ props.push(`${indent} component: lazy(() => import(${JSON.stringify(importPath)}))`);
52
+ // Data loader, only generated for routes that export `routeData`
53
+ // This prevents unnecessary async imports and avoids calling d.data() for
54
+ // levels that have no data, which kept the router's if (d.data) check clean.
55
+ if (route.hasRouteData) {
56
+ props.push(`${indent} data: (args, __owner, __isCancelled, __deps) => import(${JSON.stringify(importPath)}).then(m => { if (typeof __isCancelled === 'function' && __isCancelled()) return undefined; if (!m.routeData) return undefined; if (typeof m.routeDeps === 'function') { if (typeof __deps === 'function') __deps(m.routeDeps); args = { ...args, deps: m.routeDeps() }; } return m.routeData(args); })`);
57
+ // props.push(`${indent} data: (args) => import(${JSON.stringify(importPath)}).then(m => m.routeData ? m.routeData(args) : undefined)`);
58
+ }
59
+ // Children
60
+ if (route.children.length > 0) {
61
+ const childArray = generateRouteArray(route.children, routesDir, depth + 1);
62
+ props.push(`${indent} children: ${childArray}`);
63
+ }
64
+ return `${indent}{\n${props.join(',\n')}\n${indent}}`;
65
+ }
66
+ /**
67
+ * Compute the route path for a RouteDefinition.
68
+ * The scanner already resolves groups, dynamic segments, and index paths
69
+ * into the correct urlPath, just use it directly.
70
+ */
71
+ function computeRoutePath(route) {
72
+ return route.urlPath;
73
+ }
74
+ /**
75
+ * Build the import path for a route file.
76
+ */
77
+ function buildImportPath(filePath, routesDir) {
78
+ const base = routesDir.replace(/\\/g, '/').replace(/\/$/, '');
79
+ return `${base}/${filePath}`;
80
+ }
@@ -0,0 +1,52 @@
1
+ // src/lib/plugins/route-codegen.ts
2
+ function generateRouteModule(routes, routesDir) {
3
+ const lines = [];
4
+ lines.push(`// Auto-generated by fluixi route plugin`);
5
+ lines.push(`// Do not edit manually`);
6
+ lines.push(``);
7
+ lines.push(`import { lazy } from '@fluixi/core/router';`);
8
+ lines.push(``);
9
+ lines.push(`export const routes = ${generateRouteArray(routes, routesDir, 0)};`);
10
+ lines.push(``);
11
+ return lines.join("\n");
12
+ }
13
+ function generateRouteArray(routes, routesDir, depth) {
14
+ if (routes.length === 0) return "[]";
15
+ const indent = " ".repeat(depth);
16
+ const items = routes.map((route) => generateRouteObject(route, routesDir, depth + 1));
17
+ return `[
18
+ ${items.join(",\n")}
19
+ ${indent}]`;
20
+ }
21
+ function generateRouteObject(route, routesDir, depth) {
22
+ const indent = " ".repeat(depth);
23
+ const props = [];
24
+ const importPath = buildImportPath(route.filePath, routesDir);
25
+ const routePath = computeRoutePath(route);
26
+ props.push(`${indent} path: ${JSON.stringify(routePath)}`);
27
+ props.push(`${indent} component: lazy(() => import(${JSON.stringify(importPath)}))`);
28
+ if (route.hasRouteData) {
29
+ props.push(
30
+ `${indent} data: (args, __owner, __isCancelled, __deps) => import(${JSON.stringify(
31
+ importPath
32
+ )}).then(m => { if (typeof __isCancelled === 'function' && __isCancelled()) return undefined; if (!m.routeData) return undefined; if (typeof m.routeDeps === 'function') { if (typeof __deps === 'function') __deps(m.routeDeps); args = { ...args, deps: m.routeDeps() }; } return m.routeData(args); })`
33
+ );
34
+ }
35
+ if (route.children.length > 0) {
36
+ const childArray = generateRouteArray(route.children, routesDir, depth + 1);
37
+ props.push(`${indent} children: ${childArray}`);
38
+ }
39
+ return `${indent}{
40
+ ${props.join(",\n")}
41
+ ${indent}}`;
42
+ }
43
+ function computeRoutePath(route) {
44
+ return route.urlPath;
45
+ }
46
+ function buildImportPath(filePath, routesDir) {
47
+ const base = routesDir.replace(/\\/g, "/").replace(/\/$/, "");
48
+ return `${base}/${filePath}`;
49
+ }
50
+ export {
51
+ generateRouteModule
52
+ };
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/lib/plugins/route-scanner.ts
31
+ var route_scanner_exports = {};
32
+ __export(route_scanner_exports, {
33
+ scanRoutes: () => scanRoutes
34
+ });
35
+ module.exports = __toCommonJS(route_scanner_exports);
36
+ var fs = __toESM(require("fs"), 1);
37
+ var path = __toESM(require("path"), 1);
38
+ var DEFAULT_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
39
+ function scanRoutes(routesDir, options = {}) {
40
+ const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
41
+ const absDir = path.resolve(routesDir);
42
+ if (!fs.existsSync(absDir)) {
43
+ return [];
44
+ }
45
+ return scanDirectory(absDir, absDir, "", extensions);
46
+ }
47
+ function scanDirectory(dir, rootDir, parentUrlPath, extensions) {
48
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
49
+ const routes = [];
50
+ const layoutRoute = null;
51
+ const files = [];
52
+ const dirs = [];
53
+ for (const entry of entries) {
54
+ if (entry.isDirectory()) {
55
+ dirs.push(entry);
56
+ } else if (entry.isFile() && hasValidExtension(entry.name, extensions)) {
57
+ files.push(entry);
58
+ }
59
+ }
60
+ let layout;
61
+ const nonLayoutFiles = [];
62
+ for (const file of files) {
63
+ const baseName = stripExtension(file.name);
64
+ if (baseName === "layout") {
65
+ layout = buildFileRoute(file.name, dir, rootDir, parentUrlPath, true);
66
+ } else {
67
+ nonLayoutFiles.push(file);
68
+ }
69
+ }
70
+ const fileRoutes = [];
71
+ for (const file of nonLayoutFiles) {
72
+ fileRoutes.push(
73
+ buildFileRoute(file.name, dir, rootDir, parentUrlPath, false)
74
+ );
75
+ }
76
+ const dirRoutes = [];
77
+ for (const d of dirs) {
78
+ const dirName = d.name;
79
+ const isGroup = isRouteGroup(dirName);
80
+ const segment = isGroup ? "" : dirNameToSegment(dirName);
81
+ const childUrlPath = segment ? joinUrlPath(parentUrlPath, segment) : parentUrlPath;
82
+ const children = scanDirectory(
83
+ path.join(dir, dirName),
84
+ rootDir,
85
+ childUrlPath,
86
+ extensions
87
+ );
88
+ if (isGroup) {
89
+ dirRoutes.push(...children);
90
+ } else {
91
+ const childLayout = children.find((c) => c.isLayout && c.urlPath === childUrlPath);
92
+ const childNonLayout = children.filter((c) => !c.isLayout || c.urlPath !== childUrlPath);
93
+ if (childLayout) {
94
+ childLayout.children = [...childLayout.children, ...childNonLayout];
95
+ dirRoutes.push(childLayout);
96
+ } else if (childNonLayout.length > 0) {
97
+ dirRoutes.push(...childNonLayout);
98
+ }
99
+ }
100
+ }
101
+ if (layout) {
102
+ layout.children = [...fileRoutes, ...dirRoutes];
103
+ routes.push(layout);
104
+ } else {
105
+ routes.push(...fileRoutes, ...dirRoutes);
106
+ }
107
+ return sortRoutes(routes);
108
+ }
109
+ function buildFileRoute(fileName, dir, rootDir, parentUrlPath, isLayout) {
110
+ const baseName = stripExtension(fileName);
111
+ const filePath = path.relative(rootDir, path.join(dir, fileName));
112
+ const isIndex = baseName === "index";
113
+ const isCatchAll = baseName.startsWith("[...");
114
+ const isDynamic = !isCatchAll && baseName.startsWith("[") && baseName.endsWith("]");
115
+ let urlSegment;
116
+ if (isLayout) {
117
+ urlSegment = "";
118
+ } else if (isIndex) {
119
+ urlSegment = "/";
120
+ } else if (isCatchAll) {
121
+ const paramName = baseName.slice(4, -1);
122
+ urlSegment = `*${paramName}`;
123
+ } else if (isDynamic) {
124
+ const paramName = baseName.slice(1, -1);
125
+ urlSegment = `:${paramName}`;
126
+ } else {
127
+ urlSegment = baseName;
128
+ }
129
+ const urlPath = isLayout ? parentUrlPath || "/" : isIndex ? parentUrlPath || "/" : joinUrlPath(parentUrlPath, urlSegment);
130
+ const absoluteFilePath = path.join(dir, fileName);
131
+ let hasRouteData = false;
132
+ try {
133
+ const content = fs.readFileSync(absoluteFilePath, "utf-8");
134
+ hasRouteData = /export\s+(const|function|async\s+function)\s+routeData\b/.test(content);
135
+ } catch {
136
+ }
137
+ return {
138
+ filePath: filePath.replace(/\\/g, "/"),
139
+ // normalize to forward slashes
140
+ urlSegment,
141
+ urlPath,
142
+ isLayout,
143
+ isIndex,
144
+ isDynamic,
145
+ isCatchAll,
146
+ isGroup: false,
147
+ children: [],
148
+ parentUrlPath,
149
+ hasRouteData,
150
+ rootDir,
151
+ dir
152
+ };
153
+ }
154
+ function hasValidExtension(fileName, extensions) {
155
+ return extensions.some((ext) => fileName.endsWith(ext));
156
+ }
157
+ function stripExtension(fileName) {
158
+ const lastDot = fileName.lastIndexOf(".");
159
+ return lastDot > 0 ? fileName.slice(0, lastDot) : fileName;
160
+ }
161
+ function isRouteGroup(dirName) {
162
+ return dirName.startsWith("(") && dirName.endsWith(")");
163
+ }
164
+ function dirNameToSegment(dirName) {
165
+ if (dirName.startsWith("[...") && dirName.endsWith("]")) {
166
+ return "*" + dirName.slice(4, -1);
167
+ }
168
+ if (dirName.startsWith("[") && dirName.endsWith("]")) {
169
+ return ":" + dirName.slice(1, -1);
170
+ }
171
+ return dirName;
172
+ }
173
+ function joinUrlPath(parent, segment) {
174
+ const base = parent.endsWith("/") ? parent : parent + "/";
175
+ return base + segment;
176
+ }
177
+ function sortRoutes(routes) {
178
+ return routes.sort((a, b) => {
179
+ if (a.isLayout && !b.isLayout) return -1;
180
+ if (!a.isLayout && b.isLayout) return 1;
181
+ if (a.isIndex && !b.isIndex) return -1;
182
+ if (!a.isIndex && b.isIndex) return 1;
183
+ if (!a.isDynamic && !a.isCatchAll && (b.isDynamic || b.isCatchAll)) return -1;
184
+ if ((a.isDynamic || a.isCatchAll) && !b.isDynamic && !b.isCatchAll) return 1;
185
+ if (a.isDynamic && b.isCatchAll) return -1;
186
+ if (a.isCatchAll && b.isDynamic) return 1;
187
+ return a.urlSegment.localeCompare(b.urlSegment);
188
+ });
189
+ }