navilo 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +387 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +343 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +50 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface RouteFile {
|
|
4
|
+
type: RouteType;
|
|
5
|
+
path: string;
|
|
6
|
+
filePath: string;
|
|
7
|
+
componentName: string;
|
|
8
|
+
segments: string[];
|
|
9
|
+
}
|
|
10
|
+
type RouteType = 'layout' | 'page' | 'error' | 'loading' | 'not-found';
|
|
11
|
+
interface RouteNode {
|
|
12
|
+
segment: string;
|
|
13
|
+
path: string;
|
|
14
|
+
component?: string;
|
|
15
|
+
indexPage?: string;
|
|
16
|
+
layout?: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
loading?: string;
|
|
19
|
+
notFound?: string;
|
|
20
|
+
children: Map<string, RouteNode>;
|
|
21
|
+
isIndex?: boolean;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
interface ParserOptions {
|
|
25
|
+
dynamicSegmentTransform?: (segment: string) => string;
|
|
26
|
+
strict?: boolean;
|
|
27
|
+
allowEmptySegments?: boolean;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
interface naviloOptions {
|
|
31
|
+
pagesDir?: string;
|
|
32
|
+
typescript?: boolean;
|
|
33
|
+
showErrorModal?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare function navilo(options?: naviloOptions): Plugin;
|
|
37
|
+
|
|
38
|
+
declare function normalizeFilePath(filePath: string): string;
|
|
39
|
+
declare function getRouteType(filePath: string): RouteType;
|
|
40
|
+
declare function getComponentName(filePath: string): string;
|
|
41
|
+
declare function findRouteFiles(pagesDir: string): Promise<RouteFile[]>;
|
|
42
|
+
|
|
43
|
+
declare class SegmentParser {
|
|
44
|
+
private static readonly SEGMENT_PATTERNS;
|
|
45
|
+
static normalize(segment: string, options?: ParserOptions): string | null;
|
|
46
|
+
private static defaultTransform;
|
|
47
|
+
private static isGroupSegment;
|
|
48
|
+
private static isOptionalCatchAll;
|
|
49
|
+
private static isCatchAll;
|
|
50
|
+
private static isDynamicSegment;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare class RouteTreeBuilder {
|
|
54
|
+
private readonly typeSetters;
|
|
55
|
+
constructor();
|
|
56
|
+
build(routes: RouteFile[], options?: ParserOptions): RouteNode;
|
|
57
|
+
private sortRoutes;
|
|
58
|
+
private processRoute;
|
|
59
|
+
private applyTypeHandler;
|
|
60
|
+
private handlePageType;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare class RouteDefinitionGenerator {
|
|
64
|
+
static generate(node: RouteNode, indent?: string): string;
|
|
65
|
+
private static generateIndexRoute;
|
|
66
|
+
private static generateRouteObject;
|
|
67
|
+
private static generateElementString;
|
|
68
|
+
private static generateLayoutProps;
|
|
69
|
+
private static generateComponentProps;
|
|
70
|
+
private static generateErrorString;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { type ParserOptions, RouteDefinitionGenerator, type RouteFile, type RouteNode, RouteTreeBuilder, type RouteType, SegmentParser, findRouteFiles, getComponentName, getRouteType, navilo, normalizeFilePath };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface RouteFile {
|
|
4
|
+
type: RouteType;
|
|
5
|
+
path: string;
|
|
6
|
+
filePath: string;
|
|
7
|
+
componentName: string;
|
|
8
|
+
segments: string[];
|
|
9
|
+
}
|
|
10
|
+
type RouteType = 'layout' | 'page' | 'error' | 'loading' | 'not-found';
|
|
11
|
+
interface RouteNode {
|
|
12
|
+
segment: string;
|
|
13
|
+
path: string;
|
|
14
|
+
component?: string;
|
|
15
|
+
indexPage?: string;
|
|
16
|
+
layout?: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
loading?: string;
|
|
19
|
+
notFound?: string;
|
|
20
|
+
children: Map<string, RouteNode>;
|
|
21
|
+
isIndex?: boolean;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
interface ParserOptions {
|
|
25
|
+
dynamicSegmentTransform?: (segment: string) => string;
|
|
26
|
+
strict?: boolean;
|
|
27
|
+
allowEmptySegments?: boolean;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
interface naviloOptions {
|
|
31
|
+
pagesDir?: string;
|
|
32
|
+
typescript?: boolean;
|
|
33
|
+
showErrorModal?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare function navilo(options?: naviloOptions): Plugin;
|
|
37
|
+
|
|
38
|
+
declare function normalizeFilePath(filePath: string): string;
|
|
39
|
+
declare function getRouteType(filePath: string): RouteType;
|
|
40
|
+
declare function getComponentName(filePath: string): string;
|
|
41
|
+
declare function findRouteFiles(pagesDir: string): Promise<RouteFile[]>;
|
|
42
|
+
|
|
43
|
+
declare class SegmentParser {
|
|
44
|
+
private static readonly SEGMENT_PATTERNS;
|
|
45
|
+
static normalize(segment: string, options?: ParserOptions): string | null;
|
|
46
|
+
private static defaultTransform;
|
|
47
|
+
private static isGroupSegment;
|
|
48
|
+
private static isOptionalCatchAll;
|
|
49
|
+
private static isCatchAll;
|
|
50
|
+
private static isDynamicSegment;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare class RouteTreeBuilder {
|
|
54
|
+
private readonly typeSetters;
|
|
55
|
+
constructor();
|
|
56
|
+
build(routes: RouteFile[], options?: ParserOptions): RouteNode;
|
|
57
|
+
private sortRoutes;
|
|
58
|
+
private processRoute;
|
|
59
|
+
private applyTypeHandler;
|
|
60
|
+
private handlePageType;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare class RouteDefinitionGenerator {
|
|
64
|
+
static generate(node: RouteNode, indent?: string): string;
|
|
65
|
+
private static generateIndexRoute;
|
|
66
|
+
private static generateRouteObject;
|
|
67
|
+
private static generateElementString;
|
|
68
|
+
private static generateLayoutProps;
|
|
69
|
+
private static generateComponentProps;
|
|
70
|
+
private static generateErrorString;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { type ParserOptions, RouteDefinitionGenerator, type RouteFile, type RouteNode, RouteTreeBuilder, type RouteType, SegmentParser, findRouteFiles, getComponentName, getRouteType, navilo, normalizeFilePath };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
RouteDefinitionGenerator: () => RouteDefinitionGenerator,
|
|
34
|
+
RouteTreeBuilder: () => RouteTreeBuilder,
|
|
35
|
+
SegmentParser: () => SegmentParser,
|
|
36
|
+
findRouteFiles: () => findRouteFiles,
|
|
37
|
+
getComponentName: () => getComponentName,
|
|
38
|
+
getRouteType: () => getRouteType,
|
|
39
|
+
navilo: () => navilo,
|
|
40
|
+
normalizeFilePath: () => normalizeFilePath
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(src_exports);
|
|
43
|
+
|
|
44
|
+
// src/plugin.ts
|
|
45
|
+
var import_path2 = __toESM(require("path"));
|
|
46
|
+
var import_fs = __toESM(require("fs"));
|
|
47
|
+
|
|
48
|
+
// src/constants.ts
|
|
49
|
+
var DEFAULT_OPTIONS = {
|
|
50
|
+
pagesDir: "src/app",
|
|
51
|
+
typescript: true
|
|
52
|
+
};
|
|
53
|
+
var VIRTUAL_ROUTE_MODULE_ID = "virtual:preluder-routes";
|
|
54
|
+
var RESOLVED_VIRTUAL_ROUTE_MODULE_ID = "\0" + VIRTUAL_ROUTE_MODULE_ID;
|
|
55
|
+
|
|
56
|
+
// src/generator/createRoutes.ts
|
|
57
|
+
var import_path = __toESM(require("path"));
|
|
58
|
+
var import_fast_glob = __toESM(require("fast-glob"));
|
|
59
|
+
function normalizeFilePath(filePath) {
|
|
60
|
+
return filePath.replace(/\\/g, "/");
|
|
61
|
+
}
|
|
62
|
+
function getRouteType(filePath) {
|
|
63
|
+
const basename = import_path.default.basename(filePath);
|
|
64
|
+
const fileTypes = {
|
|
65
|
+
"layout.jsx": "layout",
|
|
66
|
+
"layout.tsx": "layout",
|
|
67
|
+
"page.jsx": "page",
|
|
68
|
+
"page.tsx": "page",
|
|
69
|
+
"error.jsx": "error",
|
|
70
|
+
"error.tsx": "error",
|
|
71
|
+
"loading.jsx": "loading",
|
|
72
|
+
"loading.tsx": "loading",
|
|
73
|
+
"not-found.jsx": "not-found",
|
|
74
|
+
"not-found.tsx": "not-found"
|
|
75
|
+
};
|
|
76
|
+
return fileTypes[basename] || "page";
|
|
77
|
+
}
|
|
78
|
+
function getComponentName(filePath) {
|
|
79
|
+
const segments = filePath.split(import_path.default.sep);
|
|
80
|
+
const fileNameWithExt = segments[segments.length - 1];
|
|
81
|
+
const fileName = import_path.default.basename(fileNameWithExt, import_path.default.extname(fileNameWithExt));
|
|
82
|
+
const relevantSegments = segments.slice(segments.indexOf("app") + 1).filter((segment) => segment !== fileName);
|
|
83
|
+
const nameParts = relevantSegments.map((segment) => {
|
|
84
|
+
if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
85
|
+
return segment.slice(1, -1).replace(/^\.\.\./g, "CatchAll").replace(/[[\]]/g, "").split(/[^a-zA-Z0-9]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
86
|
+
}
|
|
87
|
+
return segment.split(/[^a-zA-Z0-9]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
88
|
+
});
|
|
89
|
+
const typeSuffix = {
|
|
90
|
+
page: "Page",
|
|
91
|
+
layout: "Layout",
|
|
92
|
+
error: "Error",
|
|
93
|
+
loading: "Loading",
|
|
94
|
+
"not-found": "NotFound"
|
|
95
|
+
}[fileName] || "Component";
|
|
96
|
+
return nameParts.length === 0 ? `Root${typeSuffix}` : `${nameParts.join("")}${typeSuffix}`;
|
|
97
|
+
}
|
|
98
|
+
function getPathSegments(filePath, pagesDir) {
|
|
99
|
+
const relativePath = import_path.default.relative(pagesDir, filePath);
|
|
100
|
+
const segments = relativePath.split(import_path.default.sep);
|
|
101
|
+
segments.pop();
|
|
102
|
+
return segments.filter((seg) => !(seg.startsWith("(") && seg.endsWith(")"))).map((seg) => seg);
|
|
103
|
+
}
|
|
104
|
+
async function findRouteFiles(pagesDir) {
|
|
105
|
+
const files = await (0, import_fast_glob.default)(["**/*.{jsx,tsx}"], {
|
|
106
|
+
cwd: pagesDir,
|
|
107
|
+
absolute: true,
|
|
108
|
+
ignore: ["**/node_modules/**", "**/.*/**", "**/_*/**"]
|
|
109
|
+
});
|
|
110
|
+
const validFiles = files.filter((file) => {
|
|
111
|
+
const base = import_path.default.basename(file);
|
|
112
|
+
return /^(layout|page|error|loading|not-found)\.(jsx|tsx)$/.test(base);
|
|
113
|
+
});
|
|
114
|
+
return validFiles.map((file) => ({
|
|
115
|
+
type: getRouteType(file),
|
|
116
|
+
path: "/" + import_path.default.relative(pagesDir, import_path.default.dirname(file)),
|
|
117
|
+
filePath: normalizeFilePath(file),
|
|
118
|
+
componentName: getComponentName(file),
|
|
119
|
+
segments: getPathSegments(file, pagesDir)
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/generator/parser/segmentParser.ts
|
|
124
|
+
var SegmentParser = class {
|
|
125
|
+
static normalize(segment, options) {
|
|
126
|
+
if (this.isGroupSegment(segment))
|
|
127
|
+
return null;
|
|
128
|
+
const transform = options?.dynamicSegmentTransform || this.defaultTransform;
|
|
129
|
+
if (this.isOptionalCatchAll(segment)) {
|
|
130
|
+
const name = segment.match(this.SEGMENT_PATTERNS.OPTIONAL_CATCH_ALL)[1];
|
|
131
|
+
return transform(`${name}*?`);
|
|
132
|
+
}
|
|
133
|
+
if (this.isCatchAll(segment)) {
|
|
134
|
+
const name = segment.match(this.SEGMENT_PATTERNS.CATCH_ALL)[1];
|
|
135
|
+
return transform(`${name}*`);
|
|
136
|
+
}
|
|
137
|
+
if (this.isDynamicSegment(segment)) {
|
|
138
|
+
const name = segment.match(this.SEGMENT_PATTERNS.DYNAMIC)[1];
|
|
139
|
+
return transform(name);
|
|
140
|
+
}
|
|
141
|
+
return segment;
|
|
142
|
+
}
|
|
143
|
+
static defaultTransform(name) {
|
|
144
|
+
return `:${name}`;
|
|
145
|
+
}
|
|
146
|
+
static isGroupSegment(segment) {
|
|
147
|
+
return this.SEGMENT_PATTERNS.GROUP.test(segment);
|
|
148
|
+
}
|
|
149
|
+
static isOptionalCatchAll(segment) {
|
|
150
|
+
return this.SEGMENT_PATTERNS.OPTIONAL_CATCH_ALL.test(segment);
|
|
151
|
+
}
|
|
152
|
+
static isCatchAll(segment) {
|
|
153
|
+
return this.SEGMENT_PATTERNS.CATCH_ALL.test(segment);
|
|
154
|
+
}
|
|
155
|
+
static isDynamicSegment(segment) {
|
|
156
|
+
return this.SEGMENT_PATTERNS.DYNAMIC.test(segment);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
SegmentParser.SEGMENT_PATTERNS = {
|
|
160
|
+
OPTIONAL_CATCH_ALL: /^\[\[\.{3}(.+)\]\]$/,
|
|
161
|
+
CATCH_ALL: /^\[\.{3}(.+)\]$/,
|
|
162
|
+
DYNAMIC: /^\[(.+)\]$/,
|
|
163
|
+
GROUP: /^\((.+)\)$/
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/generator/parser/routeTreeBuilder.ts
|
|
167
|
+
var RouteTreeBuilder = class {
|
|
168
|
+
constructor() {
|
|
169
|
+
this.typeSetters = {
|
|
170
|
+
layout: (node, route) => {
|
|
171
|
+
node.layout = route.componentName;
|
|
172
|
+
},
|
|
173
|
+
error: (node, route) => {
|
|
174
|
+
node.error = route.componentName;
|
|
175
|
+
},
|
|
176
|
+
loading: (node, route) => {
|
|
177
|
+
node.loading = route.componentName;
|
|
178
|
+
},
|
|
179
|
+
"not-found": (node, route) => {
|
|
180
|
+
node.notFound = route.componentName;
|
|
181
|
+
},
|
|
182
|
+
page: this.handlePageType.bind(this)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
build(routes, options) {
|
|
186
|
+
const root = {
|
|
187
|
+
segment: "",
|
|
188
|
+
path: "/",
|
|
189
|
+
children: /* @__PURE__ */ new Map(),
|
|
190
|
+
metadata: options?.metadata
|
|
191
|
+
};
|
|
192
|
+
const sortedRoutes = this.sortRoutes(routes);
|
|
193
|
+
sortedRoutes.forEach((route) => {
|
|
194
|
+
this.processRoute(root, route, options);
|
|
195
|
+
});
|
|
196
|
+
return root;
|
|
197
|
+
}
|
|
198
|
+
sortRoutes(routes) {
|
|
199
|
+
return [...routes].sort(
|
|
200
|
+
(a, b) => a.type === "layout" && b.type !== "layout" ? -1 : a.type !== "layout" && b.type === "layout" ? 1 : 0
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
processRoute(root, route, options) {
|
|
204
|
+
const segments = route.segments.map((seg) => String(seg));
|
|
205
|
+
if (segments.length === 0) {
|
|
206
|
+
this.applyTypeHandler(root, route);
|
|
207
|
+
if (route.type === "page")
|
|
208
|
+
root.isIndex = true;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
let current = root;
|
|
212
|
+
segments.forEach((seg, idx) => {
|
|
213
|
+
const isLast = idx === segments.length - 1;
|
|
214
|
+
const key = SegmentParser.normalize(seg, options);
|
|
215
|
+
if (key === null)
|
|
216
|
+
return;
|
|
217
|
+
if (!current.children.has(key)) {
|
|
218
|
+
current.children.set(key, {
|
|
219
|
+
segment: key,
|
|
220
|
+
path: key,
|
|
221
|
+
children: /* @__PURE__ */ new Map(),
|
|
222
|
+
metadata: options?.metadata
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
current = current.children.get(key);
|
|
226
|
+
if (isLast)
|
|
227
|
+
this.applyTypeHandler(current, route);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
applyTypeHandler(node, route) {
|
|
231
|
+
const handler = this.typeSetters[route.type];
|
|
232
|
+
if (handler)
|
|
233
|
+
handler(node, route);
|
|
234
|
+
}
|
|
235
|
+
handlePageType(node, route) {
|
|
236
|
+
const lastSegment = route.segments[route.segments.length - 1];
|
|
237
|
+
const isDynamic = lastSegment?.startsWith("[");
|
|
238
|
+
if (isDynamic || lastSegment === "") {
|
|
239
|
+
node.component = route.componentName;
|
|
240
|
+
node.isIndex = false;
|
|
241
|
+
} else {
|
|
242
|
+
node.indexPage = route.componentName;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// src/generator/parser/routeDefinitionGenerator.ts
|
|
248
|
+
var RouteDefinitionGenerator = class {
|
|
249
|
+
static generate(node, indent = " ") {
|
|
250
|
+
const childNodes = Array.from(node.children.values());
|
|
251
|
+
const children = [];
|
|
252
|
+
if (node.indexPage) {
|
|
253
|
+
children.push(this.generateIndexRoute(node, indent));
|
|
254
|
+
}
|
|
255
|
+
children.push(...childNodes.map(
|
|
256
|
+
(child) => this.generate(child, indent + " ")
|
|
257
|
+
));
|
|
258
|
+
return this.generateRouteObject(node, children, indent);
|
|
259
|
+
}
|
|
260
|
+
static generateIndexRoute(node, indent) {
|
|
261
|
+
return `${indent} {
|
|
262
|
+
${indent} index: true,
|
|
263
|
+
${indent} element: React.createElement(RouteWrapper, {
|
|
264
|
+
${indent} Component: ${node.indexPage}
|
|
265
|
+
${indent} })
|
|
266
|
+
${indent} }`;
|
|
267
|
+
}
|
|
268
|
+
static generateRouteObject(node, children, indent) {
|
|
269
|
+
const pathStr = node.segment === "" ? "/" : node.segment;
|
|
270
|
+
const elementStr = this.generateElementString(node, indent);
|
|
271
|
+
const errorStr = this.generateErrorString(node, indent);
|
|
272
|
+
return [
|
|
273
|
+
`${indent}{`,
|
|
274
|
+
`${indent} path: '${pathStr}'${elementStr}${errorStr}`,
|
|
275
|
+
children.length > 0 ? `,
|
|
276
|
+
${indent} children: [
|
|
277
|
+
${children.join(",\n")}
|
|
278
|
+
${indent} ]` : "",
|
|
279
|
+
`${indent}}`
|
|
280
|
+
].filter(Boolean).join("");
|
|
281
|
+
}
|
|
282
|
+
static generateElementString(node, indent) {
|
|
283
|
+
if (!node.layout && !node.component)
|
|
284
|
+
return "";
|
|
285
|
+
const props = node.layout ? this.generateLayoutProps(node, indent) : this.generateComponentProps(node, indent);
|
|
286
|
+
return `,
|
|
287
|
+
${indent} element: React.createElement(RouteWrapper, ${props})`;
|
|
288
|
+
}
|
|
289
|
+
static generateLayoutProps(node, indent) {
|
|
290
|
+
return `{
|
|
291
|
+
${indent} Component: ${node.layout},
|
|
292
|
+
${indent} isLayout: true,
|
|
293
|
+
${indent} loading: ${node.loading || "undefined"},
|
|
294
|
+
${indent} notFound: ${node.notFound || "undefined"}
|
|
295
|
+
${indent} }`;
|
|
296
|
+
}
|
|
297
|
+
static generateComponentProps(node, indent) {
|
|
298
|
+
return `{
|
|
299
|
+
${indent} Component: ${node.component}
|
|
300
|
+
${indent} }`;
|
|
301
|
+
}
|
|
302
|
+
static generateErrorString(node, indent) {
|
|
303
|
+
if (!node.error)
|
|
304
|
+
return "";
|
|
305
|
+
return `,
|
|
306
|
+
${indent} errorElement: React.createElement(ErrorBoundary, {
|
|
307
|
+
${indent} Component: ${node.error}
|
|
308
|
+
${indent} })`;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/plugin.ts
|
|
313
|
+
function navilo(options = {}) {
|
|
314
|
+
const resolvedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
315
|
+
let root;
|
|
316
|
+
const routeTreeBuilder = new RouteTreeBuilder();
|
|
317
|
+
return {
|
|
318
|
+
name: "navilo",
|
|
319
|
+
configResolved(config) {
|
|
320
|
+
root = config.root;
|
|
321
|
+
},
|
|
322
|
+
configureServer(server) {
|
|
323
|
+
const pagesDir = import_path2.default.resolve(root, resolvedOptions.pagesDir);
|
|
324
|
+
server.watcher.add(pagesDir);
|
|
325
|
+
const handleFileChange = (file) => {
|
|
326
|
+
if (file.startsWith(pagesDir)) {
|
|
327
|
+
const module2 = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ROUTE_MODULE_ID);
|
|
328
|
+
if (module2) {
|
|
329
|
+
server.moduleGraph.invalidateModule(module2);
|
|
330
|
+
}
|
|
331
|
+
server.ws.send({ type: "full-reload" });
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
server.watcher.on("add", handleFileChange);
|
|
335
|
+
server.watcher.on("unlink", handleFileChange);
|
|
336
|
+
server.watcher.on("change", handleFileChange);
|
|
337
|
+
},
|
|
338
|
+
resolveId(id) {
|
|
339
|
+
if (id === VIRTUAL_ROUTE_MODULE_ID) {
|
|
340
|
+
return RESOLVED_VIRTUAL_ROUTE_MODULE_ID;
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
async load(id) {
|
|
344
|
+
if (id === RESOLVED_VIRTUAL_ROUTE_MODULE_ID) {
|
|
345
|
+
const pagesDir = import_path2.default.resolve(root, resolvedOptions.pagesDir);
|
|
346
|
+
if (!import_fs.default.existsSync(pagesDir)) {
|
|
347
|
+
import_fs.default.mkdirSync(pagesDir, { recursive: true });
|
|
348
|
+
return `export const router = null;`;
|
|
349
|
+
}
|
|
350
|
+
const routes = await findRouteFiles(pagesDir);
|
|
351
|
+
if (routes.length === 0) {
|
|
352
|
+
return `export const router = null;`;
|
|
353
|
+
}
|
|
354
|
+
const routeTree = routeTreeBuilder.build(routes, {
|
|
355
|
+
strict: true,
|
|
356
|
+
dynamicSegmentTransform: (segment) => `:${segment.toLowerCase()}`
|
|
357
|
+
});
|
|
358
|
+
const routeDefinitions = RouteDefinitionGenerator.generate(routeTree);
|
|
359
|
+
return `
|
|
360
|
+
import React from 'react';
|
|
361
|
+
import { createBrowserRouter } from 'react-router-dom';
|
|
362
|
+
import RouteWrapper from './components/RouteWrapper';
|
|
363
|
+
import ErrorBoundary from './components/ErrorBoundary';
|
|
364
|
+
|
|
365
|
+
${generateImports(routes)}
|
|
366
|
+
|
|
367
|
+
export const router = createBrowserRouter([${routeDefinitions}]);
|
|
368
|
+
`;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function generateImports(routes) {
|
|
374
|
+
return routes.map((route) => `import ${route.componentName} from '${route.filePath}';`).join("\n");
|
|
375
|
+
}
|
|
376
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
377
|
+
0 && (module.exports = {
|
|
378
|
+
RouteDefinitionGenerator,
|
|
379
|
+
RouteTreeBuilder,
|
|
380
|
+
SegmentParser,
|
|
381
|
+
findRouteFiles,
|
|
382
|
+
getComponentName,
|
|
383
|
+
getRouteType,
|
|
384
|
+
navilo,
|
|
385
|
+
normalizeFilePath
|
|
386
|
+
});
|
|
387
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/plugin.ts","../src/constants.ts","../src/generator/createRoutes.ts","../src/generator/parser/segmentParser.ts","../src/generator/parser/routeTreeBuilder.ts","../src/generator/parser/routeDefinitionGenerator.ts"],"sourcesContent":["export { navilo } from './plugin';\n\nexport type {\n RouteFile,\n RouteType,\n RouteNode,\n ParserOptions\n} from './types';\n\nexport {\n normalizeFilePath,\n getRouteType,\n getComponentName,\n findRouteFiles\n} from './generator/createRoutes';\n\nexport { SegmentParser } from './generator/parser/segmentParser';\nexport { RouteTreeBuilder } from './generator/parser/routeTreeBuilder';\nexport { RouteDefinitionGenerator } from './generator/parser/routeDefinitionGenerator';","// src/plugin.ts\nimport { Plugin } from 'vite';\nimport path from 'path';\nimport fs from 'fs';\nimport { DEFAULT_OPTIONS, RESOLVED_VIRTUAL_ROUTE_MODULE_ID, VIRTUAL_ROUTE_MODULE_ID } from './constants';\nimport {naviloOptions, RouteFile} from './types';\nimport { findRouteFiles } from './generator/createRoutes';\nimport {RouteTreeBuilder} from \"./generator/parser/routeTreeBuilder\";\nimport {RouteDefinitionGenerator} from \"./generator/parser/routeDefinitionGenerator\";\n\nexport function navilo(options: naviloOptions = {}): Plugin {\n const resolvedOptions = { ...DEFAULT_OPTIONS, ...options };\n let root: string;\n const routeTreeBuilder = new RouteTreeBuilder();\n\n return {\n name: 'navilo',\n\n configResolved(config) {\n root = config.root;\n },\n\n configureServer(server) {\n const pagesDir = path.resolve(root, resolvedOptions.pagesDir);\n\n server.watcher.add(pagesDir);\n\n const handleFileChange = (file: string) => {\n if (file.startsWith(pagesDir)) {\n const module = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ROUTE_MODULE_ID);\n if (module) {\n server.moduleGraph.invalidateModule(module);\n }\n server.ws.send({ type: 'full-reload' });\n }\n };\n\n server.watcher.on('add', handleFileChange);\n server.watcher.on('unlink', handleFileChange);\n server.watcher.on('change', handleFileChange);\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ROUTE_MODULE_ID) {\n return RESOLVED_VIRTUAL_ROUTE_MODULE_ID;\n }\n },\n\n async load(id) {\n if (id === RESOLVED_VIRTUAL_ROUTE_MODULE_ID) {\n const pagesDir = path.resolve(root, resolvedOptions.pagesDir);\n\n if (!fs.existsSync(pagesDir)) {\n fs.mkdirSync(pagesDir, { recursive: true });\n return `export const router = null;`;\n }\n\n const routes = await findRouteFiles(pagesDir);\n\n if (routes.length === 0) {\n return `export const router = null;`;\n }\n\n const routeTree = routeTreeBuilder.build(routes, {\n strict: true,\n dynamicSegmentTransform: (segment) => `:${segment.toLowerCase()}`\n });\n\n const routeDefinitions = RouteDefinitionGenerator.generate(routeTree);\n\n return `\n import React from 'react';\n import { createBrowserRouter } from 'react-router-dom';\n import RouteWrapper from './components/RouteWrapper';\n import ErrorBoundary from './components/ErrorBoundary';\n \n ${generateImports(routes)}\n \n export const router = createBrowserRouter([${routeDefinitions}]);\n `;\n }\n }\n };\n}\n\nfunction generateImports(routes: RouteFile[]): string {\n return routes\n .map(route => `import ${route.componentName} from '${route.filePath}';`)\n .join('\\n');\n}","export const DEFAULT_OPTIONS = {\n pagesDir: 'src/app',\n typescript: true,\n};\n\nexport const VIRTUAL_ROUTE_MODULE_ID = 'virtual:preluder-routes';\nexport const RESOLVED_VIRTUAL_ROUTE_MODULE_ID = '\\0' + VIRTUAL_ROUTE_MODULE_ID;","import path from 'path';\nimport glob from 'fast-glob';\nimport { RouteFile, RouteType } from '../types';\n\nexport function normalizeFilePath(filePath: string): string {\n return filePath.replace(/\\\\/g, '/');\n}\n\nexport function getRouteType(filePath: string): RouteType {\n const basename = path.basename(filePath);\n const fileTypes: Record<string, RouteType> = {\n 'layout.jsx': 'layout',\n 'layout.tsx': 'layout',\n 'page.jsx': 'page',\n 'page.tsx': 'page',\n 'error.jsx': 'error',\n 'error.tsx': 'error',\n 'loading.jsx': 'loading',\n 'loading.tsx': 'loading',\n 'not-found.jsx': 'not-found',\n 'not-found.tsx': 'not-found'\n };\n return fileTypes[basename] || 'page';\n}\n\nexport function getComponentName(filePath: string): string {\n const segments = filePath.split(path.sep);\n const fileNameWithExt = segments[segments.length - 1];\n const fileName = path.basename(fileNameWithExt, path.extname(fileNameWithExt));\n\n const relevantSegments = segments\n .slice(segments.indexOf('app') + 1)\n .filter(segment => segment !== fileName);\n\n const nameParts = relevantSegments.map(segment => {\n if (segment.startsWith('[') && segment.endsWith(']')) {\n return segment.slice(1, -1)\n .replace(/^\\.\\.\\./g, 'CatchAll')\n .replace(/[[\\]]/g, '')\n .split(/[^a-zA-Z0-9]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n }\n return segment\n .split(/[^a-zA-Z0-9]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n });\n\n const typeSuffix = {\n page: 'Page',\n layout: 'Layout',\n error: 'Error',\n loading: 'Loading',\n 'not-found': 'NotFound'\n }[fileName] || 'Component';\n\n return nameParts.length === 0 ? `Root${typeSuffix}` : `${nameParts.join('')}${typeSuffix}`;\n}\n\nexport function getPathSegments(filePath: string, pagesDir: string): string[] {\n const relativePath = path.relative(pagesDir, filePath);\n const segments = relativePath.split(path.sep);\n segments.pop();\n\n return segments\n .filter(seg => !(seg.startsWith('(') && seg.endsWith(')')))\n .map(seg => seg);\n}\n\nexport async function findRouteFiles(pagesDir: string): Promise<RouteFile[]> {\n const files = await glob(['**/*.{jsx,tsx}'], {\n cwd: pagesDir,\n absolute: true,\n ignore: ['**/node_modules/**', '**/.*/**', '**/_*/**'],\n });\n\n const validFiles = files.filter(file => {\n const base = path.basename(file);\n return /^(layout|page|error|loading|not-found)\\.(jsx|tsx)$/.test(base);\n });\n\n return validFiles.map(file => ({\n type: getRouteType(file),\n path: '/' + path.relative(pagesDir, path.dirname(file)),\n filePath: normalizeFilePath(file),\n componentName: getComponentName(file),\n segments: getPathSegments(file, pagesDir),\n }));\n}","import {ParserOptions} from \"../../types\";\n\nexport class SegmentParser {\n private static readonly SEGMENT_PATTERNS = {\n OPTIONAL_CATCH_ALL: /^\\[\\[\\.{3}(.+)\\]\\]$/,\n CATCH_ALL: /^\\[\\.{3}(.+)\\]$/,\n DYNAMIC: /^\\[(.+)\\]$/,\n GROUP: /^\\((.+)\\)$/\n };\n\n static normalize(segment: string, options?: ParserOptions): string | null {\n if (this.isGroupSegment(segment)) return null;\n\n const transform = options?.dynamicSegmentTransform || this.defaultTransform;\n\n if (this.isOptionalCatchAll(segment)) {\n const name = segment.match(this.SEGMENT_PATTERNS.OPTIONAL_CATCH_ALL)![1];\n return transform(`${name}*?`);\n }\n\n if (this.isCatchAll(segment)) {\n const name = segment.match(this.SEGMENT_PATTERNS.CATCH_ALL)![1];\n return transform(`${name}*`);\n }\n\n if (this.isDynamicSegment(segment)) {\n const name = segment.match(this.SEGMENT_PATTERNS.DYNAMIC)![1];\n return transform(name);\n }\n\n return segment;\n }\n\n private static defaultTransform(name: string): string {\n return `:${name}`;\n }\n\n private static isGroupSegment(segment: string): boolean {\n return this.SEGMENT_PATTERNS.GROUP.test(segment);\n }\n\n private static isOptionalCatchAll(segment: string): boolean {\n return this.SEGMENT_PATTERNS.OPTIONAL_CATCH_ALL.test(segment);\n }\n\n private static isCatchAll(segment: string): boolean {\n return this.SEGMENT_PATTERNS.CATCH_ALL.test(segment);\n }\n\n private static isDynamicSegment(segment: string): boolean {\n return this.SEGMENT_PATTERNS.DYNAMIC.test(segment);\n }\n}","import { SegmentParser } from './segmentParser';\nimport {ParserOptions, RouteFile, RouteNode} from \"../../types\";\n\nexport class RouteTreeBuilder {\n private readonly typeSetters: Record<string, (node: RouteNode, route: RouteFile) => void>;\n\n constructor() {\n this.typeSetters = {\n layout: (node, route) => { node.layout = route.componentName; },\n error: (node, route) => { node.error = route.componentName; },\n loading: (node, route) => { node.loading = route.componentName; },\n 'not-found': (node, route) => { node.notFound = route.componentName; },\n page: this.handlePageType.bind(this)\n };\n }\n\n build(routes: RouteFile[], options?: ParserOptions): RouteNode {\n const root: RouteNode = {\n segment: '',\n path: '/',\n children: new Map(),\n metadata: options?.metadata\n };\n\n const sortedRoutes = this.sortRoutes(routes);\n\n sortedRoutes.forEach(route => {\n this.processRoute(root, route, options);\n });\n\n return root;\n }\n\n private sortRoutes(routes: RouteFile[]): RouteFile[] {\n return [...routes].sort((a, b) =>\n a.type === 'layout' && b.type !== 'layout' ? -1 :\n a.type !== 'layout' && b.type === 'layout' ? 1 : 0\n );\n }\n\n private processRoute(root: RouteNode, route: RouteFile, options?: ParserOptions): void {\n const segments = route.segments.map(seg => String(seg));\n\n if (segments.length === 0) {\n this.applyTypeHandler(root, route);\n if (route.type === 'page') root.isIndex = true;\n return;\n }\n\n let current = root;\n segments.forEach((seg, idx) => {\n const isLast = idx === segments.length - 1;\n const key = SegmentParser.normalize(seg, options);\n\n if (key === null) return;\n\n if (!current.children.has(key)) {\n current.children.set(key, {\n segment: key,\n path: key,\n children: new Map(),\n metadata: options?.metadata\n });\n }\n\n current = current.children.get(key)!;\n\n if (isLast) this.applyTypeHandler(current, route);\n });\n }\n\n private applyTypeHandler(node: RouteNode, route: RouteFile): void {\n const handler = this.typeSetters[route.type];\n if (handler) handler(node, route);\n }\n\n private handlePageType(node: RouteNode, route: RouteFile): void {\n const lastSegment = route.segments[route.segments.length - 1];\n const isDynamic = lastSegment?.startsWith('[');\n\n if (isDynamic || lastSegment === '') {\n node.component = route.componentName;\n node.isIndex = false;\n } else {\n node.indexPage = route.componentName;\n }\n }\n}","import {RouteNode} from \"../../types\";\n\nexport class RouteDefinitionGenerator {\n static generate(node: RouteNode, indent = ' '): string {\n const childNodes = Array.from(node.children.values());\n const children: string[] = [];\n\n if (node.indexPage) {\n children.push(this.generateIndexRoute(node, indent));\n }\n\n children.push(...childNodes.map(child =>\n this.generate(child, indent + ' ')\n ));\n\n return this.generateRouteObject(node, children, indent);\n }\n\n private static generateIndexRoute(node: RouteNode, indent: string): string {\n return `${indent} {\n${indent} index: true,\n${indent} element: React.createElement(RouteWrapper, {\n${indent} Component: ${node.indexPage}\n${indent} })\n${indent} }`;\n }\n\n private static generateRouteObject(node: RouteNode, children: string[], indent: string): string {\n const pathStr = node.segment === '' ? '/' : node.segment;\n const elementStr = this.generateElementString(node, indent);\n const errorStr = this.generateErrorString(node, indent);\n\n return [\n `${indent}{`,\n `${indent} path: '${pathStr}'${elementStr}${errorStr}`,\n children.length > 0 ? `,\\n${indent} children: [\\n${children.join(',\\n')}\\n${indent} ]` : '',\n `${indent}}`\n ].filter(Boolean).join('');\n }\n\n private static generateElementString(node: RouteNode, indent: string): string {\n if (!node.layout && !node.component) return '';\n\n const props = node.layout\n ? this.generateLayoutProps(node, indent)\n : this.generateComponentProps(node, indent);\n\n return `,\\n${indent} element: React.createElement(RouteWrapper, ${props})`;\n }\n\n private static generateLayoutProps(node: RouteNode, indent: string): string {\n return `{\n${indent} Component: ${node.layout},\n${indent} isLayout: true,\n${indent} loading: ${node.loading || 'undefined'},\n${indent} notFound: ${node.notFound || 'undefined'}\n${indent} }`;\n }\n\n private static generateComponentProps(node: RouteNode, indent: string): string {\n return `{\n${indent} Component: ${node.component}\n${indent} }`;\n }\n\n private static generateErrorString(node: RouteNode, indent: string): string {\n if (!node.error) return '';\n\n return `,\\n${indent} errorElement: React.createElement(ErrorBoundary, {\n${indent} Component: ${node.error}\n${indent} })`;\n }\n}"],"mappings":"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","names":["import_path","path","glob","path","module","fs"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
// src/plugin.ts
|
|
2
|
+
import path2 from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
|
|
5
|
+
// src/constants.ts
|
|
6
|
+
var DEFAULT_OPTIONS = {
|
|
7
|
+
pagesDir: "src/app",
|
|
8
|
+
typescript: true
|
|
9
|
+
};
|
|
10
|
+
var VIRTUAL_ROUTE_MODULE_ID = "virtual:preluder-routes";
|
|
11
|
+
var RESOLVED_VIRTUAL_ROUTE_MODULE_ID = "\0" + VIRTUAL_ROUTE_MODULE_ID;
|
|
12
|
+
|
|
13
|
+
// src/generator/createRoutes.ts
|
|
14
|
+
import path from "path";
|
|
15
|
+
import glob from "fast-glob";
|
|
16
|
+
function normalizeFilePath(filePath) {
|
|
17
|
+
return filePath.replace(/\\/g, "/");
|
|
18
|
+
}
|
|
19
|
+
function getRouteType(filePath) {
|
|
20
|
+
const basename = path.basename(filePath);
|
|
21
|
+
const fileTypes = {
|
|
22
|
+
"layout.jsx": "layout",
|
|
23
|
+
"layout.tsx": "layout",
|
|
24
|
+
"page.jsx": "page",
|
|
25
|
+
"page.tsx": "page",
|
|
26
|
+
"error.jsx": "error",
|
|
27
|
+
"error.tsx": "error",
|
|
28
|
+
"loading.jsx": "loading",
|
|
29
|
+
"loading.tsx": "loading",
|
|
30
|
+
"not-found.jsx": "not-found",
|
|
31
|
+
"not-found.tsx": "not-found"
|
|
32
|
+
};
|
|
33
|
+
return fileTypes[basename] || "page";
|
|
34
|
+
}
|
|
35
|
+
function getComponentName(filePath) {
|
|
36
|
+
const segments = filePath.split(path.sep);
|
|
37
|
+
const fileNameWithExt = segments[segments.length - 1];
|
|
38
|
+
const fileName = path.basename(fileNameWithExt, path.extname(fileNameWithExt));
|
|
39
|
+
const relevantSegments = segments.slice(segments.indexOf("app") + 1).filter((segment) => segment !== fileName);
|
|
40
|
+
const nameParts = relevantSegments.map((segment) => {
|
|
41
|
+
if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
42
|
+
return segment.slice(1, -1).replace(/^\.\.\./g, "CatchAll").replace(/[[\]]/g, "").split(/[^a-zA-Z0-9]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
43
|
+
}
|
|
44
|
+
return segment.split(/[^a-zA-Z0-9]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
45
|
+
});
|
|
46
|
+
const typeSuffix = {
|
|
47
|
+
page: "Page",
|
|
48
|
+
layout: "Layout",
|
|
49
|
+
error: "Error",
|
|
50
|
+
loading: "Loading",
|
|
51
|
+
"not-found": "NotFound"
|
|
52
|
+
}[fileName] || "Component";
|
|
53
|
+
return nameParts.length === 0 ? `Root${typeSuffix}` : `${nameParts.join("")}${typeSuffix}`;
|
|
54
|
+
}
|
|
55
|
+
function getPathSegments(filePath, pagesDir) {
|
|
56
|
+
const relativePath = path.relative(pagesDir, filePath);
|
|
57
|
+
const segments = relativePath.split(path.sep);
|
|
58
|
+
segments.pop();
|
|
59
|
+
return segments.filter((seg) => !(seg.startsWith("(") && seg.endsWith(")"))).map((seg) => seg);
|
|
60
|
+
}
|
|
61
|
+
async function findRouteFiles(pagesDir) {
|
|
62
|
+
const files = await glob(["**/*.{jsx,tsx}"], {
|
|
63
|
+
cwd: pagesDir,
|
|
64
|
+
absolute: true,
|
|
65
|
+
ignore: ["**/node_modules/**", "**/.*/**", "**/_*/**"]
|
|
66
|
+
});
|
|
67
|
+
const validFiles = files.filter((file) => {
|
|
68
|
+
const base = path.basename(file);
|
|
69
|
+
return /^(layout|page|error|loading|not-found)\.(jsx|tsx)$/.test(base);
|
|
70
|
+
});
|
|
71
|
+
return validFiles.map((file) => ({
|
|
72
|
+
type: getRouteType(file),
|
|
73
|
+
path: "/" + path.relative(pagesDir, path.dirname(file)),
|
|
74
|
+
filePath: normalizeFilePath(file),
|
|
75
|
+
componentName: getComponentName(file),
|
|
76
|
+
segments: getPathSegments(file, pagesDir)
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/generator/parser/segmentParser.ts
|
|
81
|
+
var SegmentParser = class {
|
|
82
|
+
static normalize(segment, options) {
|
|
83
|
+
if (this.isGroupSegment(segment))
|
|
84
|
+
return null;
|
|
85
|
+
const transform = options?.dynamicSegmentTransform || this.defaultTransform;
|
|
86
|
+
if (this.isOptionalCatchAll(segment)) {
|
|
87
|
+
const name = segment.match(this.SEGMENT_PATTERNS.OPTIONAL_CATCH_ALL)[1];
|
|
88
|
+
return transform(`${name}*?`);
|
|
89
|
+
}
|
|
90
|
+
if (this.isCatchAll(segment)) {
|
|
91
|
+
const name = segment.match(this.SEGMENT_PATTERNS.CATCH_ALL)[1];
|
|
92
|
+
return transform(`${name}*`);
|
|
93
|
+
}
|
|
94
|
+
if (this.isDynamicSegment(segment)) {
|
|
95
|
+
const name = segment.match(this.SEGMENT_PATTERNS.DYNAMIC)[1];
|
|
96
|
+
return transform(name);
|
|
97
|
+
}
|
|
98
|
+
return segment;
|
|
99
|
+
}
|
|
100
|
+
static defaultTransform(name) {
|
|
101
|
+
return `:${name}`;
|
|
102
|
+
}
|
|
103
|
+
static isGroupSegment(segment) {
|
|
104
|
+
return this.SEGMENT_PATTERNS.GROUP.test(segment);
|
|
105
|
+
}
|
|
106
|
+
static isOptionalCatchAll(segment) {
|
|
107
|
+
return this.SEGMENT_PATTERNS.OPTIONAL_CATCH_ALL.test(segment);
|
|
108
|
+
}
|
|
109
|
+
static isCatchAll(segment) {
|
|
110
|
+
return this.SEGMENT_PATTERNS.CATCH_ALL.test(segment);
|
|
111
|
+
}
|
|
112
|
+
static isDynamicSegment(segment) {
|
|
113
|
+
return this.SEGMENT_PATTERNS.DYNAMIC.test(segment);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
SegmentParser.SEGMENT_PATTERNS = {
|
|
117
|
+
OPTIONAL_CATCH_ALL: /^\[\[\.{3}(.+)\]\]$/,
|
|
118
|
+
CATCH_ALL: /^\[\.{3}(.+)\]$/,
|
|
119
|
+
DYNAMIC: /^\[(.+)\]$/,
|
|
120
|
+
GROUP: /^\((.+)\)$/
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// src/generator/parser/routeTreeBuilder.ts
|
|
124
|
+
var RouteTreeBuilder = class {
|
|
125
|
+
constructor() {
|
|
126
|
+
this.typeSetters = {
|
|
127
|
+
layout: (node, route) => {
|
|
128
|
+
node.layout = route.componentName;
|
|
129
|
+
},
|
|
130
|
+
error: (node, route) => {
|
|
131
|
+
node.error = route.componentName;
|
|
132
|
+
},
|
|
133
|
+
loading: (node, route) => {
|
|
134
|
+
node.loading = route.componentName;
|
|
135
|
+
},
|
|
136
|
+
"not-found": (node, route) => {
|
|
137
|
+
node.notFound = route.componentName;
|
|
138
|
+
},
|
|
139
|
+
page: this.handlePageType.bind(this)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
build(routes, options) {
|
|
143
|
+
const root = {
|
|
144
|
+
segment: "",
|
|
145
|
+
path: "/",
|
|
146
|
+
children: /* @__PURE__ */ new Map(),
|
|
147
|
+
metadata: options?.metadata
|
|
148
|
+
};
|
|
149
|
+
const sortedRoutes = this.sortRoutes(routes);
|
|
150
|
+
sortedRoutes.forEach((route) => {
|
|
151
|
+
this.processRoute(root, route, options);
|
|
152
|
+
});
|
|
153
|
+
return root;
|
|
154
|
+
}
|
|
155
|
+
sortRoutes(routes) {
|
|
156
|
+
return [...routes].sort(
|
|
157
|
+
(a, b) => a.type === "layout" && b.type !== "layout" ? -1 : a.type !== "layout" && b.type === "layout" ? 1 : 0
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
processRoute(root, route, options) {
|
|
161
|
+
const segments = route.segments.map((seg) => String(seg));
|
|
162
|
+
if (segments.length === 0) {
|
|
163
|
+
this.applyTypeHandler(root, route);
|
|
164
|
+
if (route.type === "page")
|
|
165
|
+
root.isIndex = true;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
let current = root;
|
|
169
|
+
segments.forEach((seg, idx) => {
|
|
170
|
+
const isLast = idx === segments.length - 1;
|
|
171
|
+
const key = SegmentParser.normalize(seg, options);
|
|
172
|
+
if (key === null)
|
|
173
|
+
return;
|
|
174
|
+
if (!current.children.has(key)) {
|
|
175
|
+
current.children.set(key, {
|
|
176
|
+
segment: key,
|
|
177
|
+
path: key,
|
|
178
|
+
children: /* @__PURE__ */ new Map(),
|
|
179
|
+
metadata: options?.metadata
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
current = current.children.get(key);
|
|
183
|
+
if (isLast)
|
|
184
|
+
this.applyTypeHandler(current, route);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
applyTypeHandler(node, route) {
|
|
188
|
+
const handler = this.typeSetters[route.type];
|
|
189
|
+
if (handler)
|
|
190
|
+
handler(node, route);
|
|
191
|
+
}
|
|
192
|
+
handlePageType(node, route) {
|
|
193
|
+
const lastSegment = route.segments[route.segments.length - 1];
|
|
194
|
+
const isDynamic = lastSegment?.startsWith("[");
|
|
195
|
+
if (isDynamic || lastSegment === "") {
|
|
196
|
+
node.component = route.componentName;
|
|
197
|
+
node.isIndex = false;
|
|
198
|
+
} else {
|
|
199
|
+
node.indexPage = route.componentName;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/generator/parser/routeDefinitionGenerator.ts
|
|
205
|
+
var RouteDefinitionGenerator = class {
|
|
206
|
+
static generate(node, indent = " ") {
|
|
207
|
+
const childNodes = Array.from(node.children.values());
|
|
208
|
+
const children = [];
|
|
209
|
+
if (node.indexPage) {
|
|
210
|
+
children.push(this.generateIndexRoute(node, indent));
|
|
211
|
+
}
|
|
212
|
+
children.push(...childNodes.map(
|
|
213
|
+
(child) => this.generate(child, indent + " ")
|
|
214
|
+
));
|
|
215
|
+
return this.generateRouteObject(node, children, indent);
|
|
216
|
+
}
|
|
217
|
+
static generateIndexRoute(node, indent) {
|
|
218
|
+
return `${indent} {
|
|
219
|
+
${indent} index: true,
|
|
220
|
+
${indent} element: React.createElement(RouteWrapper, {
|
|
221
|
+
${indent} Component: ${node.indexPage}
|
|
222
|
+
${indent} })
|
|
223
|
+
${indent} }`;
|
|
224
|
+
}
|
|
225
|
+
static generateRouteObject(node, children, indent) {
|
|
226
|
+
const pathStr = node.segment === "" ? "/" : node.segment;
|
|
227
|
+
const elementStr = this.generateElementString(node, indent);
|
|
228
|
+
const errorStr = this.generateErrorString(node, indent);
|
|
229
|
+
return [
|
|
230
|
+
`${indent}{`,
|
|
231
|
+
`${indent} path: '${pathStr}'${elementStr}${errorStr}`,
|
|
232
|
+
children.length > 0 ? `,
|
|
233
|
+
${indent} children: [
|
|
234
|
+
${children.join(",\n")}
|
|
235
|
+
${indent} ]` : "",
|
|
236
|
+
`${indent}}`
|
|
237
|
+
].filter(Boolean).join("");
|
|
238
|
+
}
|
|
239
|
+
static generateElementString(node, indent) {
|
|
240
|
+
if (!node.layout && !node.component)
|
|
241
|
+
return "";
|
|
242
|
+
const props = node.layout ? this.generateLayoutProps(node, indent) : this.generateComponentProps(node, indent);
|
|
243
|
+
return `,
|
|
244
|
+
${indent} element: React.createElement(RouteWrapper, ${props})`;
|
|
245
|
+
}
|
|
246
|
+
static generateLayoutProps(node, indent) {
|
|
247
|
+
return `{
|
|
248
|
+
${indent} Component: ${node.layout},
|
|
249
|
+
${indent} isLayout: true,
|
|
250
|
+
${indent} loading: ${node.loading || "undefined"},
|
|
251
|
+
${indent} notFound: ${node.notFound || "undefined"}
|
|
252
|
+
${indent} }`;
|
|
253
|
+
}
|
|
254
|
+
static generateComponentProps(node, indent) {
|
|
255
|
+
return `{
|
|
256
|
+
${indent} Component: ${node.component}
|
|
257
|
+
${indent} }`;
|
|
258
|
+
}
|
|
259
|
+
static generateErrorString(node, indent) {
|
|
260
|
+
if (!node.error)
|
|
261
|
+
return "";
|
|
262
|
+
return `,
|
|
263
|
+
${indent} errorElement: React.createElement(ErrorBoundary, {
|
|
264
|
+
${indent} Component: ${node.error}
|
|
265
|
+
${indent} })`;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// src/plugin.ts
|
|
270
|
+
function navilo(options = {}) {
|
|
271
|
+
const resolvedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
272
|
+
let root;
|
|
273
|
+
const routeTreeBuilder = new RouteTreeBuilder();
|
|
274
|
+
return {
|
|
275
|
+
name: "navilo",
|
|
276
|
+
configResolved(config) {
|
|
277
|
+
root = config.root;
|
|
278
|
+
},
|
|
279
|
+
configureServer(server) {
|
|
280
|
+
const pagesDir = path2.resolve(root, resolvedOptions.pagesDir);
|
|
281
|
+
server.watcher.add(pagesDir);
|
|
282
|
+
const handleFileChange = (file) => {
|
|
283
|
+
if (file.startsWith(pagesDir)) {
|
|
284
|
+
const module = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ROUTE_MODULE_ID);
|
|
285
|
+
if (module) {
|
|
286
|
+
server.moduleGraph.invalidateModule(module);
|
|
287
|
+
}
|
|
288
|
+
server.ws.send({ type: "full-reload" });
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
server.watcher.on("add", handleFileChange);
|
|
292
|
+
server.watcher.on("unlink", handleFileChange);
|
|
293
|
+
server.watcher.on("change", handleFileChange);
|
|
294
|
+
},
|
|
295
|
+
resolveId(id) {
|
|
296
|
+
if (id === VIRTUAL_ROUTE_MODULE_ID) {
|
|
297
|
+
return RESOLVED_VIRTUAL_ROUTE_MODULE_ID;
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
async load(id) {
|
|
301
|
+
if (id === RESOLVED_VIRTUAL_ROUTE_MODULE_ID) {
|
|
302
|
+
const pagesDir = path2.resolve(root, resolvedOptions.pagesDir);
|
|
303
|
+
if (!fs.existsSync(pagesDir)) {
|
|
304
|
+
fs.mkdirSync(pagesDir, { recursive: true });
|
|
305
|
+
return `export const router = null;`;
|
|
306
|
+
}
|
|
307
|
+
const routes = await findRouteFiles(pagesDir);
|
|
308
|
+
if (routes.length === 0) {
|
|
309
|
+
return `export const router = null;`;
|
|
310
|
+
}
|
|
311
|
+
const routeTree = routeTreeBuilder.build(routes, {
|
|
312
|
+
strict: true,
|
|
313
|
+
dynamicSegmentTransform: (segment) => `:${segment.toLowerCase()}`
|
|
314
|
+
});
|
|
315
|
+
const routeDefinitions = RouteDefinitionGenerator.generate(routeTree);
|
|
316
|
+
return `
|
|
317
|
+
import React from 'react';
|
|
318
|
+
import { createBrowserRouter } from 'react-router-dom';
|
|
319
|
+
import RouteWrapper from './components/RouteWrapper';
|
|
320
|
+
import ErrorBoundary from './components/ErrorBoundary';
|
|
321
|
+
|
|
322
|
+
${generateImports(routes)}
|
|
323
|
+
|
|
324
|
+
export const router = createBrowserRouter([${routeDefinitions}]);
|
|
325
|
+
`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function generateImports(routes) {
|
|
331
|
+
return routes.map((route) => `import ${route.componentName} from '${route.filePath}';`).join("\n");
|
|
332
|
+
}
|
|
333
|
+
export {
|
|
334
|
+
RouteDefinitionGenerator,
|
|
335
|
+
RouteTreeBuilder,
|
|
336
|
+
SegmentParser,
|
|
337
|
+
findRouteFiles,
|
|
338
|
+
getComponentName,
|
|
339
|
+
getRouteType,
|
|
340
|
+
navilo,
|
|
341
|
+
normalizeFilePath
|
|
342
|
+
};
|
|
343
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts","../src/constants.ts","../src/generator/createRoutes.ts","../src/generator/parser/segmentParser.ts","../src/generator/parser/routeTreeBuilder.ts","../src/generator/parser/routeDefinitionGenerator.ts"],"sourcesContent":["// src/plugin.ts\nimport { Plugin } from 'vite';\nimport path from 'path';\nimport fs from 'fs';\nimport { DEFAULT_OPTIONS, RESOLVED_VIRTUAL_ROUTE_MODULE_ID, VIRTUAL_ROUTE_MODULE_ID } from './constants';\nimport {naviloOptions, RouteFile} from './types';\nimport { findRouteFiles } from './generator/createRoutes';\nimport {RouteTreeBuilder} from \"./generator/parser/routeTreeBuilder\";\nimport {RouteDefinitionGenerator} from \"./generator/parser/routeDefinitionGenerator\";\n\nexport function navilo(options: naviloOptions = {}): Plugin {\n const resolvedOptions = { ...DEFAULT_OPTIONS, ...options };\n let root: string;\n const routeTreeBuilder = new RouteTreeBuilder();\n\n return {\n name: 'navilo',\n\n configResolved(config) {\n root = config.root;\n },\n\n configureServer(server) {\n const pagesDir = path.resolve(root, resolvedOptions.pagesDir);\n\n server.watcher.add(pagesDir);\n\n const handleFileChange = (file: string) => {\n if (file.startsWith(pagesDir)) {\n const module = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ROUTE_MODULE_ID);\n if (module) {\n server.moduleGraph.invalidateModule(module);\n }\n server.ws.send({ type: 'full-reload' });\n }\n };\n\n server.watcher.on('add', handleFileChange);\n server.watcher.on('unlink', handleFileChange);\n server.watcher.on('change', handleFileChange);\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ROUTE_MODULE_ID) {\n return RESOLVED_VIRTUAL_ROUTE_MODULE_ID;\n }\n },\n\n async load(id) {\n if (id === RESOLVED_VIRTUAL_ROUTE_MODULE_ID) {\n const pagesDir = path.resolve(root, resolvedOptions.pagesDir);\n\n if (!fs.existsSync(pagesDir)) {\n fs.mkdirSync(pagesDir, { recursive: true });\n return `export const router = null;`;\n }\n\n const routes = await findRouteFiles(pagesDir);\n\n if (routes.length === 0) {\n return `export const router = null;`;\n }\n\n const routeTree = routeTreeBuilder.build(routes, {\n strict: true,\n dynamicSegmentTransform: (segment) => `:${segment.toLowerCase()}`\n });\n\n const routeDefinitions = RouteDefinitionGenerator.generate(routeTree);\n\n return `\n import React from 'react';\n import { createBrowserRouter } from 'react-router-dom';\n import RouteWrapper from './components/RouteWrapper';\n import ErrorBoundary from './components/ErrorBoundary';\n \n ${generateImports(routes)}\n \n export const router = createBrowserRouter([${routeDefinitions}]);\n `;\n }\n }\n };\n}\n\nfunction generateImports(routes: RouteFile[]): string {\n return routes\n .map(route => `import ${route.componentName} from '${route.filePath}';`)\n .join('\\n');\n}","export const DEFAULT_OPTIONS = {\n pagesDir: 'src/app',\n typescript: true,\n};\n\nexport const VIRTUAL_ROUTE_MODULE_ID = 'virtual:preluder-routes';\nexport const RESOLVED_VIRTUAL_ROUTE_MODULE_ID = '\\0' + VIRTUAL_ROUTE_MODULE_ID;","import path from 'path';\nimport glob from 'fast-glob';\nimport { RouteFile, RouteType } from '../types';\n\nexport function normalizeFilePath(filePath: string): string {\n return filePath.replace(/\\\\/g, '/');\n}\n\nexport function getRouteType(filePath: string): RouteType {\n const basename = path.basename(filePath);\n const fileTypes: Record<string, RouteType> = {\n 'layout.jsx': 'layout',\n 'layout.tsx': 'layout',\n 'page.jsx': 'page',\n 'page.tsx': 'page',\n 'error.jsx': 'error',\n 'error.tsx': 'error',\n 'loading.jsx': 'loading',\n 'loading.tsx': 'loading',\n 'not-found.jsx': 'not-found',\n 'not-found.tsx': 'not-found'\n };\n return fileTypes[basename] || 'page';\n}\n\nexport function getComponentName(filePath: string): string {\n const segments = filePath.split(path.sep);\n const fileNameWithExt = segments[segments.length - 1];\n const fileName = path.basename(fileNameWithExt, path.extname(fileNameWithExt));\n\n const relevantSegments = segments\n .slice(segments.indexOf('app') + 1)\n .filter(segment => segment !== fileName);\n\n const nameParts = relevantSegments.map(segment => {\n if (segment.startsWith('[') && segment.endsWith(']')) {\n return segment.slice(1, -1)\n .replace(/^\\.\\.\\./g, 'CatchAll')\n .replace(/[[\\]]/g, '')\n .split(/[^a-zA-Z0-9]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n }\n return segment\n .split(/[^a-zA-Z0-9]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n });\n\n const typeSuffix = {\n page: 'Page',\n layout: 'Layout',\n error: 'Error',\n loading: 'Loading',\n 'not-found': 'NotFound'\n }[fileName] || 'Component';\n\n return nameParts.length === 0 ? `Root${typeSuffix}` : `${nameParts.join('')}${typeSuffix}`;\n}\n\nexport function getPathSegments(filePath: string, pagesDir: string): string[] {\n const relativePath = path.relative(pagesDir, filePath);\n const segments = relativePath.split(path.sep);\n segments.pop();\n\n return segments\n .filter(seg => !(seg.startsWith('(') && seg.endsWith(')')))\n .map(seg => seg);\n}\n\nexport async function findRouteFiles(pagesDir: string): Promise<RouteFile[]> {\n const files = await glob(['**/*.{jsx,tsx}'], {\n cwd: pagesDir,\n absolute: true,\n ignore: ['**/node_modules/**', '**/.*/**', '**/_*/**'],\n });\n\n const validFiles = files.filter(file => {\n const base = path.basename(file);\n return /^(layout|page|error|loading|not-found)\\.(jsx|tsx)$/.test(base);\n });\n\n return validFiles.map(file => ({\n type: getRouteType(file),\n path: '/' + path.relative(pagesDir, path.dirname(file)),\n filePath: normalizeFilePath(file),\n componentName: getComponentName(file),\n segments: getPathSegments(file, pagesDir),\n }));\n}","import {ParserOptions} from \"../../types\";\n\nexport class SegmentParser {\n private static readonly SEGMENT_PATTERNS = {\n OPTIONAL_CATCH_ALL: /^\\[\\[\\.{3}(.+)\\]\\]$/,\n CATCH_ALL: /^\\[\\.{3}(.+)\\]$/,\n DYNAMIC: /^\\[(.+)\\]$/,\n GROUP: /^\\((.+)\\)$/\n };\n\n static normalize(segment: string, options?: ParserOptions): string | null {\n if (this.isGroupSegment(segment)) return null;\n\n const transform = options?.dynamicSegmentTransform || this.defaultTransform;\n\n if (this.isOptionalCatchAll(segment)) {\n const name = segment.match(this.SEGMENT_PATTERNS.OPTIONAL_CATCH_ALL)![1];\n return transform(`${name}*?`);\n }\n\n if (this.isCatchAll(segment)) {\n const name = segment.match(this.SEGMENT_PATTERNS.CATCH_ALL)![1];\n return transform(`${name}*`);\n }\n\n if (this.isDynamicSegment(segment)) {\n const name = segment.match(this.SEGMENT_PATTERNS.DYNAMIC)![1];\n return transform(name);\n }\n\n return segment;\n }\n\n private static defaultTransform(name: string): string {\n return `:${name}`;\n }\n\n private static isGroupSegment(segment: string): boolean {\n return this.SEGMENT_PATTERNS.GROUP.test(segment);\n }\n\n private static isOptionalCatchAll(segment: string): boolean {\n return this.SEGMENT_PATTERNS.OPTIONAL_CATCH_ALL.test(segment);\n }\n\n private static isCatchAll(segment: string): boolean {\n return this.SEGMENT_PATTERNS.CATCH_ALL.test(segment);\n }\n\n private static isDynamicSegment(segment: string): boolean {\n return this.SEGMENT_PATTERNS.DYNAMIC.test(segment);\n }\n}","import { SegmentParser } from './segmentParser';\nimport {ParserOptions, RouteFile, RouteNode} from \"../../types\";\n\nexport class RouteTreeBuilder {\n private readonly typeSetters: Record<string, (node: RouteNode, route: RouteFile) => void>;\n\n constructor() {\n this.typeSetters = {\n layout: (node, route) => { node.layout = route.componentName; },\n error: (node, route) => { node.error = route.componentName; },\n loading: (node, route) => { node.loading = route.componentName; },\n 'not-found': (node, route) => { node.notFound = route.componentName; },\n page: this.handlePageType.bind(this)\n };\n }\n\n build(routes: RouteFile[], options?: ParserOptions): RouteNode {\n const root: RouteNode = {\n segment: '',\n path: '/',\n children: new Map(),\n metadata: options?.metadata\n };\n\n const sortedRoutes = this.sortRoutes(routes);\n\n sortedRoutes.forEach(route => {\n this.processRoute(root, route, options);\n });\n\n return root;\n }\n\n private sortRoutes(routes: RouteFile[]): RouteFile[] {\n return [...routes].sort((a, b) =>\n a.type === 'layout' && b.type !== 'layout' ? -1 :\n a.type !== 'layout' && b.type === 'layout' ? 1 : 0\n );\n }\n\n private processRoute(root: RouteNode, route: RouteFile, options?: ParserOptions): void {\n const segments = route.segments.map(seg => String(seg));\n\n if (segments.length === 0) {\n this.applyTypeHandler(root, route);\n if (route.type === 'page') root.isIndex = true;\n return;\n }\n\n let current = root;\n segments.forEach((seg, idx) => {\n const isLast = idx === segments.length - 1;\n const key = SegmentParser.normalize(seg, options);\n\n if (key === null) return;\n\n if (!current.children.has(key)) {\n current.children.set(key, {\n segment: key,\n path: key,\n children: new Map(),\n metadata: options?.metadata\n });\n }\n\n current = current.children.get(key)!;\n\n if (isLast) this.applyTypeHandler(current, route);\n });\n }\n\n private applyTypeHandler(node: RouteNode, route: RouteFile): void {\n const handler = this.typeSetters[route.type];\n if (handler) handler(node, route);\n }\n\n private handlePageType(node: RouteNode, route: RouteFile): void {\n const lastSegment = route.segments[route.segments.length - 1];\n const isDynamic = lastSegment?.startsWith('[');\n\n if (isDynamic || lastSegment === '') {\n node.component = route.componentName;\n node.isIndex = false;\n } else {\n node.indexPage = route.componentName;\n }\n }\n}","import {RouteNode} from \"../../types\";\n\nexport class RouteDefinitionGenerator {\n static generate(node: RouteNode, indent = ' '): string {\n const childNodes = Array.from(node.children.values());\n const children: string[] = [];\n\n if (node.indexPage) {\n children.push(this.generateIndexRoute(node, indent));\n }\n\n children.push(...childNodes.map(child =>\n this.generate(child, indent + ' ')\n ));\n\n return this.generateRouteObject(node, children, indent);\n }\n\n private static generateIndexRoute(node: RouteNode, indent: string): string {\n return `${indent} {\n${indent} index: true,\n${indent} element: React.createElement(RouteWrapper, {\n${indent} Component: ${node.indexPage}\n${indent} })\n${indent} }`;\n }\n\n private static generateRouteObject(node: RouteNode, children: string[], indent: string): string {\n const pathStr = node.segment === '' ? '/' : node.segment;\n const elementStr = this.generateElementString(node, indent);\n const errorStr = this.generateErrorString(node, indent);\n\n return [\n `${indent}{`,\n `${indent} path: '${pathStr}'${elementStr}${errorStr}`,\n children.length > 0 ? `,\\n${indent} children: [\\n${children.join(',\\n')}\\n${indent} ]` : '',\n `${indent}}`\n ].filter(Boolean).join('');\n }\n\n private static generateElementString(node: RouteNode, indent: string): string {\n if (!node.layout && !node.component) return '';\n\n const props = node.layout\n ? this.generateLayoutProps(node, indent)\n : this.generateComponentProps(node, indent);\n\n return `,\\n${indent} element: React.createElement(RouteWrapper, ${props})`;\n }\n\n private static generateLayoutProps(node: RouteNode, indent: string): string {\n return `{\n${indent} Component: ${node.layout},\n${indent} isLayout: true,\n${indent} loading: ${node.loading || 'undefined'},\n${indent} notFound: ${node.notFound || 'undefined'}\n${indent} }`;\n }\n\n private static generateComponentProps(node: RouteNode, indent: string): string {\n return `{\n${indent} Component: ${node.component}\n${indent} }`;\n }\n\n private static generateErrorString(node: RouteNode, indent: string): string {\n if (!node.error) return '';\n\n return `,\\n${indent} errorElement: React.createElement(ErrorBoundary, {\n${indent} Component: ${node.error}\n${indent} })`;\n }\n}"],"mappings":"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","names":["path","path"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "navilo",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"description": "File-based routing plugin for Vite + React applications",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"dev": "tsup --watch",
|
|
14
|
+
"typecheck": "tsc --noEmit"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"vite",
|
|
18
|
+
"vite-plugin",
|
|
19
|
+
"react",
|
|
20
|
+
"router",
|
|
21
|
+
"file-based-routing"
|
|
22
|
+
],
|
|
23
|
+
"author": "Zubeyr Anwar",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "^18.0.0",
|
|
27
|
+
"react-dom": "^18.0.0",
|
|
28
|
+
"react-router-dom": "^6.16.0",
|
|
29
|
+
"vite": "^4.0.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"fast-glob": "^3.3.1"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@testing-library/jest-dom": "^6.8.0",
|
|
36
|
+
"@testing-library/react": "^16.3.0",
|
|
37
|
+
"@types/node": "^20.8.0",
|
|
38
|
+
"@types/react": "^18.2.0",
|
|
39
|
+
"@types/react-dom": "^18.2.0",
|
|
40
|
+
"jsdom": "^26.1.0",
|
|
41
|
+
"react": "^18.2.0",
|
|
42
|
+
"react-router-dom": "^6.16.0",
|
|
43
|
+
"react-dom": "^18.2.0",
|
|
44
|
+
"tsup": "^7.2.0",
|
|
45
|
+
"typescript": "^5.2.2",
|
|
46
|
+
"vite": "^4.4.9",
|
|
47
|
+
"vitest": "^3.2.4"
|
|
48
|
+
},
|
|
49
|
+
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
|
|
50
|
+
}
|