@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.
Files changed (3) hide show
  1. package/dist/index.d.ts +13 -15
  2. package/dist/index.js +440 -353
  3. 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
- * An array of [minimatch](https://www.npmjs.com/package/minimatch) globs that match files to ignore.
14
- * Defaults to `[]`.
15
- */
16
- ignoredRouteFiles?: string[];
17
- /**
18
- * The directory containing file system routes, relative to the app directory.
19
- * Defaults to `"./routes"`.
20
- */
21
- rootDirectory?: string;
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
- //#endregion
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-8cee090e4
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
- import fs from "node:fs";
12
- import path from "node:path";
13
- import { getAppDirectory } from "@react-router/dev/routes";
14
- import { makeRe } from "minimatch";
15
- //#region manifest.ts
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
- let routeConfigById = {};
18
- for (let id in routeManifest) {
19
- let route = routeManifest[id];
20
- routeConfigById[id] = {
21
- id: route.id,
22
- file: route.file,
23
- path: route.path,
24
- index: route.index,
25
- caseSensitive: route.caseSensitive
26
- };
27
- }
28
- let routeConfig = [];
29
- for (let id in routeConfigById) {
30
- let route = routeConfigById[id];
31
- let parentId = routeManifest[route.id].parentId;
32
- if (parentId === rootId) routeConfig.push(route);
33
- else {
34
- let parentRoute = parentId && routeConfigById[parentId];
35
- if (parentRoute) {
36
- parentRoute.children = parentRoute.children || [];
37
- parentRoute.children.push(route);
38
- }
39
- }
40
- }
41
- return routeConfig;
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
- //#endregion
44
- //#region normalizeSlashes.ts
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
- return file.replaceAll(path.win32.sep, "/");
88
+ return file.replaceAll(import_node_path.default.win32.sep, "/");
47
89
  }
