path-rush 1.4.1 → 1.6.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.
@@ -1,16 +1,16 @@
1
1
  // src/plugin.ts
2
- import fs7 from "fs";
3
- import path5 from "path";
2
+ import fs8 from "fs";
3
+ import path6 from "path";
4
4
 
5
5
  // src/core/build-generator.ts
6
- import fs4 from "fs";
7
- import path3 from "path";
6
+ import fs5 from "fs";
7
+ import path4 from "path";
8
8
 
9
9
  // src/core/metadata.ts
10
- import fs from "fs";
10
+ import fs2 from "fs";
11
11
  function extractMetadata(filePath) {
12
12
  try {
13
- const content = fs.readFileSync(filePath, "utf-8");
13
+ const content = fs2.readFileSync(filePath, "utf-8");
14
14
  const jsdocMetadata = extractJSDocMetadata(content);
15
15
  if (jsdocMetadata) {
16
16
  return jsdocMetadata;
@@ -91,7 +91,7 @@ ${urls}
91
91
  </urlset>`;
92
92
  }
93
93
  function generateRobots(baseUrl, disallowPaths = []) {
94
- const disallowRules = disallowPaths.map((path6) => `Disallow: ${path6}`).join("\n");
94
+ const disallowRules = disallowPaths.map((path7) => `Disallow: ${path7}`).join("\n");
95
95
  return `User-agent: *
96
96
  Allow: /
97
97
  ${disallowRules ? `
@@ -101,19 +101,19 @@ Sitemap: ${baseUrl}/sitemap.xml`;
101
101
  }
102
102
 
103
103
  // src/core/route-generator.ts
104
- import path2 from "path";
104
+ import path3 from "path";
105
105
 
106
106
  // src/core/scanner.ts
107
107
  import fg from "fast-glob";
108
- import fs3 from "fs";
109
- import path from "path";
108
+ import fs4 from "fs";
109
+ import path2 from "path";
110
110
 
111
111
  // src/core/utils.ts
112
- import fs2 from "fs";
113
- var slash = (p) => p.replace(/\\/g, "/");
112
+ import fs3 from "fs";
113
+ var slash2 = (p) => p.replace(/\\/g, "/");
114
114
  function detectExportType(filePath) {
115
115
  try {
116
- const content = fs2.readFileSync(filePath, "utf-8");
116
+ const content = fs3.readFileSync(filePath, "utf-8");
117
117
  const hasDefaultExport = /export\s+default\s+/.test(content);
118
118
  const hasNamedExport = /export\s+(const|function|class)\s+\w+\s*[=(]/.test(
119
119
  content
@@ -136,24 +136,24 @@ function detectExportType(filePath) {
136
136
  // src/core/scanner.ts
137
137
  async function scanPages(resolvedPagesDir, opts) {
138
138
  const exts = opts.extensions.join("|");
139
- const pattern = `${slash(resolvedPagesDir)}/**/${opts.pageFileName}.+(${exts})`;
139
+ const pattern = `${slash2(resolvedPagesDir)}/**/${opts.pageFileName}.+(${exts})`;
140
140
  const files = await fg(pattern, { dot: true });
141
- return files.map((f) => path.resolve(f));
141
+ return files.map((f) => path2.resolve(f));
142
142
  }
143
143
  function collectLayouts(filePath, resolvedPagesDir, opts) {
144
- const rel = slash(path.relative(resolvedPagesDir, filePath));
145
- const dir = path.dirname(rel);
144
+ const rel = slash2(path2.relative(resolvedPagesDir, filePath));
145
+ const dir = path2.dirname(rel);
146
146
  const parts = dir === "." ? [] : dir.split("/").filter(Boolean);
147
147
  const layouts = [];
148
148
  for (let i = 0; i <= parts.length; i++) {
149
149
  const p = parts.slice(0, i).join("/");
150
150
  for (const ext of opts.extensions) {
151
- const candidate = path.resolve(
151
+ const candidate = path2.resolve(
152
152
  resolvedPagesDir,
153
153
  p || "",
154
154
  `${opts.layoutFileName}.${ext}`
155
155
  );
156
- if (fs3.existsSync(candidate)) {
156
+ if (fs4.existsSync(candidate)) {
157
157
  layouts.push(candidate);
158
158
  break;
159
159
  }
@@ -161,6 +161,35 @@ function collectLayouts(filePath, resolvedPagesDir, opts) {
161
161
  }
162
162
  return layouts;
163
163
  }
164
+ function findSpecialFile(filePath, resolvedPagesDir, opts, fileName) {
165
+ const rel = slash2(path2.relative(resolvedPagesDir, filePath));
166
+ const dir = path2.dirname(rel);
167
+ for (const ext of opts.extensions) {
168
+ const candidate = path2.resolve(
169
+ resolvedPagesDir,
170
+ dir === "." ? "" : dir,
171
+ `${opts[`${fileName}FileName`]}.${ext}`
172
+ );
173
+ if (fs4.existsSync(candidate)) {
174
+ return candidate;
175
+ }
176
+ }
177
+ const parts = dir === "." ? [] : dir.split("/").filter(Boolean);
178
+ for (let i = parts.length; i >= 0; i--) {
179
+ const p = parts.slice(0, i).join("/");
180
+ for (const ext of opts.extensions) {
181
+ const candidate = path2.resolve(
182
+ resolvedPagesDir,
183
+ p || "",
184
+ `${opts[`${fileName}FileName`]}.${ext}`
185
+ );
186
+ if (fs4.existsSync(candidate)) {
187
+ return candidate;
188
+ }
189
+ }
190
+ }
191
+ return void 0;
192
+ }
164
193
 
165
194
  // src/core/route-generator.ts
166
195
  function segmentToRoute(seg) {
@@ -175,8 +204,8 @@ function segmentToRoute(seg) {
175
204
  return seg;
176
205
  }
177
206
  function filePathToRoute(filePath, resolvedPagesDir) {
178
- const rel = slash(path2.relative(resolvedPagesDir, filePath));
179
- const dir = path2.dirname(rel);
207
+ const rel = slash2(path3.relative(resolvedPagesDir, filePath));
208
+ const dir = path3.dirname(rel);
180
209
  const parts = dir === "." ? [] : dir.split("/").filter(Boolean);
181
210
  const filteredParts = parts.filter(
182
211
  (part) => (!part.startsWith("(") || !part.endsWith(")")) && !part.startsWith("_")
@@ -187,19 +216,25 @@ function filePathToRoute(filePath, resolvedPagesDir) {
187
216
  }
188
217
  function createRouteEntry(filePath, resolvedPagesDir, opts) {
189
218
  const route = filePathToRoute(filePath, resolvedPagesDir);
190
- const id = slash(path2.relative(resolvedPagesDir, filePath)).replace(/\//g, "_").replace(/\.[^.]+$/, "");
219
+ const id = slash2(path3.relative(resolvedPagesDir, filePath)).replace(/\//g, "_").replace(/\.[^.]+$/, "");
191
220
  const exportType = detectExportType(filePath);
192
- const loader = `() => import(${JSON.stringify(slash(filePath))})`;
221
+ const loader = `() => import(${JSON.stringify(slash2(filePath))})`;
193
222
  const layouts = collectLayouts(filePath, resolvedPagesDir, opts).map(
194
- (lp) => `() => import(${JSON.stringify(slash(lp))})`
223
+ (lp) => `() => import(${JSON.stringify(slash2(lp))})`
195
224
  );
225
+ const loading = findSpecialFile(filePath, resolvedPagesDir, opts, "loading");
226
+ const notFound = findSpecialFile(filePath, resolvedPagesDir, opts, "not-found");
227
+ const error = findSpecialFile(filePath, resolvedPagesDir, opts, "error");
196
228
  return {
197
229
  id,
198
230
  path: route,
199
- filePath: slash(filePath),
231
+ filePath: slash2(filePath),
200
232
  loader,
201
233
  exportType,
202
- layouts
234
+ layouts,
235
+ loading: loading ? `() => import(${JSON.stringify(slash2(loading))})` : void 0,
236
+ notFound: notFound ? `() => import(${JSON.stringify(slash2(notFound))})` : void 0,
237
+ error: error ? `() => import(${JSON.stringify(slash2(error))})` : void 0
203
238
  };
204
239
  }
205
240
 
@@ -225,9 +260,9 @@ function generateRouteTypes(routes) {
225
260
  }
226
261
  });
227
262
  const routeParamsType = Object.keys(routeParams).length > 0 ? `export type RouteParams = {
228
- ${Object.entries(routeParams).map(([path6, params]) => {
263
+ ${Object.entries(routeParams).map(([path7, params]) => {
229
264
  const paramEntries = Object.entries(params).map(([key, type]) => `${key}: ${type}`).join(", ");
230
- return ` '${path6}': { ${paramEntries} }`;
265
+ return ` '${path7}': { ${paramEntries} }`;
231
266
  }).join("\n")}
232
267
  }` : "export type RouteParams = Record<string, never>";
233
268
  return `// \u0410\u0432\u0442\u043E\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0442\u0438\u043F\u044B \u043C\u0430\u0440\u0448\u0440\u0443\u0442\u043E\u0432
@@ -293,12 +328,12 @@ async function generateTypes(resolvedPagesDir, opts) {
293
328
  const routes = pages.map((fp) => createRouteEntry(fp, resolvedPagesDir, opts));
294
329
  const routeTypes = generateRouteTypes(routes);
295
330
  const metadataTypes = generateMetadataTypes();
296
- const typesDir = path3.join(process.cwd(), "dist", "types");
297
- if (!fs4.existsSync(typesDir)) {
298
- fs4.mkdirSync(typesDir, { recursive: true });
331
+ const typesDir = path4.join(process.cwd(), "dist", "types");
332
+ if (!fs5.existsSync(typesDir)) {
333
+ fs5.mkdirSync(typesDir, { recursive: true });
299
334
  }
300
- fs4.writeFileSync(path3.join(typesDir, "routes.d.ts"), routeTypes);
301
- fs4.writeFileSync(path3.join(typesDir, "metadata.d.ts"), metadataTypes);
335
+ fs5.writeFileSync(path4.join(typesDir, "routes.d.ts"), routeTypes);
336
+ fs5.writeFileSync(path4.join(typesDir, "metadata.d.ts"), metadataTypes);
302
337
  }
303
338
  async function generateSEOFiles(resolvedPagesDir, opts) {
304
339
  const pages = await scanPages(resolvedPagesDir, opts);
@@ -314,16 +349,16 @@ async function generateSEOFiles(resolvedPagesDir, opts) {
314
349
  );
315
350
  const sitemap = generateSitemap(routesWithMetadata, opts.baseUrl);
316
351
  const robots = generateRobots(opts.baseUrl, opts.disallowPaths);
317
- const distDir = path3.join(process.cwd(), "dist");
318
- if (!fs4.existsSync(distDir)) {
319
- fs4.mkdirSync(distDir, { recursive: true });
352
+ const distDir = path4.join(process.cwd(), "dist");
353
+ if (!fs5.existsSync(distDir)) {
354
+ fs5.mkdirSync(distDir, { recursive: true });
320
355
  }
321
- fs4.writeFileSync(path3.join(distDir, "sitemap.xml"), sitemap);
322
- fs4.writeFileSync(path3.join(distDir, "robots.txt"), robots);
356
+ fs5.writeFileSync(path4.join(distDir, "sitemap.xml"), sitemap);
357
+ fs5.writeFileSync(path4.join(distDir, "robots.txt"), robots);
323
358
  }
324
359
 
325
360
  // src/core/hmr-handlers.ts
326
- import fs5 from "fs";
361
+ import fs6 from "fs";
327
362
  function createHMRHandlers(resolvedPagesDir, resolvedVirtualId, root) {
328
363
  const configureServer = (srv) => {
329
364
  const invalidateVirtual = async () => {
@@ -332,7 +367,7 @@ function createHMRHandlers(resolvedPagesDir, resolvedVirtualId, root) {
332
367
  srv.moduleGraph?.invalidateModule?.(mod);
333
368
  }
334
369
  };
335
- const watchPath = fs5.existsSync(resolvedPagesDir) ? resolvedPagesDir : root;
370
+ const watchPath = fs6.existsSync(resolvedPagesDir) ? resolvedPagesDir : root;
336
371
  srv.watcher?.add?.(watchPath);
337
372
  srv.watcher?.on?.("add", (p) => {
338
373
  if (p.startsWith(resolvedPagesDir)) invalidateVirtual();
@@ -354,13 +389,13 @@ function createHMRHandlers(resolvedPagesDir, resolvedVirtualId, root) {
354
389
  }
355
390
 
356
391
  // src/core/manifest-loader.ts
357
- import fs6 from "fs";
358
- import path4 from "path";
392
+ import fs7 from "fs";
393
+ import path5 from "path";
359
394
  async function loadGeneratedManifest(root) {
360
- const manifestPath = path4.join(root, "dist", "routes-manifest.js");
361
- if (fs6.existsSync(manifestPath)) {
395
+ const manifestPath = path5.join(root, "dist", "routes-manifest.js");
396
+ if (fs7.existsSync(manifestPath)) {
362
397
  try {
363
- const manifestContent = fs6.readFileSync(manifestPath, "utf-8");
398
+ const manifestContent = fs7.readFileSync(manifestPath, "utf-8");
364
399
  const manifestRegex = /export const manifest = \[.*?\];/s;
365
400
  const manifestMatch = manifestRegex.exec(manifestContent);
366
401
  const basePathRegex = /export const basePath = ['"](.*?)['"];/;
@@ -398,13 +433,28 @@ async function generateVirtualModuleCode(resolvedPagesDir, opts) {
398
433
  filePath: ${JSON.stringify(entry.filePath)},
399
434
  loader: ${entry.loader},
400
435
  exportType: ${JSON.stringify(entry.exportType)},
401
- layouts: [${entry.layouts.join(",")}]
436
+ layouts: [${entry.layouts.join(",")}],
437
+ ${entry.loading ? `loading: ${entry.loading},` : ""}
438
+ ${entry.notFound ? `notFound: ${entry.notFound},` : ""}
439
+ ${entry.error ? `error: ${entry.error},` : ""}
402
440
  }`;
403
441
  });
442
+ let globalNotFound;
443
+ for (const ext of opts.extensions) {
444
+ const notFoundPath = path.resolve(
445
+ resolvedPagesDir,
446
+ `${opts.notFoundFileName}.${ext}`
447
+ );
448
+ if (fs.existsSync(notFoundPath)) {
449
+ globalNotFound = `() => import(${JSON.stringify(slash(notFoundPath))})`;
450
+ break;
451
+ }
452
+ }
404
453
  return `// Auto-generated routes manifest
405
454
  const manifest = [${entries.join(",\n")}];
406
455
 
407
456
  export const basePath = ${JSON.stringify(opts.basePath ?? "/")};
457
+ ${globalNotFound ? `export const globalNotFound = ${globalNotFound};` : "export const globalNotFound = undefined;"}
408
458
 
409
459
  export { manifest };
410
460
  export default manifest;
@@ -417,6 +467,9 @@ function pathRushRouting(rawOpts = {}) {
417
467
  pagesDir: rawOpts.pagesDir ?? "src/pages",
418
468
  pageFileName: rawOpts.pageFileName ?? "page",
419
469
  layoutFileName: rawOpts.layoutFileName ?? "layout",
470
+ loadingFileName: rawOpts.loadingFileName ?? "loading",
471
+ notFoundFileName: rawOpts.notFoundFileName ?? "not-found",
472
+ errorFileName: rawOpts.errorFileName ?? "error",
420
473
  extensions: rawOpts.extensions ?? ["tsx"],
421
474
  baseUrl: rawOpts.baseUrl ?? "http://localhost:5173",
422
475
  basePath: rawOpts.basePath ?? "/",
@@ -427,7 +480,7 @@ function pathRushRouting(rawOpts = {}) {
427
480
  const VIRTUAL_ID = "virtual:routes";
428
481
  const RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ID;
429
482
  let root = process.cwd();
430
- let resolvedPagesDir = path5.resolve(root, opts.pagesDir);
483
+ let resolvedPagesDir = path6.resolve(root, opts.pagesDir);
431
484
  const plugin = {
432
485
  name: "vite-plugin-file-router-mwv",
433
486
  enforce: "pre",
@@ -456,7 +509,7 @@ function pathRushRouting(rawOpts = {}) {
456
509
  },
457
510
  async load(id) {
458
511
  if (id === RESOLVED_VIRTUAL_ID) {
459
- if (!fs7.existsSync(resolvedPagesDir)) {
512
+ if (!fs8.existsSync(resolvedPagesDir)) {
460
513
  return await loadGeneratedManifest(root);
461
514
  }
462
515
  return await generateVirtualModuleCode(resolvedPagesDir, opts);
@@ -465,15 +518,15 @@ function pathRushRouting(rawOpts = {}) {
465
518
  },
466
519
  async buildStart() {
467
520
  root = process.cwd();
468
- resolvedPagesDir = path5.resolve(root, opts.pagesDir);
469
- if (!fs7.existsSync(resolvedPagesDir)) {
521
+ resolvedPagesDir = path6.resolve(root, opts.pagesDir);
522
+ if (!fs8.existsSync(resolvedPagesDir)) {
470
523
  console.warn(
471
524
  `[vite-plugin-file-router] pagesDir "${resolvedPagesDir}" not found.`
472
525
  );
473
526
  }
474
527
  },
475
528
  async generateBundle() {
476
- if (!fs7.existsSync(resolvedPagesDir)) {
529
+ if (!fs8.existsSync(resolvedPagesDir)) {
477
530
  console.warn(
478
531
  `[vite-plugin-file-router] pagesDir "${resolvedPagesDir}" not found in production build. Skipping route generation.`
479
532
  );
@@ -514,4 +567,4 @@ function pathRushRouting(rawOpts = {}) {
514
567
  export {
515
568
  pathRushRouting
516
569
  };
517
- //# sourceMappingURL=chunk-VBEFTPIV.mjs.map
570
+ //# sourceMappingURL=chunk-VZP7N76J.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin.ts","../src/core/build-generator.ts","../src/core/metadata.ts","../src/core/route-generator.ts","../src/core/scanner.ts","../src/core/utils.ts","../src/core/type-generator.ts","../src/core/hmr-handlers.ts","../src/core/manifest-loader.ts","../src/core/virtual-module.ts"],"sourcesContent":["import fs from 'node:fs'\r\nimport path from 'node:path'\r\nimport type { Plugin, UserConfig } from 'vite'\r\nimport { generateSEOFiles, generateTypes } from './core/build-generator'\r\nimport { createHMRHandlers } from './core/hmr-handlers'\r\nimport { loadGeneratedManifest } from './core/manifest-loader'\r\nimport type { Options } from './core/types/types'\r\nimport { generateVirtualModuleCode } from './core/virtual-module'\r\n\r\nexport type { Options } from './core/types/types'\r\n\r\nexport function pathRushRouting(rawOpts: Options = {}): Plugin {\r\n\tconst opts: Required<Options> = {\r\n\t\tpagesDir: rawOpts.pagesDir ?? 'src/pages',\r\n\t\tpageFileName: rawOpts.pageFileName ?? 'page',\r\n\t\tlayoutFileName: rawOpts.layoutFileName ?? 'layout',\r\n\t\tloadingFileName: rawOpts.loadingFileName ?? 'loading',\r\n\t\tnotFoundFileName: rawOpts.notFoundFileName ?? 'not-found',\r\n\t\terrorFileName: rawOpts.errorFileName ?? 'error',\r\n\t\textensions: rawOpts.extensions ?? ['tsx'],\r\n\t\tbaseUrl: rawOpts.baseUrl ?? 'http://localhost:5173',\r\n\t\tbasePath: rawOpts.basePath ?? '/',\r\n\t\tdisallowPaths: rawOpts.disallowPaths ?? [],\r\n\t\tgenerateTypes: rawOpts.generateTypes ?? true,\r\n\t\tenableSEO: rawOpts.enableSEO ?? true,\r\n\t}\r\n\r\n\tconst VIRTUAL_ID = 'virtual:routes'\r\n\tconst RESOLVED_VIRTUAL_ID = '\\0' + VIRTUAL_ID\r\n\tlet root = process.cwd()\r\n\tlet resolvedPagesDir = path.resolve(root, opts.pagesDir)\r\n\r\n\tconst plugin: Plugin = {\r\n\t\tname: 'vite-plugin-file-router-mwv',\r\n\t\tenforce: 'pre',\r\n\r\n\t\t// Автоматическая настройка Vite конфигурации\r\n\t\tconfig(userConfig: UserConfig): UserConfig {\r\n\t\t\tconst existingAlias = userConfig.resolve?.alias\r\n\t\t\tconst existingExclude = userConfig.optimizeDeps?.exclude\r\n\r\n\t\t\t// Безопасно мерджим с существующей конфигурацией\r\n\t\t\treturn {\r\n\t\t\t\toptimizeDeps: {\r\n\t\t\t\t\texclude: [\r\n\t\t\t\t\t\t...(Array.isArray(existingExclude) ? existingExclude : []),\r\n\t\t\t\t\t\t'virtual:routes',\r\n\t\t\t\t\t],\r\n\t\t\t\t},\r\n\t\t\t\tresolve: {\r\n\t\t\t\t\talias: {\r\n\t\t\t\t\t\t...(typeof existingAlias === 'object' ? existingAlias : {}),\r\n\t\t\t\t\t\t'virtual:routes': 'virtual:routes',\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tresolveId(id: string) {\r\n\t\t\tif (id === VIRTUAL_ID) return RESOLVED_VIRTUAL_ID\r\n\t\t\treturn null\r\n\t\t},\r\n\r\n\t\tasync load(id: string) {\r\n\t\t\tif (id === RESOLVED_VIRTUAL_ID) {\r\n\t\t\t\t// В production режиме проверяем существование директории pages\r\n\t\t\t\tif (!fs.existsSync(resolvedPagesDir)) {\r\n\t\t\t\t\t// В production пытаемся загрузить сгенерированный манифест\r\n\t\t\t\t\treturn await loadGeneratedManifest(root)\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn await generateVirtualModuleCode(resolvedPagesDir, opts)\r\n\t\t\t}\r\n\t\t\treturn null\r\n\t\t},\r\n\r\n\t\tasync buildStart() {\r\n\t\t\troot = process.cwd()\r\n\t\t\tresolvedPagesDir = path.resolve(root, opts.pagesDir)\r\n\r\n\t\t\tif (!fs.existsSync(resolvedPagesDir)) {\r\n\t\t\t\tconsole.warn(\r\n\t\t\t\t\t`[vite-plugin-file-router] pagesDir \"${resolvedPagesDir}\" not found.`\r\n\t\t\t\t)\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tasync generateBundle() {\r\n\t\t\t// Проверяем существование директории pages\r\n\t\t\tif (!fs.existsSync(resolvedPagesDir)) {\r\n\t\t\t\tconsole.warn(\r\n\t\t\t\t\t`[vite-plugin-file-router] pagesDir \"${resolvedPagesDir}\" not found in production build. Skipping route generation.`\r\n\t\t\t\t)\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\t// Генерируем TypeScript типы\r\n\t\t\tif (opts.generateTypes) {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tawait generateTypes(resolvedPagesDir, opts)\r\n\t\t\t\t} catch (error) {\r\n\t\t\t\t\tconsole.warn(\r\n\t\t\t\t\t\t`[vite-plugin-file-router] Failed to generate types: ${error}`\r\n\t\t\t\t\t)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Генерируем SEO файлы\r\n\t\t\tif (opts.enableSEO) {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tawait generateSEOFiles(resolvedPagesDir, opts)\r\n\t\t\t\t} catch (error) {\r\n\t\t\t\t\tconsole.warn(\r\n\t\t\t\t\t\t`[vite-plugin-file-router] Failed to generate SEO files: ${error}`\r\n\t\t\t\t\t)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t},\r\n\t}\r\n\r\n\t// Добавляем HMR только если в development режиме\r\n\tif (process.env.NODE_ENV !== 'production') {\r\n\t\tconst { configureServer, handleHotUpdate } = createHMRHandlers(\r\n\t\t\tresolvedPagesDir,\r\n\t\t\tRESOLVED_VIRTUAL_ID,\r\n\t\t\troot\r\n\t\t)\r\n\t\tplugin.configureServer = configureServer\r\n\t\tplugin.handleHotUpdate = handleHotUpdate\r\n\t}\r\n\r\n\treturn plugin\r\n}\r\n","import fs from 'node:fs'\r\nimport path from 'node:path'\r\nimport { extractMetadata, generateRobots, generateSitemap } from './metadata'\r\nimport { createRouteEntry } from './route-generator'\r\nimport { scanPages } from './scanner'\r\nimport { generateMetadataTypes, generateRouteTypes } from './type-generator'\r\nimport type { Options } from './types/types'\r\n\r\nexport async function generateTypes(\r\n\tresolvedPagesDir: string,\r\n\topts: Required<Options>\r\n) {\r\n\tconst pages = await scanPages(resolvedPagesDir, opts)\r\n\tconst routes = pages.map(fp => createRouteEntry(fp, resolvedPagesDir, opts))\r\n\r\n\tconst routeTypes = generateRouteTypes(routes)\r\n\tconst metadataTypes = generateMetadataTypes()\r\n\r\n\t// Создаем директорию для типов\r\n\tconst typesDir = path.join(process.cwd(), 'dist', 'types')\r\n\tif (!fs.existsSync(typesDir)) {\r\n\t\tfs.mkdirSync(typesDir, { recursive: true })\r\n\t}\r\n\r\n\tfs.writeFileSync(path.join(typesDir, 'routes.d.ts'), routeTypes)\r\n\tfs.writeFileSync(path.join(typesDir, 'metadata.d.ts'), metadataTypes)\r\n}\r\n\r\nexport async function generateSEOFiles(\r\n\tresolvedPagesDir: string,\r\n\topts: Required<Options>\r\n) {\r\n\tconst pages = await scanPages(resolvedPagesDir, opts)\r\n\tconst routesWithMetadata = pages\r\n\t\t.map(fp => {\r\n\t\t\tconst entry = createRouteEntry(fp, resolvedPagesDir, opts)\r\n\t\t\tconst metadata = extractMetadata(fp)\r\n\t\t\treturn {\r\n\t\t\t\tpath: entry.path,\r\n\t\t\t\tmetadata: metadata || undefined,\r\n\t\t\t}\r\n\t\t})\r\n\t\t.filter(\r\n\t\t\troute =>\r\n\t\t\t\t!opts.disallowPaths.some(disallow => route.path.startsWith(disallow))\r\n\t\t)\r\n\r\n\t// Генерируем sitemap.xml и robots.txt\r\n\tconst sitemap = generateSitemap(routesWithMetadata, opts.baseUrl)\r\n\tconst robots = generateRobots(opts.baseUrl, opts.disallowPaths)\r\n\r\n\tconst distDir = path.join(process.cwd(), 'dist')\r\n\tif (!fs.existsSync(distDir)) {\r\n\t\tfs.mkdirSync(distDir, { recursive: true })\r\n\t}\r\n\r\n\tfs.writeFileSync(path.join(distDir, 'sitemap.xml'), sitemap)\r\n\tfs.writeFileSync(path.join(distDir, 'robots.txt'), robots)\r\n}\r\n","import fs from 'node:fs'\r\nimport type { PageMetadata } from './types/types'\r\n\r\nexport function extractMetadata(filePath: string): PageMetadata | null {\r\n\ttry {\r\n\t\tconst content = fs.readFileSync(filePath, 'utf-8')\r\n\r\n\t\t// Парсинг JSDoc комментариев\r\n\t\tconst jsdocMetadata = extractJSDocMetadata(content)\r\n\t\tif (jsdocMetadata) {\r\n\t\t\treturn jsdocMetadata\r\n\t\t}\r\n\r\n\t\t// Парсинг экспорта metadata\r\n\t\tconst exportMetadata = extractExportMetadata(content)\r\n\t\tif (exportMetadata) {\r\n\t\t\treturn exportMetadata\r\n\t\t}\r\n\r\n\t\treturn null\r\n\t} catch (error) {\r\n\t\tconsole.warn(\r\n\t\t\t`[vite-plugin-file-router] Failed to extract metadata from ${filePath}:`,\r\n\t\t\terror\r\n\t\t)\r\n\t\treturn null\r\n\t}\r\n}\r\n\r\nfunction extractJSDocMetadata(content: string): PageMetadata | null {\r\n\tconst jsdocMatch = content.match(/\\/\\*\\*([\\s\\S]*?)\\*\\//)\r\n\tif (!jsdocMatch) return null\r\n\r\n\tconst jsdoc = jsdocMatch[1]\r\n\tconst metadata: PageMetadata = {}\r\n\r\n\t// Парсинг JSDoc тегов\r\n\tconst titleMatch = jsdoc.match(/@title\\s+(.+)/)\r\n\tif (titleMatch) metadata.title = titleMatch[1].trim()\r\n\r\n\tconst descMatch = jsdoc.match(/@description\\s+(.+)/)\r\n\tif (descMatch) metadata.description = descMatch[1].trim()\r\n\r\n\tconst keywordsMatch = jsdoc.match(/@keywords\\s+(.+)/)\r\n\tif (keywordsMatch) {\r\n\t\tmetadata.keywords = keywordsMatch[1].split(',').map(k => k.trim())\r\n\t}\r\n\r\n\tconst authorMatch = jsdoc.match(/@author\\s+(.+)/)\r\n\tif (authorMatch) metadata.author = authorMatch[1].trim()\r\n\r\n\tconst changefreqMatch = jsdoc.match(\r\n\t\t/@changefreq\\s+(always|hourly|daily|weekly|monthly|yearly|never)/\r\n\t)\r\n\tif (changefreqMatch) {\r\n\t\tmetadata.changefreq = changefreqMatch[1] as PageMetadata['changefreq']\r\n\t}\r\n\r\n\tconst priorityMatch = jsdoc.match(/@priority\\s+([0-9.]+)/)\r\n\tif (priorityMatch) {\r\n\t\tmetadata.priority = parseFloat(priorityMatch[1])\r\n\t}\r\n\r\n\treturn Object.keys(metadata).length > 0 ? metadata : null\r\n}\r\n\r\nfunction extractExportMetadata(content: string): PageMetadata | null {\r\n\t// Ищем экспорт const metadata = { ... }\r\n\tconst metadataMatch = content.match(\r\n\t\t/export\\s+const\\s+metadata\\s*=\\s*({[\\s\\S]*?});?/\r\n\t)\r\n\tif (!metadataMatch) return null\r\n\r\n\ttry {\r\n\t\t// Безопасное выполнение кода для извлечения объекта\r\n\t\tconst metadataCode = metadataMatch[1]\r\n\t\t// Заменяем возможные импорты и функции на безопасные значения\r\n\t\tconst safeCode = metadataCode\r\n\t\t\t.replace(/import\\s+.*?from\\s+['\"][^'\"]*['\"];?/g, '')\r\n\t\t\t.replace(/require\\s*\\([^)]*\\)/g, '\"\"')\r\n\t\t\t.replace(/process\\.env\\.[A-Z_]+/g, '\"\"')\r\n\r\n\t\t// Создаем функцию для безопасного выполнения\r\n\t\tconst func = new Function(`return ${safeCode}`)\r\n\t\treturn func()\r\n\t} catch (error) {\r\n\t\tconsole.warn(\r\n\t\t\t`[vite-plugin-file-router] Failed to parse metadata export:`,\r\n\t\t\terror\r\n\t\t)\r\n\t\treturn null\r\n\t}\r\n}\r\n\r\nexport function generateSitemap(\r\n\troutes: Array<{ path: string; metadata?: PageMetadata }>,\r\n\tbaseUrl: string\r\n): string {\r\n\tconst urls = routes\r\n\t\t.map(route => {\r\n\t\t\tconst lastmod = new Date().toISOString()\r\n\t\t\tconst changefreq = route.metadata?.changefreq || 'monthly'\r\n\t\t\tconst priority = route.metadata?.priority || 0.8\r\n\r\n\t\t\treturn ` <url>\r\n <loc>${baseUrl}${route.path}</loc>\r\n <lastmod>${lastmod}</lastmod>\r\n <changefreq>${changefreq}</changefreq>\r\n <priority>${priority}</priority>\r\n </url>`\r\n\t\t})\r\n\t\t.join('\\n')\r\n\r\n\treturn `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\r\n${urls}\r\n</urlset>`\r\n}\r\n\r\nexport function generateRobots(\r\n\tbaseUrl: string,\r\n\tdisallowPaths: string[] = []\r\n): string {\r\n\tconst disallowRules = disallowPaths\r\n\t\t.map(path => `Disallow: ${path}`)\r\n\t\t.join('\\n')\r\n\r\n\treturn `User-agent: *\r\nAllow: /\r\n${disallowRules ? `\\n${disallowRules}` : ''}\r\n\r\nSitemap: ${baseUrl}/sitemap.xml`\r\n}\r\n","import path from 'node:path'\r\nimport { collectLayouts, findSpecialFile } from './scanner'\r\nimport type { Options, RouteEntry } from './types/types'\r\nimport { detectExportType, slash } from './utils'\r\n\r\nexport function segmentToRoute(seg: string): string {\r\n\tif (/^\\[\\.{3}.+\\]$/.test(seg)) {\r\n\t\tconst name = seg.slice(4, -1)\r\n\t\treturn `:${name}(.*)`\r\n\t}\r\n\tif (/^\\[.+\\]$/.test(seg)) {\r\n\t\tconst name = seg.slice(1, -1)\r\n\t\treturn `:${name}`\r\n\t}\r\n\treturn seg\r\n}\r\n\r\nexport function filePathToRoute(\r\n\tfilePath: string,\r\n\tresolvedPagesDir: string\r\n): string {\r\n\tconst rel = slash(path.relative(resolvedPagesDir, filePath))\r\n\tconst dir = path.dirname(rel)\r\n\tconst parts = dir === '.' ? [] : dir.split('/').filter(Boolean)\r\n\t// 1. Выбрасываем группы маршрутов (auth), (shop) …\r\n\t// 2. Добавляем фильтр папок, начинающихся с _\r\n\tconst filteredParts = parts.filter(\r\n\t\tpart =>\r\n\t\t\t(!part.startsWith('(') || !part.endsWith(')')) && !part.startsWith('_')\r\n\t)\r\n\tconst segments = filteredParts.map(segmentToRoute)\r\n\tconst route = '/' + segments.join('/')\r\n\treturn route === '/' ? '/' : route.replace(/\\/+/g, '/')\r\n}\r\n\r\nexport function createRouteEntry(\r\n\tfilePath: string,\r\n\tresolvedPagesDir: string,\r\n\topts: Required<Options>\r\n): RouteEntry {\r\n\tconst route = filePathToRoute(filePath, resolvedPagesDir)\r\n\tconst id = slash(path.relative(resolvedPagesDir, filePath))\r\n\t\t.replace(/\\//g, '_')\r\n\t\t.replace(/\\.[^.]+$/, '')\r\n\tconst exportType = detectExportType(filePath)\r\n\tconst loader = `() => import(${JSON.stringify(slash(filePath))})`\r\n\tconst layouts = collectLayouts(filePath, resolvedPagesDir, opts).map(\r\n\t\tlp => `() => import(${JSON.stringify(slash(lp))})`\r\n\t)\r\n\r\n\t// Ищем специальные файлы\r\n\tconst loading = findSpecialFile(filePath, resolvedPagesDir, opts, 'loading')\r\n\tconst notFound = findSpecialFile(filePath, resolvedPagesDir, opts, 'not-found')\r\n\tconst error = findSpecialFile(filePath, resolvedPagesDir, opts, 'error')\r\n\r\n\treturn {\r\n\t\tid,\r\n\t\tpath: route,\r\n\t\tfilePath: slash(filePath),\r\n\t\tloader,\r\n\t\texportType,\r\n\t\tlayouts,\r\n\t\tloading: loading ? `() => import(${JSON.stringify(slash(loading))})` : undefined,\r\n\t\tnotFound: notFound ? `() => import(${JSON.stringify(slash(notFound))})` : undefined,\r\n\t\terror: error ? `() => import(${JSON.stringify(slash(error))})` : undefined,\r\n\t}\r\n}\r\n","import fg from 'fast-glob'\r\nimport fs from 'node:fs'\r\nimport path from 'node:path'\r\nimport type { Options } from './types/types'\r\nimport { slash } from './utils'\r\n\r\nexport async function scanPages(\r\n\tresolvedPagesDir: string,\r\n\topts: Required<Options>\r\n): Promise<string[]> {\r\n\t// pattern like /abs/path/**/page.tsx\r\n\tconst exts = opts.extensions.join('|')\r\n\tconst pattern = `${slash(resolvedPagesDir)}/**/${\r\n\t\topts.pageFileName\r\n\t}.+(${exts})`\r\n\tconst files = await fg(pattern, { dot: true })\r\n\t// return absolute normalized paths\r\n\treturn files.map((f: string) => path.resolve(f))\r\n}\r\n\r\nexport function collectLayouts(\r\n\tfilePath: string,\r\n\tresolvedPagesDir: string,\r\n\topts: Required<Options>\r\n): string[] {\r\n\tconst rel = slash(path.relative(resolvedPagesDir, filePath))\r\n\tconst dir = path.dirname(rel)\r\n\tconst parts = dir === '.' ? [] : dir.split('/').filter(Boolean)\r\n\r\n\tconst layouts: string[] = []\r\n\t// for each level from 0..parts.length include layout if exists\r\n\t// НЕ фильтруем группы маршрутов при поиске layout файлов\r\n\tfor (let i = 0; i <= parts.length; i++) {\r\n\t\tconst p = parts.slice(0, i).join('/')\r\n\t\tfor (const ext of opts.extensions) {\r\n\t\t\tconst candidate = path.resolve(\r\n\t\t\t\tresolvedPagesDir,\r\n\t\t\t\tp || '',\r\n\t\t\t\t`${opts.layoutFileName}.${ext}`\r\n\t\t\t)\r\n\t\t\tif (fs.existsSync(candidate)) {\r\n\t\t\t\tlayouts.push(candidate)\r\n\t\t\t\tbreak\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn layouts\r\n}\r\n\r\n/**\r\n * Находит файл loading, not-found или error в директории страницы\r\n */\r\nexport function findSpecialFile(\r\n\tfilePath: string,\r\n\tresolvedPagesDir: string,\r\n\topts: Required<Options>,\r\n\tfileName: 'loading' | 'not-found' | 'error'\r\n): string | undefined {\r\n\tconst rel = slash(path.relative(resolvedPagesDir, filePath))\r\n\tconst dir = path.dirname(rel)\r\n\t\r\n\t// Ищем файл в той же директории, что и страница\r\n\tfor (const ext of opts.extensions) {\r\n\t\tconst candidate = path.resolve(\r\n\t\t\tresolvedPagesDir,\r\n\t\t\tdir === '.' ? '' : dir,\r\n\t\t\t`${opts[`${fileName}FileName` as keyof typeof opts]}.${ext}`\r\n\t\t)\r\n\t\tif (fs.existsSync(candidate)) {\r\n\t\t\treturn candidate\r\n\t\t}\r\n\t}\r\n\t\r\n\t// Если не найден, ищем в родительских директориях (как layouts)\r\n\tconst parts = dir === '.' ? [] : dir.split('/').filter(Boolean)\r\n\tfor (let i = parts.length; i >= 0; i--) {\r\n\t\tconst p = parts.slice(0, i).join('/')\r\n\t\tfor (const ext of opts.extensions) {\r\n\t\t\tconst candidate = path.resolve(\r\n\t\t\t\tresolvedPagesDir,\r\n\t\t\t\tp || '',\r\n\t\t\t\t`${opts[`${fileName}FileName` as keyof typeof opts]}.${ext}`\r\n\t\t\t)\r\n\t\t\tif (fs.existsSync(candidate)) {\r\n\t\t\t\treturn candidate\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\treturn undefined\r\n}","import fs from 'node:fs'\r\nimport type { ExportType } from './types/types'\r\n\r\nexport const slash = (p: string) => p.replace(/\\\\/g, '/')\r\n\r\nexport function detectExportType(filePath: string): ExportType {\r\n\ttry {\r\n\t\tconst content = fs.readFileSync(filePath, 'utf-8')\r\n\r\n\t\t// Проверяем наличие default экспорта\r\n\t\tconst hasDefaultExport = /export\\s+default\\s+/.test(content)\r\n\r\n\t\t// Проверяем наличие любого именованного экспорта (функция, константа или класс)\r\n\t\tconst hasNamedExport = /export\\s+(const|function|class)\\s+\\w+\\s*[=(]/.test(\r\n\t\t\tcontent\r\n\t\t)\r\n\r\n\t\tif (hasDefaultExport) {\r\n\t\t\treturn 'default'\r\n\t\t} else if (hasNamedExport) {\r\n\t\t\treturn 'named'\r\n\t\t}\r\n\r\n\t\t// По умолчанию предполагаем default экспорт для обратной совместимости\r\n\t\treturn 'default'\r\n\t} catch (error) {\r\n\t\t// В случае ошибки чтения файла, предполагаем default экспорт\r\n\t\tconsole.warn(\r\n\t\t\t`[vite-plugin-file-router] Failed to read file ${filePath}:`,\r\n\t\t\terror\r\n\t\t)\r\n\t\treturn 'default'\r\n\t}\r\n}\r\n","import type { RouteEntry } from './types/types'\r\n\r\nexport function generateRouteTypes(routes: RouteEntry[]): string {\r\n\tconst routePaths = routes.map(route => `'${route.path}'`).join(' | ')\r\n\r\n\t// Извлекаем параметры из динамических маршрутов\r\n\tconst routeParams: Record<string, Record<string, string>> = {}\r\n\troutes.forEach(route => {\r\n\t\tconst params: Record<string, string> = {}\r\n\r\n\t\t// Парсим [id] параметры\r\n\t\tconst idMatches = route.path.match(/\\[([^\\]]+)\\]/g)\r\n\t\tif (idMatches) {\r\n\t\t\tidMatches.forEach(match => {\r\n\t\t\t\tconst paramName = match.slice(1, -1)\r\n\t\t\t\tif (paramName.startsWith('...')) {\r\n\t\t\t\t\t// Catch-all параметр\r\n\t\t\t\t\tparams[paramName.slice(3)] = 'string[]'\r\n\t\t\t\t} else {\r\n\t\t\t\t\tparams[paramName] = 'string'\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t}\r\n\r\n\t\tif (Object.keys(params).length > 0) {\r\n\t\t\trouteParams[route.path] = params\r\n\t\t}\r\n\t})\r\n\r\n\tconst routeParamsType =\r\n\t\tObject.keys(routeParams).length > 0\r\n\t\t\t? `export type RouteParams = {\r\n${Object.entries(routeParams)\r\n\t.map(([path, params]) => {\r\n\t\tconst paramEntries = Object.entries(params)\r\n\t\t\t.map(([key, type]) => `${key}: ${type}`)\r\n\t\t\t.join(', ')\r\n\t\treturn ` '${path}': { ${paramEntries} }`\r\n\t})\r\n\t.join('\\n')}\r\n}`\r\n\t\t\t: 'export type RouteParams = Record<string, never>'\r\n\r\n\treturn `// Автогенерированные типы маршрутов\r\nexport type RoutePath = ${routePaths || 'never'}\r\n\r\n${routeParamsType}\r\n\r\nexport type PageProps<T extends RoutePath = RoutePath> = {\r\n params: RouteParams[T]\r\n searchParams?: Record<string, string>\r\n}\r\n\r\nexport interface LayoutProps {\r\n children: React.ReactNode\r\n}\r\n\r\nexport interface RouteInfo {\r\n id: string\r\n path: string\r\n filePath: string\r\n exportType: 'default' | 'named'\r\n layouts: string[]\r\n}\r\n\r\n// Утилиты для навигации\r\nexport function navigate<T extends RoutePath>(\r\n path: T, \r\n params?: RouteParams[T]\r\n): void {\r\n // Реализация навигации\r\n window.location.href = path\r\n}\r\n\r\nexport function useParams<T extends RoutePath>(): RouteParams[T] {\r\n // Реализация хука для получения параметров\r\n return {} as RouteParams[T]\r\n}\r\n`\r\n}\r\n\r\nexport function generateMetadataTypes(): string {\r\n\treturn `// Типы для метаданных страниц\r\nexport interface PageMetadata {\r\n title?: string\r\n description?: string\r\n keywords?: string[]\r\n author?: string\r\n changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\r\n priority?: number\r\n}\r\n\r\nexport interface SEOConfig {\r\n baseUrl: string\r\n defaultTitle?: string\r\n defaultDescription?: string\r\n disallowPaths?: string[]\r\n}\r\n`\r\n}\r\n","import fs from 'node:fs'\r\n\r\nexport function createHMRHandlers(\r\n\tresolvedPagesDir: string,\r\n\tresolvedVirtualId: string,\r\n\troot: string\r\n) {\r\n\tconst configureServer = (srv: any) => {\r\n\t\tconst invalidateVirtual = async () => {\r\n\t\t\tconst mod = srv.moduleGraph?.getModuleById?.(resolvedVirtualId)\r\n\t\t\tif (mod) {\r\n\t\t\t\tsrv.moduleGraph?.invalidateModule?.(mod)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst watchPath = fs.existsSync(resolvedPagesDir) ? resolvedPagesDir : root\r\n\t\tsrv.watcher?.add?.(watchPath)\r\n\r\n\t\tsrv.watcher?.on?.('add', (p: string) => {\r\n\t\t\tif (p.startsWith(resolvedPagesDir)) invalidateVirtual()\r\n\t\t})\r\n\t\tsrv.watcher?.on?.('unlink', (p: string) => {\r\n\t\t\tif (p.startsWith(resolvedPagesDir)) invalidateVirtual()\r\n\t\t})\r\n\t}\r\n\r\n\tconst handleHotUpdate = (ctx: any) => {\r\n\t\tconst f = ctx.file\r\n\t\tif (!f?.startsWith(resolvedPagesDir)) return\r\n\r\n\t\tconst mod = ctx.server.moduleGraph?.getModuleById?.(resolvedVirtualId)\r\n\t\tif (mod) {\r\n\t\t\tctx.server.moduleGraph?.invalidateModule?.(mod)\r\n\t\t\treturn [mod]\r\n\t\t}\r\n\t}\r\n\r\n\treturn { configureServer, handleHotUpdate }\r\n}\r\n","import fs from 'node:fs'\r\nimport path from 'node:path'\r\n\r\nexport async function loadGeneratedManifest(root: string): Promise<string> {\r\n\tconst manifestPath = path.join(root, 'dist', 'routes-manifest.js')\r\n\tif (fs.existsSync(manifestPath)) {\r\n\t\ttry {\r\n\t\t\tconst manifestContent = fs.readFileSync(manifestPath, 'utf-8')\r\n\t\t\tconst manifestRegex = /export const manifest = \\[.*?\\];/s\r\n\t\t\tconst manifestMatch = manifestRegex.exec(manifestContent)\r\n\t\t\tconst basePathRegex = /export const basePath = ['\"](.*?)['\"];/\r\n\t\t\tconst basePathMatch = basePathRegex.exec(manifestContent)\r\n\t\t\tconst basePath = basePathMatch ? basePathMatch[1] : '/'\r\n\t\t\t\r\n\t\t\tif (manifestMatch) {\r\n\t\t\t\treturn `// virtual routes (production mode - using generated manifest)\r\n${manifestMatch[0]}\r\nexport const basePath = ${JSON.stringify(basePath)};\r\nexport default manifest;\r\n`\r\n\t\t\t}\r\n\t\t} catch (error) {\r\n\t\t\tconsole.warn(\r\n\t\t\t\t'[vite-plugin-file-router] Failed to load generated manifest:',\r\n\t\t\t\terror\r\n\t\t\t)\r\n\t\t}\r\n\t}\r\n\r\n\t// Fallback - пустой манифест\r\n\treturn `// virtual routes (production mode - empty manifest)\r\nexport const manifest = [];\r\nexport const basePath = '/';\r\nexport default manifest;\r\n`\r\n}\r\n","import { createRouteEntry } from './route-generator'\r\nimport { scanPages } from './scanner'\r\nimport type { Options } from './types/types'\r\n\r\nexport async function generateVirtualModuleCode(\r\n\tresolvedPagesDir: string,\r\n\topts: Required<Options>\r\n): Promise<string> {\r\n\tconst pages = await scanPages(resolvedPagesDir, opts)\r\n\tconst entries = pages.map(fp => {\r\n\t\tconst entry = createRouteEntry(fp, resolvedPagesDir, opts)\r\n\t\treturn `{\r\n id: ${JSON.stringify(entry.id)},\r\n path: ${JSON.stringify(entry.path)},\r\n filePath: ${JSON.stringify(entry.filePath)},\r\n loader: ${entry.loader},\r\n exportType: ${JSON.stringify(entry.exportType)},\r\n layouts: [${entry.layouts.join(',')}],\r\n ${entry.loading ? `loading: ${entry.loading},` : ''}\r\n ${entry.notFound ? `notFound: ${entry.notFound},` : ''}\r\n ${entry.error ? `error: ${entry.error},` : ''}\r\n }`\r\n\t})\r\n\r\n\t// Ищем глобальный not-found в корне pages\r\n\tlet globalNotFound: string | undefined\r\n\tfor (const ext of opts.extensions) {\r\n\t\tconst notFoundPath = path.resolve(\r\n\t\t\tresolvedPagesDir,\r\n\t\t\t`${opts.notFoundFileName}.${ext}`\r\n\t\t)\r\n\t\tif (fs.existsSync(notFoundPath)) {\r\n\t\t\tglobalNotFound = `() => import(${JSON.stringify(slash(notFoundPath))})`\r\n\t\t\tbreak\r\n\t\t}\r\n\t}\r\n\r\n\treturn `// Auto-generated routes manifest\r\nconst manifest = [${entries.join(',\\n')}];\r\n\r\nexport const basePath = ${JSON.stringify(opts.basePath ?? '/')};\r\n${globalNotFound ? `export const globalNotFound = ${globalNotFound};` : 'export const globalNotFound = undefined;'}\r\n\r\nexport { manifest };\r\nexport default manifest;\r\n`\r\n}\r\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","path","fs","path","fs","slash","slash","path","fs","slash","path","path","path","fs","fs","fs","path","path","fs"]}
package/dist/core.js CHANGED
@@ -127,7 +127,7 @@ ${urls}
127
127
  </urlset>`;
128
128
  }
129
129
  function generateRobots(baseUrl, disallowPaths = []) {
130
- const disallowRules = disallowPaths.map((path6) => `Disallow: ${path6}`).join("\n");
130
+ const disallowRules = disallowPaths.map((path7) => `Disallow: ${path7}`).join("\n");
131
131
  return `User-agent: *
132
132
  Allow: /
133
133
  ${disallowRules ? `
@@ -146,7 +146,7 @@ var import_node_path = __toESM(require("path"), 1);
146
146
 
147
147
  // src/core/utils.ts
148
148
  var import_node_fs2 = __toESM(require("fs"), 1);
149
- var slash = (p) => p.replace(/\\/g, "/");
149
+ var slash2 = (p) => p.replace(/\\/g, "/");
150
150
  function detectExportType(filePath) {
151
151
  try {
152
152
  const content = import_node_fs2.default.readFileSync(filePath, "utf-8");
@@ -172,12 +172,12 @@ function detectExportType(filePath) {
172
172
  // src/core/scanner.ts
173
173
  async function scanPages(resolvedPagesDir, opts) {
174
174
  const exts = opts.extensions.join("|");
175
- const pattern = `${slash(resolvedPagesDir)}/**/${opts.pageFileName}.+(${exts})`;
175
+ const pattern = `${slash2(resolvedPagesDir)}/**/${opts.pageFileName}.+(${exts})`;
176
176
  const files = await (0, import_fast_glob.default)(pattern, { dot: true });
177
177
  return files.map((f) => import_node_path.default.resolve(f));
178
178
  }
179
179
  function collectLayouts(filePath, resolvedPagesDir, opts) {
180
- const rel = slash(import_node_path.default.relative(resolvedPagesDir, filePath));
180
+ const rel = slash2(import_node_path.default.relative(resolvedPagesDir, filePath));
181
181
  const dir = import_node_path.default.dirname(rel);
182
182
  const parts = dir === "." ? [] : dir.split("/").filter(Boolean);
183
183
  const layouts = [];
@@ -197,6 +197,35 @@ function collectLayouts(filePath, resolvedPagesDir, opts) {
197
197
  }
198
198
  return layouts;
199
199
  }
200
+ function findSpecialFile(filePath, resolvedPagesDir, opts, fileName) {
201
+ const rel = slash2(import_node_path.default.relative(resolvedPagesDir, filePath));
202
+ const dir = import_node_path.default.dirname(rel);
203
+ for (const ext of opts.extensions) {
204
+ const candidate = import_node_path.default.resolve(
205
+ resolvedPagesDir,
206
+ dir === "." ? "" : dir,
207
+ `${opts[`${fileName}FileName`]}.${ext}`
208
+ );
209
+ if (import_node_fs3.default.existsSync(candidate)) {
210
+ return candidate;
211
+ }
212
+ }
213
+ const parts = dir === "." ? [] : dir.split("/").filter(Boolean);
214
+ for (let i = parts.length; i >= 0; i--) {
215
+ const p = parts.slice(0, i).join("/");
216
+ for (const ext of opts.extensions) {
217
+ const candidate = import_node_path.default.resolve(
218
+ resolvedPagesDir,
219
+ p || "",
220
+ `${opts[`${fileName}FileName`]}.${ext}`
221
+ );
222
+ if (import_node_fs3.default.existsSync(candidate)) {
223
+ return candidate;
224
+ }
225
+ }
226
+ }
227
+ return void 0;
228
+ }
200
229
 
201
230
  // src/core/route-generator.ts
202
231
  function segmentToRoute(seg) {
@@ -211,7 +240,7 @@ function segmentToRoute(seg) {
211
240
  return seg;
212
241
  }
213
242
  function filePathToRoute(filePath, resolvedPagesDir) {
214
- const rel = slash(import_node_path2.default.relative(resolvedPagesDir, filePath));
243
+ const rel = slash2(import_node_path2.default.relative(resolvedPagesDir, filePath));
215
244
  const dir = import_node_path2.default.dirname(rel);
216
245
  const parts = dir === "." ? [] : dir.split("/").filter(Boolean);
217
246
  const filteredParts = parts.filter(
@@ -223,19 +252,25 @@ function filePathToRoute(filePath, resolvedPagesDir) {
223
252
  }
224
253
  function createRouteEntry(filePath, resolvedPagesDir, opts) {
225
254
  const route = filePathToRoute(filePath, resolvedPagesDir);
226
- const id = slash(import_node_path2.default.relative(resolvedPagesDir, filePath)).replace(/\//g, "_").replace(/\.[^.]+$/, "");
255
+ const id = slash2(import_node_path2.default.relative(resolvedPagesDir, filePath)).replace(/\//g, "_").replace(/\.[^.]+$/, "");
227
256
  const exportType = detectExportType(filePath);
228
- const loader = `() => import(${JSON.stringify(slash(filePath))})`;
257
+ const loader = `() => import(${JSON.stringify(slash2(filePath))})`;
229
258
  const layouts = collectLayouts(filePath, resolvedPagesDir, opts).map(
230
- (lp) => `() => import(${JSON.stringify(slash(lp))})`
259
+ (lp) => `() => import(${JSON.stringify(slash2(lp))})`
231
260
  );
261
+ const loading = findSpecialFile(filePath, resolvedPagesDir, opts, "loading");
262
+ const notFound = findSpecialFile(filePath, resolvedPagesDir, opts, "not-found");
263
+ const error = findSpecialFile(filePath, resolvedPagesDir, opts, "error");
232
264
  return {
233
265
  id,
234
266
  path: route,
235
- filePath: slash(filePath),
267
+ filePath: slash2(filePath),
236
268
  loader,
237
269
  exportType,
238
- layouts
270
+ layouts,
271
+ loading: loading ? `() => import(${JSON.stringify(slash2(loading))})` : void 0,
272
+ notFound: notFound ? `() => import(${JSON.stringify(slash2(notFound))})` : void 0,
273
+ error: error ? `() => import(${JSON.stringify(slash2(error))})` : void 0
239
274
  };
240
275
  }
241
276
 
@@ -261,9 +296,9 @@ function generateRouteTypes(routes) {
261
296
  }
262
297
  });
263
298
  const routeParamsType = Object.keys(routeParams).length > 0 ? `export type RouteParams = {
264
- ${Object.entries(routeParams).map(([path6, params]) => {
299
+ ${Object.entries(routeParams).map(([path7, params]) => {
265
300
  const paramEntries = Object.entries(params).map(([key, type]) => `${key}: ${type}`).join(", ");
266
- return ` '${path6}': { ${paramEntries} }`;
301
+ return ` '${path7}': { ${paramEntries} }`;
267
302
  }).join("\n")}
268
303
  }` : "export type RouteParams = Record<string, never>";
269
304
  return `// \u0410\u0432\u0442\u043E\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0442\u0438\u043F\u044B \u043C\u0430\u0440\u0448\u0440\u0443\u0442\u043E\u0432
@@ -434,13 +469,28 @@ async function generateVirtualModuleCode(resolvedPagesDir, opts) {
434
469
  filePath: ${JSON.stringify(entry.filePath)},
435
470
  loader: ${entry.loader},
436
471
  exportType: ${JSON.stringify(entry.exportType)},
437
- layouts: [${entry.layouts.join(",")}]
472
+ layouts: [${entry.layouts.join(",")}],
473
+ ${entry.loading ? `loading: ${entry.loading},` : ""}
474
+ ${entry.notFound ? `notFound: ${entry.notFound},` : ""}
475
+ ${entry.error ? `error: ${entry.error},` : ""}
438
476
  }`;
439
477
  });
478
+ let globalNotFound;
479
+ for (const ext of opts.extensions) {
480
+ const notFoundPath = path.resolve(
481
+ resolvedPagesDir,
482
+ `${opts.notFoundFileName}.${ext}`
483
+ );
484
+ if (fs.existsSync(notFoundPath)) {
485
+ globalNotFound = `() => import(${JSON.stringify(slash(notFoundPath))})`;
486
+ break;
487
+ }
488
+ }
440
489
  return `// Auto-generated routes manifest
441
490
  const manifest = [${entries.join(",\n")}];
442
491
 
443
492
  export const basePath = ${JSON.stringify(opts.basePath ?? "/")};
493
+ ${globalNotFound ? `export const globalNotFound = ${globalNotFound};` : "export const globalNotFound = undefined;"}
444
494
 
445
495
  export { manifest };
446
496
  export default manifest;
@@ -453,6 +503,9 @@ function pathRushRouting(rawOpts = {}) {
453
503
  pagesDir: rawOpts.pagesDir ?? "src/pages",
454
504
  pageFileName: rawOpts.pageFileName ?? "page",
455
505
  layoutFileName: rawOpts.layoutFileName ?? "layout",
506
+ loadingFileName: rawOpts.loadingFileName ?? "loading",
507
+ notFoundFileName: rawOpts.notFoundFileName ?? "not-found",
508
+ errorFileName: rawOpts.errorFileName ?? "error",
456
509
  extensions: rawOpts.extensions ?? ["tsx"],
457
510
  baseUrl: rawOpts.baseUrl ?? "http://localhost:5173",
458
511
  basePath: rawOpts.basePath ?? "/",