@solidjs/start 1.2.0 → 2.0.0-alpha.0

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 (184) hide show
  1. package/README.md +88 -8
  2. package/dist/client/StartClient.d.ts +0 -1
  3. package/dist/client/StartClient.jsx +2 -4
  4. package/dist/client/index.d.ts +2 -3
  5. package/dist/client/index.jsx +2 -4
  6. package/dist/client/mount.js +10 -2
  7. package/dist/client/spa/index.d.ts +1 -2
  8. package/dist/client/spa/index.jsx +1 -2
  9. package/dist/config/constants.d.ts +16 -0
  10. package/dist/config/constants.js +16 -0
  11. package/dist/config/dev-server.d.ts +2 -0
  12. package/dist/config/dev-server.js +108 -0
  13. package/dist/config/fs-router.d.ts +37 -0
  14. package/dist/config/fs-router.js +153 -0
  15. package/dist/config/fs-routes/fs-watcher.d.ts +3 -0
  16. package/dist/config/fs-routes/fs-watcher.js +49 -0
  17. package/dist/config/fs-routes/index.d.ts +7 -0
  18. package/dist/config/fs-routes/index.js +109 -0
  19. package/dist/config/fs-routes/router.d.ts +37 -0
  20. package/dist/config/fs-routes/router.js +136 -0
  21. package/dist/config/fs-routes/tree-shake.d.ts +2 -0
  22. package/dist/config/fs-routes/tree-shake.js +357 -0
  23. package/dist/config/index.d.ts +10 -0
  24. package/dist/config/index.js +217 -0
  25. package/dist/config/lazy.d.ts +3 -0
  26. package/dist/config/lazy.js +82 -0
  27. package/dist/config/manifest.d.ts +3 -0
  28. package/dist/config/manifest.js +74 -0
  29. package/dist/config/utils.d.ts +4 -0
  30. package/dist/config/utils.js +6 -0
  31. package/dist/config/vite-utils.d.ts +16 -0
  32. package/dist/config/vite-utils.js +81 -0
  33. package/dist/http/index.d.ts +84 -0
  34. package/dist/http/index.js +131 -0
  35. package/dist/index.d.ts +6 -5
  36. package/dist/index.jsx +5 -5
  37. package/dist/middleware/index.d.ts +6 -6
  38. package/dist/middleware/index.js +39 -0
  39. package/dist/{router/FileRoutes.js → router.jsx} +5 -6
  40. package/dist/server/StartServer.d.ts +3 -3
  41. package/dist/server/StartServer.jsx +9 -63
  42. package/dist/server/assets/index.d.ts +2 -0
  43. package/dist/server/assets/index.js +52 -0
  44. package/dist/server/assets/render.d.ts +19 -0
  45. package/dist/server/{renderAsset.jsx → assets/render.jsx} +1 -1
  46. package/dist/server/collect-styles.d.ts +3 -0
  47. package/dist/server/collect-styles.js +69 -0
  48. package/dist/server/fetchEvent.d.ts +4 -3
  49. package/dist/server/fetchEvent.js +12 -74
  50. package/dist/server/fetchEvent.spec.js +18 -197
  51. package/dist/server/handler.d.ts +6 -12
  52. package/dist/server/handler.js +180 -97
  53. package/dist/server/index.d.ts +8 -6
  54. package/dist/server/index.jsx +14 -6
  55. package/dist/server/manifest/client-manifest.d.ts +9 -0
  56. package/dist/server/manifest/client-manifest.js +6 -0
  57. package/dist/server/manifest/dev-client-manifest.d.ts +4 -0
  58. package/dist/server/manifest/dev-client-manifest.js +24 -0
  59. package/dist/server/manifest/dev-ssr-manifest.d.ts +5 -0
  60. package/dist/server/manifest/dev-ssr-manifest.js +23 -0
  61. package/dist/server/manifest/prod-client-manifest.d.ts +5 -0
  62. package/dist/server/manifest/prod-client-manifest.js +26 -0
  63. package/dist/server/manifest/prod-ssr-manifest.d.ts +6 -0
  64. package/dist/server/manifest/prod-ssr-manifest.js +90 -0
  65. package/dist/server/manifest/ssr-manifest.d.ts +4 -0
  66. package/dist/server/manifest/ssr-manifest.js +6 -0
  67. package/dist/{router → server}/routes.d.ts +5 -10
  68. package/dist/{router → server}/routes.js +35 -36
  69. package/dist/{runtime → server}/server-fns-runtime.d.ts +1 -1
  70. package/dist/{runtime → server}/server-fns-runtime.js +7 -6
  71. package/dist/server/server-functions-handler.d.ts +2 -0
  72. package/dist/{runtime/server-handler.js → server/server-functions-handler.js} +65 -100
  73. package/dist/server/server-runtime.d.ts +3 -0
  74. package/dist/{runtime → server}/server-runtime.js +9 -16
  75. package/dist/server/spa/StartServer.d.ts +1 -1
  76. package/dist/server/spa/StartServer.jsx +7 -9
  77. package/dist/server/spa/handler.d.ts +3 -3
  78. package/dist/server/spa/handler.js +9 -11
  79. package/dist/server/spa/index.d.ts +4 -3
  80. package/dist/server/spa/index.jsx +4 -3
  81. package/dist/server/types.d.ts +7 -9
  82. package/dist/server/util.d.ts +2 -0
  83. package/dist/server/util.js +8 -0
  84. package/dist/shared/ErrorBoundary.jsx +2 -2
  85. package/dist/shared/HttpHeader.jsx +4 -2
  86. package/dist/shared/{HttpStatusCode.jsx → HttpStatusCode.js} +3 -1
  87. package/dist/shared/dev-overlay/DevOverlayDialog.d.ts +2 -2
  88. package/dist/shared/dev-overlay/DevOverlayDialog.jsx +75 -66
  89. package/dist/shared/dev-overlay/createStackFrame.js +3 -3
  90. package/dist/shared/dev-overlay/index.jsx +5 -8
  91. package/dist/shared/lazy.d.ts +3 -0
  92. package/dist/shared/lazy.js +40 -0
  93. package/dist/shared/serverFunction.d.ts +1 -1
  94. package/package.json +45 -67
  95. package/scripts/build.js +15 -0
  96. package/scripts/validate-imports.js +316 -0
  97. package/src/client/StartClient.tsx +34 -0
  98. package/src/client/index.tsx +2 -0
  99. package/src/client/mount.ts +114 -0
  100. package/src/client/spa/index.tsx +9 -0
  101. package/src/config/constants.ts +19 -0
  102. package/src/config/dev-server.ts +149 -0
  103. package/src/config/fs-router.ts +177 -0
  104. package/src/config/fs-routes/fs-watcher.ts +81 -0
  105. package/src/config/fs-routes/index.ts +138 -0
  106. package/src/config/fs-routes/router.ts +169 -0
  107. package/src/config/fs-routes/tree-shake.ts +392 -0
  108. package/src/config/index.ts +236 -0
  109. package/src/config/lazy.ts +100 -0
  110. package/src/config/manifest.ts +84 -0
  111. package/src/config/utils.ts +5 -0
  112. package/src/config/vite-utils.ts +113 -0
  113. package/src/env.d.ts +35 -0
  114. package/src/http/index.ts +253 -0
  115. package/src/index.tsx +19 -0
  116. package/src/internal.d.ts +14 -0
  117. package/src/middleware/index.ts +82 -0
  118. package/src/router.tsx +36 -0
  119. package/src/server/StartServer.tsx +62 -0
  120. package/src/server/assets/index.ts +70 -0
  121. package/src/server/assets/render.tsx +42 -0
  122. package/src/server/collect-styles.ts +94 -0
  123. package/src/server/fetchEvent.spec.ts +89 -0
  124. package/src/server/fetchEvent.ts +36 -0
  125. package/src/server/handler.ts +250 -0
  126. package/src/server/index.tsx +16 -0
  127. package/src/server/manifest/client-manifest.ts +8 -0
  128. package/src/server/manifest/dev-client-manifest.ts +19 -0
  129. package/src/server/manifest/dev-ssr-manifest.ts +21 -0
  130. package/src/server/manifest/prod-client-manifest.ts +18 -0
  131. package/src/server/manifest/prod-ssr-manifest.ts +125 -0
  132. package/src/server/manifest/ssr-manifest.ts +8 -0
  133. package/src/server/routes.ts +138 -0
  134. package/src/server/server-fns-runtime.ts +31 -0
  135. package/src/server/server-functions-handler.ts +364 -0
  136. package/src/server/server-runtime.ts +240 -0
  137. package/src/server/spa/StartServer.tsx +40 -0
  138. package/src/server/spa/handler.ts +29 -0
  139. package/src/server/spa/index.tsx +5 -0
  140. package/src/server/types.ts +82 -0
  141. package/src/server/util.ts +12 -0
  142. package/src/shared/ErrorBoundary.tsx +52 -0
  143. package/src/shared/GET.ts +8 -0
  144. package/src/shared/HttpHeader.tsx +42 -0
  145. package/src/shared/HttpStatusCode.ts +27 -0
  146. package/src/shared/clientOnly.ts +44 -0
  147. package/src/shared/dev-overlay/CodeView.tsx +95 -0
  148. package/src/shared/dev-overlay/DevOverlayDialog.tsx +316 -0
  149. package/src/shared/dev-overlay/createStackFrame.ts +77 -0
  150. package/src/shared/dev-overlay/download.ts +6 -0
  151. package/src/shared/dev-overlay/env.d.ts +4 -0
  152. package/src/shared/dev-overlay/get-source-map.ts +35 -0
  153. package/src/shared/dev-overlay/icons.tsx +288 -0
  154. package/src/shared/dev-overlay/index.tsx +63 -0
  155. package/src/shared/dev-overlay/styles.css +292 -0
  156. package/src/shared/lazy.ts +51 -0
  157. package/src/shared/serverFunction.ts +10 -0
  158. package/src/virtual.d.ts +27 -0
  159. package/tsconfig.json +23 -0
  160. package/vitest.config.ts +9 -0
  161. package/config/fs-router.js +0 -142
  162. package/config/index.d.ts +0 -40
  163. package/config/index.js +0 -304
  164. package/config/server-components.js +0 -106
  165. package/dist/client/islands.d.ts +0 -7
  166. package/dist/client/islands.jsx +0 -10
  167. package/dist/middleware/index.jsx +0 -35
  168. package/dist/router/index.d.ts +0 -1
  169. package/dist/router/index.jsx +0 -2
  170. package/dist/router/lazyRoute.d.ts +0 -6
  171. package/dist/router/lazyRoute.js +0 -53
  172. package/dist/runtime/server-handler.d.ts +0 -2
  173. package/dist/runtime/server-runtime.d.ts +0 -3
  174. package/dist/server/islands/index.d.ts +0 -19
  175. package/dist/server/islands/index.jsx +0 -62
  176. package/dist/server/islands/utils.d.ts +0 -1
  177. package/dist/server/islands/utils.jsx +0 -85
  178. package/dist/server/pageEvent.d.ts +0 -2
  179. package/dist/server/pageEvent.js +0 -56
  180. package/dist/server/renderAsset.d.ts +0 -2
  181. package/env.d.ts +0 -26
  182. /package/dist/{router/FileRoutes.d.ts → router.d.ts} +0 -0
  183. /package/dist/shared/{clientOnly.jsx → clientOnly.js} +0 -0
  184. /package/dist/shared/{serverFunction.jsx → serverFunction.js} +0 -0