48
- //#endregion
49
- //#region flatRoutes.ts
50
- const routeModuleExts = [
51
- ".js",
52
- ".jsx",
53
- ".ts",
54
- ".tsx",
55
- ".md",
56
- ".mdx"
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
- root = { [PrefixLookupTrieEndSymbol]: false };
61
- add(value) {
62
- if (!value) throw new Error("Cannot add empty string to PrefixLookupTrie");
63
- let node = this.root;
64
- for (let char of value) {
65
- if (!node[char]) node[char] = { [PrefixLookupTrieEndSymbol]: false };
66
- node = node[char];
67
- }
68
- node[PrefixLookupTrieEndSymbol] = true;
69
- }
70
- findAndRemove(prefix, filter) {
71
- let node = this.root;
72
- for (let char of prefix) {
73
- if (!node[char]) return [];
74
- node = node[char];
75
- }
76
- return this.#findAndRemoveRecursive([], node, prefix, filter);
77
- }
78
- #findAndRemoveRecursive(values, node, prefix, filter) {
79
- for (let char of Object.keys(node)) this.#findAndRemoveRecursive(values, node[char], prefix + char, filter);
80
- if (node[PrefixLookupTrieEndSymbol] && filter(prefix)) {
81
- node[PrefixLookupTrieEndSymbol] = false;
82
- values.push(prefix);
83
- }
84
- return values;
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$1(appDirectory, ignoredFilePatterns = [], prefix = "routes") {
88
- let ignoredFileRegex = Array.from(new Set(["**/.*", ...ignoredFilePatterns])).map((re) => makeRe(re)).filter((re) => !!re);
89
- let routesDir = path.join(appDirectory, prefix);
90
- if (!findFile(appDirectory, "root", routeModuleExts)) throw new Error(`Could not find a root route module in the app directory: ${appDirectory}`);
91
- if (!fs.existsSync(routesDir)) throw new Error(`Could not find the routes directory: ${routesDir}. Did you forget to create it?`);
92
- let entries = fs.readdirSync(routesDir, {
93
- withFileTypes: true,
94
- encoding: "utf-8"
95
- });
96
- let routes = [];
97
- for (let entry of entries) {
98
- let filepath = normalizeSlashes(path.join(routesDir, entry.name));
99
- let route = null;
100
- if (entry.isDirectory()) route = findRouteModuleForFolder(appDirectory, filepath, ignoredFileRegex);
101
- else if (entry.isFile()) route = findRouteModuleForFile(appDirectory, filepath, ignoredFileRegex);
102
- if (route) routes.push(route);
103
- }
104
- return flatRoutesUniversal(appDirectory, routes, prefix);
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
- let urlConflicts = /* @__PURE__ */ new Map();
108
- let routeManifest = {};
109
- let prefixLookup = new PrefixLookupTrie();
110
- let uniqueRoutes = /* @__PURE__ */ new Map();
111
- let routeIdConflicts = /* @__PURE__ */ new Map();
112
- let normalizedApp = normalizeSlashes(appDirectory);
113
- let appWithPrefix = path.posix.join(normalizedApp, prefix);
114
- let routeIds = /* @__PURE__ */ new Map();
115
- for (let file of routes) {
116
- let normalizedFile = normalizeSlashes(file);
117
- let routeExt = path.extname(normalizedFile);
118
- let routeDir = path.dirname(normalizedFile);
119
- let routeId = routeDir === appWithPrefix ? path.posix.relative(normalizedApp, normalizedFile).slice(0, -routeExt.length) : path.posix.relative(normalizedApp, routeDir);
120
- let conflict = routeIds.get(routeId);
121
- if (conflict) {
122
- let currentConflicts = routeIdConflicts.get(routeId);
123
- if (!currentConflicts) currentConflicts = [path.posix.relative(normalizedApp, conflict)];
124
- currentConflicts.push(path.posix.relative(normalizedApp, normalizedFile));
125
- routeIdConflicts.set(routeId, currentConflicts);
126
- continue;
127
- }
128
- routeIds.set(routeId, normalizedFile);
129
- }
130
- let sortedRouteIds = Array.from(routeIds).sort(([a], [b]) => b.length - a.length);
131
- for (let [routeId, file] of sortedRouteIds) {
132
- let index = routeId.endsWith("_index");
133
- let [segments, raw] = getRouteSegments(routeId.slice(prefix.length + 1));
134
- let pathname = createRoutePath(segments, raw, index);
135
- routeManifest[routeId] = {
136
- file: path.posix.relative(normalizedApp, file),
137
- id: routeId,
138
- path: pathname
139
- };
140
- if (index) routeManifest[routeId].index = true;
141
- let childRouteIds = prefixLookup.findAndRemove(routeId, (value) => {
142
- return [".", "/"].includes(value.slice(routeId.length).charAt(0));
143
- });
144
- prefixLookup.add(routeId);
145
- if (childRouteIds.length > 0) for (let childRouteId of childRouteIds) routeManifest[childRouteId].parentId = routeId;
146
- }
147
- let parentChildrenMap = /* @__PURE__ */ new Map();
148
- for (let [routeId] of sortedRouteIds) {
149
- let config = routeManifest[routeId];
150
- if (!config.parentId) continue;
151
- let existingChildren = parentChildrenMap.get(config.parentId) || [];
152
- existingChildren.push(config);
153
- parentChildrenMap.set(config.parentId, existingChildren);
154
- }
155
- for (let [routeId] of sortedRouteIds) {
156
- let config = routeManifest[routeId];
157
- let originalPathname = config.path || "";
158
- let pathname = config.path;
159
- let parentConfig = config.parentId ? routeManifest[config.parentId] : null;
160
- if (parentConfig?.path && pathname) pathname = pathname.slice(parentConfig.path.length).replace(/^\//, "").replace(/\/$/, "");
161
- if (!config.parentId) config.parentId = "root";
162
- config.path = pathname || void 0;
163
- /**
164
- * We do not try to detect path collisions for pathless layout route
165
- * files because, by definition, they create the potential for route
166
- * collisions _at that level in the tree_.
167
- *
168
- * Consider example where a user may want multiple pathless layout routes
169
- * for different subfolders
170
- *
171
- * routes/
172
- * account.tsx
173
- * account._private.tsx
174
- * account._private.orders.tsx
175
- * account._private.profile.tsx
176
- * account._public.tsx
177
- * account._public.login.tsx
178
- * account._public.perks.tsx
179
- *
180
- * In order to support both a public and private layout for `/account/*`
181
- * URLs, we are creating a mutually exclusive set of URLs beneath 2
182
- * separate pathless layout routes. In this case, the route paths for
183
- * both account._public.tsx and account._private.tsx is the same
184
- * (/account), but we're again not expecting to match at that level.
185
- *
186
- * By only ignoring this check when the final portion of the filename is
187
- * pathless, we will still detect path collisions such as:
188
- *
189
- * routes/parent._pathless.foo.tsx
190
- * routes/parent._pathless2.foo.tsx
191
- *
192
- * and
193
- *
194
- * routes/parent._pathless/index.tsx
195
- * routes/parent._pathless2/index.tsx
196
- */
197
- let lastRouteSegment = config.id.replace(new RegExp(`^${prefix}/`), "").split(".").pop();
198
- if (lastRouteSegment && lastRouteSegment.startsWith("_") && lastRouteSegment !== "_index") continue;
199
- let conflictRouteId = originalPathname + (config.index ? "?index" : "");
200
- let conflict = uniqueRoutes.get(conflictRouteId);
201
- uniqueRoutes.set(conflictRouteId, config);
202
- if (conflict && (originalPathname || config.index)) {
203
- let currentConflicts = urlConflicts.get(originalPathname);
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
- let relativePath = normalizeSlashes(path.relative(appDirectory, filepath));
220
- if (ignoredFileRegex.some((regex) => regex.test(relativePath))) return null;
221
- return filepath;
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
- let relativePath = path.relative(appDirectory, filepath);
225
- if (ignoredFileRegex.some((regex) => regex.test(relativePath))) return null;
226
- let routeRouteModule = findFile(filepath, "route", routeModuleExts);
227
- let routeIndexModule = findFile(filepath, "index", routeModuleExts);
228
- if (routeRouteModule && routeIndexModule) {
229
- let [segments, raw] = getRouteSegments(path.relative(appDirectory, filepath));
230
- let routePath = createRoutePath(segments, raw, false);
231
- console.error(getRoutePathConflictErrorMessage(routePath || "/", [routeRouteModule, routeIndexModule]));
232
- }
233
- return routeRouteModule || routeIndexModule || null;
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
- let routeSegments = [];
237
- let rawRouteSegments = [];
238
- let index = 0;
239
- let routeSegment = "";
240
- let rawRouteSegment = "";
241
- let state = "NORMAL";
242
- let pushRouteSegment = (segment, rawSegment) => {
243
- if (!segment) return;
244
- let notSupportedInRR = (segment, char) => {
245
- throw new Error(`Route segment "${segment}" for "${routeId}" cannot contain "${char}".\nIf this is something you need, upvote this proposal for React Router https://github.com/remix-run/react-router/discussions/9822.`);
246
- };
247
- if (rawSegment.includes("*")) return notSupportedInRR(rawSegment, "*");
248
- if (rawSegment.includes(":")) return notSupportedInRR(rawSegment, ":");
249
- if (rawSegment.includes("/")) return notSupportedInRR(segment, "/");
250
- routeSegments.push(segment);
251
- rawRouteSegments.push(rawSegment);
252
- };
253
- while (index < routeId.length) {
254
- let char = routeId[index];
255
- index++;
256
- switch (state) {
257
- case "NORMAL":
258
- if (isSegmentSeparator(char)) {
259
- pushRouteSegment(routeSegment, rawRouteSegment);
260
- routeSegment = "";
261
- rawRouteSegment = "";
262
- state = "NORMAL";
263
- break;
264
- }
265
- if (char === "[") {
266
- state = "ESCAPE";
267
- rawRouteSegment += char;
268
- break;
269
- }
270
- if (char === "(") {
271
- state = "OPTIONAL";
272
- rawRouteSegment += char;
273
- break;
274
- }
275
- if (!routeSegment && char === "$") {
276
- if (index === routeId.length) {
277
- routeSegment += "*";
278
- rawRouteSegment += char;
279
- } else {
280
- routeSegment += ":";
281
- rawRouteSegment += char;
282
- }
283
- break;
284
- }
285
- routeSegment += char;
286
- rawRouteSegment += char;
287
- break;
288
- case "ESCAPE":
289
- if (char === "]") {
290
- state = "NORMAL";
291
- rawRouteSegment += char;
292
- break;
293
- }
294
- routeSegment += char;
295
- rawRouteSegment += char;
296
- break;
297
- case "OPTIONAL":
298
- if (char === ")") {
299
- routeSegment += "?";
300
- rawRouteSegment += char;
301
- state = "NORMAL";
302
- break;
303
- }
304
- if (char === "[") {
305
- state = "OPTIONAL_ESCAPE";
306
- rawRouteSegment += char;
307
- break;
308
- }
309
- if (!routeSegment && char === "$") {
310
- if (index === routeId.length) {
311
- routeSegment += "*";
312
- rawRouteSegment += char;
313
- } else {
314
- routeSegment += ":";
315
- rawRouteSegment += char;
316
- }
317
- break;
318
- }
319
- routeSegment += char;
320
- rawRouteSegment += char;
321
- break;
322
- case "OPTIONAL_ESCAPE":
323
- if (char === "]") {
324
- state = "OPTIONAL";
325
- rawRouteSegment += char;
326
- break;
327
- }
328
- routeSegment += char;
329
- rawRouteSegment += char;
330
- break;
331
- }
332
- }
333
- pushRouteSegment(routeSegment, rawRouteSegment);
334
- return [routeSegments, rawRouteSegments];
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
- let result = [];
338
- if (isIndex) routeSegments = routeSegments.slice(0, -1);
339
- for (let index = 0; index < routeSegments.length; index++) {
340
- let segment = routeSegments[index];
341
- let rawSegment = rawRouteSegments[index];
342
- if (segment.startsWith("_") && rawSegment.startsWith("_")) continue;
343
- if (segment.endsWith("_") && rawSegment.endsWith("_")) segment = segment.slice(0, -1);
344
- result.push(segment);
345
- }
346
- return result.length ? result.join("/") : void 0;
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
- let [taken, ...others] = routes;
350
- if (!pathname.startsWith("/")) pathname = "/" + pathname;
351
- return `⚠️ Route Path Collision: "${pathname}"\n\nThe following routes all define the same URL, only the first one will be used\n\n🟢 ${taken}\n` + others.map((route) => `⭕️️ ${route}`).join("\n") + "\n";
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
- let [taken, ...others] = files;
355
- return `⚠️ Route ID Collision: "${routeId}"\n\nThe following routes all define the same Route ID, only the first one will be used\n\n🟢 ${taken}\n` + others.map((route) => `⭕️️ ${route}`).join("\n") + "\n";
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
- if (!checkChar) return false;
359
- return [
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
- for (let ext of extensions) {
367
- let name = basename + ext;
368
- let file = path.join(dir, name);
369
- if (fs.existsSync(file)) return file;
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
- //#endregion
373
- //#region index.ts
374
- /**
375
- * Creates route config from the file system using a convention that matches
376
- * [Remix v2's route file
377
- * naming](https://v2.remix.run/docs/file-conventions/routes), for use
378
- * within `routes.ts`.
379
- */
380
- async function flatRoutes(options = {}) {
381
- let { ignoredRouteFiles = [], rootDirectory: userRootDirectory = "routes" } = options;
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
- //#endregion
388
- export { flatRoutes };
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
- "type": "module",
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": "tsdown",
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": "^10.2.5"
38
+ "minimatch": "^9.0.0"
40
39
  },
41
40
  "devDependencies": {
42
- "@types/node": "^22.19.19",
43
- "tsdown": "^0.22.0",
44
- "typescript": "^6.0.3",
45
- "wireit": "0.14.12",
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-8cee090e4"
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": ">=22.12.0"
56
+ "node": ">=20.0.0"
59
57
  },
60
58
  "files": [
61
59
  "dist/",