@react-router/fs-routes 0.0.0-experimental-8cee090e4 → 0.0.0-experimental-63fd291ad
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.ts +13 -15
- package/dist/index.js +440 -353
- package/package.json +9 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
+
import { RouteConfigEntry } from '@react-router/dev/routes';
|
|
1
2
|
|
|
2
|
-
import { RouteConfigEntry } from "@react-router/dev/routes";
|
|
3
|
-
|
|
4
|
-
//#region index.d.ts
|
|
5
3
|
/**
|
|
6
4
|
* Creates route config from the file system using a convention that matches
|
|
7
5
|
* [Remix v2's route file
|
|
@@ -9,16 +7,16 @@ import { RouteConfigEntry } from "@react-router/dev/routes";
|
|
|
9
7
|
* within `routes.ts`.
|
|
10
8
|
*/
|
|
11
9
|
declare function flatRoutes(options?: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
/**
|
|
11
|
+
* An array of [minimatch](https://www.npmjs.com/package/minimatch) globs that match files to ignore.
|
|
12
|
+
* Defaults to `[]`.
|
|
13
|
+
*/
|
|
14
|
+
ignoredRouteFiles?: string[];
|
|
15
|
+
/**
|
|
16
|
+
* The directory containing file system routes, relative to the app directory.
|
|
17
|
+
* Defaults to `"./routes"`.
|
|
18
|
+
*/
|
|
19
|
+
rootDirectory?: string;
|
|
22
20
|
}): Promise<RouteConfigEntry[]>;
|
|
23
|
-
|
|
24
|
-
export { flatRoutes };
|
|
21
|
+
|
|
22
|
+
export { flatRoutes };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/fs-routes v0.0.0-experimental-
|
|
2
|
+
* @react-router/fs-routes v0.0.0-experimental-63fd291ad
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -8,381 +8,468 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
"use strict";
|
|
12
|
+
var __create = Object.create;
|
|
13
|
+
var __defProp = Object.defineProperty;
|
|
14
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
15
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
16
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
17
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
18
|
+
var __export = (target, all) => {
|
|
19
|
+
for (var name in all)
|
|
20
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
21
|
+
};
|
|
22
|
+
var __copyProps = (to, from, except, desc) => {
|
|
23
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
24
|
+
for (let key of __getOwnPropNames(from))
|
|
25
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
26
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
27
|
+
}
|
|
28
|
+
return to;
|
|
29
|
+
};
|
|
30
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
31
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
32
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
33
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
34
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
35
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
36
|
+
mod
|
|
37
|
+
));
|
|
38
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
39
|
+
|
|
40
|
+
// index.ts
|
|
41
|
+
var index_exports = {};
|
|
42
|
+
__export(index_exports, {
|
|
43
|
+
flatRoutes: () => flatRoutes2
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(index_exports);
|
|
46
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
47
|
+
var import_node_path3 = __toESM(require("path"));
|
|
48
|
+
var import_routes = require("@react-router/dev/routes");
|
|
49
|
+
|
|
50
|
+
// manifest.ts
|
|
16
51
|
function routeManifestToRouteConfig(routeManifest, rootId = "root") {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
let routeConfigById = {};
|
|
53
|
+
for (let id in routeManifest) {
|
|
54
|
+
let route = routeManifest[id];
|
|
55
|
+
routeConfigById[id] = {
|
|
56
|
+
id: route.id,
|
|
57
|
+
file: route.file,
|
|
58
|
+
path: route.path,
|
|
59
|
+
index: route.index,
|
|
60
|
+
caseSensitive: route.caseSensitive
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
let routeConfig = [];
|
|
64
|
+
for (let id in routeConfigById) {
|
|
65
|
+
let route = routeConfigById[id];
|
|
66
|
+
let parentId = routeManifest[route.id].parentId;
|
|
67
|
+
if (parentId === rootId) {
|
|
68
|
+
routeConfig.push(route);
|
|
69
|
+
} else {
|
|
70
|
+
let parentRoute = parentId && routeConfigById[parentId];
|
|
71
|
+
if (parentRoute) {
|
|
72
|
+
parentRoute.children = parentRoute.children || [];
|
|
73
|
+
parentRoute.children.push(route);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return routeConfig;
|
|
42
78
|
}
|
|
43
|
-
|
|
44
|
-
|
|
79
|
+
|
|
80
|
+
// flatRoutes.ts
|
|
81
|
+
var import_node_fs = __toESM(require("fs"));
|
|
82
|
+
var import_node_path2 = __toESM(require("path"));
|
|
83
|
+
var import_minimatch = require("minimatch");
|
|
84
|
+
|
|
85
|
+
// normalizeSlashes.ts
|
|
86
|
+
var import_node_path = __toESM(require("path"));
|
|
45
87
|
function normalizeSlashes(file) {
|
|
46
|
-
|
|
88
|
+
return file.replaceAll(import_node_path.default.win32.sep, "/");
|
|
47
89
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
];
|
|
58
|
-
const PrefixLookupTrieEndSymbol = Symbol("PrefixLookupTrieEndSymbol");
|
|
90
|
+
|
|
91
|
+
// flatRoutes.ts
|
|
92
|
+
var routeModuleExts = [".js", ".jsx", ".ts", ".tsx", ".md", ".mdx"];
|
|
93
|
+
var paramPrefixChar = "$";
|
|
94
|
+
var escapeStart = "[";
|
|
95
|
+
var escapeEnd = "]";
|
|
96
|
+
var optionalStart = "(";
|
|
97
|
+
var optionalEnd = ")";
|
|
98
|
+
var PrefixLookupTrieEndSymbol = Symbol("PrefixLookupTrieEndSymbol");
|
|
59
99
|
var PrefixLookupTrie = class {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
root = {
|
|
101
|
+
[PrefixLookupTrieEndSymbol]: false
|
|
102
|
+
};
|
|
103
|
+
add(value) {
|
|
104
|
+
if (!value) throw new Error("Cannot add empty string to PrefixLookupTrie");
|
|
105
|
+
let node = this.root;
|
|
106
|
+
for (let char of value) {
|
|
107
|
+
if (!node[char]) {
|
|
108
|
+
node[char] = {
|
|
109
|
+
[PrefixLookupTrieEndSymbol]: false
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
node = node[char];
|
|
113
|
+
}
|
|
114
|
+
node[PrefixLookupTrieEndSymbol] = true;
|
|
115
|
+
}
|
|
116
|
+
findAndRemove(prefix, filter) {
|
|
117
|
+
let node = this.root;
|
|
118
|
+
for (let char of prefix) {
|
|
119
|
+
if (!node[char]) return [];
|
|
120
|
+
node = node[char];
|
|
121
|
+
}
|
|
122
|
+
return this.#findAndRemoveRecursive([], node, prefix, filter);
|
|
123
|
+
}
|
|
124
|
+
#findAndRemoveRecursive(values, node, prefix, filter) {
|
|
125
|
+
for (let char of Object.keys(node)) {
|
|
126
|
+
this.#findAndRemoveRecursive(values, node[char], prefix + char, filter);
|
|
127
|
+
}
|
|
128
|
+
if (node[PrefixLookupTrieEndSymbol] && filter(prefix)) {
|
|
129
|
+
node[PrefixLookupTrieEndSymbol] = false;
|
|
130
|
+
values.push(prefix);
|
|
131
|
+
}
|
|
132
|
+
return values;
|
|
133
|
+
}
|
|
86
134
|
};
|
|
87
|
-
function flatRoutes
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
135
|
+
function flatRoutes(appDirectory, ignoredFilePatterns = [], prefix = "routes") {
|
|
136
|
+
let ignoredFileRegex = Array.from(/* @__PURE__ */ new Set(["**/.*", ...ignoredFilePatterns])).map((re) => (0, import_minimatch.makeRe)(re)).filter((re) => !!re);
|
|
137
|
+
let routesDir = import_node_path2.default.join(appDirectory, prefix);
|
|
138
|
+
let rootRoute = findFile(appDirectory, "root", routeModuleExts);
|
|
139
|
+
if (!rootRoute) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Could not find a root route module in the app directory: ${appDirectory}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (!import_node_fs.default.existsSync(routesDir)) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Could not find the routes directory: ${routesDir}. Did you forget to create it?`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
let entries = import_node_fs.default.readdirSync(routesDir, {
|
|
150
|
+
withFileTypes: true,
|
|
151
|
+
encoding: "utf-8"
|
|
152
|
+
});
|
|
153
|
+
let routes = [];
|
|
154
|
+
for (let entry of entries) {
|
|
155
|
+
let filepath = normalizeSlashes(import_node_path2.default.join(routesDir, entry.name));
|
|
156
|
+
let route = null;
|
|
157
|
+
if (entry.isDirectory()) {
|
|
158
|
+
route = findRouteModuleForFolder(
|
|
159
|
+
appDirectory,
|
|
160
|
+
filepath,
|
|
161
|
+
ignoredFileRegex
|
|
162
|
+
);
|
|
163
|
+
} else if (entry.isFile()) {
|
|
164
|
+
route = findRouteModuleForFile(appDirectory, filepath, ignoredFileRegex);
|
|
165
|
+
}
|
|
166
|
+
if (route) routes.push(route);
|
|
167
|
+
}
|
|
168
|
+
let routeManifest = flatRoutesUniversal(appDirectory, routes, prefix);
|
|
169
|
+
return routeManifest;
|
|
105
170
|
}
|
|
106
171
|
function flatRoutesUniversal(appDirectory, routes, prefix = "routes") {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!currentConflicts) currentConflicts = [conflict];
|
|
205
|
-
currentConflicts.push(config);
|
|
206
|
-
urlConflicts.set(originalPathname, currentConflicts);
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (routeIdConflicts.size > 0) for (let [routeId, files] of routeIdConflicts.entries()) console.error(getRouteIdConflictErrorMessage(routeId, files));
|
|
211
|
-
if (urlConflicts.size > 0) for (let [path, routes] of urlConflicts.entries()) {
|
|
212
|
-
for (let i = 1; i < routes.length; i++) delete routeManifest[routes[i].id];
|
|
213
|
-
let files = routes.map((r) => r.file);
|
|
214
|
-
console.error(getRoutePathConflictErrorMessage(path, files));
|
|
215
|
-
}
|
|
216
|
-
return routeManifest;
|
|
172
|
+
let urlConflicts = /* @__PURE__ */ new Map();
|
|
173
|
+
let routeManifest = {};
|
|
174
|
+
let prefixLookup = new PrefixLookupTrie();
|
|
175
|
+
let uniqueRoutes = /* @__PURE__ */ new Map();
|
|
176
|
+
let routeIdConflicts = /* @__PURE__ */ new Map();
|
|
177
|
+
let normalizedApp = normalizeSlashes(appDirectory);
|
|
178
|
+
let appWithPrefix = import_node_path2.default.posix.join(normalizedApp, prefix);
|
|
179
|
+
let routeIds = /* @__PURE__ */ new Map();
|
|
180
|
+
for (let file of routes) {
|
|
181
|
+
let normalizedFile = normalizeSlashes(file);
|
|
182
|
+
let routeExt = import_node_path2.default.extname(normalizedFile);
|
|
183
|
+
let routeDir = import_node_path2.default.dirname(normalizedFile);
|
|
184
|
+
let routeId = routeDir === appWithPrefix ? import_node_path2.default.posix.relative(normalizedApp, normalizedFile).slice(0, -routeExt.length) : import_node_path2.default.posix.relative(normalizedApp, routeDir);
|
|
185
|
+
let conflict = routeIds.get(routeId);
|
|
186
|
+
if (conflict) {
|
|
187
|
+
let currentConflicts = routeIdConflicts.get(routeId);
|
|
188
|
+
if (!currentConflicts) {
|
|
189
|
+
currentConflicts = [import_node_path2.default.posix.relative(normalizedApp, conflict)];
|
|
190
|
+
}
|
|
191
|
+
currentConflicts.push(import_node_path2.default.posix.relative(normalizedApp, normalizedFile));
|
|
192
|
+
routeIdConflicts.set(routeId, currentConflicts);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
routeIds.set(routeId, normalizedFile);
|
|
196
|
+
}
|
|
197
|
+
let sortedRouteIds = Array.from(routeIds).sort(
|
|
198
|
+
([a], [b]) => b.length - a.length
|
|
199
|
+
);
|
|
200
|
+
for (let [routeId, file] of sortedRouteIds) {
|
|
201
|
+
let index = routeId.endsWith("_index");
|
|
202
|
+
let [segments, raw] = getRouteSegments(routeId.slice(prefix.length + 1));
|
|
203
|
+
let pathname = createRoutePath(segments, raw, index);
|
|
204
|
+
routeManifest[routeId] = {
|
|
205
|
+
file: import_node_path2.default.posix.relative(normalizedApp, file),
|
|
206
|
+
id: routeId,
|
|
207
|
+
path: pathname
|
|
208
|
+
};
|
|
209
|
+
if (index) routeManifest[routeId].index = true;
|
|
210
|
+
let childRouteIds = prefixLookup.findAndRemove(routeId, (value) => {
|
|
211
|
+
return [".", "/"].includes(value.slice(routeId.length).charAt(0));
|
|
212
|
+
});
|
|
213
|
+
prefixLookup.add(routeId);
|
|
214
|
+
if (childRouteIds.length > 0) {
|
|
215
|
+
for (let childRouteId of childRouteIds) {
|
|
216
|
+
routeManifest[childRouteId].parentId = routeId;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
let parentChildrenMap = /* @__PURE__ */ new Map();
|
|
221
|
+
for (let [routeId] of sortedRouteIds) {
|
|
222
|
+
let config = routeManifest[routeId];
|
|
223
|
+
if (!config.parentId) continue;
|
|
224
|
+
let existingChildren = parentChildrenMap.get(config.parentId) || [];
|
|
225
|
+
existingChildren.push(config);
|
|
226
|
+
parentChildrenMap.set(config.parentId, existingChildren);
|
|
227
|
+
}
|
|
228
|
+
for (let [routeId] of sortedRouteIds) {
|
|
229
|
+
let config = routeManifest[routeId];
|
|
230
|
+
let originalPathname = config.path || "";
|
|
231
|
+
let pathname = config.path;
|
|
232
|
+
let parentConfig = config.parentId ? routeManifest[config.parentId] : null;
|
|
233
|
+
if (parentConfig?.path && pathname) {
|
|
234
|
+
pathname = pathname.slice(parentConfig.path.length).replace(/^\//, "").replace(/\/$/, "");
|
|
235
|
+
}
|
|
236
|
+
if (!config.parentId) config.parentId = "root";
|
|
237
|
+
config.path = pathname || void 0;
|
|
238
|
+
let lastRouteSegment = config.id.replace(new RegExp(`^${prefix}/`), "").split(".").pop();
|
|
239
|
+
let isPathlessLayoutRoute = lastRouteSegment && lastRouteSegment.startsWith("_") && lastRouteSegment !== "_index";
|
|
240
|
+
if (isPathlessLayoutRoute) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
let conflictRouteId = originalPathname + (config.index ? "?index" : "");
|
|
244
|
+
let conflict = uniqueRoutes.get(conflictRouteId);
|
|
245
|
+
uniqueRoutes.set(conflictRouteId, config);
|
|
246
|
+
if (conflict && (originalPathname || config.index)) {
|
|
247
|
+
let currentConflicts = urlConflicts.get(originalPathname);
|
|
248
|
+
if (!currentConflicts) currentConflicts = [conflict];
|
|
249
|
+
currentConflicts.push(config);
|
|
250
|
+
urlConflicts.set(originalPathname, currentConflicts);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (routeIdConflicts.size > 0) {
|
|
255
|
+
for (let [routeId, files] of routeIdConflicts.entries()) {
|
|
256
|
+
console.error(getRouteIdConflictErrorMessage(routeId, files));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (urlConflicts.size > 0) {
|
|
260
|
+
for (let [path4, routes2] of urlConflicts.entries()) {
|
|
261
|
+
for (let i = 1; i < routes2.length; i++) {
|
|
262
|
+
delete routeManifest[routes2[i].id];
|
|
263
|
+
}
|
|
264
|
+
let files = routes2.map((r) => r.file);
|
|
265
|
+
console.error(getRoutePathConflictErrorMessage(path4, files));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return routeManifest;
|
|
217
269
|
}
|
|
218
270
|
function findRouteModuleForFile(appDirectory, filepath, ignoredFileRegex) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
271
|
+
let relativePath = normalizeSlashes(import_node_path2.default.relative(appDirectory, filepath));
|
|
272
|
+
let isIgnored = ignoredFileRegex.some((regex) => regex.test(relativePath));
|
|
273
|
+
if (isIgnored) return null;
|
|
274
|
+
return filepath;
|
|
222
275
|
}
|
|
223
276
|
function findRouteModuleForFolder(appDirectory, filepath, ignoredFileRegex) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
277
|
+
let relativePath = import_node_path2.default.relative(appDirectory, filepath);
|
|
278
|
+
let isIgnored = ignoredFileRegex.some((regex) => regex.test(relativePath));
|
|
279
|
+
if (isIgnored) return null;
|
|
280
|
+
let routeRouteModule = findFile(filepath, "route", routeModuleExts);
|
|
281
|
+
let routeIndexModule = findFile(filepath, "index", routeModuleExts);
|
|
282
|
+
if (routeRouteModule && routeIndexModule) {
|
|
283
|
+
let [segments, raw] = getRouteSegments(
|
|
284
|
+
import_node_path2.default.relative(appDirectory, filepath)
|
|
285
|
+
);
|
|
286
|
+
let routePath = createRoutePath(segments, raw, false);
|
|
287
|
+
console.error(
|
|
288
|
+
getRoutePathConflictErrorMessage(routePath || "/", [
|
|
289
|
+
routeRouteModule,
|
|
290
|
+
routeIndexModule
|
|
291
|
+
])
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
return routeRouteModule || routeIndexModule || null;
|
|
234
295
|
}
|
|
235
296
|
function getRouteSegments(routeId) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
297
|
+
let routeSegments = [];
|
|
298
|
+
let rawRouteSegments = [];
|
|
299
|
+
let index = 0;
|
|
300
|
+
let routeSegment = "";
|
|
301
|
+
let rawRouteSegment = "";
|
|
302
|
+
let state = "NORMAL";
|
|
303
|
+
let pushRouteSegment = (segment, rawSegment) => {
|
|
304
|
+
if (!segment) return;
|
|
305
|
+
let notSupportedInRR = (segment2, char) => {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`Route segment "${segment2}" for "${routeId}" cannot contain "${char}".
|
|
308
|
+
If this is something you need, upvote this proposal for React Router https://github.com/remix-run/react-router/discussions/9822.`
|
|
309
|
+
);
|
|
310
|
+
};
|
|
311
|
+
if (rawSegment.includes("*")) {
|
|
312
|
+
return notSupportedInRR(rawSegment, "*");
|
|
313
|
+
}
|
|
314
|
+
if (rawSegment.includes(":")) {
|
|
315
|
+
return notSupportedInRR(rawSegment, ":");
|
|
316
|
+
}
|
|
317
|
+
if (rawSegment.includes("/")) {
|
|
318
|
+
return notSupportedInRR(segment, "/");
|
|
319
|
+
}
|
|
320
|
+
routeSegments.push(segment);
|
|
321
|
+
rawRouteSegments.push(rawSegment);
|
|
322
|
+
};
|
|
323
|
+
while (index < routeId.length) {
|
|
324
|
+
let char = routeId[index];
|
|
325
|
+
index++;
|
|
326
|
+
switch (state) {
|
|
327
|
+
case "NORMAL": {
|
|
328
|
+
if (isSegmentSeparator(char)) {
|
|
329
|
+
pushRouteSegment(routeSegment, rawRouteSegment);
|
|
330
|
+
routeSegment = "";
|
|
331
|
+
rawRouteSegment = "";
|
|
332
|
+
state = "NORMAL";
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
if (char === escapeStart) {
|
|
336
|
+
state = "ESCAPE";
|
|
337
|
+
rawRouteSegment += char;
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
if (char === optionalStart) {
|
|
341
|
+
state = "OPTIONAL";
|
|
342
|
+
rawRouteSegment += char;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
if (!routeSegment && char === paramPrefixChar) {
|
|
346
|
+
if (index === routeId.length) {
|
|
347
|
+
routeSegment += "*";
|
|
348
|
+
rawRouteSegment += char;
|
|
349
|
+
} else {
|
|
350
|
+
routeSegment += ":";
|
|
351
|
+
rawRouteSegment += char;
|
|
352
|
+
}
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
routeSegment += char;
|
|
356
|
+
rawRouteSegment += char;
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
case "ESCAPE": {
|
|
360
|
+
if (char === escapeEnd) {
|
|
361
|
+
state = "NORMAL";
|
|
362
|
+
rawRouteSegment += char;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
routeSegment += char;
|
|
366
|
+
rawRouteSegment += char;
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case "OPTIONAL": {
|
|
370
|
+
if (char === optionalEnd) {
|
|
371
|
+
routeSegment += "?";
|
|
372
|
+
rawRouteSegment += char;
|
|
373
|
+
state = "NORMAL";
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
if (char === escapeStart) {
|
|
377
|
+
state = "OPTIONAL_ESCAPE";
|
|
378
|
+
rawRouteSegment += char;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
if (!routeSegment && char === paramPrefixChar) {
|
|
382
|
+
if (index === routeId.length) {
|
|
383
|
+
routeSegment += "*";
|
|
384
|
+
rawRouteSegment += char;
|
|
385
|
+
} else {
|
|
386
|
+
routeSegment += ":";
|
|
387
|
+
rawRouteSegment += char;
|
|
388
|
+
}
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
routeSegment += char;
|
|
392
|
+
rawRouteSegment += char;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
case "OPTIONAL_ESCAPE": {
|
|
396
|
+
if (char === escapeEnd) {
|
|
397
|
+
state = "OPTIONAL";
|
|
398
|
+
rawRouteSegment += char;
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
routeSegment += char;
|
|
402
|
+
rawRouteSegment += char;
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
pushRouteSegment(routeSegment, rawRouteSegment);
|
|
408
|
+
return [routeSegments, rawRouteSegments];
|
|
335
409
|
}
|
|
336
410
|
function createRoutePath(routeSegments, rawRouteSegments, isIndex) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
411
|
+
let result = [];
|
|
412
|
+
if (isIndex) {
|
|
413
|
+
routeSegments = routeSegments.slice(0, -1);
|
|
414
|
+
}
|
|
415
|
+
for (let index = 0; index < routeSegments.length; index++) {
|
|
416
|
+
let segment = routeSegments[index];
|
|
417
|
+
let rawSegment = rawRouteSegments[index];
|
|
418
|
+
if (segment.startsWith("_") && rawSegment.startsWith("_")) {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
if (segment.endsWith("_") && rawSegment.endsWith("_")) {
|
|
422
|
+
segment = segment.slice(0, -1);
|
|
423
|
+
}
|
|
424
|
+
result.push(segment);
|
|
425
|
+
}
|
|
426
|
+
return result.length ? result.join("/") : void 0;
|
|
347
427
|
}
|
|
348
428
|
function getRoutePathConflictErrorMessage(pathname, routes) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
429
|
+
let [taken, ...others] = routes;
|
|
430
|
+
if (!pathname.startsWith("/")) {
|
|
431
|
+
pathname = "/" + pathname;
|
|
432
|
+
}
|
|
433
|
+
return `\u26A0\uFE0F Route Path Collision: "${pathname}"
|
|
434
|
+
|
|
435
|
+
The following routes all define the same URL, only the first one will be used
|
|
436
|
+
|
|
437
|
+
\u{1F7E2} ${taken}
|
|
438
|
+
` + others.map((route) => `\u2B55\uFE0F\uFE0F ${route}`).join("\n") + "\n";
|
|
352
439
|
}
|
|
353
440
|
function getRouteIdConflictErrorMessage(routeId, files) {
|
|
354
|
-
|
|
355
|
-
|
|
441
|
+
let [taken, ...others] = files;
|
|
442
|
+
return `\u26A0\uFE0F Route ID Collision: "${routeId}"
|
|
443
|
+
|
|
444
|
+
The following routes all define the same Route ID, only the first one will be used
|
|
445
|
+
|
|
446
|
+
\u{1F7E2} ${taken}
|
|
447
|
+
` + others.map((route) => `\u2B55\uFE0F\uFE0F ${route}`).join("\n") + "\n";
|
|
356
448
|
}
|
|
357
449
|
function isSegmentSeparator(checkChar) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
"/",
|
|
361
|
-
".",
|
|
362
|
-
path.win32.sep
|
|
363
|
-
].includes(checkChar);
|
|
450
|
+
if (!checkChar) return false;
|
|
451
|
+
return ["/", ".", import_node_path2.default.win32.sep].includes(checkChar);
|
|
364
452
|
}
|
|
365
453
|
function findFile(dir, basename, extensions) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
454
|
+
for (let ext of extensions) {
|
|
455
|
+
let name = basename + ext;
|
|
456
|
+
let file = import_node_path2.default.join(dir, name);
|
|
457
|
+
if (import_node_fs.default.existsSync(file)) return file;
|
|
458
|
+
}
|
|
459
|
+
return void 0;
|
|
371
460
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
let appDirectory = getAppDirectory();
|
|
383
|
-
let rootDirectory = path.resolve(appDirectory, userRootDirectory);
|
|
384
|
-
let prefix = normalizeSlashes(path.relative(appDirectory, rootDirectory));
|
|
385
|
-
return routeManifestToRouteConfig(fs.existsSync(rootDirectory) ? flatRoutes$1(appDirectory, ignoredRouteFiles, prefix) : {});
|
|
461
|
+
|
|
462
|
+
// index.ts
|
|
463
|
+
async function flatRoutes2(options = {}) {
|
|
464
|
+
let { ignoredRouteFiles = [], rootDirectory: userRootDirectory = "routes" } = options;
|
|
465
|
+
let appDirectory = (0, import_routes.getAppDirectory)();
|
|
466
|
+
let rootDirectory = import_node_path3.default.resolve(appDirectory, userRootDirectory);
|
|
467
|
+
let relativeRootDirectory = import_node_path3.default.relative(appDirectory, rootDirectory);
|
|
468
|
+
let prefix = normalizeSlashes(relativeRootDirectory);
|
|
469
|
+
let routes = import_node_fs2.default.existsSync(rootDirectory) ? flatRoutes(appDirectory, ignoredRouteFiles, prefix) : {};
|
|
470
|
+
return routeManifestToRouteConfig(routes);
|
|
386
471
|
}
|
|
387
|
-
|
|
388
|
-
|
|
472
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
473
|
+
0 && (module.exports = {
|
|
474
|
+
flatRoutes
|
|
475
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-router/fs-routes",
|
|
3
|
-
"
|
|
4
|
-
"version": "0.0.0-experimental-8cee090e4",
|
|
3
|
+
"version": "0.0.0-experimental-63fd291ad",
|
|
5
4
|
"description": "File system routing conventions for React Router, for use within routes.ts",
|
|
6
5
|
"bugs": {
|
|
7
6
|
"url": "https://github.com/remix-run/react-router/issues"
|
|
@@ -23,7 +22,7 @@
|
|
|
23
22
|
},
|
|
24
23
|
"wireit": {
|
|
25
24
|
"build": {
|
|
26
|
-
"command": "
|
|
25
|
+
"command": "tsup",
|
|
27
26
|
"files": [
|
|
28
27
|
"../../pnpm-workspace.yaml",
|
|
29
28
|
"*.ts",
|
|
@@ -36,18 +35,17 @@
|
|
|
36
35
|
}
|
|
37
36
|
},
|
|
38
37
|
"dependencies": {
|
|
39
|
-
"minimatch": "^
|
|
38
|
+
"minimatch": "^9.0.0"
|
|
40
39
|
},
|
|
41
40
|
"devDependencies": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"@react-router/dev": "0.0.0-experimental-8cee090e4"
|
|
41
|
+
"tsup": "^8.3.0",
|
|
42
|
+
"typescript": "^5.4.5",
|
|
43
|
+
"wireit": "0.14.9",
|
|
44
|
+
"@react-router/dev": "0.0.0-experimental-63fd291ad"
|
|
47
45
|
},
|
|
48
46
|
"peerDependencies": {
|
|
49
47
|
"typescript": "^5.1.0 || ^6.0.0",
|
|
50
|
-
"@react-router/dev": "^0.0.0-experimental-
|
|
48
|
+
"@react-router/dev": "^0.0.0-experimental-63fd291ad"
|
|
51
49
|
},
|
|
52
50
|
"peerDependenciesMeta": {
|
|
53
51
|
"typescript": {
|
|
@@ -55,7 +53,7 @@
|
|
|
55
53
|
}
|
|
56
54
|
},
|
|
57
55
|
"engines": {
|
|
58
|
-
"node": ">=
|
|
56
|
+
"node": ">=20.0.0"
|
|
59
57
|
},
|
|
60
58
|
"files": [
|
|
61
59
|
"dist/",
|