@@ -0,0 +1,177 @@
1
+ import type { ExportSpecifier } from "es-module-lexer";
2
+ import {
3
+ analyzeModule,
4
+ BaseFileSystemRouter,
5
+ cleanPath,
6
+ type FileSystemRouterConfig,
7
+ } from "./fs-routes/router.ts";
8
+
9
+ export class SolidStartClientFileRouter extends BaseFileSystemRouter {
10
+ toPath(src: string) {
11
+ const routePath = cleanPath(src, this.config)
12
+ // remove the initial slash
13
+ .slice(1)
14
+ .replace(/index$/, "")
15
+ .replace(/\[([^/]+)\]/g, (_, m) => {
16
+ if (m.length > 3 && m.startsWith("...")) {
17
+ return `*${m.slice(3)}`;
18
+ }
19
+ if (m.length > 2 && m.startsWith("[") && m.endsWith("]")) {
20
+ return `:${m.slice(1, -1)}?`;
21
+ }
22
+ return `:${m}`;
23
+ });
24
+
25
+ return routePath?.length > 0 ? `/${routePath}` : "/";
26
+ }
27
+
28
+ toRoute(src: string) {
29
+ const path = this.toPath(src);
30
+
31
+ if (src.endsWith(".md") || src.endsWith(".mdx")) {
32
+ return {
33
+ page: true,
34
+ $component: {
35
+ src: src,
36
+ pick: ["$css"],
37
+ },
38
+ $$route: undefined,
39
+ path,
40
+ // filePath: src
41
+ };
42
+ }
43
+
44
+ const [_, exports] = analyzeModule(src);
45
+ const hasDefault = !!exports.find((e) => e.n === "default");
46
+ const hasRouteConfig = !!exports.find((e) => e.n === "route");
47
+ if (hasDefault) {
48
+ return {
49
+ page: true,
50
+ $component: {
51
+ src: src,
52
+ pick: [
53
+ ...exports
54
+ .filter((e) => e.n === e.ln && e.n !== "route")
55
+ .map((e) => e.n),
56
+ "default",
57
+ "$css",
58
+ ],
59
+ },
60
+ $$route: hasRouteConfig
61
+ ? {
62
+ src: src,
63
+ pick: ["route"],
64
+ }
65
+ : undefined,
66
+ path,
67
+ // filePath: src
68
+ };
69
+ }
70
+ }
71
+ }
72
+
73
+ const HTTP_METHODS = [
74
+ "HEAD",
75
+ "GET",
76
+ "POST",
77
+ "PUT",
78
+ "DELETE",
79
+ "PATCH",
80
+ "OPTIONS",
81
+ ];
82
+ function createHTTPHandlers(src: string, exports: readonly ExportSpecifier[]) {
83
+ const handlers: Record<string, any> = {};
84
+ for (const exp of exports) {
85
+ if (HTTP_METHODS.includes(exp.n)) {
86
+ handlers[`$${exp.n}`] = {
87
+ src: src,
88
+ pick: [exp.n],
89
+ };
90
+ if (exp.n === "GET" && !exports.find((exp) => exp.n === "HEAD")) {
91
+ handlers.$HEAD = {
92
+ src: src,
93
+ pick: [exp.n],
94
+ };
95
+ }
96
+ }
97
+ }
98
+
99
+ return handlers;
100
+ }
101
+
102
+ export class SolidStartServerFileRouter extends BaseFileSystemRouter {
103
+ declare config: FileSystemRouterConfig & { dataOnly?: boolean };
104
+
105
+ constructor(config: FileSystemRouterConfig & { dataOnly?: boolean }) {
106
+ super(config);
107
+ }
108
+
109
+ toPath(src: string) {
110
+ const routePath = cleanPath(src, this.config)
111
+ // remove the initial slash
112
+ .slice(1)
113
+ .replace(/index$/, "")
114
+ .replace(/\[([^/]+)\]/g, (_, m) => {
115
+ if (m.length > 3 && m.startsWith("...")) {
116
+ return `*${m.slice(3)}`;
117
+ }
118
+ if (m.length > 2 && m.startsWith("[") && m.endsWith("]")) {
119
+ return `:${m.slice(1, -1)}?`;
120
+ }
121
+ return `:${m}`;
122
+ });
123
+
124
+ return routePath?.length > 0 ? `/${routePath}` : "/";
125
+ }
126
+
127
+ toRoute(src: string) {
128
+ const path = this.toPath(src);
129
+ if (src.endsWith(".md") || src.endsWith(".mdx")) {
130
+ return {
131
+ page: true,
132
+ $component: {
133
+ src: src,
134
+ pick: ["$css"],
135
+ },
136
+ $$route: undefined,
137
+ path,
138
+ };
139
+ }
140
+
141
+ const [_, exports] = analyzeModule(src);
142
+ const hasRouteConfig = exports.find((e) => e.n === "route");
143
+ const hasDefault = !!exports.find((e) => e.n === "default");
144
+ const hasAPIRoutes = !!exports.find((exp) => HTTP_METHODS.includes(exp.n));
145
+ if (hasDefault || hasAPIRoutes) {
146
+ return {
147
+ page: hasDefault,
148
+ $component:
149
+ !this.config.dataOnly && hasDefault
150
+ ? {
151
+ src: src,
152
+ pick: [
153
+ ...exports
154
+ .filter(
155
+ (e) =>
156
+ e.n === e.ln &&
157
+ e.n !== "route" &&
158
+ !HTTP_METHODS.includes(e.n),
159
+ )
160
+ .map((e) => e.n),
161
+ "default",
162
+ "$css",
163
+ ],
164
+ }
165
+ : undefined,
166
+ $$route: hasRouteConfig
167
+ ? {
168
+ src: src,
169
+ pick: ["route"],
170
+ }
171
+ : undefined,
172
+ ...createHTTPHandlers(src, exports),
173
+ path,
174
+ };
175
+ }
176
+ }
177
+ }
@@ -0,0 +1,81 @@
1
+ import type {
2
+ EnvironmentModuleNode,
3
+ FSWatcher,
4
+ ModuleGraph,
5
+ ModuleNode,
6
+ PluginOption,
7
+ ViteDevServer,
8
+ } from "vite";
9
+ import { moduleId } from "./index.ts";
10
+ import type { BaseFileSystemRouter } from "./router.ts";
11
+
12
+ interface CompiledRouter {
13
+ removeRoute(path: string): void;
14
+ addRoute(path: string): void;
15
+ updateRoute(path: string): void;
16
+ addEventListener(event: "reload", handler: () => void): void;
17
+ removeEventListener(event: "reload", handler: () => void): void;
18
+ }
19
+
20
+ function setupWatcher(watcher: FSWatcher, routes: CompiledRouter): void {
21
+ watcher.on("unlink", (path) => routes.removeRoute(path));
22
+ watcher.on("add", (path) => routes.addRoute(path));
23
+ watcher.on("change", (path) => routes.updateRoute(path));
24
+ }
25
+
26
+ function createRoutesReloader(
27
+ server: ViteDevServer,
28
+ routes: CompiledRouter,
29
+ environment: "client" | "ssr",
30
+ ): () => void {
31
+ routes.addEventListener("reload", handleRoutesReload);
32
+ return () => routes.removeEventListener("reload", handleRoutesReload);
33
+
34
+ function handleRoutesReload(): void {
35
+ if (environment === "ssr") {
36
+ // Handle server environment HMR reload
37
+ const serverEnv = server.environments.server;
38
+ if (serverEnv && serverEnv.moduleGraph) {
39
+ const mod: EnvironmentModuleNode | undefined =
40
+ serverEnv.moduleGraph.getModuleById(moduleId);
41
+ if (mod) {
42
+ const seen = new Set<EnvironmentModuleNode>();
43
+ serverEnv.moduleGraph.invalidateModule(mod, seen);
44
+ }
45
+ }
46
+ } else {
47
+ // Handle client environment HMR reload
48
+ const { moduleGraph }: { moduleGraph: ModuleGraph } = server;
49
+ const mod: ModuleNode | undefined = moduleGraph.getModuleById(moduleId);
50
+ if (mod) {
51
+ const seen = new Set<ModuleNode>();
52
+ moduleGraph.invalidateModule(mod, seen);
53
+ server.reloadModule(mod);
54
+ }
55
+ }
56
+
57
+ if (!server.hot) {
58
+ server.ws.send({ type: "full-reload" });
59
+ }
60
+ }
61
+ }
62
+
63
+ export const fileSystemWatcher = (
64
+ routers: Record<"client" | "ssr", BaseFileSystemRouter>,
65
+ ): PluginOption => {
66
+ const plugin: PluginOption = {
67
+ name: "fs-watcher",
68
+ async configureServer(server: ViteDevServer) {
69
+ Object.keys(routers).forEach((environment) => {
70
+ const router = (globalThis as any).ROUTERS[environment];
71
+ setupWatcher(server.watcher, router);
72
+ createRoutesReloader(
73
+ server,
74
+ router,
75
+ environment as keyof typeof routers,
76
+ );
77
+ });
78
+ },
79
+ };
80
+ return plugin;
81
+ };
@@ -0,0 +1,138 @@
1
+ import { relative } from "node:path";
2
+ import { type PluginOption } from "vite";
3
+
4
+ import { fileSystemWatcher } from "./fs-watcher.ts";
5
+ import type { BaseFileSystemRouter } from "./router.ts";
6
+ import { treeShake } from "./tree-shake.ts";
7
+
8
+ export const moduleId = "solid-start:routes";
9
+
10
+ export interface FsRoutesArgs {
11
+ routers: Record<"client" | "ssr", BaseFileSystemRouter>;
12
+ }
13
+
14
+ export function fsRoutes({ routers }: FsRoutesArgs): Array<PluginOption> {
15
+ (globalThis as any).ROUTERS = routers;
16
+
17
+ return [
18
+ {
19
+ name: "solid-start-fs-routes",
20
+ enforce: "pre",
21
+ resolveId(id) {
22
+ if (id === moduleId) return id;
23
+ },
24
+ async load(id) {
25
+ const root = this.environment.config.root;
26
+ const isBuild = this.environment.mode === "build";
27
+
28
+ if (id !== moduleId) return;
29
+ const js = jsCode();
30
+
31
+ const router = (globalThis as any).ROUTERS[this.environment.name];
32
+
33
+ const routes = await router.getRoutes();
34
+
35
+ let routesCode = JSON.stringify(routes ?? [], (k, v) => {
36
+ if (v === undefined) return undefined;
37
+
38
+ if (k.startsWith("$$")) {
39
+ const buildId = `${v.src}?${v.pick.map((p: any) => `pick=${p}`).join("&")}`;
40
+
41
+ /**
42
+ * @type {{ [key: string]: string }}
43
+ */
44
+ const refs: Record<string, string> = {};
45
+ for (var pick of v.pick) {
46
+ refs[pick] = js.addNamedImport(pick, buildId);
47
+ }
48
+ return {
49
+ require: `_$() => ({ ${Object.entries(refs)
50
+ .map(([pick, namedImport]) => `'${pick}': ${namedImport}`)
51
+ .join(", ")} })$_`,
52
+ // src: isBuild ? relative(root, buildId) : buildId
53
+ };
54
+ } else if (k.startsWith("$")) {
55
+ const buildId = `${v.src}?${v.pick.map((p: any) => `pick=${p}`).join("&")}`;
56
+ return {
57
+ src: relative(root, buildId),
58
+ build: isBuild
59
+ ? `_$() => import(/* @vite-ignore */ '${buildId}')$_`
60
+ : undefined,
61
+ import: `_$() => import(/* @vite-ignore */ '${buildId}')$_`
62
+ };
63
+ }
64
+ return v;
65
+ });
66
+
67
+ routesCode = routesCode.replaceAll('"_$(', "(").replaceAll(')$_"', ")");
68
+
69
+ const code = `
70
+ ${js.getImportStatements()}
71
+ export default ${routesCode}`;
72
+ return code;
73
+ },
74
+ },
75
+ treeShake(),
76
+ fileSystemWatcher(routers),
77
+ ];
78
+ }
79
+
80
+ function jsCode() {
81
+ const imports = new Map();
82
+ let vars = 0;
83
+
84
+ function addImport(p: any) {
85
+ let id = imports.get(p);
86
+ if (!id) {
87
+ id = {};
88
+ imports.set(p, id);
89
+ }
90
+
91
+ const d = "routeData" + vars++;
92
+ id["default"] = d;
93
+ return d;
94
+ }
95
+
96
+ function addNamedImport(name: string | number, p: any) {
97
+ let id = imports.get(p);
98
+ if (!id) {
99
+ id = {};
100
+ imports.set(p, id);
101
+ }
102
+
103
+ const d = "routeData" + vars++;
104
+ id[name] = d;
105
+ return d;
106
+ }
107
+
108
+ const getNamedExport = (p: any) => {
109
+ const id = imports.get(p);
110
+
111
+ delete id["default"];
112
+
113
+ return Object.keys(id).length > 0
114
+ ? `{ ${Object.keys(id)
115
+ .map((k) => `${k} as ${id[k]}`)
116
+ .join(", ")} }`
117
+ : "";
118
+ };
119
+
120
+ const getImportStatements = () => {
121
+ return `${[...imports.keys()]
122
+ .map(
123
+ (i) =>
124
+ `import ${
125
+ imports.get(i).default
126
+ ? `${imports.get(i).default}${Object.keys(imports.get(i)).length > 1 ? ", " : ""}`
127
+ : ""
128
+ } ${getNamedExport(i)} from '${i}';`,
129
+ )
130
+ .join("\n")}`;
131
+ };
132
+
133
+ return {
134
+ addImport,
135
+ addNamedImport,
136
+ getImportStatements,
137
+ };
138
+ }
@@ -0,0 +1,169 @@
1
+ import { init } from "es-module-lexer";
2
+ import { parse } from "es-module-lexer";
3
+ import esbuild from "esbuild";
4
+ import fg from "fast-glob";
5
+ import fs from "fs";
6
+ import micromatch from "micromatch";
7
+ import { posix } from "path";
8
+ import { pathToRegexp } from "path-to-regexp";
9
+
10
+ import { normalize } from "node:path";
11
+
12
+ export { pathToRegexp };
13
+
14
+ export const glob = (path: string) => fg.sync(path, { absolute: true });
15
+
16
+ export type FileSystemRouterConfig = { dir: string; extensions: string[] };
17
+ type Route = { path: string } & Record<string, any>;
18
+
19
+ export function cleanPath(src: string, config: FileSystemRouterConfig) {
20
+ return src
21
+ .slice(config.dir.length)
22
+ .replace(new RegExp(`\.(${(config.extensions ?? []).join("|")})$`), "");
23
+ }
24
+
25
+ export function analyzeModule(src: string) {
26
+ return parse(
27
+ esbuild.transformSync(fs.readFileSync(src, "utf-8"), {
28
+ jsx: "transform",
29
+ format: "esm",
30
+ loader: "tsx"
31
+ }).code,
32
+ src
33
+ );
34
+ }
35
+
36
+ export class BaseFileSystemRouter extends EventTarget {
37
+ routes: Route[];
38
+
39
+ config: FileSystemRouterConfig;
40
+
41
+ /**
42
+ *
43
+ * @param {} config
44
+ */
45
+ constructor(config: FileSystemRouterConfig) {
46
+ super();
47
+ this.routes = [];
48
+ this.config = config;
49
+ }
50
+
51
+ glob() {
52
+ return (
53
+ posix.join(fg.convertPathToPattern(this.config.dir), "**/*") +
54
+ `.{${this.config.extensions.join(",")}}`
55
+ );
56
+ }
57
+
58
+ async buildRoutes(): Promise<any[]> {
59
+ await init;
60
+ for (var src of glob(this.glob())) {
61
+ await this.addRoute(src);
62
+ }
63
+
64
+ return this.routes;
65
+ }
66
+
67
+ isRoute(src: string) {
68
+ return Boolean(micromatch(src as any, this.glob())?.length);
69
+ }
70
+
71
+ toPath(src: string) {
72
+ throw new Error("Not implemented");
73
+ }
74
+
75
+ toRoute(src: string): Route | undefined {
76
+ let path = this.toPath(src);
77
+
78
+ if (path === undefined) return;
79
+
80
+ const [_, exports] = analyzeModule(src);
81
+
82
+ if (!exports.find(e => e.n === "default")) {
83
+ console.warn("No default export", src);
84
+ }
85
+
86
+ return {
87
+ $component: {
88
+ src: src,
89
+ pick: ["default", "$css"]
90
+ },
91
+ path
92
+ // filePath: src
93
+ };
94
+ }
95
+
96
+ /**
97
+ * To be attached by vite plugin to the vite dev server
98
+ */
99
+ update = undefined;
100
+
101
+ _addRoute(route: Route) {
102
+ this.routes = this.routes.filter(r => r.path !== route.path);
103
+ this.routes.push(route);
104
+ }
105
+
106
+ async addRoute(src: string) {
107
+ src = normalize(src);
108
+ if (this.isRoute(src)) {
109
+ try {
110
+ const route = this.toRoute(src);
111
+ if (route) {
112
+ this._addRoute(route);
113
+ this.reload(route.path);
114
+ }
115
+ } catch (e) {
116
+ console.error(e);
117
+ }
118
+ }
119
+ }
120
+
121
+ reload(route: string) {
122
+ this.dispatchEvent(
123
+ new Event("reload", {
124
+ // @ts-ignore
125
+ detail: {
126
+ route
127
+ }
128
+ })
129
+ );
130
+ }
131
+
132
+ async updateRoute(src: string) {
133
+ src = normalize(src);
134
+ if (this.isRoute(src)) {
135
+ try {
136
+ const route = this.toRoute(src);
137
+ if (route) {
138
+ this._addRoute(route);
139
+ this.reload(route.path);
140
+ }
141
+ } catch (e) {
142
+ console.error(e);
143
+ }
144
+ // this.update?.();
145
+ }
146
+ }
147
+
148
+ removeRoute(src: string) {
149
+ src = normalize(src);
150
+ if (this.isRoute(src)) {
151
+ const path = this.toPath(src);
152
+ if (path === undefined) {
153
+ return;
154
+ }
155
+ this.routes = this.routes.filter(r => r.path !== path);
156
+ this.dispatchEvent(new Event("reload", {}));
157
+ }
158
+ }
159
+
160
+ buildRoutesPromise?: Promise<any[]>;
161
+
162
+ async getRoutes() {
163
+ if (!this.buildRoutesPromise) {
164
+ this.buildRoutesPromise = this.buildRoutes();
165
+ }
166
+ await this.buildRoutesPromise;
167
+ return this.routes;
168
+ }
169
+ }