phial 0.0.1

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -0
  3. package/bin/phial.mjs +6 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +2 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +2 -0
  8. package/dist/lib/cli/index.d.ts +5 -0
  9. package/dist/lib/cli/index.d.ts.map +1 -0
  10. package/dist/lib/cli/index.js +101 -0
  11. package/dist/lib/cli/index.js.map +1 -0
  12. package/dist/lib/generated-routes.d.ts +176 -0
  13. package/dist/lib/server-routes/errors.js +16 -0
  14. package/dist/lib/server-routes/errors.js.map +1 -0
  15. package/dist/lib/vite-plugin/config.d.ts +54 -0
  16. package/dist/lib/vite-plugin/config.d.ts.map +1 -0
  17. package/dist/lib/vite-plugin/config.js +54 -0
  18. package/dist/lib/vite-plugin/config.js.map +1 -0
  19. package/dist/lib/vite-plugin/generated/client-entry.js +36 -0
  20. package/dist/lib/vite-plugin/generated/client-entry.js.map +1 -0
  21. package/dist/lib/vite-plugin/generated/virtual-modules.js +940 -0
  22. package/dist/lib/vite-plugin/generated/virtual-modules.js.map +1 -0
  23. package/dist/lib/vite-plugin/host/index.d.ts +4 -0
  24. package/dist/lib/vite-plugin/host/index.js +5 -0
  25. package/dist/lib/vite-plugin/host/plugin-build.d.ts +15 -0
  26. package/dist/lib/vite-plugin/host/plugin-build.d.ts.map +1 -0
  27. package/dist/lib/vite-plugin/host/plugin-build.js +242 -0
  28. package/dist/lib/vite-plugin/host/plugin-build.js.map +1 -0
  29. package/dist/lib/vite-plugin/host/plugin-dev-server.d.ts +19 -0
  30. package/dist/lib/vite-plugin/host/plugin-dev-server.d.ts.map +1 -0
  31. package/dist/lib/vite-plugin/host/plugin-dev-server.js +255 -0
  32. package/dist/lib/vite-plugin/host/plugin-dev-server.js.map +1 -0
  33. package/dist/lib/vite-plugin/host/plugin-prepare.d.ts +12 -0
  34. package/dist/lib/vite-plugin/host/plugin-prepare.d.ts.map +1 -0
  35. package/dist/lib/vite-plugin/host/plugin-prepare.js +29 -0
  36. package/dist/lib/vite-plugin/host/plugin-prepare.js.map +1 -0
  37. package/dist/lib/vite-plugin/host/plugin-server.d.ts +19 -0
  38. package/dist/lib/vite-plugin/host/plugin-server.d.ts.map +1 -0
  39. package/dist/lib/vite-plugin/host/plugin-server.js +60 -0
  40. package/dist/lib/vite-plugin/host/plugin-server.js.map +1 -0
  41. package/dist/lib/vite-plugin/index.d.ts +9 -0
  42. package/dist/lib/vite-plugin/index.d.ts.map +1 -0
  43. package/dist/lib/vite-plugin/index.js +261 -0
  44. package/dist/lib/vite-plugin/index.js.map +1 -0
  45. package/dist/lib/vite-plugin/scanners/app-pages-scanner.js +162 -0
  46. package/dist/lib/vite-plugin/scanners/app-pages-scanner.js.map +1 -0
  47. package/dist/lib/vite-plugin/scanners/app-runtime-scanner.js +39 -0
  48. package/dist/lib/vite-plugin/scanners/app-runtime-scanner.js.map +1 -0
  49. package/dist/lib/vite-plugin/scanners/route-manifest.js +60 -0
  50. package/dist/lib/vite-plugin/scanners/route-manifest.js.map +1 -0
  51. package/dist/lib/vite-plugin/scanners/routes-scanner.js +72 -0
  52. package/dist/lib/vite-plugin/scanners/routes-scanner.js.map +1 -0
  53. package/dist/lib/vite-plugin/scanners/scanner-utils.js +129 -0
  54. package/dist/lib/vite-plugin/scanners/scanner-utils.js.map +1 -0
  55. package/dist/lib/vite-plugin/scanners/server-routes-scanner.js +83 -0
  56. package/dist/lib/vite-plugin/scanners/server-routes-scanner.js.map +1 -0
  57. package/dist/lib/vite-plugin/scanners/types-generator.d.ts +9 -0
  58. package/dist/lib/vite-plugin/scanners/types-generator.d.ts.map +1 -0
  59. package/dist/lib/vite-plugin/scanners/types-generator.js +190 -0
  60. package/dist/lib/vite-plugin/scanners/types-generator.js.map +1 -0
  61. package/dist/vite-plugin.d.ts +7 -0
  62. package/dist/vite-plugin.js +8 -0
  63. package/package.json +89 -0
