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.
- package/dist/{chunk-VBEFTPIV.mjs → chunk-VZP7N76J.mjs} +105 -52
- package/dist/chunk-VZP7N76J.mjs.map +1 -0
- package/dist/core.js +66 -13
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/plugin.d.cts +30 -0
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +66 -13
- package/dist/plugin.js.map +1 -1
- package/dist/plugin.mjs +1 -1
- package/dist/react.d.cts +168 -4
- package/dist/react.d.ts +168 -4
- package/dist/react.js +228 -28
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +225 -29
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-VBEFTPIV.mjs.map +0 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// src/plugin.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs8 from "fs";
|
|
3
|
+
import path6 from "path";
|
|
4
4
|
|
|
5
5
|
// src/core/build-generator.ts
|
|
6
|
-
import
|
|
7
|
-
import
|
|
6
|
+
import fs5 from "fs";
|
|
7
|
+
import path4 from "path";
|
|
8
8
|
|
|
9
9
|
// src/core/metadata.ts
|
|
10
|
-
import
|
|
10
|
+
import fs2 from "fs";
|
|
11
11
|
function extractMetadata(filePath) {
|
|
12
12
|
try {
|
|
13
|
-
const content =
|
|
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((
|
|
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
|
|
104
|
+
import path3 from "path";
|
|
105
105
|
|
|
106
106
|
// src/core/scanner.ts
|
|
107
107
|
import fg from "fast-glob";
|
|
108
|
-
import
|
|
109
|
-
import
|
|
108
|
+
import fs4 from "fs";
|
|
109
|
+
import path2 from "path";
|
|
110
110
|
|
|
111
111
|
// src/core/utils.ts
|
|
112
|
-
import
|
|
113
|
-
var
|
|
112
|
+
import fs3 from "fs";
|
|
113
|
+
var slash2 = (p) => p.replace(/\\/g, "/");
|
|
114
114
|
function detectExportType(filePath) {
|
|
115
115
|
try {
|
|
116
|
-
const content =
|
|
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 = `${
|
|
139
|
+
const pattern = `${slash2(resolvedPagesDir)}/**/${opts.pageFileName}.+(${exts})`;
|
|
140
140
|
const files = await fg(pattern, { dot: true });
|
|
141
|
-
return files.map((f) =>
|
|
141
|
+
return files.map((f) => path2.resolve(f));
|
|
142
142
|
}
|
|
143
143
|
function collectLayouts(filePath, resolvedPagesDir, opts) {
|
|
144
|
-
const rel =
|
|
145
|
-
const dir =
|
|
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 =
|
|
151
|
+
const candidate = path2.resolve(
|
|
152
152
|
resolvedPagesDir,
|
|
153
153
|
p || "",
|
|
154
154
|
`${opts.layoutFileName}.${ext}`
|
|
155
155
|
);
|
|
156
|
-
if (
|
|
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 =
|
|
179
|
-
const dir =
|
|
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 =
|
|
219
|
+
const id = slash2(path3.relative(resolvedPagesDir, filePath)).replace(/\//g, "_").replace(/\.[^.]+$/, "");
|
|
191
220
|
const exportType = detectExportType(filePath);
|
|
192
|
-
const loader = `() => import(${JSON.stringify(
|
|
221
|
+
const loader = `() => import(${JSON.stringify(slash2(filePath))})`;
|
|
193
222
|
const layouts = collectLayouts(filePath, resolvedPagesDir, opts).map(
|
|
194
|
-
(lp) => `() => import(${JSON.stringify(
|
|
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:
|
|
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(([
|
|
263
|
+
${Object.entries(routeParams).map(([path7, params]) => {
|
|
229
264
|
const paramEntries = Object.entries(params).map(([key, type]) => `${key}: ${type}`).join(", ");
|
|
230
|
-
return ` '${
|
|
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 =
|
|
297
|
-
if (!
|
|
298
|
-
|
|
331
|
+
const typesDir = path4.join(process.cwd(), "dist", "types");
|
|
332
|
+
if (!fs5.existsSync(typesDir)) {
|
|
333
|
+
fs5.mkdirSync(typesDir, { recursive: true });
|
|
299
334
|
}
|
|
300
|
-
|
|
301
|
-
|
|
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 =
|
|
318
|
-
if (!
|
|
319
|
-
|
|
352
|
+
const distDir = path4.join(process.cwd(), "dist");
|
|
353
|
+
if (!fs5.existsSync(distDir)) {
|
|
354
|
+
fs5.mkdirSync(distDir, { recursive: true });
|
|
320
355
|
}
|
|
321
|
-
|
|
322
|
-
|
|
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
|
|
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 =
|
|
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
|
|
358
|
-
import
|
|
392
|
+
import fs7 from "fs";
|
|
393
|
+
import path5 from "path";
|
|
359
394
|
async function loadGeneratedManifest(root) {
|
|
360
|
-
const manifestPath =
|
|
361
|
-
if (
|
|
395
|
+
const manifestPath = path5.join(root, "dist", "routes-manifest.js");
|
|
396
|
+
if (fs7.existsSync(manifestPath)) {
|
|
362
397
|
try {
|
|
363
|
-
const manifestContent =
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
469
|
-
if (!
|
|
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 (!
|
|
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-
|
|
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((
|
|
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
|
|
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 = `${
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
257
|
+
const loader = `() => import(${JSON.stringify(slash2(filePath))})`;
|
|
229
258
|
const layouts = collectLayouts(filePath, resolvedPagesDir, opts).map(
|
|
230
|
-
(lp) => `() => import(${JSON.stringify(
|
|
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:
|
|
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(([
|
|
299
|
+
${Object.entries(routeParams).map(([path7, params]) => {
|
|
265
300
|
const paramEntries = Object.entries(params).map(([key, type]) => `${key}: ${type}`).join(", ");
|
|
266
|
-
return ` '${
|
|
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 ?? "/",
|