@@ -0,0 +1,261 @@
1
+ import { isPhialConfigFile, loadPhialConfig } from "./config.js";
2
+ import { createClientEntryModule } from "./generated/client-entry.js";
3
+ import { scanRoutes } from "./scanners/routes-scanner.js";
4
+ import { writePhialProjectTypes } from "./scanners/types-generator.js";
5
+ import { RESOLVED_GENERATED_APP_LOADER_ID, RESOLVED_GENERATED_APP_MIDDLEWARE_ID, RESOLVED_GENERATED_APP_PLUGIN_ID, RESOLVED_GENERATED_APP_RUNTIME_ID, RESOLVED_GENERATED_CONFIG_ID, RESOLVED_GENERATED_ROUTES_MANIFEST_ID, RESOLVED_GENERATED_ROUTES_MODULES_ID, RESOLVED_GENERATED_SERVER_MIDDLEWARE_ID, RESOLVED_GENERATED_SERVER_PLUGIN_ID, RESOLVED_GENERATED_SERVER_ROUTES_ID, RESOLVED_VIRTUAL_APP_RUNTIME_ID, RESOLVED_VIRTUAL_ROUTES_MANIFEST_ID, RESOLVED_VIRTUAL_ROUTES_MODULES_ID, createVirtualAppLoaderModule, createVirtualAppMiddlewareModule, createVirtualAppPluginModule, createVirtualAppRuntimeModule, createVirtualConfigModule, createVirtualRoutesManifestModule, createVirtualRoutesModulesModule, createVirtualServerMiddlewareModule, createVirtualServerPluginModule, createVirtualServerRoutesModule, resolveVirtualModuleId } from "./generated/virtual-modules.js";
6
+ import { basename, extname, relative, resolve } from "node:path";
7
+ //#region src/lib/vite-plugin/index.ts
8
+ const DEFAULT_CLIENT_ENTRY_PUBLIC_PATH = "/@phial/client-entry.js";
9
+ function phialVitePlugin(options = {}) {
10
+ let viteConfig = null;
11
+ let scannedRoutesPromise = null;
12
+ let phialConfigPromise = null;
13
+ let watcherReady = false;
14
+ let refreshPromise = null;
15
+ let refreshRequested = false;
16
+ let refreshNeedsConfigReload = false;
17
+ let refreshNeedsFullReload = false;
18
+ return {
19
+ name: "phial:routes",
20
+ enforce: "pre",
21
+ configResolved(resolvedConfig) {
22
+ viteConfig = resolvedConfig;
23
+ scannedRoutesPromise = null;
24
+ phialConfigPromise = null;
25
+ },
26
+ async buildStart() {
27
+ await Promise.all([getScannedRoutes(), getPhialConfig()]);
28
+ },
29
+ resolveId(id) {
30
+ return resolveVirtualModuleId(id);
31
+ },
32
+ async load(id) {
33
+ const resolvedId = resolveVirtualModuleId(id);
34
+ if (!resolvedId) return null;
35
+ if (resolvedId === RESOLVED_GENERATED_CONFIG_ID) {
36
+ const phialConfig = await getPhialConfig();
37
+ return createVirtualConfigModule({
38
+ config: phialConfig.config,
39
+ hasConfigFile: Boolean(phialConfig.file)
40
+ });
41
+ }
42
+ const scannedRoutes = await getScannedRoutes();
43
+ if (resolvedId === RESOLVED_VIRTUAL_ROUTES_MANIFEST_ID) return createVirtualRoutesManifestModule(scannedRoutes);
44
+ if (resolvedId === RESOLVED_VIRTUAL_ROUTES_MODULES_ID) return createVirtualRoutesModulesModule(scannedRoutes, { moduleImportMode: options.moduleImportMode === "dynamic" ? "lazy" : "eager" });
45
+ if (resolvedId === RESOLVED_VIRTUAL_APP_RUNTIME_ID) return createVirtualAppRuntimeModule(scannedRoutes);
46
+ if (resolvedId === RESOLVED_GENERATED_ROUTES_MANIFEST_ID) return createVirtualRoutesManifestModule(scannedRoutes);
47
+ if (resolvedId === RESOLVED_GENERATED_ROUTES_MODULES_ID) return createVirtualRoutesModulesModule(scannedRoutes, { moduleImportMode: "eager" });
48
+ if (resolvedId === RESOLVED_GENERATED_APP_RUNTIME_ID) return createVirtualAppRuntimeModule(scannedRoutes);
49
+ if (resolvedId === RESOLVED_GENERATED_APP_LOADER_ID) return createVirtualAppLoaderModule(scannedRoutes);
50
+ if (resolvedId === RESOLVED_GENERATED_APP_MIDDLEWARE_ID) return createVirtualAppMiddlewareModule(scannedRoutes);
51
+ if (resolvedId === RESOLVED_GENERATED_APP_PLUGIN_ID) return createVirtualAppPluginModule();
52
+ if (resolvedId === RESOLVED_GENERATED_SERVER_ROUTES_ID) return createVirtualServerRoutesModule(scannedRoutes);
53
+ if (resolvedId === RESOLVED_GENERATED_SERVER_MIDDLEWARE_ID) return createVirtualServerMiddlewareModule(scannedRoutes);
54
+ if (resolvedId === RESOLVED_GENERATED_SERVER_PLUGIN_ID) return createVirtualServerPluginModule();
55
+ return null;
56
+ },
57
+ configureServer(server) {
58
+ const scheduleRefresh = (options) => {
59
+ refreshRequested = true;
60
+ refreshNeedsConfigReload ||= options.configReload === true;
61
+ refreshNeedsFullReload ||= options.fullReload === true;
62
+ if (refreshPromise) return refreshPromise;
63
+ refreshPromise = (async () => {
64
+ while (refreshRequested) {
65
+ refreshRequested = false;
66
+ const reloadConfig = refreshNeedsConfigReload;
67
+ const reloadFullPage = refreshNeedsFullReload;
68
+ refreshNeedsConfigReload = false;
69
+ refreshNeedsFullReload = false;
70
+ scannedRoutesPromise = null;
71
+ if (reloadConfig) phialConfigPromise = null;
72
+ await getPhialConfig();
73
+ await getScannedRoutes();
74
+ invalidateVirtualModules(server);
75
+ if (reloadFullPage) sendFullReload(server);
76
+ }
77
+ })().finally(() => {
78
+ refreshPromise = null;
79
+ });
80
+ return refreshPromise;
81
+ };
82
+ const handleStructureChange = async (file) => {
83
+ if (!watcherReady) return;
84
+ const scannedAppFile = await isScannedAppFile(file);
85
+ const scannedServerFile = await isScannedServerFile(file);
86
+ const phialConfigFile = isPhialConfigFile(file);
87
+ if (!scannedAppFile && !scannedServerFile && !phialConfigFile) return;
88
+ await scheduleRefresh({
89
+ configReload: phialConfigFile,
90
+ fullReload: scannedAppFile || phialConfigFile
91
+ });
92
+ };
93
+ const handleConfigChange = async (file) => {
94
+ if (!watcherReady || !isPhialConfigFile(file)) return;
95
+ await scheduleRefresh({
96
+ configReload: true,
97
+ fullReload: true
98
+ });
99
+ };
100
+ server.watcher.on("ready", () => {
101
+ watcherReady = true;
102
+ });
103
+ server.watcher.on("add", (file) => {
104
+ handleStructureChange(file);
105
+ });
106
+ server.watcher.on("unlink", (file) => {
107
+ handleStructureChange(file);
108
+ });
109
+ server.watcher.on("change", (file) => {
110
+ handleConfigChange(file);
111
+ });
112
+ server.middlewares.use(async (req, res, next) => {
113
+ if (req.url?.split("?")[0] !== "/@phial/client-entry.js") {
114
+ next();
115
+ return;
116
+ }
117
+ const source = createClientEntryModule();
118
+ res.setHeader("content-type", "application/javascript; charset=utf-8");
119
+ res.end(source);
120
+ });
121
+ }
122
+ };
123
+ async function getScannedRoutes() {
124
+ const resolvedOptions = await getResolvedOptions();
125
+ scannedRoutesPromise ??= scanRoutes({
126
+ root: resolvedOptions.root ?? viteConfig?.root,
127
+ appDir: resolvedOptions.appDir,
128
+ routesDir: resolvedOptions.routesDir,
129
+ serverRoutesDir: resolvedOptions.serverRoutesDir,
130
+ serverMiddlewareDir: resolvedOptions.serverMiddlewareDir,
131
+ extensions: resolvedOptions.extensions
132
+ }).then(async (result) => {
133
+ await writePhialProjectTypes(result);
134
+ return result;
135
+ });
136
+ return scannedRoutesPromise;
137
+ }
138
+ async function getPhialConfig() {
139
+ phialConfigPromise ??= loadPhialConfig({
140
+ root: options.root ?? viteConfig?.root,
141
+ command: viteConfig?.command ?? "serve",
142
+ mode: viteConfig?.mode,
143
+ isSsrBuild: Boolean(viteConfig?.build?.ssr),
144
+ isPreview: false,
145
+ logLevel: viteConfig?.logLevel
146
+ });
147
+ return phialConfigPromise;
148
+ }
149
+ async function getResolvedOptions() {
150
+ const phialConfig = await getPhialConfig();
151
+ const configuredOptions = phialConfig.config.plugin ?? {};
152
+ const appDir = options.appDir ?? configuredOptions.appDir ?? "app";
153
+ return {
154
+ ...configuredOptions,
155
+ ...options,
156
+ root: options.root ?? configuredOptions.root ?? phialConfig.configRoot,
157
+ appDir,
158
+ routesDir: options.routesDir ?? configuredOptions.routesDir ?? `${appDir}/pages`,
159
+ serverRoutesDir: options.serverRoutesDir ?? configuredOptions.serverRoutesDir ?? "server/routes",
160
+ serverMiddlewareDir: options.serverMiddlewareDir ?? configuredOptions.serverMiddlewareDir ?? "server/middleware"
161
+ };
162
+ }
163
+ async function isScannedAppFile(file) {
164
+ const resolvedOptions = await getResolvedOptions();
165
+ const root = resolve(resolvedOptions.root ?? viteConfig?.root ?? process.cwd());
166
+ const appDir = resolve(root, resolvedOptions.appDir ?? "app");
167
+ const middlewareDir = resolve(appDir, "middleware");
168
+ const configuredRoutesDir = resolvedOptions.routesDir ?? `${resolvedOptions.appDir ?? "app"}/pages`;
169
+ const configuredExtensions = resolvedOptions.extensions;
170
+ const routesDir = resolve(root, configuredRoutesDir);
171
+ const absoluteFile = resolve(file);
172
+ const relativeToAppDir = normalizePath(relative(appDir, absoluteFile));
173
+ const relativeToMiddlewareDir = normalizePath(relative(middlewareDir, absoluteFile));
174
+ const relativeToRoot = normalizePath(relative(root, absoluteFile));
175
+ const relativePath = normalizePath(relative(routesDir, resolve(file)));
176
+ const extension = extname(relativePath);
177
+ const normalizedExtensions = normalizeExtensions(configuredExtensions);
178
+ if (!relativeToAppDir.startsWith("../")) {
179
+ if (relativeToAppDir.startsWith("app.config.") && normalizedExtensions.includes(extname(relativeToAppDir))) return true;
180
+ const appFileExtension = extname(relativeToAppDir);
181
+ const appFileName = basename(relativeToAppDir, appFileExtension);
182
+ if (normalizedExtensions.includes(appFileExtension) && (appFileName === "app" || appFileName === "error" || appFileName === "loader")) return true;
183
+ }
184
+ if (!relativeToMiddlewareDir.startsWith("../") && normalizedExtensions.includes(extname(relativeToMiddlewareDir))) return true;
185
+ if (!relativeToRoot.startsWith("../") && relativeToRoot.startsWith("app.config.") && normalizedExtensions.includes(extname(relativeToRoot))) return true;
186
+ if (!relativePath || relativePath.startsWith("../")) return false;
187
+ if (!normalizedExtensions.includes(extension)) return false;
188
+ const fileName = basename(relativePath, extension);
189
+ return [
190
+ "layout",
191
+ "page",
192
+ "error",
193
+ "loading",
194
+ "action",
195
+ "loader",
196
+ "middleware",
197
+ "_middleware"
198
+ ].includes(fileName);
199
+ }
200
+ async function isScannedServerFile(file) {
201
+ const resolvedOptions = await getResolvedOptions();
202
+ const root = resolve(resolvedOptions.root ?? viteConfig?.root ?? process.cwd());
203
+ const serverRoutesDir = resolve(root, resolvedOptions.serverRoutesDir ?? "server/routes");
204
+ const serverMiddlewareDir = resolve(root, resolvedOptions.serverMiddlewareDir ?? "server/middleware");
205
+ const absoluteFile = resolve(file);
206
+ const relativeToRoutesDir = normalizePath(relative(serverRoutesDir, absoluteFile));
207
+ const relativeToMiddlewareDir = normalizePath(relative(serverMiddlewareDir, absoluteFile));
208
+ const normalizedExtensions = normalizeExtensions(resolvedOptions.extensions).filter((extension) => extension !== ".vue");
209
+ return !relativeToRoutesDir.startsWith("../") && normalizedExtensions.includes(extname(relativeToRoutesDir)) || !relativeToMiddlewareDir.startsWith("../") && normalizedExtensions.includes(extname(relativeToMiddlewareDir));
210
+ }
211
+ }
212
+ function invalidateVirtualModules(server) {
213
+ invalidateVirtualModule(server, RESOLVED_VIRTUAL_ROUTES_MANIFEST_ID);
214
+ invalidateVirtualModule(server, RESOLVED_VIRTUAL_ROUTES_MODULES_ID);
215
+ invalidateVirtualModule(server, RESOLVED_VIRTUAL_APP_RUNTIME_ID);
216
+ invalidateVirtualModule(server, RESOLVED_GENERATED_ROUTES_MANIFEST_ID);
217
+ invalidateVirtualModule(server, RESOLVED_GENERATED_ROUTES_MODULES_ID);
218
+ invalidateVirtualModule(server, RESOLVED_GENERATED_APP_RUNTIME_ID);
219
+ invalidateVirtualModule(server, RESOLVED_GENERATED_APP_LOADER_ID);
220
+ invalidateVirtualModule(server, RESOLVED_GENERATED_APP_MIDDLEWARE_ID);
221
+ invalidateVirtualModule(server, RESOLVED_GENERATED_APP_PLUGIN_ID);
222
+ invalidateVirtualModule(server, RESOLVED_GENERATED_SERVER_ROUTES_ID);
223
+ invalidateVirtualModule(server, RESOLVED_GENERATED_SERVER_MIDDLEWARE_ID);
224
+ invalidateVirtualModule(server, RESOLVED_GENERATED_SERVER_PLUGIN_ID);
225
+ invalidateVirtualModule(server, RESOLVED_GENERATED_CONFIG_ID);
226
+ }
227
+ function sendFullReload(server) {
228
+ const clientEnvironment = server.environments.client;
229
+ if (clientEnvironment) {
230
+ clientEnvironment.hot.send({
231
+ type: "full-reload",
232
+ path: "*"
233
+ });
234
+ return;
235
+ }
236
+ server.ws.send({
237
+ type: "full-reload",
238
+ path: "*"
239
+ });
240
+ }
241
+ function invalidateVirtualModule(server, id) {
242
+ const module = server.moduleGraph.getModuleById(id);
243
+ if (module) server.moduleGraph.invalidateModule(module, /* @__PURE__ */ new Set(), Date.now(), true);
244
+ }
245
+ function normalizeExtensions(extensions) {
246
+ const resolved = extensions?.length ? extensions : [
247
+ ".vue",
248
+ ".ts",
249
+ ".js",
250
+ ".tsx",
251
+ ".jsx"
252
+ ];
253
+ return Array.from(new Set(resolved.map((extension) => extension.startsWith(".") ? extension : `.${extension}`)));
254
+ }
255
+ function normalizePath(path) {
256
+ return path.replace(/\\/g, "/");
257
+ }
258
+ //#endregion
259
+ export { DEFAULT_CLIENT_ENTRY_PUBLIC_PATH, phialVitePlugin };
260
+
261
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/lib/vite-plugin/index.ts"],"sourcesContent":["import { basename, extname, relative, resolve } from \"node:path\";\nimport type { Plugin, ResolvedConfig, ViteDevServer } from \"vite\";\nimport type { PhialPluginOptions } from \"./config\";\nimport { isPhialConfigFile, loadPhialConfig, type LoadedPhialConfig } from \"./config\";\nimport { createClientEntryModule } from \"./generated/client-entry\";\nimport { scanRoutes } from \"./scanners/routes-scanner\";\nimport type { ScannedRoutesResult } from \"./scanners/route-manifest\";\nimport { writePhialProjectTypes } from \"./scanners/types-generator\";\nimport {\n createVirtualAppLoaderModule,\n createVirtualAppMiddlewareModule,\n createVirtualAppPluginModule,\n createVirtualAppRuntimeModule,\n createVirtualServerPluginModule,\n createVirtualServerMiddlewareModule,\n createVirtualServerRoutesModule,\n createVirtualConfigModule,\n RESOLVED_GENERATED_APP_LOADER_ID,\n RESOLVED_GENERATED_APP_MIDDLEWARE_ID,\n RESOLVED_GENERATED_APP_PLUGIN_ID,\n RESOLVED_GENERATED_SERVER_MIDDLEWARE_ID,\n RESOLVED_GENERATED_SERVER_PLUGIN_ID,\n RESOLVED_GENERATED_SERVER_ROUTES_ID,\n RESOLVED_GENERATED_CONFIG_ID,\n RESOLVED_GENERATED_APP_RUNTIME_ID,\n createVirtualRoutesManifestModule,\n createVirtualRoutesModulesModule,\n RESOLVED_GENERATED_ROUTES_MANIFEST_ID,\n RESOLVED_GENERATED_ROUTES_MODULES_ID,\n RESOLVED_VIRTUAL_APP_RUNTIME_ID,\n RESOLVED_VIRTUAL_ROUTES_MANIFEST_ID,\n RESOLVED_VIRTUAL_ROUTES_MODULES_ID,\n resolveVirtualModuleId,\n} from \"./generated/virtual-modules\";\n\nexport interface PhialVitePluginOptions extends PhialPluginOptions {}\nexport const DEFAULT_CLIENT_ENTRY_PUBLIC_PATH = \"/@phial/client-entry.js\";\n\nexport function phialVitePlugin(options: PhialVitePluginOptions = {}): Plugin {\n let viteConfig: ResolvedConfig | null = null;\n let scannedRoutesPromise: Promise<ScannedRoutesResult> | null = null;\n let phialConfigPromise: Promise<LoadedPhialConfig> | null = null;\n let watcherReady = false;\n let refreshPromise: Promise<void> | null = null;\n let refreshRequested = false;\n let refreshNeedsConfigReload = false;\n let refreshNeedsFullReload = false;\n\n return {\n name: \"phial:routes\",\n enforce: \"pre\",\n configResolved(resolvedConfig) {\n viteConfig = resolvedConfig;\n scannedRoutesPromise = null;\n phialConfigPromise = null;\n },\n async buildStart() {\n await Promise.all([getScannedRoutes(), getPhialConfig()]);\n },\n resolveId(id) {\n return resolveVirtualModuleId(id);\n },\n async load(id) {\n const resolvedId = resolveVirtualModuleId(id);\n if (!resolvedId) {\n return null;\n }\n\n if (resolvedId === RESOLVED_GENERATED_CONFIG_ID) {\n const phialConfig = await getPhialConfig();\n\n return createVirtualConfigModule({\n config: phialConfig.config as Record<string, unknown>,\n hasConfigFile: Boolean(phialConfig.file),\n });\n }\n\n const scannedRoutes = await getScannedRoutes();\n\n if (resolvedId === RESOLVED_VIRTUAL_ROUTES_MANIFEST_ID) {\n return createVirtualRoutesManifestModule(scannedRoutes);\n }\n\n if (resolvedId === RESOLVED_VIRTUAL_ROUTES_MODULES_ID) {\n return createVirtualRoutesModulesModule(scannedRoutes, {\n moduleImportMode: options.moduleImportMode === \"dynamic\" ? \"lazy\" : \"eager\",\n });\n }\n\n if (resolvedId === RESOLVED_VIRTUAL_APP_RUNTIME_ID) {\n return createVirtualAppRuntimeModule(scannedRoutes);\n }\n\n if (resolvedId === RESOLVED_GENERATED_ROUTES_MANIFEST_ID) {\n return createVirtualRoutesManifestModule(scannedRoutes);\n }\n\n if (resolvedId === RESOLVED_GENERATED_ROUTES_MODULES_ID) {\n return createVirtualRoutesModulesModule(scannedRoutes, {\n moduleImportMode: \"eager\",\n });\n }\n\n if (resolvedId === RESOLVED_GENERATED_APP_RUNTIME_ID) {\n return createVirtualAppRuntimeModule(scannedRoutes);\n }\n\n if (resolvedId === RESOLVED_GENERATED_APP_LOADER_ID) {\n return createVirtualAppLoaderModule(scannedRoutes);\n }\n\n if (resolvedId === RESOLVED_GENERATED_APP_MIDDLEWARE_ID) {\n return createVirtualAppMiddlewareModule(scannedRoutes);\n }\n\n if (resolvedId === RESOLVED_GENERATED_APP_PLUGIN_ID) {\n return createVirtualAppPluginModule();\n }\n\n if (resolvedId === RESOLVED_GENERATED_SERVER_ROUTES_ID) {\n return createVirtualServerRoutesModule(scannedRoutes);\n }\n\n if (resolvedId === RESOLVED_GENERATED_SERVER_MIDDLEWARE_ID) {\n return createVirtualServerMiddlewareModule(scannedRoutes);\n }\n\n if (resolvedId === RESOLVED_GENERATED_SERVER_PLUGIN_ID) {\n return createVirtualServerPluginModule();\n }\n\n return null;\n },\n configureServer(server) {\n const scheduleRefresh = (options: { configReload?: boolean; fullReload?: boolean }) => {\n refreshRequested = true;\n refreshNeedsConfigReload ||= options.configReload === true;\n refreshNeedsFullReload ||= options.fullReload === true;\n\n if (refreshPromise) {\n return refreshPromise;\n }\n\n refreshPromise = (async () => {\n while (refreshRequested) {\n refreshRequested = false;\n const reloadConfig = refreshNeedsConfigReload;\n const reloadFullPage = refreshNeedsFullReload;\n refreshNeedsConfigReload = false;\n refreshNeedsFullReload = false;\n\n scannedRoutesPromise = null;\n if (reloadConfig) {\n phialConfigPromise = null;\n }\n\n await getPhialConfig();\n await getScannedRoutes();\n invalidateVirtualModules(server);\n\n if (reloadFullPage) {\n sendFullReload(server);\n }\n }\n })().finally(() => {\n refreshPromise = null;\n });\n\n return refreshPromise;\n };\n\n const handleStructureChange = async (file: string) => {\n if (!watcherReady) {\n return;\n }\n\n const scannedAppFile = await isScannedAppFile(file);\n const scannedServerFile = await isScannedServerFile(file);\n const phialConfigFile = isPhialConfigFile(file);\n\n if (!scannedAppFile && !scannedServerFile && !phialConfigFile) {\n return;\n }\n\n await scheduleRefresh({\n configReload: phialConfigFile,\n fullReload: scannedAppFile || phialConfigFile,\n });\n };\n\n const handleConfigChange = async (file: string) => {\n if (!watcherReady || !isPhialConfigFile(file)) {\n return;\n }\n\n await scheduleRefresh({\n configReload: true,\n fullReload: true,\n });\n };\n\n server.watcher.on(\"ready\", () => {\n watcherReady = true;\n });\n server.watcher.on(\"add\", (file) => {\n void handleStructureChange(file);\n });\n server.watcher.on(\"unlink\", (file) => {\n void handleStructureChange(file);\n });\n server.watcher.on(\"change\", (file) => {\n void handleConfigChange(file);\n });\n\n server.middlewares.use(async (req, res, next) => {\n const requestPath = req.url?.split(\"?\")[0];\n if (requestPath !== DEFAULT_CLIENT_ENTRY_PUBLIC_PATH) {\n next();\n return;\n }\n\n const source = createClientEntryModule();\n\n res.setHeader(\"content-type\", \"application/javascript; charset=utf-8\");\n res.end(source);\n });\n },\n };\n\n async function getScannedRoutes(): Promise<ScannedRoutesResult> {\n const resolvedOptions = await getResolvedOptions();\n\n scannedRoutesPromise ??= scanRoutes({\n root: resolvedOptions.root ?? viteConfig?.root,\n appDir: resolvedOptions.appDir,\n routesDir: resolvedOptions.routesDir,\n serverRoutesDir: resolvedOptions.serverRoutesDir,\n serverMiddlewareDir: resolvedOptions.serverMiddlewareDir,\n extensions: resolvedOptions.extensions,\n }).then(async (result) => {\n await writePhialProjectTypes(result);\n return result;\n });\n\n return scannedRoutesPromise;\n }\n\n async function getPhialConfig(): Promise<LoadedPhialConfig> {\n phialConfigPromise ??= loadPhialConfig({\n root: options.root ?? viteConfig?.root,\n command: viteConfig?.command ?? \"serve\",\n mode: viteConfig?.mode,\n isSsrBuild: Boolean(viteConfig?.build?.ssr),\n isPreview: false,\n logLevel: viteConfig?.logLevel,\n });\n\n return phialConfigPromise;\n }\n\n async function getResolvedOptions(): Promise<PhialVitePluginOptions> {\n const phialConfig = await getPhialConfig();\n const configuredOptions = phialConfig.config.plugin ?? {};\n const appDir = options.appDir ?? configuredOptions.appDir ?? \"app\";\n\n return {\n ...configuredOptions,\n ...options,\n root: options.root ?? configuredOptions.root ?? phialConfig.configRoot,\n appDir,\n routesDir: options.routesDir ?? configuredOptions.routesDir ?? `${appDir}/pages`,\n serverRoutesDir:\n options.serverRoutesDir ?? configuredOptions.serverRoutesDir ?? \"server/routes\",\n serverMiddlewareDir:\n options.serverMiddlewareDir ?? configuredOptions.serverMiddlewareDir ?? \"server/middleware\",\n };\n }\n\n async function isScannedAppFile(file: string): Promise<boolean> {\n const resolvedOptions = await getResolvedOptions();\n const root = resolve(resolvedOptions.root ?? viteConfig?.root ?? process.cwd());\n const appDir = resolve(root, resolvedOptions.appDir ?? \"app\");\n const middlewareDir = resolve(appDir, \"middleware\");\n const configuredRoutesDir =\n resolvedOptions.routesDir ?? `${resolvedOptions.appDir ?? \"app\"}/pages`;\n const configuredExtensions = resolvedOptions.extensions;\n const routesDir = resolve(root, configuredRoutesDir);\n const absoluteFile = resolve(file);\n const relativeToAppDir = normalizePath(relative(appDir, absoluteFile));\n const relativeToMiddlewareDir = normalizePath(relative(middlewareDir, absoluteFile));\n const relativeToRoot = normalizePath(relative(root, absoluteFile));\n const relativePath = normalizePath(relative(routesDir, resolve(file)));\n const extension = extname(relativePath);\n const normalizedExtensions = normalizeExtensions(configuredExtensions);\n\n if (!relativeToAppDir.startsWith(\"../\")) {\n if (\n relativeToAppDir.startsWith(\"app.config.\") &&\n normalizedExtensions.includes(extname(relativeToAppDir))\n ) {\n return true;\n }\n\n const appFileExtension = extname(relativeToAppDir);\n const appFileName = basename(relativeToAppDir, appFileExtension);\n if (\n normalizedExtensions.includes(appFileExtension) &&\n (appFileName === \"app\" || appFileName === \"error\" || appFileName === \"loader\")\n ) {\n return true;\n }\n }\n\n if (\n !relativeToMiddlewareDir.startsWith(\"../\") &&\n normalizedExtensions.includes(extname(relativeToMiddlewareDir))\n ) {\n return true;\n }\n\n if (\n !relativeToRoot.startsWith(\"../\") &&\n relativeToRoot.startsWith(\"app.config.\") &&\n normalizedExtensions.includes(extname(relativeToRoot))\n ) {\n return true;\n }\n\n if (!relativePath || relativePath.startsWith(\"../\")) {\n return false;\n }\n\n if (!normalizedExtensions.includes(extension)) {\n return false;\n }\n\n const fileName = basename(relativePath, extension);\n return [\n \"layout\",\n \"page\",\n \"error\",\n \"loading\",\n \"action\",\n \"loader\",\n \"middleware\",\n \"_middleware\",\n ].includes(fileName);\n }\n\n async function isScannedServerFile(file: string): Promise<boolean> {\n const resolvedOptions = await getResolvedOptions();\n const root = resolve(resolvedOptions.root ?? viteConfig?.root ?? process.cwd());\n const serverRoutesDir = resolve(root, resolvedOptions.serverRoutesDir ?? \"server/routes\");\n const serverMiddlewareDir = resolve(\n root,\n resolvedOptions.serverMiddlewareDir ?? \"server/middleware\",\n );\n const absoluteFile = resolve(file);\n const relativeToRoutesDir = normalizePath(relative(serverRoutesDir, absoluteFile));\n const relativeToMiddlewareDir = normalizePath(relative(serverMiddlewareDir, absoluteFile));\n const normalizedExtensions = normalizeExtensions(resolvedOptions.extensions).filter(\n (extension) => extension !== \".vue\",\n );\n\n return (\n (!relativeToRoutesDir.startsWith(\"../\") &&\n normalizedExtensions.includes(extname(relativeToRoutesDir))) ||\n (!relativeToMiddlewareDir.startsWith(\"../\") &&\n normalizedExtensions.includes(extname(relativeToMiddlewareDir)))\n );\n }\n}\n\nfunction invalidateVirtualModules(server: ViteDevServer): void {\n invalidateVirtualModule(server, RESOLVED_VIRTUAL_ROUTES_MANIFEST_ID);\n invalidateVirtualModule(server, RESOLVED_VIRTUAL_ROUTES_MODULES_ID);\n invalidateVirtualModule(server, RESOLVED_VIRTUAL_APP_RUNTIME_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_ROUTES_MANIFEST_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_ROUTES_MODULES_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_APP_RUNTIME_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_APP_LOADER_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_APP_MIDDLEWARE_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_APP_PLUGIN_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_SERVER_ROUTES_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_SERVER_MIDDLEWARE_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_SERVER_PLUGIN_ID);\n invalidateVirtualModule(server, RESOLVED_GENERATED_CONFIG_ID);\n}\n\nfunction sendFullReload(server: ViteDevServer): void {\n const clientEnvironment = server.environments.client;\n if (clientEnvironment) {\n clientEnvironment.hot.send({\n type: \"full-reload\",\n path: \"*\",\n });\n return;\n }\n\n server.ws.send({\n type: \"full-reload\",\n path: \"*\",\n });\n}\n\nfunction invalidateVirtualModule(server: ViteDevServer, id: string): void {\n const module = server.moduleGraph.getModuleById(id);\n if (module) {\n server.moduleGraph.invalidateModule(module, new Set(), Date.now(), true);\n }\n}\n\nfunction normalizeExtensions(extensions?: string[]): string[] {\n const resolved = extensions?.length ? extensions : [\".vue\", \".ts\", \".js\", \".tsx\", \".jsx\"];\n return Array.from(\n new Set(resolved.map((extension) => (extension.startsWith(\".\") ? extension : `.${extension}`))),\n );\n}\n\nfunction normalizePath(path: string): string {\n return path.replace(/\\\\/g, \"/\");\n}\n"],"mappings":";;;;;;;AAoCA,MAAa,mCAAmC;AAEhD,SAAgB,gBAAgB,UAAkC,EAAE,EAAU;CAC5E,IAAI,aAAoC;CACxC,IAAI,uBAA4D;CAChE,IAAI,qBAAwD;CAC5D,IAAI,eAAe;CACnB,IAAI,iBAAuC;CAC3C,IAAI,mBAAmB;CACvB,IAAI,2BAA2B;CAC/B,IAAI,yBAAyB;AAE7B,QAAO;EACL,MAAM;EACN,SAAS;EACT,eAAe,gBAAgB;AAC7B,gBAAa;AACb,0BAAuB;AACvB,wBAAqB;;EAEvB,MAAM,aAAa;AACjB,SAAM,QAAQ,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;;EAE3D,UAAU,IAAI;AACZ,UAAO,uBAAuB,GAAG;;EAEnC,MAAM,KAAK,IAAI;GACb,MAAM,aAAa,uBAAuB,GAAG;AAC7C,OAAI,CAAC,WACH,QAAO;AAGT,OAAI,eAAe,8BAA8B;IAC/C,MAAM,cAAc,MAAM,gBAAgB;AAE1C,WAAO,0BAA0B;KAC/B,QAAQ,YAAY;KACpB,eAAe,QAAQ,YAAY,KAAK;KACzC,CAAC;;GAGJ,MAAM,gBAAgB,MAAM,kBAAkB;AAE9C,OAAI,eAAe,oCACjB,QAAO,kCAAkC,cAAc;AAGzD,OAAI,eAAe,mCACjB,QAAO,iCAAiC,eAAe,EACrD,kBAAkB,QAAQ,qBAAqB,YAAY,SAAS,SACrE,CAAC;AAGJ,OAAI,eAAe,gCACjB,QAAO,8BAA8B,cAAc;AAGrD,OAAI,eAAe,sCACjB,QAAO,kCAAkC,cAAc;AAGzD,OAAI,eAAe,qCACjB,QAAO,iCAAiC,eAAe,EACrD,kBAAkB,SACnB,CAAC;AAGJ,OAAI,eAAe,kCACjB,QAAO,8BAA8B,cAAc;AAGrD,OAAI,eAAe,iCACjB,QAAO,6BAA6B,cAAc;AAGpD,OAAI,eAAe,qCACjB,QAAO,iCAAiC,cAAc;AAGxD,OAAI,eAAe,iCACjB,QAAO,8BAA8B;AAGvC,OAAI,eAAe,oCACjB,QAAO,gCAAgC,cAAc;AAGvD,OAAI,eAAe,wCACjB,QAAO,oCAAoC,cAAc;AAG3D,OAAI,eAAe,oCACjB,QAAO,iCAAiC;AAG1C,UAAO;;EAET,gBAAgB,QAAQ;GACtB,MAAM,mBAAmB,YAA8D;AACrF,uBAAmB;AACnB,iCAA6B,QAAQ,iBAAiB;AACtD,+BAA2B,QAAQ,eAAe;AAElD,QAAI,eACF,QAAO;AAGT,sBAAkB,YAAY;AAC5B,YAAO,kBAAkB;AACvB,yBAAmB;MACnB,MAAM,eAAe;MACrB,MAAM,iBAAiB;AACvB,iCAA2B;AAC3B,+BAAyB;AAEzB,6BAAuB;AACvB,UAAI,aACF,sBAAqB;AAGvB,YAAM,gBAAgB;AACtB,YAAM,kBAAkB;AACxB,+BAAyB,OAAO;AAEhC,UAAI,eACF,gBAAe,OAAO;;QAGxB,CAAC,cAAc;AACjB,sBAAiB;MACjB;AAEF,WAAO;;GAGT,MAAM,wBAAwB,OAAO,SAAiB;AACpD,QAAI,CAAC,aACH;IAGF,MAAM,iBAAiB,MAAM,iBAAiB,KAAK;IACnD,MAAM,oBAAoB,MAAM,oBAAoB,KAAK;IACzD,MAAM,kBAAkB,kBAAkB,KAAK;AAE/C,QAAI,CAAC,kBAAkB,CAAC,qBAAqB,CAAC,gBAC5C;AAGF,UAAM,gBAAgB;KACpB,cAAc;KACd,YAAY,kBAAkB;KAC/B,CAAC;;GAGJ,MAAM,qBAAqB,OAAO,SAAiB;AACjD,QAAI,CAAC,gBAAgB,CAAC,kBAAkB,KAAK,CAC3C;AAGF,UAAM,gBAAgB;KACpB,cAAc;KACd,YAAY;KACb,CAAC;;AAGJ,UAAO,QAAQ,GAAG,eAAe;AAC/B,mBAAe;KACf;AACF,UAAO,QAAQ,GAAG,QAAQ,SAAS;AAC5B,0BAAsB,KAAK;KAChC;AACF,UAAO,QAAQ,GAAG,WAAW,SAAS;AAC/B,0BAAsB,KAAK;KAChC;AACF,UAAO,QAAQ,GAAG,WAAW,SAAS;AAC/B,uBAAmB,KAAK;KAC7B;AAEF,UAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;AAE/C,QADoB,IAAI,KAAK,MAAM,IAAI,CAAC,OAAA,2BACc;AACpD,WAAM;AACN;;IAGF,MAAM,SAAS,yBAAyB;AAExC,QAAI,UAAU,gBAAgB,wCAAwC;AACtE,QAAI,IAAI,OAAO;KACf;;EAEL;CAED,eAAe,mBAAiD;EAC9D,MAAM,kBAAkB,MAAM,oBAAoB;AAElD,2BAAyB,WAAW;GAClC,MAAM,gBAAgB,QAAQ,YAAY;GAC1C,QAAQ,gBAAgB;GACxB,WAAW,gBAAgB;GAC3B,iBAAiB,gBAAgB;GACjC,qBAAqB,gBAAgB;GACrC,YAAY,gBAAgB;GAC7B,CAAC,CAAC,KAAK,OAAO,WAAW;AACxB,SAAM,uBAAuB,OAAO;AACpC,UAAO;IACP;AAEF,SAAO;;CAGT,eAAe,iBAA6C;AAC1D,yBAAuB,gBAAgB;GACrC,MAAM,QAAQ,QAAQ,YAAY;GAClC,SAAS,YAAY,WAAW;GAChC,MAAM,YAAY;GAClB,YAAY,QAAQ,YAAY,OAAO,IAAI;GAC3C,WAAW;GACX,UAAU,YAAY;GACvB,CAAC;AAEF,SAAO;;CAGT,eAAe,qBAAsD;EACnE,MAAM,cAAc,MAAM,gBAAgB;EAC1C,MAAM,oBAAoB,YAAY,OAAO,UAAU,EAAE;EACzD,MAAM,SAAS,QAAQ,UAAU,kBAAkB,UAAU;AAE7D,SAAO;GACL,GAAG;GACH,GAAG;GACH,MAAM,QAAQ,QAAQ,kBAAkB,QAAQ,YAAY;GAC5D;GACA,WAAW,QAAQ,aAAa,kBAAkB,aAAa,GAAG,OAAO;GACzE,iBACE,QAAQ,mBAAmB,kBAAkB,mBAAmB;GAClE,qBACE,QAAQ,uBAAuB,kBAAkB,uBAAuB;GAC3E;;CAGH,eAAe,iBAAiB,MAAgC;EAC9D,MAAM,kBAAkB,MAAM,oBAAoB;EAClD,MAAM,OAAO,QAAQ,gBAAgB,QAAQ,YAAY,QAAQ,QAAQ,KAAK,CAAC;EAC/E,MAAM,SAAS,QAAQ,MAAM,gBAAgB,UAAU,MAAM;EAC7D,MAAM,gBAAgB,QAAQ,QAAQ,aAAa;EACnD,MAAM,sBACJ,gBAAgB,aAAa,GAAG,gBAAgB,UAAU,MAAM;EAClE,MAAM,uBAAuB,gBAAgB;EAC7C,MAAM,YAAY,QAAQ,MAAM,oBAAoB;EACpD,MAAM,eAAe,QAAQ,KAAK;EAClC,MAAM,mBAAmB,cAAc,SAAS,QAAQ,aAAa,CAAC;EACtE,MAAM,0BAA0B,cAAc,SAAS,eAAe,aAAa,CAAC;EACpF,MAAM,iBAAiB,cAAc,SAAS,MAAM,aAAa,CAAC;EAClE,MAAM,eAAe,cAAc,SAAS,WAAW,QAAQ,KAAK,CAAC,CAAC;EACtE,MAAM,YAAY,QAAQ,aAAa;EACvC,MAAM,uBAAuB,oBAAoB,qBAAqB;AAEtE,MAAI,CAAC,iBAAiB,WAAW,MAAM,EAAE;AACvC,OACE,iBAAiB,WAAW,cAAc,IAC1C,qBAAqB,SAAS,QAAQ,iBAAiB,CAAC,CAExD,QAAO;GAGT,MAAM,mBAAmB,QAAQ,iBAAiB;GAClD,MAAM,cAAc,SAAS,kBAAkB,iBAAiB;AAChE,OACE,qBAAqB,SAAS,iBAAiB,KAC9C,gBAAgB,SAAS,gBAAgB,WAAW,gBAAgB,UAErE,QAAO;;AAIX,MACE,CAAC,wBAAwB,WAAW,MAAM,IAC1C,qBAAqB,SAAS,QAAQ,wBAAwB,CAAC,CAE/D,QAAO;AAGT,MACE,CAAC,eAAe,WAAW,MAAM,IACjC,eAAe,WAAW,cAAc,IACxC,qBAAqB,SAAS,QAAQ,eAAe,CAAC,CAEtD,QAAO;AAGT,MAAI,CAAC,gBAAgB,aAAa,WAAW,MAAM,CACjD,QAAO;AAGT,MAAI,CAAC,qBAAqB,SAAS,UAAU,CAC3C,QAAO;EAGT,MAAM,WAAW,SAAS,cAAc,UAAU;AAClD,SAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,SAAS,SAAS;;CAGtB,eAAe,oBAAoB,MAAgC;EACjE,MAAM,kBAAkB,MAAM,oBAAoB;EAClD,MAAM,OAAO,QAAQ,gBAAgB,QAAQ,YAAY,QAAQ,QAAQ,KAAK,CAAC;EAC/E,MAAM,kBAAkB,QAAQ,MAAM,gBAAgB,mBAAmB,gBAAgB;EACzF,MAAM,sBAAsB,QAC1B,MACA,gBAAgB,uBAAuB,oBACxC;EACD,MAAM,eAAe,QAAQ,KAAK;EAClC,MAAM,sBAAsB,cAAc,SAAS,iBAAiB,aAAa,CAAC;EAClF,MAAM,0BAA0B,cAAc,SAAS,qBAAqB,aAAa,CAAC;EAC1F,MAAM,uBAAuB,oBAAoB,gBAAgB,WAAW,CAAC,QAC1E,cAAc,cAAc,OAC9B;AAED,SACG,CAAC,oBAAoB,WAAW,MAAM,IACrC,qBAAqB,SAAS,QAAQ,oBAAoB,CAAC,IAC5D,CAAC,wBAAwB,WAAW,MAAM,IACzC,qBAAqB,SAAS,QAAQ,wBAAwB,CAAC;;;AAKvE,SAAS,yBAAyB,QAA6B;AAC7D,yBAAwB,QAAQ,oCAAoC;AACpE,yBAAwB,QAAQ,mCAAmC;AACnE,yBAAwB,QAAQ,gCAAgC;AAChE,yBAAwB,QAAQ,sCAAsC;AACtE,yBAAwB,QAAQ,qCAAqC;AACrE,yBAAwB,QAAQ,kCAAkC;AAClE,yBAAwB,QAAQ,iCAAiC;AACjE,yBAAwB,QAAQ,qCAAqC;AACrE,yBAAwB,QAAQ,iCAAiC;AACjE,yBAAwB,QAAQ,oCAAoC;AACpE,yBAAwB,QAAQ,wCAAwC;AACxE,yBAAwB,QAAQ,oCAAoC;AACpE,yBAAwB,QAAQ,6BAA6B;;AAG/D,SAAS,eAAe,QAA6B;CACnD,MAAM,oBAAoB,OAAO,aAAa;AAC9C,KAAI,mBAAmB;AACrB,oBAAkB,IAAI,KAAK;GACzB,MAAM;GACN,MAAM;GACP,CAAC;AACF;;AAGF,QAAO,GAAG,KAAK;EACb,MAAM;EACN,MAAM;EACP,CAAC;;AAGJ,SAAS,wBAAwB,QAAuB,IAAkB;CACxE,MAAM,SAAS,OAAO,YAAY,cAAc,GAAG;AACnD,KAAI,OACF,QAAO,YAAY,iBAAiB,wBAAQ,IAAI,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK;;AAI5E,SAAS,oBAAoB,YAAiC;CAC5D,MAAM,WAAW,YAAY,SAAS,aAAa;EAAC;EAAQ;EAAO;EAAO;EAAQ;EAAO;AACzF,QAAO,MAAM,KACX,IAAI,IAAI,SAAS,KAAK,cAAe,UAAU,WAAW,IAAI,GAAG,YAAY,IAAI,YAAa,CAAC,CAChG;;AAGH,SAAS,cAAc,MAAsB;AAC3C,QAAO,KAAK,QAAQ,OAAO,IAAI"}
@@ -0,0 +1,162 @@
1
+ import { DIRECTORY_MIDDLEWARE_BASENAME, ROUTE_FILE_BASENAMES, collectEntryFiles, getLastPathSegment, isRouteDirectory, normalizeDirectoryMiddlewareExtensions, resolveNestedFiles, resolveSingleEntryFile, toPosixPath, toRootRelativeRouteFile } from "./scanner-utils.js";
2
+ import { basename, extname, relative, resolve } from "node:path";
3
+ import { build } from "fs-route-ir";
4
+ //#region src/lib/vite-plugin/scanners/app-pages-scanner.ts
5
+ async function scanAppPageRoutes(options) {
6
+ const files = await resolveNestedFiles(options.routesDir);
7
+ if (files.length === 0) return [];
8
+ const directoryMiddlewareExtensions = normalizeDirectoryMiddlewareExtensions(options.extensions);
9
+ return collectAppRouteModules(build(files, {
10
+ profile: "directory-based",
11
+ root: "",
12
+ ignore(entry, kind) {
13
+ if (kind === "dir") return !isRouteDirectory(getLastPathSegment(entry));
14
+ return !options.extensions.includes(extname(entry));
15
+ },
16
+ defineEntry({ file, baseName }) {
17
+ if (baseName === "_middleware") {
18
+ if (!directoryMiddlewareExtensions.includes(extname(file))) return null;
19
+ return {
20
+ kind: "directory-middleware",
21
+ scope: "directory"
22
+ };
23
+ }
24
+ if (ROUTE_FILE_BASENAMES.includes(baseName)) return { kind: baseName };
25
+ return null;
26
+ }
27
+ }).tree.nodes, options);
28
+ }
29
+ function collectAppRouteModules(nodes, options) {
30
+ const modules = [];
31
+ for (const node of nodes) visit(node, null, []);
32
+ return modules;
33
+ function visit(node, inheritedLayout, inheritedDirectoryMiddleware) {
34
+ const { routeFiles, directoryMiddlewareFile } = collectAppNodeEntries(node, options);
35
+ const nextDirectoryMiddleware = directoryMiddlewareFile ? [...inheritedDirectoryMiddleware, directoryMiddlewareFile] : inheritedDirectoryMiddleware;
36
+ const routeSegments = toManifestRouteSegments(node.segments);
37
+ let currentLayout = inheritedLayout;
38
+ assertRouteDirectoryFiles(node.dir, routeFiles);
39
+ if (routeFiles.layout) {
40
+ const layoutId = createRouteId(node.id, "layout");
41
+ modules.push(createScannedRouteModule({
42
+ id: layoutId,
43
+ kind: "layout",
44
+ root: options.root,
45
+ routesDir: options.routesDir,
46
+ file: routeFiles.layout,
47
+ directoryMiddleware: createDirectoryMiddlewareDelta(nextDirectoryMiddleware, inheritedLayout?.middlewareDepth),
48
+ files: createPrimaryRouteFiles(routeFiles, "layout"),
49
+ path: createLayoutPath(routeSegments, inheritedLayout?.segments),
50
+ parentId: inheritedLayout?.id
51
+ }));
52
+ currentLayout = {
53
+ id: layoutId,
54
+ segments: routeSegments,
55
+ middlewareDepth: nextDirectoryMiddleware.length
56
+ };
57
+ }
58
+ if (routeFiles.page) modules.push(createScannedRouteModule({
59
+ id: createRouteId(node.id, "page"),
60
+ kind: "page",
61
+ root: options.root,
62
+ routesDir: options.routesDir,
63
+ file: routeFiles.page,
64
+ directoryMiddleware: createDirectoryMiddlewareDelta(nextDirectoryMiddleware, currentLayout?.middlewareDepth),
65
+ files: routeFiles.layout ? { page: routeFiles.page } : createPrimaryRouteFiles(routeFiles, "page"),
66
+ ...createPageRouteLocation(routeSegments, inheritedLayout, currentLayout, Boolean(routeFiles.layout))
67
+ }));
68
+ for (const child of node.children) visit(child, currentLayout, nextDirectoryMiddleware);
69
+ }
70
+ }
71
+ function collectAppNodeEntries(node, options) {
72
+ const routeFiles = {};
73
+ for (const kind of ROUTE_FILE_BASENAMES) {
74
+ const file = resolveSingleEntryFile(collectEntryFiles(node.entries, kind), node.dir, kind);
75
+ if (file) routeFiles[kind] = file;
76
+ }
77
+ const directoryMiddleware = resolveSingleEntryFile(collectEntryFiles(node.entries, "directory-middleware"), node.dir, DIRECTORY_MIDDLEWARE_BASENAME);
78
+ return {
79
+ routeFiles,
80
+ directoryMiddlewareFile: directoryMiddleware ? toRootRelativeRouteFile(options.root, options.routesDir, directoryMiddleware) : void 0
81
+ };
82
+ }
83
+ function createPrimaryRouteFiles(routeFiles, primaryKind) {
84
+ const primaryFile = routeFiles[primaryKind];
85
+ const files = primaryFile ? { [primaryKind]: primaryFile } : {};
86
+ for (const key of ROUTE_FILE_BASENAMES) {
87
+ if (key === "page" || key === "layout") continue;
88
+ const file = routeFiles[key];
89
+ if (file) files[key] = file;
90
+ }
91
+ return files;
92
+ }
93
+ function assertRouteDirectoryFiles(relativeDir, routeFiles) {
94
+ if (!ROUTE_FILE_BASENAMES.some((fileName) => fileName !== "page" && fileName !== "layout" && routeFiles[fileName])) return;
95
+ if (routeFiles.page || routeFiles.layout) return;
96
+ const presentFiles = ROUTE_FILE_BASENAMES.filter((fileName) => routeFiles[fileName]).map((fileName) => basename(routeFiles[fileName] ?? ""));
97
+ throw new Error(`Route directory ${relativeDir || "."} contains ${presentFiles.join(", ")} but is missing page/layout.`);
98
+ }
99
+ function createPageRouteLocation(segments, inheritedLayout, currentLayout, hasLocalLayout) {
100
+ if (hasLocalLayout && currentLayout) return {
101
+ path: "",
102
+ parentId: currentLayout.id,
103
+ index: true
104
+ };
105
+ if (currentLayout) {
106
+ const relativeSegments = segments.slice(currentLayout.segments.length);
107
+ return {
108
+ path: relativeSegments.length > 0 ? relativeSegments.join("/") : "/",
109
+ parentId: currentLayout.id,
110
+ index: relativeSegments.length === 0
111
+ };
112
+ }
113
+ if (inheritedLayout) {
114
+ const relativeSegments = segments.slice(inheritedLayout.segments.length);
115
+ return {
116
+ path: relativeSegments.length > 0 ? relativeSegments.join("/") : "",
117
+ parentId: inheritedLayout.id,
118
+ index: relativeSegments.length === 0
119
+ };
120
+ }
121
+ return { path: segments.length > 0 ? segments.join("/") : "/" };
122
+ }
123
+ function createLayoutPath(segments, parentSegments) {
124
+ if (!parentSegments) return segments.length > 0 ? segments.join("/") : "/";
125
+ const relativeSegments = segments.slice(parentSegments.length);
126
+ return relativeSegments.length > 0 ? relativeSegments.join("/") : "";
127
+ }
128
+ function createScannedRouteModule(options) {
129
+ const absoluteFile = resolve(options.routesDir, options.file);
130
+ return {
131
+ id: options.id,
132
+ kind: options.kind,
133
+ file: toPosixPath(relative(options.root, absoluteFile)),
134
+ absoluteFile: toPosixPath(absoluteFile),
135
+ directoryMiddleware: options.directoryMiddleware,
136
+ files: Object.fromEntries(Object.entries(options.files).map(([kind, file]) => [kind, toRootRelativeRouteFile(options.root, options.routesDir, file)])),
137
+ path: options.path,
138
+ parentId: options.parentId,
139
+ index: options.index
140
+ };
141
+ }
142
+ function createRouteId(nodeId, kind) {
143
+ if (!nodeId) return kind;
144
+ return `${nodeId}/${kind}`;
145
+ }
146
+ function createDirectoryMiddlewareDelta(directoryMiddleware, previousDepth) {
147
+ if (!previousDepth) return directoryMiddleware;
148
+ return directoryMiddleware.slice(previousDepth);
149
+ }
150
+ function toManifestRouteSegments(segments) {
151
+ return segments.flatMap((segment) => {
152
+ if (segment.type === "group") return [];
153
+ if (segment.type === "static") return [segment.value];
154
+ if (segment.type === "dynamic") return [`:${segment.name}`];
155
+ if (segment.type === "catchall" || segment.type === "optional-catchall") return ["*"];
156
+ return [];
157
+ });
158
+ }
159
+ //#endregion
160
+ export { scanAppPageRoutes };
161
+
162
+ //# sourceMappingURL=app-pages-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-pages-scanner.js","names":[],"sources":["../../../../src/lib/vite-plugin/scanners/app-pages-scanner.ts"],"sourcesContent":["import { basename, extname, relative, resolve } from \"node:path\";\nimport { build, type RouteNode, type SegmentToken } from \"fs-route-ir\";\nimport type { ScannedRouteModule } from \"./route-manifest\";\nimport {\n collectEntryFiles,\n DIRECTORY_MIDDLEWARE_BASENAME,\n getLastPathSegment,\n isRouteDirectory,\n normalizeDirectoryMiddlewareExtensions,\n resolveNestedFiles,\n resolveSingleEntryFile,\n ROUTE_FILE_BASENAMES,\n toPosixPath,\n toRootRelativeRouteFile,\n type AppEntryKind,\n type RouteFileBaseName,\n} from \"./scanner-utils\";\n\ninterface LayoutAnchor {\n id: string;\n segments: string[];\n middlewareDepth: number;\n}\n\nexport async function scanAppPageRoutes(options: {\n root: string;\n routesDir: string;\n extensions: string[];\n}): Promise<ScannedRouteModule[]> {\n const files = await resolveNestedFiles(options.routesDir);\n if (files.length === 0) {\n return [];\n }\n\n const directoryMiddlewareExtensions = normalizeDirectoryMiddlewareExtensions(options.extensions);\n const result = build(files, {\n profile: \"directory-based\",\n root: \"\",\n ignore(entry, kind) {\n if (kind === \"dir\") {\n return !isRouteDirectory(getLastPathSegment(entry));\n }\n\n return !options.extensions.includes(extname(entry));\n },\n defineEntry({ file, baseName }) {\n if (baseName === DIRECTORY_MIDDLEWARE_BASENAME) {\n if (!directoryMiddlewareExtensions.includes(extname(file))) {\n return null;\n }\n\n return {\n kind: \"directory-middleware\" as const,\n scope: \"directory\" as const,\n };\n }\n\n if ((ROUTE_FILE_BASENAMES as readonly string[]).includes(baseName)) {\n return {\n kind: baseName as RouteFileBaseName,\n };\n }\n\n return null;\n },\n });\n\n return collectAppRouteModules(result.tree.nodes, options);\n}\n\nfunction collectAppRouteModules(\n nodes: RouteNode<unknown, AppEntryKind>[],\n options: {\n root: string;\n routesDir: string;\n },\n): ScannedRouteModule[] {\n const modules: ScannedRouteModule[] = [];\n\n for (const node of nodes) {\n visit(node, null, []);\n }\n\n return modules;\n\n function visit(\n node: RouteNode<unknown, AppEntryKind>,\n inheritedLayout: LayoutAnchor | null,\n inheritedDirectoryMiddleware: string[],\n ): void {\n const { routeFiles, directoryMiddlewareFile } = collectAppNodeEntries(node, options);\n const nextDirectoryMiddleware = directoryMiddlewareFile\n ? [...inheritedDirectoryMiddleware, directoryMiddlewareFile]\n : inheritedDirectoryMiddleware;\n const routeSegments = toManifestRouteSegments(node.segments);\n let currentLayout = inheritedLayout;\n\n assertRouteDirectoryFiles(node.dir, routeFiles);\n\n if (routeFiles.layout) {\n const layoutId = createRouteId(node.id, \"layout\");\n\n modules.push(\n createScannedRouteModule({\n id: layoutId,\n kind: \"layout\",\n root: options.root,\n routesDir: options.routesDir,\n file: routeFiles.layout,\n directoryMiddleware: createDirectoryMiddlewareDelta(\n nextDirectoryMiddleware,\n inheritedLayout?.middlewareDepth,\n ),\n files: createPrimaryRouteFiles(routeFiles, \"layout\"),\n path: createLayoutPath(routeSegments, inheritedLayout?.segments),\n parentId: inheritedLayout?.id,\n }),\n );\n\n currentLayout = {\n id: layoutId,\n segments: routeSegments,\n middlewareDepth: nextDirectoryMiddleware.length,\n };\n }\n\n if (routeFiles.page) {\n modules.push(\n createScannedRouteModule({\n id: createRouteId(node.id, \"page\"),\n kind: \"page\",\n root: options.root,\n routesDir: options.routesDir,\n file: routeFiles.page,\n directoryMiddleware: createDirectoryMiddlewareDelta(\n nextDirectoryMiddleware,\n currentLayout?.middlewareDepth,\n ),\n files: routeFiles.layout\n ? { page: routeFiles.page }\n : createPrimaryRouteFiles(routeFiles, \"page\"),\n ...createPageRouteLocation(\n routeSegments,\n inheritedLayout,\n currentLayout,\n Boolean(routeFiles.layout),\n ),\n }),\n );\n }\n\n for (const child of node.children) {\n visit(child, currentLayout, nextDirectoryMiddleware);\n }\n }\n}\n\nfunction collectAppNodeEntries(\n node: RouteNode<unknown, AppEntryKind>,\n options: {\n root: string;\n routesDir: string;\n },\n): {\n routeFiles: Partial<Record<RouteFileBaseName, string>>;\n directoryMiddlewareFile?: string;\n} {\n const routeFiles: Partial<Record<RouteFileBaseName, string>> = {};\n\n for (const kind of ROUTE_FILE_BASENAMES) {\n const file = resolveSingleEntryFile(collectEntryFiles(node.entries, kind), node.dir, kind);\n if (file) {\n routeFiles[kind] = file;\n }\n }\n\n const directoryMiddleware = resolveSingleEntryFile(\n collectEntryFiles(node.entries, \"directory-middleware\"),\n node.dir,\n DIRECTORY_MIDDLEWARE_BASENAME,\n );\n\n return {\n routeFiles,\n directoryMiddlewareFile: directoryMiddleware\n ? toRootRelativeRouteFile(options.root, options.routesDir, directoryMiddleware)\n : undefined,\n };\n}\n\nfunction createPrimaryRouteFiles(\n routeFiles: Partial<Record<RouteFileBaseName, string>>,\n primaryKind: \"layout\" | \"page\",\n): ScannedRouteModule[\"files\"] {\n const primaryFile = routeFiles[primaryKind];\n const files: ScannedRouteModule[\"files\"] = primaryFile ? { [primaryKind]: primaryFile } : {};\n\n for (const key of ROUTE_FILE_BASENAMES) {\n if (key === \"page\" || key === \"layout\") {\n continue;\n }\n\n const file = routeFiles[key];\n if (file) {\n files[key] = file;\n }\n }\n\n return files;\n}\n\nfunction assertRouteDirectoryFiles(\n relativeDir: string,\n routeFiles: Partial<Record<RouteFileBaseName, string>>,\n): void {\n const hasAuxiliaryFiles = ROUTE_FILE_BASENAMES.some(\n (fileName) => fileName !== \"page\" && fileName !== \"layout\" && routeFiles[fileName],\n );\n if (!hasAuxiliaryFiles) {\n return;\n }\n\n if (routeFiles.page || routeFiles.layout) {\n return;\n }\n\n const presentFiles = ROUTE_FILE_BASENAMES.filter((fileName) => routeFiles[fileName]).map(\n (fileName) => basename(routeFiles[fileName] ?? \"\"),\n );\n\n throw new Error(\n `Route directory ${relativeDir || \".\"} contains ${presentFiles.join(\", \")} but is missing page/layout.`,\n );\n}\n\nfunction createPageRouteLocation(\n segments: string[],\n inheritedLayout: LayoutAnchor | null,\n currentLayout: LayoutAnchor | null,\n hasLocalLayout: boolean,\n): Pick<ScannedRouteModule, \"path\" | \"parentId\" | \"index\"> {\n if (hasLocalLayout && currentLayout) {\n return {\n path: \"\",\n parentId: currentLayout.id,\n index: true,\n };\n }\n\n if (currentLayout) {\n const relativeSegments = segments.slice(currentLayout.segments.length);\n return {\n path: relativeSegments.length > 0 ? relativeSegments.join(\"/\") : \"/\",\n parentId: currentLayout.id,\n index: relativeSegments.length === 0,\n };\n }\n\n if (inheritedLayout) {\n const relativeSegments = segments.slice(inheritedLayout.segments.length);\n return {\n path: relativeSegments.length > 0 ? relativeSegments.join(\"/\") : \"\",\n parentId: inheritedLayout.id,\n index: relativeSegments.length === 0,\n };\n }\n\n return {\n path: segments.length > 0 ? segments.join(\"/\") : \"/\",\n };\n}\n\nfunction createLayoutPath(segments: string[], parentSegments?: string[]): string {\n if (!parentSegments) {\n return segments.length > 0 ? segments.join(\"/\") : \"/\";\n }\n\n const relativeSegments = segments.slice(parentSegments.length);\n return relativeSegments.length > 0 ? relativeSegments.join(\"/\") : \"\";\n}\n\nfunction createScannedRouteModule(options: {\n id: string;\n kind: ScannedRouteModule[\"kind\"];\n root: string;\n routesDir: string;\n file: string;\n directoryMiddleware: string[];\n files: ScannedRouteModule[\"files\"];\n path: string;\n parentId?: string;\n index?: boolean;\n}): ScannedRouteModule {\n const absoluteFile = resolve(options.routesDir, options.file);\n\n return {\n id: options.id,\n kind: options.kind,\n file: toPosixPath(relative(options.root, absoluteFile)),\n absoluteFile: toPosixPath(absoluteFile),\n directoryMiddleware: options.directoryMiddleware,\n files: Object.fromEntries(\n Object.entries(options.files).map(([kind, file]) => [\n kind,\n toRootRelativeRouteFile(options.root, options.routesDir, file),\n ]),\n ) as ScannedRouteModule[\"files\"],\n path: options.path,\n parentId: options.parentId,\n index: options.index,\n };\n}\n\nfunction createRouteId(nodeId: string, kind: \"layout\" | \"page\"): string {\n if (!nodeId) {\n return kind;\n }\n\n return `${nodeId}/${kind}`;\n}\n\nfunction createDirectoryMiddlewareDelta(\n directoryMiddleware: string[],\n previousDepth?: number,\n): string[] {\n if (!previousDepth) {\n return directoryMiddleware;\n }\n\n return directoryMiddleware.slice(previousDepth);\n}\n\nfunction toManifestRouteSegments(segments: SegmentToken[]): string[] {\n return segments.flatMap((segment) => {\n if (segment.type === \"group\") {\n return [];\n }\n\n if (segment.type === \"static\") {\n return [segment.value];\n }\n\n if (segment.type === \"dynamic\") {\n return [`:${segment.name}`];\n }\n\n if (segment.type === \"catchall\" || segment.type === \"optional-catchall\") {\n return [\"*\"];\n }\n\n return [];\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { resolveDirectoryFiles, resolveNamedFile, resolveNestedFiles, toPosixPath } from "./scanner-utils.js";
2
+ import { extname, relative, resolve } from "node:path";
3
+ //#region src/lib/vite-plugin/scanners/app-runtime-scanner.ts
4
+ async function scanAppRuntime(options) {
5
+ const appFiles = await resolveDirectoryFiles(options.appDir);
6
+ const configExtensions = options.extensions.filter((extension) => extension !== ".vue");
7
+ const appComponent = resolveNamedFile(options.appDir, appFiles, "app", options.extensions);
8
+ const errorComponent = resolveNamedFile(options.appDir, appFiles, "error", options.extensions);
9
+ const appLoader = resolveNamedFile(options.appDir, appFiles, "loader", configExtensions);
10
+ const rootFiles = await resolveDirectoryFiles(options.root);
11
+ const appConfigFromAppDir = resolveNamedFile(options.appDir, appFiles, "app.config", configExtensions);
12
+ const appConfigFromRoot = resolveNamedFile(options.root, rootFiles, "app.config", configExtensions);
13
+ const appConfig = appConfigFromAppDir ?? appConfigFromRoot;
14
+ const middleware = await resolveAppMiddlewareFiles(options.root, options.appDir, configExtensions);
15
+ return {
16
+ app: appComponent ? toPosixPath(relative(options.root, resolve(options.appDir, appComponent))) : void 0,
17
+ error: errorComponent ? toPosixPath(relative(options.root, resolve(options.appDir, errorComponent))) : void 0,
18
+ loader: appLoader ? toPosixPath(relative(options.root, resolve(options.appDir, appLoader))) : void 0,
19
+ config: appConfig ? toPosixPath(relative(options.root, resolve(appConfigFromAppDir ? options.appDir : options.root, appConfig))) : void 0,
20
+ middleware
21
+ };
22
+ }
23
+ async function resolveAppMiddlewareFiles(root, appDir, extensions) {
24
+ const middlewareDir = resolve(appDir, "middleware");
25
+ const entries = await resolveNestedFiles(middlewareDir);
26
+ const registry = /* @__PURE__ */ new Map();
27
+ for (const entry of entries) {
28
+ const extension = extname(entry);
29
+ if (!extensions.includes(extension)) continue;
30
+ const middlewareName = toPosixPath(entry.slice(0, -extension.length));
31
+ if (registry.has(middlewareName)) throw new Error(`Duplicate middleware definitions found for "${middlewareName}" in app/middleware.`);
32
+ registry.set(middlewareName, toPosixPath(relative(root, resolve(middlewareDir, entry))));
33
+ }
34
+ return Object.fromEntries([...registry.entries()].sort(([left], [right]) => left.localeCompare(right)));
35
+ }
36
+ //#endregion
37
+ export { scanAppRuntime };
38
+
39
+ //# sourceMappingURL=app-runtime-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-runtime-scanner.js","names":[],"sources":["../../../../src/lib/vite-plugin/scanners/app-runtime-scanner.ts"],"sourcesContent":["import { extname, relative, resolve } from \"node:path\";\nimport type { ScannedAppRuntime } from \"./route-manifest\";\nimport {\n resolveDirectoryFiles,\n resolveNamedFile,\n resolveNestedFiles,\n toPosixPath,\n} from \"./scanner-utils\";\n\nexport async function scanAppRuntime(options: {\n root: string;\n appDir: string;\n extensions: string[];\n}): Promise<ScannedAppRuntime> {\n const appFiles = await resolveDirectoryFiles(options.appDir);\n const configExtensions = options.extensions.filter((extension) => extension !== \".vue\");\n const appComponent = resolveNamedFile(options.appDir, appFiles, \"app\", options.extensions);\n const errorComponent = resolveNamedFile(options.appDir, appFiles, \"error\", options.extensions);\n const appLoader = resolveNamedFile(options.appDir, appFiles, \"loader\", configExtensions);\n const rootFiles = await resolveDirectoryFiles(options.root);\n const appConfigFromAppDir = resolveNamedFile(\n options.appDir,\n appFiles,\n \"app.config\",\n configExtensions,\n );\n const appConfigFromRoot = resolveNamedFile(\n options.root,\n rootFiles,\n \"app.config\",\n configExtensions,\n );\n const appConfig = appConfigFromAppDir ?? appConfigFromRoot;\n const middleware = await resolveAppMiddlewareFiles(\n options.root,\n options.appDir,\n configExtensions,\n );\n\n return {\n app: appComponent\n ? toPosixPath(relative(options.root, resolve(options.appDir, appComponent)))\n : undefined,\n error: errorComponent\n ? toPosixPath(relative(options.root, resolve(options.appDir, errorComponent)))\n : undefined,\n loader: appLoader\n ? toPosixPath(relative(options.root, resolve(options.appDir, appLoader)))\n : undefined,\n config: appConfig\n ? toPosixPath(\n relative(\n options.root,\n resolve(appConfigFromAppDir ? options.appDir : options.root, appConfig),\n ),\n )\n : undefined,\n middleware,\n };\n}\n\nasync function resolveAppMiddlewareFiles(\n root: string,\n appDir: string,\n extensions: string[],\n): Promise<Record<string, string>> {\n const middlewareDir = resolve(appDir, \"middleware\");\n const entries = await resolveNestedFiles(middlewareDir);\n const registry = new Map<string, string>();\n\n for (const entry of entries) {\n const extension = extname(entry);\n if (!extensions.includes(extension)) {\n continue;\n }\n\n const middlewareName = toPosixPath(entry.slice(0, -extension.length));\n if (registry.has(middlewareName)) {\n throw new Error(\n `Duplicate middleware definitions found for \"${middlewareName}\" in app/middleware.`,\n );\n }\n\n registry.set(middlewareName, toPosixPath(relative(root, resolve(middlewareDir, entry))));\n }\n\n return Object.fromEntries(\n [...registry.entries()].sort(([left], [right]) => left.localeCompare(right)),\n );\n}\n"],"mappings":";;;AASA,eAAsB,eAAe,SAIN;CAC7B,MAAM,WAAW,MAAM,sBAAsB,QAAQ,OAAO;CAC5D,MAAM,mBAAmB,QAAQ,WAAW,QAAQ,cAAc,cAAc,OAAO;CACvF,MAAM,eAAe,iBAAiB,QAAQ,QAAQ,UAAU,OAAO,QAAQ,WAAW;CAC1F,MAAM,iBAAiB,iBAAiB,QAAQ,QAAQ,UAAU,SAAS,QAAQ,WAAW;CAC9F,MAAM,YAAY,iBAAiB,QAAQ,QAAQ,UAAU,UAAU,iBAAiB;CACxF,MAAM,YAAY,MAAM,sBAAsB,QAAQ,KAAK;CAC3D,MAAM,sBAAsB,iBAC1B,QAAQ,QACR,UACA,cACA,iBACD;CACD,MAAM,oBAAoB,iBACxB,QAAQ,MACR,WACA,cACA,iBACD;CACD,MAAM,YAAY,uBAAuB;CACzC,MAAM,aAAa,MAAM,0BACvB,QAAQ,MACR,QAAQ,QACR,iBACD;AAED,QAAO;EACL,KAAK,eACD,YAAY,SAAS,QAAQ,MAAM,QAAQ,QAAQ,QAAQ,aAAa,CAAC,CAAC,GAC1E,KAAA;EACJ,OAAO,iBACH,YAAY,SAAS,QAAQ,MAAM,QAAQ,QAAQ,QAAQ,eAAe,CAAC,CAAC,GAC5E,KAAA;EACJ,QAAQ,YACJ,YAAY,SAAS,QAAQ,MAAM,QAAQ,QAAQ,QAAQ,UAAU,CAAC,CAAC,GACvE,KAAA;EACJ,QAAQ,YACJ,YACE,SACE,QAAQ,MACR,QAAQ,sBAAsB,QAAQ,SAAS,QAAQ,MAAM,UAAU,CACxE,CACF,GACD,KAAA;EACJ;EACD;;AAGH,eAAe,0BACb,MACA,QACA,YACiC;CACjC,MAAM,gBAAgB,QAAQ,QAAQ,aAAa;CACnD,MAAM,UAAU,MAAM,mBAAmB,cAAc;CACvD,MAAM,2BAAW,IAAI,KAAqB;AAE1C,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,YAAY,QAAQ,MAAM;AAChC,MAAI,CAAC,WAAW,SAAS,UAAU,CACjC;EAGF,MAAM,iBAAiB,YAAY,MAAM,MAAM,GAAG,CAAC,UAAU,OAAO,CAAC;AACrE,MAAI,SAAS,IAAI,eAAe,CAC9B,OAAM,IAAI,MACR,+CAA+C,eAAe,sBAC/D;AAGH,WAAS,IAAI,gBAAgB,YAAY,SAAS,MAAM,QAAQ,eAAe,MAAM,CAAC,CAAC,CAAC;;AAG1F,QAAO,OAAO,YACZ,CAAC,GAAG,SAAS,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,MAAM,CAAC,CAC7E"}
@@ -0,0 +1,60 @@
1
+ //#region src/lib/vite-plugin/scanners/route-manifest.ts
2
+ function createRouteManifest(modules) {
3
+ return modules.map((module) => ({
4
+ id: module.id,
5
+ kind: module.kind,
6
+ path: module.path,
7
+ file: module.file,
8
+ parentId: module.parentId,
9
+ index: module.index
10
+ }));
11
+ }
12
+ function collectAppPagePaths(modules) {
13
+ const records = /* @__PURE__ */ new Map();
14
+ for (const module of modules) records.set(module.id, {
15
+ id: module.id,
16
+ kind: module.kind,
17
+ path: normalizeRouteSegment(module.path, module.index),
18
+ parentId: module.parentId,
19
+ index: module.index,
20
+ children: [],
21
+ fullPath: "/"
22
+ });
23
+ const roots = [];
24
+ for (const record of records.values()) {
25
+ if (record.parentId) {
26
+ const parent = records.get(record.parentId);
27
+ if (parent) {
28
+ parent.children.push(record.id);
29
+ continue;
30
+ }
31
+ }
32
+ roots.push(record.id);
33
+ }
34
+ for (const rootId of roots) assignFullPath(rootId, "/");
35
+ return [...records.values()].filter((record) => record.kind === "page").map((record) => record.fullPath).sort();
36
+ function assignFullPath(id, parentPath) {
37
+ const record = records.get(id);
38
+ if (!record) return;
39
+ record.fullPath = record.index ? normalizeRoutePath(parentPath) : joinRoutePaths(parentPath, record.path);
40
+ for (const childId of record.children) assignFullPath(childId, record.fullPath);
41
+ }
42
+ }
43
+ function normalizeRouteSegment(path, index) {
44
+ if (index || !path || path === "/") return "";
45
+ return path.replace(/^\/|\/$/g, "");
46
+ }
47
+ function normalizeRoutePath(path) {
48
+ if (!path || path === "/") return "/";
49
+ const normalized = path.replace(/\/+/g, "/").replace(/^\/|\/$/g, "");
50
+ return normalized ? `/${normalized}` : "/";
51
+ }
52
+ function joinRoutePaths(parentPath, childPath) {
53
+ if (!childPath) return normalizeRoutePath(parentPath);
54
+ const joined = [parentPath === "/" ? "" : normalizeRoutePath(parentPath).slice(1), childPath.replace(/^\/|\/$/g, "")].filter(Boolean).join("/");
55
+ return joined ? `/${joined}` : "/";
56
+ }
57
+ //#endregion
58
+ export { collectAppPagePaths, createRouteManifest };
59
+
60
+ //# sourceMappingURL=route-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-manifest.js","names":[],"sources":["../../../../src/lib/vite-plugin/scanners/route-manifest.ts"],"sourcesContent":["export interface RouteManifestEntry {\n id: string;\n kind?: \"layout\" | \"page\";\n path: string;\n file: string;\n parentId?: string;\n index?: boolean;\n}\n\nexport interface ScannedRouteModule {\n id: string;\n kind: \"layout\" | \"page\";\n file: string;\n absoluteFile: string;\n directoryMiddleware: string[];\n path: string;\n parentId?: string;\n index?: boolean;\n files: Partial<\n Record<\"page\" | \"layout\" | \"error\" | \"loading\" | \"action\" | \"loader\" | \"middleware\", string>\n >;\n}\n\nexport interface ScannedAppRuntime {\n app?: string;\n error?: string;\n config?: string;\n loader?: string;\n middleware: Record<string, string>;\n}\n\nexport interface ScannedServerRoute {\n id: string;\n file: string;\n absoluteFile: string;\n directoryMiddleware: string[];\n path: string;\n}\n\nexport interface ScannedServerRuntime {\n routesDir: string;\n middlewareDir: string;\n routes: ScannedServerRoute[];\n middleware: Record<string, string>;\n}\n\nexport interface ScannedRoutesResult {\n root: string;\n appDir: string;\n routesDir: string;\n serverRoutesDir: string;\n serverMiddlewareDir: string;\n app: ScannedAppRuntime;\n server: ScannedServerRuntime;\n modules: ScannedRouteModule[];\n manifest: RouteManifestEntry[];\n}\n\nexport function createRouteManifest(modules: ScannedRouteModule[]): RouteManifestEntry[] {\n return modules.map((module) => ({\n id: module.id,\n kind: module.kind,\n path: module.path,\n file: module.file,\n parentId: module.parentId,\n index: module.index,\n }));\n}\n\nexport function collectAppPagePaths(modules: ScannedRouteModule[]): string[] {\n const records = new Map<\n string,\n {\n id: string;\n kind: ScannedRouteModule[\"kind\"];\n path: string;\n parentId?: string;\n index?: boolean;\n children: string[];\n fullPath: string;\n }\n >();\n\n for (const module of modules) {\n records.set(module.id, {\n id: module.id,\n kind: module.kind,\n path: normalizeRouteSegment(module.path, module.index),\n parentId: module.parentId,\n index: module.index,\n children: [],\n fullPath: \"/\",\n });\n }\n\n const roots: string[] = [];\n\n for (const record of records.values()) {\n if (record.parentId) {\n const parent = records.get(record.parentId);\n if (parent) {\n parent.children.push(record.id);\n continue;\n }\n }\n\n roots.push(record.id);\n }\n\n for (const rootId of roots) {\n assignFullPath(rootId, \"/\");\n }\n\n return [...records.values()]\n .filter((record) => record.kind === \"page\")\n .map((record) => record.fullPath)\n .sort();\n\n function assignFullPath(id: string, parentPath: string): void {\n const record = records.get(id);\n if (!record) {\n return;\n }\n\n record.fullPath = record.index\n ? normalizeRoutePath(parentPath)\n : joinRoutePaths(parentPath, record.path);\n\n for (const childId of record.children) {\n assignFullPath(childId, record.fullPath);\n }\n }\n}\n\nfunction normalizeRouteSegment(path: string, index?: boolean): string {\n if (index || !path || path === \"/\") {\n return \"\";\n }\n\n return path.replace(/^\\/|\\/$/g, \"\");\n}\n\nfunction normalizeRoutePath(path: string): string {\n if (!path || path === \"/\") {\n return \"/\";\n }\n\n const normalized = path.replace(/\\/+/g, \"/\").replace(/^\\/|\\/$/g, \"\");\n\n return normalized ? `/${normalized}` : \"/\";\n}\n\nfunction joinRoutePaths(parentPath: string, childPath: string): string {\n if (!childPath) {\n return normalizeRoutePath(parentPath);\n }\n\n const parent = parentPath === \"/\" ? \"\" : normalizeRoutePath(parentPath).slice(1);\n const child = childPath.replace(/^\\/|\\/$/g, \"\");\n const joined = [parent, child].filter(Boolean).join(\"/\");\n\n return joined ? `/${joined}` : \"/\";\n}\n"],"mappings":";AA0DA,SAAgB,oBAAoB,SAAqD;AACvF,QAAO,QAAQ,KAAK,YAAY;EAC9B,IAAI,OAAO;EACX,MAAM,OAAO;EACb,MAAM,OAAO;EACb,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,OAAO,OAAO;EACf,EAAE;;AAGL,SAAgB,oBAAoB,SAAyC;CAC3E,MAAM,0BAAU,IAAI,KAWjB;AAEH,MAAK,MAAM,UAAU,QACnB,SAAQ,IAAI,OAAO,IAAI;EACrB,IAAI,OAAO;EACX,MAAM,OAAO;EACb,MAAM,sBAAsB,OAAO,MAAM,OAAO,MAAM;EACtD,UAAU,OAAO;EACjB,OAAO,OAAO;EACd,UAAU,EAAE;EACZ,UAAU;EACX,CAAC;CAGJ,MAAM,QAAkB,EAAE;AAE1B,MAAK,MAAM,UAAU,QAAQ,QAAQ,EAAE;AACrC,MAAI,OAAO,UAAU;GACnB,MAAM,SAAS,QAAQ,IAAI,OAAO,SAAS;AAC3C,OAAI,QAAQ;AACV,WAAO,SAAS,KAAK,OAAO,GAAG;AAC/B;;;AAIJ,QAAM,KAAK,OAAO,GAAG;;AAGvB,MAAK,MAAM,UAAU,MACnB,gBAAe,QAAQ,IAAI;AAG7B,QAAO,CAAC,GAAG,QAAQ,QAAQ,CAAC,CACzB,QAAQ,WAAW,OAAO,SAAS,OAAO,CAC1C,KAAK,WAAW,OAAO,SAAS,CAChC,MAAM;CAET,SAAS,eAAe,IAAY,YAA0B;EAC5D,MAAM,SAAS,QAAQ,IAAI,GAAG;AAC9B,MAAI,CAAC,OACH;AAGF,SAAO,WAAW,OAAO,QACrB,mBAAmB,WAAW,GAC9B,eAAe,YAAY,OAAO,KAAK;AAE3C,OAAK,MAAM,WAAW,OAAO,SAC3B,gBAAe,SAAS,OAAO,SAAS;;;AAK9C,SAAS,sBAAsB,MAAc,OAAyB;AACpE,KAAI,SAAS,CAAC,QAAQ,SAAS,IAC7B,QAAO;AAGT,QAAO,KAAK,QAAQ,YAAY,GAAG;;AAGrC,SAAS,mBAAmB,MAAsB;AAChD,KAAI,CAAC,QAAQ,SAAS,IACpB,QAAO;CAGT,MAAM,aAAa,KAAK,QAAQ,QAAQ,IAAI,CAAC,QAAQ,YAAY,GAAG;AAEpE,QAAO,aAAa,IAAI,eAAe;;AAGzC,SAAS,eAAe,YAAoB,WAA2B;AACrE,KAAI,CAAC,UACH,QAAO,mBAAmB,WAAW;CAKvC,MAAM,SAAS,CAFA,eAAe,MAAM,KAAK,mBAAmB,WAAW,CAAC,MAAM,EAAE,EAClE,UAAU,QAAQ,YAAY,GAAG,CACjB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;AAExD,QAAO,SAAS,IAAI,WAAW"}