@benjavicente/router-generator 1.166.24
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/LICENSE +21 -0
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cjs/config.cjs +134 -0
- package/dist/cjs/config.cjs.map +1 -0
- package/dist/cjs/config.d.cts +254 -0
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs +234 -0
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -0
- package/dist/cjs/filesystem/physical/getRouteNodes.d.cts +25 -0
- package/dist/cjs/filesystem/physical/rootPathId.cjs +6 -0
- package/dist/cjs/filesystem/physical/rootPathId.cjs.map +1 -0
- package/dist/cjs/filesystem/physical/rootPathId.d.cts +1 -0
- package/dist/cjs/filesystem/virtual/config.cjs +39 -0
- package/dist/cjs/filesystem/virtual/config.cjs.map +1 -0
- package/dist/cjs/filesystem/virtual/config.d.cts +3 -0
- package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +175 -0
- package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -0
- package/dist/cjs/filesystem/virtual/getRouteNodes.d.cts +9 -0
- package/dist/cjs/filesystem/virtual/loadConfigFile.cjs +12 -0
- package/dist/cjs/filesystem/virtual/loadConfigFile.cjs.map +1 -0
- package/dist/cjs/filesystem/virtual/loadConfigFile.d.cts +1 -0
- package/dist/cjs/generator.cjs +805 -0
- package/dist/cjs/generator.cjs.map +1 -0
- package/dist/cjs/generator.d.cts +116 -0
- package/dist/cjs/index.cjs +33 -0
- package/dist/cjs/index.d.cts +12 -0
- package/dist/cjs/logger.cjs +31 -0
- package/dist/cjs/logger.cjs.map +1 -0
- package/dist/cjs/logger.d.cts +10 -0
- package/dist/cjs/plugin/types.d.cts +18 -0
- package/dist/cjs/template.cjs +203 -0
- package/dist/cjs/template.cjs.map +1 -0
- package/dist/cjs/template.d.cts +34 -0
- package/dist/cjs/transform/transform.cjs +302 -0
- package/dist/cjs/transform/transform.cjs.map +1 -0
- package/dist/cjs/transform/transform.d.cts +4 -0
- package/dist/cjs/transform/types.d.cts +31 -0
- package/dist/cjs/transform/utils.cjs +34 -0
- package/dist/cjs/transform/utils.cjs.map +1 -0
- package/dist/cjs/transform/utils.d.cts +2 -0
- package/dist/cjs/types.d.cts +57 -0
- package/dist/cjs/utils.cjs +653 -0
- package/dist/cjs/utils.cjs.map +1 -0
- package/dist/cjs/utils.d.cts +212 -0
- package/dist/cjs/validate-route-params.cjs +73 -0
- package/dist/cjs/validate-route-params.cjs.map +1 -0
- package/dist/cjs/validate-route-params.d.cts +9 -0
- package/dist/esm/config.d.ts +254 -0
- package/dist/esm/config.js +129 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/filesystem/physical/getRouteNodes.d.ts +25 -0
- package/dist/esm/filesystem/physical/getRouteNodes.js +230 -0
- package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -0
- package/dist/esm/filesystem/physical/rootPathId.d.ts +1 -0
- package/dist/esm/filesystem/physical/rootPathId.js +6 -0
- package/dist/esm/filesystem/physical/rootPathId.js.map +1 -0
- package/dist/esm/filesystem/virtual/config.d.ts +3 -0
- package/dist/esm/filesystem/virtual/config.js +38 -0
- package/dist/esm/filesystem/virtual/config.js.map +1 -0
- package/dist/esm/filesystem/virtual/getRouteNodes.d.ts +9 -0
- package/dist/esm/filesystem/virtual/getRouteNodes.js +173 -0
- package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -0
- package/dist/esm/filesystem/virtual/loadConfigFile.d.ts +1 -0
- package/dist/esm/filesystem/virtual/loadConfigFile.js +11 -0
- package/dist/esm/filesystem/virtual/loadConfigFile.js.map +1 -0
- package/dist/esm/generator.d.ts +116 -0
- package/dist/esm/generator.js +801 -0
- package/dist/esm/generator.js.map +1 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/logger.d.ts +10 -0
- package/dist/esm/logger.js +31 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/plugin/types.d.ts +18 -0
- package/dist/esm/template.d.ts +34 -0
- package/dist/esm/template.js +202 -0
- package/dist/esm/template.js.map +1 -0
- package/dist/esm/transform/transform.d.ts +4 -0
- package/dist/esm/transform/transform.js +301 -0
- package/dist/esm/transform/transform.js.map +1 -0
- package/dist/esm/transform/types.d.ts +31 -0
- package/dist/esm/transform/utils.d.ts +2 -0
- package/dist/esm/transform/utils.js +34 -0
- package/dist/esm/transform/utils.js.map +1 -0
- package/dist/esm/types.d.ts +57 -0
- package/dist/esm/utils.d.ts +212 -0
- package/dist/esm/utils.js +609 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/validate-route-params.d.ts +9 -0
- package/dist/esm/validate-route-params.js +73 -0
- package/dist/esm/validate-route-params.js.map +1 -0
- package/package.json +82 -0
- package/src/config.ts +247 -0
- package/src/filesystem/physical/getRouteNodes.ts +541 -0
- package/src/filesystem/physical/rootPathId.ts +1 -0
- package/src/filesystem/virtual/config.ts +45 -0
- package/src/filesystem/virtual/getRouteNodes.ts +307 -0
- package/src/filesystem/virtual/loadConfigFile.ts +8 -0
- package/src/generator.ts +1686 -0
- package/src/index.ts +54 -0
- package/src/logger.ts +43 -0
- package/src/plugin/types.ts +18 -0
- package/src/template.ts +313 -0
- package/src/transform/transform.ts +534 -0
- package/src/transform/types.ts +39 -0
- package/src/transform/utils.ts +42 -0
- package/src/types.ts +74 -0
- package/src/utils.ts +1067 -0
- package/src/validate-route-params.ts +118 -0
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_logger = require("./logger.cjs");
|
|
3
|
+
const require_rootPathId = require("./filesystem/physical/rootPathId.cjs");
|
|
4
|
+
const require_utils = require("./utils.cjs");
|
|
5
|
+
const require_getRouteNodes = require("./filesystem/virtual/getRouteNodes.cjs");
|
|
6
|
+
const require_getRouteNodes$1 = require("./filesystem/physical/getRouteNodes.cjs");
|
|
7
|
+
const require_template = require("./template.cjs");
|
|
8
|
+
const require_transform = require("./transform/transform.cjs");
|
|
9
|
+
const require_validate_route_params = require("./validate-route-params.cjs");
|
|
10
|
+
let node_path = require("node:path");
|
|
11
|
+
node_path = require_runtime.__toESM(node_path);
|
|
12
|
+
let node_fs = require("node:fs");
|
|
13
|
+
let node_fs_promises = require("node:fs/promises");
|
|
14
|
+
node_fs_promises = require_runtime.__toESM(node_fs_promises);
|
|
15
|
+
let node_crypto = require("node:crypto");
|
|
16
|
+
node_crypto = require_runtime.__toESM(node_crypto);
|
|
17
|
+
let _benjavicente_router_core = require("@benjavicente/router-core");
|
|
18
|
+
//#region src/generator.ts
|
|
19
|
+
var DefaultFileSystem = {
|
|
20
|
+
stat: async (filePath) => {
|
|
21
|
+
const res = await node_fs_promises.stat(filePath, { bigint: true });
|
|
22
|
+
return {
|
|
23
|
+
mtimeMs: res.mtimeMs,
|
|
24
|
+
mode: Number(res.mode),
|
|
25
|
+
uid: Number(res.uid),
|
|
26
|
+
gid: Number(res.gid)
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
rename: (oldPath, newPath) => node_fs_promises.rename(oldPath, newPath),
|
|
30
|
+
writeFile: (filePath, content) => node_fs_promises.writeFile(filePath, content),
|
|
31
|
+
readFile: async (filePath) => {
|
|
32
|
+
try {
|
|
33
|
+
const fileHandle = await node_fs_promises.open(filePath, "r");
|
|
34
|
+
const stat = await fileHandle.stat({ bigint: true });
|
|
35
|
+
const fileContent = (await fileHandle.readFile()).toString();
|
|
36
|
+
await fileHandle.close();
|
|
37
|
+
return {
|
|
38
|
+
stat,
|
|
39
|
+
fileContent
|
|
40
|
+
};
|
|
41
|
+
} catch (e) {
|
|
42
|
+
if ("code" in e) {
|
|
43
|
+
if (e.code === "ENOENT") return "file-not-existing";
|
|
44
|
+
}
|
|
45
|
+
throw e;
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
chmod: (filePath, mode) => node_fs_promises.chmod(filePath, mode),
|
|
49
|
+
chown: (filePath, uid, gid) => node_fs_promises.chown(filePath, uid, gid)
|
|
50
|
+
};
|
|
51
|
+
function rerun(opts) {
|
|
52
|
+
const { event, ...rest } = opts;
|
|
53
|
+
return {
|
|
54
|
+
rerun: true,
|
|
55
|
+
event: event ?? { type: "rerun" },
|
|
56
|
+
...rest
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function isRerun(result) {
|
|
60
|
+
return typeof result === "object" && result !== null && "rerun" in result && result.rerun === true;
|
|
61
|
+
}
|
|
62
|
+
var Generator = class Generator {
|
|
63
|
+
static {
|
|
64
|
+
this.routeGroupPatternRegex = /\(.+\)/;
|
|
65
|
+
}
|
|
66
|
+
static {
|
|
67
|
+
this.componentPieceRegex = /[./](component|errorComponent|notFoundComponent|pendingComponent|loader|lazy)[.]/;
|
|
68
|
+
}
|
|
69
|
+
constructor(opts) {
|
|
70
|
+
this.routeNodeCache = /* @__PURE__ */ new Map();
|
|
71
|
+
this.routeNodeShadowCache = /* @__PURE__ */ new Map();
|
|
72
|
+
this.fileEventQueue = [];
|
|
73
|
+
this.plugins = [];
|
|
74
|
+
this.physicalDirectories = [];
|
|
75
|
+
this.config = opts.config;
|
|
76
|
+
this.logger = require_logger.logging({ disabled: this.config.disableLogging });
|
|
77
|
+
this.root = opts.root;
|
|
78
|
+
this.fs = opts.fs || DefaultFileSystem;
|
|
79
|
+
this.generatedRouteTreePath = this.getGeneratedRouteTreePath();
|
|
80
|
+
this.targetTemplate = require_template.getTargetTemplate(this.config);
|
|
81
|
+
this.routesDirectoryPath = this.getRoutesDirectoryPath();
|
|
82
|
+
this.plugins.push(...opts.config.plugins || []);
|
|
83
|
+
this.indexTokenFilenameRegex = require_utils.createTokenRegex(this.config.indexToken, { type: "filename" });
|
|
84
|
+
this.routeTokenFilenameRegex = require_utils.createTokenRegex(this.config.routeToken, { type: "filename" });
|
|
85
|
+
this.indexTokenSegmentRegex = require_utils.createTokenRegex(this.config.indexToken, { type: "segment" });
|
|
86
|
+
this.routeTokenSegmentRegex = require_utils.createTokenRegex(this.config.routeToken, { type: "segment" });
|
|
87
|
+
for (const plugin of this.plugins) plugin.init?.({ generator: this });
|
|
88
|
+
}
|
|
89
|
+
getGeneratedRouteTreePath() {
|
|
90
|
+
const generatedRouteTreePath = node_path.default.isAbsolute(this.config.generatedRouteTree) ? this.config.generatedRouteTree : node_path.default.resolve(this.root, this.config.generatedRouteTree);
|
|
91
|
+
const generatedRouteTreeDir = node_path.default.dirname(generatedRouteTreePath);
|
|
92
|
+
if (!(0, node_fs.existsSync)(generatedRouteTreeDir)) (0, node_fs.mkdirSync)(generatedRouteTreeDir, { recursive: true });
|
|
93
|
+
return generatedRouteTreePath;
|
|
94
|
+
}
|
|
95
|
+
getRoutesDirectoryPath() {
|
|
96
|
+
return node_path.default.isAbsolute(this.config.routesDirectory) ? this.config.routesDirectory : node_path.default.resolve(this.root, this.config.routesDirectory);
|
|
97
|
+
}
|
|
98
|
+
getRoutesByFileMap() {
|
|
99
|
+
return new Map([...this.routeNodeCache.entries()].map(([filePath, cacheEntry]) => [filePath, { routePath: cacheEntry.routeId }]));
|
|
100
|
+
}
|
|
101
|
+
async run(event) {
|
|
102
|
+
if (event && event.type !== "rerun" && !this.isFileRelevantForRouteTreeGeneration(event.path)) return;
|
|
103
|
+
this.fileEventQueue.push(event ?? { type: "rerun" });
|
|
104
|
+
if (this.runPromise) return this.runPromise;
|
|
105
|
+
this.runPromise = (async () => {
|
|
106
|
+
do {
|
|
107
|
+
const tempQueue = this.fileEventQueue;
|
|
108
|
+
this.fileEventQueue = [];
|
|
109
|
+
if ((await Promise.all(tempQueue.map(async (e) => {
|
|
110
|
+
if (e.type === "update") {
|
|
111
|
+
let cacheEntry;
|
|
112
|
+
if (e.path === this.generatedRouteTreePath) cacheEntry = this.routeTreeFileCache;
|
|
113
|
+
else cacheEntry = this.routeNodeCache.get(e.path);
|
|
114
|
+
if ((await this.didFileChangeComparedToCache({ path: e.path }, cacheEntry)).result === false) return null;
|
|
115
|
+
}
|
|
116
|
+
return e;
|
|
117
|
+
}))).filter((e) => e !== null).length === 0) break;
|
|
118
|
+
try {
|
|
119
|
+
await this.generatorInternal();
|
|
120
|
+
} catch (err) {
|
|
121
|
+
const errArray = !Array.isArray(err) ? [err] : err;
|
|
122
|
+
const recoverableErrors = errArray.filter((e) => isRerun(e));
|
|
123
|
+
if (recoverableErrors.length === errArray.length) {
|
|
124
|
+
this.fileEventQueue.push(...recoverableErrors.map((e) => e.event));
|
|
125
|
+
recoverableErrors.forEach((e) => {
|
|
126
|
+
if (e.msg) this.logger.info(e.msg);
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
const unrecoverableErrors = errArray.filter((e) => !isRerun(e));
|
|
130
|
+
this.runPromise = void 0;
|
|
131
|
+
throw new Error(unrecoverableErrors.map((e) => e.message).join());
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} while (this.fileEventQueue.length);
|
|
135
|
+
this.runPromise = void 0;
|
|
136
|
+
})();
|
|
137
|
+
return this.runPromise;
|
|
138
|
+
}
|
|
139
|
+
async generatorInternal() {
|
|
140
|
+
let writeRouteTreeFile = false;
|
|
141
|
+
let getRouteNodesResult;
|
|
142
|
+
if (this.config.virtualRouteConfig) getRouteNodesResult = await require_getRouteNodes.getRouteNodes(this.config, this.root, {
|
|
143
|
+
indexTokenSegmentRegex: this.indexTokenSegmentRegex,
|
|
144
|
+
routeTokenSegmentRegex: this.routeTokenSegmentRegex
|
|
145
|
+
});
|
|
146
|
+
else getRouteNodesResult = await require_getRouteNodes$1.getRouteNodes(this.config, this.root, {
|
|
147
|
+
indexTokenSegmentRegex: this.indexTokenSegmentRegex,
|
|
148
|
+
routeTokenSegmentRegex: this.routeTokenSegmentRegex
|
|
149
|
+
});
|
|
150
|
+
const { rootRouteNode, routeNodes: beforeRouteNodes, physicalDirectories } = getRouteNodesResult;
|
|
151
|
+
if (rootRouteNode === void 0) {
|
|
152
|
+
let errorMessage = `rootRouteNode must not be undefined. Make sure you've added your root route into the route-tree.`;
|
|
153
|
+
if (!this.config.virtualRouteConfig) {
|
|
154
|
+
const ext = this.config.target === "angular" ? "ts" : this.config.disableTypes ? "js" : "tsx";
|
|
155
|
+
errorMessage += `\nMake sure that you add a "${require_rootPathId.rootPathId}.${ext}" file to your routes directory.\nAdd the file in: "${this.config.routesDirectory}/${require_rootPathId.rootPathId}.${ext}"`;
|
|
156
|
+
}
|
|
157
|
+
throw new Error(errorMessage);
|
|
158
|
+
}
|
|
159
|
+
this.physicalDirectories = physicalDirectories;
|
|
160
|
+
await this.handleRootNode(rootRouteNode);
|
|
161
|
+
const preRouteNodes = require_utils.multiSortBy(beforeRouteNodes, [
|
|
162
|
+
(d) => d.routePath === "/" ? -1 : 1,
|
|
163
|
+
(d) => d.routePath?.split("/").length,
|
|
164
|
+
(d) => d.filePath.match(this.indexTokenFilenameRegex) ? 1 : -1,
|
|
165
|
+
(d) => d.filePath.match(Generator.componentPieceRegex) ? 1 : -1,
|
|
166
|
+
(d) => d.filePath.match(this.routeTokenFilenameRegex) ? -1 : 1,
|
|
167
|
+
(d) => d.routePath?.endsWith("/") ? -1 : 1,
|
|
168
|
+
(d) => d.routePath
|
|
169
|
+
]).filter((d) => {
|
|
170
|
+
if (d.routePath === `/__root`) return [
|
|
171
|
+
"component",
|
|
172
|
+
"errorComponent",
|
|
173
|
+
"notFoundComponent",
|
|
174
|
+
"pendingComponent",
|
|
175
|
+
"loader",
|
|
176
|
+
"lazy"
|
|
177
|
+
].includes(d._fsRouteType);
|
|
178
|
+
return true;
|
|
179
|
+
});
|
|
180
|
+
const routeFileAllResult = await Promise.allSettled(preRouteNodes.filter((n) => !n.isVirtualParentRoute && !n.isVirtual).map((n) => this.processRouteNodeFile(n)));
|
|
181
|
+
const rejections = routeFileAllResult.filter((result) => result.status === "rejected");
|
|
182
|
+
if (rejections.length > 0) throw rejections.map((e) => e.reason);
|
|
183
|
+
const routeFileResult = routeFileAllResult.flatMap((result) => {
|
|
184
|
+
if (result.status === "fulfilled" && result.value !== null) {
|
|
185
|
+
if (result.value.shouldWriteTree) writeRouteTreeFile = true;
|
|
186
|
+
return result.value.node;
|
|
187
|
+
}
|
|
188
|
+
return [];
|
|
189
|
+
});
|
|
190
|
+
routeFileResult.forEach((r) => r.children = void 0);
|
|
191
|
+
const acc = {
|
|
192
|
+
routeTree: [],
|
|
193
|
+
routeNodes: [],
|
|
194
|
+
routePiecesByPath: {},
|
|
195
|
+
routeNodesByPath: /* @__PURE__ */ new Map()
|
|
196
|
+
};
|
|
197
|
+
const prefixMap = new require_utils.RoutePrefixMap(routeFileResult);
|
|
198
|
+
for (const node of routeFileResult) Generator.handleNode(node, acc, prefixMap, this.config);
|
|
199
|
+
this.crawlingResult = {
|
|
200
|
+
rootRouteNode,
|
|
201
|
+
routeFileResult,
|
|
202
|
+
acc
|
|
203
|
+
};
|
|
204
|
+
if (!this.routeTreeFileCache) {
|
|
205
|
+
const routeTreeFile = await this.fs.readFile(this.generatedRouteTreePath);
|
|
206
|
+
if (routeTreeFile !== "file-not-existing") this.routeTreeFileCache = {
|
|
207
|
+
fileContent: routeTreeFile.fileContent,
|
|
208
|
+
mtimeMs: routeTreeFile.stat.mtimeMs
|
|
209
|
+
};
|
|
210
|
+
writeRouteTreeFile = true;
|
|
211
|
+
} else {
|
|
212
|
+
const routeTreeFileChange = await this.didFileChangeComparedToCache({ path: this.generatedRouteTreePath }, this.routeTreeFileCache);
|
|
213
|
+
if (routeTreeFileChange.result !== false) {
|
|
214
|
+
writeRouteTreeFile = "force";
|
|
215
|
+
if (routeTreeFileChange.result === true) {
|
|
216
|
+
const routeTreeFile = await this.fs.readFile(this.generatedRouteTreePath);
|
|
217
|
+
if (routeTreeFile !== "file-not-existing") this.routeTreeFileCache = {
|
|
218
|
+
fileContent: routeTreeFile.fileContent,
|
|
219
|
+
mtimeMs: routeTreeFile.stat.mtimeMs
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (!writeRouteTreeFile) {
|
|
225
|
+
if (this.routeNodeCache.size !== this.routeNodeShadowCache.size) writeRouteTreeFile = true;
|
|
226
|
+
else for (const fullPath of this.routeNodeCache.keys()) if (!this.routeNodeShadowCache.has(fullPath)) {
|
|
227
|
+
writeRouteTreeFile = true;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (!writeRouteTreeFile) {
|
|
232
|
+
this.swapCaches();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const buildResult = this.buildRouteTree({
|
|
236
|
+
rootRouteNode,
|
|
237
|
+
acc,
|
|
238
|
+
routeFileResult
|
|
239
|
+
});
|
|
240
|
+
let routeTreeContent = buildResult.routeTreeContent;
|
|
241
|
+
routeTreeContent = this.config.enableRouteTreeFormatting ? await require_utils.format(routeTreeContent, this.config) : routeTreeContent;
|
|
242
|
+
let newMtimeMs;
|
|
243
|
+
if (this.routeTreeFileCache) if (this.routeTreeFileCache.fileContent === routeTreeContent) {} else newMtimeMs = (await this.safeFileWrite({
|
|
244
|
+
filePath: this.generatedRouteTreePath,
|
|
245
|
+
newContent: routeTreeContent,
|
|
246
|
+
strategy: {
|
|
247
|
+
type: "mtime",
|
|
248
|
+
expectedMtimeMs: this.routeTreeFileCache.mtimeMs
|
|
249
|
+
}
|
|
250
|
+
})).mtimeMs;
|
|
251
|
+
else newMtimeMs = (await this.safeFileWrite({
|
|
252
|
+
filePath: this.generatedRouteTreePath,
|
|
253
|
+
newContent: routeTreeContent,
|
|
254
|
+
strategy: { type: "new-file" }
|
|
255
|
+
})).mtimeMs;
|
|
256
|
+
if (newMtimeMs !== void 0) this.routeTreeFileCache = {
|
|
257
|
+
fileContent: routeTreeContent,
|
|
258
|
+
mtimeMs: newMtimeMs
|
|
259
|
+
};
|
|
260
|
+
this.plugins.map((plugin) => {
|
|
261
|
+
return plugin.onRouteTreeChanged?.({
|
|
262
|
+
routeTree: buildResult.routeTree,
|
|
263
|
+
routeNodes: buildResult.routeNodes,
|
|
264
|
+
acc,
|
|
265
|
+
rootRouteNode
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
this.swapCaches();
|
|
269
|
+
}
|
|
270
|
+
swapCaches() {
|
|
271
|
+
this.routeNodeCache = this.routeNodeShadowCache;
|
|
272
|
+
this.routeNodeShadowCache = /* @__PURE__ */ new Map();
|
|
273
|
+
}
|
|
274
|
+
buildRouteTree(opts) {
|
|
275
|
+
const config = {
|
|
276
|
+
...this.config,
|
|
277
|
+
...opts.config || {}
|
|
278
|
+
};
|
|
279
|
+
const { rootRouteNode, acc } = opts;
|
|
280
|
+
const indexTokenSegmentRegex = config.indexToken === this.config.indexToken ? this.indexTokenSegmentRegex : require_utils.createTokenRegex(config.indexToken, { type: "segment" });
|
|
281
|
+
const sortedRouteNodes = require_utils.multiSortBy(acc.routeNodes, [
|
|
282
|
+
(d) => d.routePath?.includes(`/__root`) ? -1 : 1,
|
|
283
|
+
(d) => d.routePath?.split("/").length,
|
|
284
|
+
(d) => {
|
|
285
|
+
const segments = d.routePath?.split("/").filter(Boolean) ?? [];
|
|
286
|
+
const last = segments[segments.length - 1] ?? "";
|
|
287
|
+
return indexTokenSegmentRegex.test(last) ? -1 : 1;
|
|
288
|
+
},
|
|
289
|
+
(d) => d
|
|
290
|
+
]);
|
|
291
|
+
const routeImports = [];
|
|
292
|
+
const virtualRouteNodes = [];
|
|
293
|
+
for (const node of sortedRouteNodes) if (node.isVirtual) virtualRouteNodes.push(`const ${node.variableName}RouteImport = createFileRoute('${node.routePath}')()`);
|
|
294
|
+
else routeImports.push(require_utils.getImportForRouteNode(node, config, this.generatedRouteTreePath, this.root));
|
|
295
|
+
const imports = [];
|
|
296
|
+
if (virtualRouteNodes.length > 0) imports.push({
|
|
297
|
+
specifiers: [{ imported: "createFileRoute" }],
|
|
298
|
+
source: this.targetTemplate.fullPkg
|
|
299
|
+
});
|
|
300
|
+
let hasComponentPieces = false;
|
|
301
|
+
let hasLoaderPieces = false;
|
|
302
|
+
for (const node of sortedRouteNodes) {
|
|
303
|
+
const pieces = acc.routePiecesByPath[node.routePath];
|
|
304
|
+
if (pieces) {
|
|
305
|
+
if (pieces.component || pieces.errorComponent || pieces.notFoundComponent || pieces.pendingComponent) hasComponentPieces = true;
|
|
306
|
+
if (pieces.loader) hasLoaderPieces = true;
|
|
307
|
+
if (hasComponentPieces && hasLoaderPieces) break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (hasComponentPieces || hasLoaderPieces) {
|
|
311
|
+
if (hasComponentPieces && !this.targetTemplate.supportsLazyRouteComponent) throw new Error(`The '${this.config.target}' target does not support code-splitting route component exports with lazyRouteComponent. Remove route component pieces such as '.component', '.pendingComponent', '.errorComponent', and '.notFoundComponent', or switch to route-level lazy loading.`);
|
|
312
|
+
const runtimeImport = {
|
|
313
|
+
specifiers: [],
|
|
314
|
+
source: this.targetTemplate.fullPkg
|
|
315
|
+
};
|
|
316
|
+
if (hasComponentPieces) runtimeImport.specifiers.push({ imported: "lazyRouteComponent" });
|
|
317
|
+
if (hasLoaderPieces) runtimeImport.specifiers.push({ imported: "lazyFn" });
|
|
318
|
+
imports.push(runtimeImport);
|
|
319
|
+
}
|
|
320
|
+
if (config.verboseFileRoutes === false) {
|
|
321
|
+
const typeImport = {
|
|
322
|
+
specifiers: [],
|
|
323
|
+
source: this.targetTemplate.fullPkg,
|
|
324
|
+
importKind: "type"
|
|
325
|
+
};
|
|
326
|
+
let needsCreateFileRoute = false;
|
|
327
|
+
let needsCreateLazyFileRoute = false;
|
|
328
|
+
for (const node of sortedRouteNodes) {
|
|
329
|
+
if (require_utils.isRouteNodeValidForAugmentation(node)) {
|
|
330
|
+
if (node._fsRouteType !== "lazy") needsCreateFileRoute = true;
|
|
331
|
+
if (acc.routePiecesByPath[node.routePath]?.lazy) needsCreateLazyFileRoute = true;
|
|
332
|
+
}
|
|
333
|
+
if (needsCreateFileRoute && needsCreateLazyFileRoute) break;
|
|
334
|
+
}
|
|
335
|
+
if (needsCreateFileRoute) typeImport.specifiers.push({ imported: "CreateFileRoute" });
|
|
336
|
+
if (needsCreateLazyFileRoute) typeImport.specifiers.push({ imported: "CreateLazyFileRoute" });
|
|
337
|
+
if (typeImport.specifiers.length > 0) {
|
|
338
|
+
typeImport.specifiers.push({ imported: "FileRoutesByPath" });
|
|
339
|
+
imports.push(typeImport);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const routeTreeConfig = require_utils.buildRouteTreeConfig(acc.routeTree, config.disableTypes);
|
|
343
|
+
const createUpdateRoutes = sortedRouteNodes.map((node) => {
|
|
344
|
+
const pieces = acc.routePiecesByPath[node.routePath];
|
|
345
|
+
const loaderNode = pieces?.loader;
|
|
346
|
+
const componentNode = pieces?.component;
|
|
347
|
+
const errorComponentNode = pieces?.errorComponent;
|
|
348
|
+
const notFoundComponentNode = pieces?.notFoundComponent;
|
|
349
|
+
const pendingComponentNode = pieces?.pendingComponent;
|
|
350
|
+
const lazyComponentNode = pieces?.lazy;
|
|
351
|
+
return [[
|
|
352
|
+
`const ${node.variableName}Route = ${node.variableName}RouteImport.update({
|
|
353
|
+
${[
|
|
354
|
+
`id: '${node.path}'`,
|
|
355
|
+
!node.isNonPath || node._fsRouteType === "pathless_layout" && node.cleanedPath ? `path: '${node.cleanedPath}'` : void 0,
|
|
356
|
+
`getParentRoute: () => ${require_utils.findParent(node)}`
|
|
357
|
+
].filter(Boolean).join(",")}
|
|
358
|
+
}${config.disableTypes ? "" : "as any"})`,
|
|
359
|
+
loaderNode ? `.updateLoader({ loader: lazyFn(() => import('./${require_utils.replaceBackslash(require_utils.removeExt(node_path.default.relative(node_path.default.dirname(config.generatedRouteTree), node_path.default.resolve(config.routesDirectory, loaderNode.filePath)), config.addExtensions))}'), 'loader') })` : "",
|
|
360
|
+
componentNode || errorComponentNode || notFoundComponentNode || pendingComponentNode ? `.update({
|
|
361
|
+
${[
|
|
362
|
+
["component", componentNode],
|
|
363
|
+
["errorComponent", errorComponentNode],
|
|
364
|
+
["notFoundComponent", notFoundComponentNode],
|
|
365
|
+
["pendingComponent", pendingComponentNode]
|
|
366
|
+
].filter((d) => d[1]).map((d) => {
|
|
367
|
+
const isVueFile = d[1].filePath.endsWith(".vue");
|
|
368
|
+
const exportName = isVueFile ? "default" : d[0];
|
|
369
|
+
const importPath = require_utils.replaceBackslash(isVueFile ? node_path.default.relative(node_path.default.dirname(config.generatedRouteTree), node_path.default.resolve(config.routesDirectory, d[1].filePath)) : require_utils.removeExt(node_path.default.relative(node_path.default.dirname(config.generatedRouteTree), node_path.default.resolve(config.routesDirectory, d[1].filePath)), config.addExtensions));
|
|
370
|
+
return `${d[0]}: lazyRouteComponent(() => import('./${importPath}'), '${exportName}')`;
|
|
371
|
+
}).join("\n,")}
|
|
372
|
+
})` : "",
|
|
373
|
+
lazyComponentNode ? (() => {
|
|
374
|
+
const isVueFile = lazyComponentNode.filePath.endsWith(".vue");
|
|
375
|
+
const exportAccessor = isVueFile ? "d.default" : "d.Route";
|
|
376
|
+
return `.lazy(() => import('./${require_utils.replaceBackslash(isVueFile ? node_path.default.relative(node_path.default.dirname(config.generatedRouteTree), node_path.default.resolve(config.routesDirectory, lazyComponentNode.filePath)) : require_utils.removeExt(node_path.default.relative(node_path.default.dirname(config.generatedRouteTree), node_path.default.resolve(config.routesDirectory, lazyComponentNode.filePath)), config.addExtensions))}').then((d) => ${exportAccessor}))`;
|
|
377
|
+
})() : ""
|
|
378
|
+
].join("")].join("\n\n");
|
|
379
|
+
});
|
|
380
|
+
const rootRoutePath = `/${require_rootPathId.rootPathId}`;
|
|
381
|
+
const rootPieces = acc.routePiecesByPath[rootRoutePath];
|
|
382
|
+
const rootComponentNode = rootPieces?.component;
|
|
383
|
+
const rootErrorComponentNode = rootPieces?.errorComponent;
|
|
384
|
+
const rootNotFoundComponentNode = rootPieces?.notFoundComponent;
|
|
385
|
+
const rootPendingComponentNode = rootPieces?.pendingComponent;
|
|
386
|
+
let rootRouteUpdate = "";
|
|
387
|
+
if (rootComponentNode || rootErrorComponentNode || rootNotFoundComponentNode || rootPendingComponentNode) rootRouteUpdate = `const rootRouteWithChildren = rootRouteImport${rootComponentNode || rootErrorComponentNode || rootNotFoundComponentNode || rootPendingComponentNode ? `.update({
|
|
388
|
+
${[
|
|
389
|
+
["component", rootComponentNode],
|
|
390
|
+
["errorComponent", rootErrorComponentNode],
|
|
391
|
+
["notFoundComponent", rootNotFoundComponentNode],
|
|
392
|
+
["pendingComponent", rootPendingComponentNode]
|
|
393
|
+
].filter((d) => d[1]).map((d) => {
|
|
394
|
+
const isVueFile = d[1].filePath.endsWith(".vue");
|
|
395
|
+
const exportName = isVueFile ? "default" : d[0];
|
|
396
|
+
const importPath = require_utils.replaceBackslash(isVueFile ? node_path.default.relative(node_path.default.dirname(config.generatedRouteTree), node_path.default.resolve(config.routesDirectory, d[1].filePath)) : require_utils.removeExt(node_path.default.relative(node_path.default.dirname(config.generatedRouteTree), node_path.default.resolve(config.routesDirectory, d[1].filePath)), config.addExtensions));
|
|
397
|
+
return `${d[0]}: lazyRouteComponent(() => import('./${importPath}'), '${exportName}')`;
|
|
398
|
+
}).join("\n,")}
|
|
399
|
+
})` : ""}._addFileChildren(rootRouteChildren)${config.disableTypes ? "" : `._addFileTypes<FileRouteTypes>()`}`;
|
|
400
|
+
let fileRoutesByPathInterface = "";
|
|
401
|
+
let fileRoutesByFullPath = "";
|
|
402
|
+
if (!config.disableTypes) {
|
|
403
|
+
const routeNodesByFullPath = require_utils.createRouteNodesByFullPath(acc.routeNodes);
|
|
404
|
+
const routeNodesByTo = require_utils.createRouteNodesByTo(acc.routeNodes);
|
|
405
|
+
const routeNodesById = require_utils.createRouteNodesById(acc.routeNodes);
|
|
406
|
+
fileRoutesByFullPath = [
|
|
407
|
+
`export interface FileRoutesByFullPath {
|
|
408
|
+
${[...routeNodesByFullPath.entries()].filter(([fullPath]) => fullPath).map(([fullPath, routeNode]) => {
|
|
409
|
+
return `'${fullPath}': typeof ${require_utils.getResolvedRouteNodeVariableName(routeNode)}`;
|
|
410
|
+
})}
|
|
411
|
+
}`,
|
|
412
|
+
`export interface FileRoutesByTo {
|
|
413
|
+
${[...routeNodesByTo.entries()].filter(([to]) => to).map(([to, routeNode]) => {
|
|
414
|
+
return `'${to}': typeof ${require_utils.getResolvedRouteNodeVariableName(routeNode)}`;
|
|
415
|
+
})}
|
|
416
|
+
}`,
|
|
417
|
+
`export interface FileRoutesById {
|
|
418
|
+
'${_benjavicente_router_core.rootRouteId}': typeof rootRouteImport,
|
|
419
|
+
${[...routeNodesById.entries()].map(([id, routeNode]) => {
|
|
420
|
+
return `'${id}': typeof ${require_utils.getResolvedRouteNodeVariableName(routeNode)}`;
|
|
421
|
+
})}
|
|
422
|
+
}`,
|
|
423
|
+
`export interface FileRouteTypes {
|
|
424
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
425
|
+
fullPaths: ${acc.routeNodes.length > 0 ? [...routeNodesByFullPath.keys()].filter((fullPath) => fullPath).map((fullPath) => `'${fullPath}'`).join("|") : "never"}
|
|
426
|
+
fileRoutesByTo: FileRoutesByTo
|
|
427
|
+
to: ${acc.routeNodes.length > 0 ? [...routeNodesByTo.keys()].filter((to) => to).map((to) => `'${to}'`).join("|") : "never"}
|
|
428
|
+
id: ${[`'${_benjavicente_router_core.rootRouteId}'`, ...[...routeNodesById.keys()].map((id) => `'${id}'`)].join("|")}
|
|
429
|
+
fileRoutesById: FileRoutesById
|
|
430
|
+
}`,
|
|
431
|
+
`export interface RootRouteChildren {
|
|
432
|
+
${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${require_utils.getResolvedRouteNodeVariableName(child)}`).join(",")}
|
|
433
|
+
}`
|
|
434
|
+
].join("\n");
|
|
435
|
+
fileRoutesByPathInterface = require_utils.buildFileRoutesByPathInterface({
|
|
436
|
+
module: this.targetTemplate.fullPkg,
|
|
437
|
+
interfaceName: "FileRoutesByPath",
|
|
438
|
+
routeNodes: sortedRouteNodes,
|
|
439
|
+
config
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const routeTree = [`const rootRouteChildren${config.disableTypes ? "" : `: RootRouteChildren`} = {
|
|
443
|
+
${acc.routeTree.map((child) => `${child.variableName}Route: ${require_utils.getResolvedRouteNodeVariableName(child)}`).join(",")}
|
|
444
|
+
}`, rootRouteUpdate ? rootRouteUpdate.replace("const rootRouteWithChildren = ", "export const routeTree = ") : `export const routeTree = rootRouteImport._addFileChildren(rootRouteChildren)${config.disableTypes ? "" : `._addFileTypes<FileRouteTypes>()`}`].join("\n");
|
|
445
|
+
require_utils.checkRouteFullPathUniqueness(sortedRouteNodes.filter((d) => d.children === void 0 && "lazy" !== d._fsRouteType), config);
|
|
446
|
+
let mergedImports = require_utils.mergeImportDeclarations(imports);
|
|
447
|
+
if (config.disableTypes) mergedImports = mergedImports.filter((d) => d.importKind !== "type");
|
|
448
|
+
const importStatements = mergedImports.map(require_utils.buildImportString);
|
|
449
|
+
let moduleAugmentation = "";
|
|
450
|
+
if (config.verboseFileRoutes === false && !config.disableTypes) moduleAugmentation = opts.routeFileResult.map((node) => {
|
|
451
|
+
const getModuleDeclaration = (routeNode) => {
|
|
452
|
+
if (!require_utils.isRouteNodeValidForAugmentation(routeNode)) return "";
|
|
453
|
+
let moduleAugmentation = "";
|
|
454
|
+
if (routeNode._fsRouteType === "lazy") moduleAugmentation = `const createLazyFileRoute: CreateLazyFileRoute<FileRoutesByPath['${routeNode.routePath}']['preLoaderRoute']>`;
|
|
455
|
+
else moduleAugmentation = `const createFileRoute: CreateFileRoute<'${routeNode.routePath}',
|
|
456
|
+
FileRoutesByPath['${routeNode.routePath}']['parentRoute'],
|
|
457
|
+
FileRoutesByPath['${routeNode.routePath}']['id'],
|
|
458
|
+
FileRoutesByPath['${routeNode.routePath}']['path'],
|
|
459
|
+
FileRoutesByPath['${routeNode.routePath}']['fullPath']
|
|
460
|
+
>
|
|
461
|
+
`;
|
|
462
|
+
return `declare module './${require_utils.getImportPath(routeNode, config, this.generatedRouteTreePath)}' {
|
|
463
|
+
${moduleAugmentation}
|
|
464
|
+
}`;
|
|
465
|
+
};
|
|
466
|
+
return getModuleDeclaration(node);
|
|
467
|
+
}).join("\n");
|
|
468
|
+
const rootRouteImport = require_utils.getImportForRouteNode(rootRouteNode, config, this.generatedRouteTreePath, this.root);
|
|
469
|
+
routeImports.unshift(rootRouteImport);
|
|
470
|
+
let footer = [];
|
|
471
|
+
if (config.routeTreeFileFooter) if (Array.isArray(config.routeTreeFileFooter)) footer = config.routeTreeFileFooter;
|
|
472
|
+
else footer = config.routeTreeFileFooter();
|
|
473
|
+
return {
|
|
474
|
+
routeTreeContent: [
|
|
475
|
+
...config.routeTreeFileHeader,
|
|
476
|
+
`// This file was automatically generated by TanStack Router.
|
|
477
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
478
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.`,
|
|
479
|
+
[...importStatements].join("\n"),
|
|
480
|
+
require_utils.mergeImportDeclarations(routeImports).map(require_utils.buildImportString).join("\n"),
|
|
481
|
+
virtualRouteNodes.join("\n"),
|
|
482
|
+
createUpdateRoutes.join("\n"),
|
|
483
|
+
fileRoutesByFullPath,
|
|
484
|
+
fileRoutesByPathInterface,
|
|
485
|
+
moduleAugmentation,
|
|
486
|
+
routeTreeConfig.join("\n"),
|
|
487
|
+
routeTree,
|
|
488
|
+
...footer
|
|
489
|
+
].filter(Boolean).join("\n\n"),
|
|
490
|
+
routeTree: acc.routeTree,
|
|
491
|
+
routeNodes: acc.routeNodes
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
async processRouteNodeFile(node) {
|
|
495
|
+
const result = await this.isRouteFileCacheFresh(node);
|
|
496
|
+
if (result.status === "fresh") return {
|
|
497
|
+
node: result.cacheEntry.node,
|
|
498
|
+
shouldWriteTree: false,
|
|
499
|
+
cacheEntry: result.cacheEntry
|
|
500
|
+
};
|
|
501
|
+
const previousCacheEntry = result.cacheEntry;
|
|
502
|
+
const existingRouteFile = await this.fs.readFile(node.fullPath);
|
|
503
|
+
if (existingRouteFile === "file-not-existing") throw new Error(`⚠️ File ${node.fullPath} does not exist`);
|
|
504
|
+
if (node.routePath) require_validate_route_params.validateRouteParams(node.routePath, node.filePath, this.logger);
|
|
505
|
+
const updatedCacheEntry = {
|
|
506
|
+
fileContent: existingRouteFile.fileContent,
|
|
507
|
+
mtimeMs: existingRouteFile.stat.mtimeMs,
|
|
508
|
+
routeId: node.routePath ?? "$$TSR_NO_ROUTE_PATH_ASSIGNED$$",
|
|
509
|
+
node
|
|
510
|
+
};
|
|
511
|
+
const escapedRoutePath = node.routePath?.replaceAll("$", "$$") ?? "";
|
|
512
|
+
let shouldWriteRouteFile = false;
|
|
513
|
+
let shouldWriteTree = false;
|
|
514
|
+
if (!existingRouteFile.fileContent) {
|
|
515
|
+
shouldWriteRouteFile = true;
|
|
516
|
+
shouldWriteTree = true;
|
|
517
|
+
if (node._fsRouteType === "lazy") {
|
|
518
|
+
const tLazyRouteTemplate = this.targetTemplate.lazyRoute;
|
|
519
|
+
updatedCacheEntry.fileContent = await require_template.fillTemplate(this.config, (this.config.customScaffolding?.lazyRouteTemplate || this.config.customScaffolding?.routeTemplate) ?? tLazyRouteTemplate.template(), {
|
|
520
|
+
tsrImports: tLazyRouteTemplate.imports.tsrImports(),
|
|
521
|
+
tsrPath: escapedRoutePath.replaceAll(/\{(.+?)\}/gm, "$1"),
|
|
522
|
+
tsrExportStart: tLazyRouteTemplate.imports.tsrExportStart(escapedRoutePath),
|
|
523
|
+
tsrExportEnd: tLazyRouteTemplate.imports.tsrExportEnd()
|
|
524
|
+
});
|
|
525
|
+
} else if (["layout", "static"].some((d) => d === node._fsRouteType) || [
|
|
526
|
+
"component",
|
|
527
|
+
"pendingComponent",
|
|
528
|
+
"errorComponent",
|
|
529
|
+
"notFoundComponent",
|
|
530
|
+
"loader"
|
|
531
|
+
].every((d) => d !== node._fsRouteType)) {
|
|
532
|
+
const tRouteTemplate = this.targetTemplate.route;
|
|
533
|
+
updatedCacheEntry.fileContent = await require_template.fillTemplate(this.config, this.config.customScaffolding?.routeTemplate ?? tRouteTemplate.template(), {
|
|
534
|
+
tsrImports: tRouteTemplate.imports.tsrImports(),
|
|
535
|
+
tsrPath: escapedRoutePath.replaceAll(/\{(.+?)\}/gm, "$1"),
|
|
536
|
+
tsrExportStart: tRouteTemplate.imports.tsrExportStart(escapedRoutePath),
|
|
537
|
+
tsrExportEnd: tRouteTemplate.imports.tsrExportEnd()
|
|
538
|
+
});
|
|
539
|
+
} else return null;
|
|
540
|
+
}
|
|
541
|
+
if (!node.filePath.endsWith(".vue")) {
|
|
542
|
+
const transformResult = await require_transform.transform({
|
|
543
|
+
source: updatedCacheEntry.fileContent,
|
|
544
|
+
ctx: {
|
|
545
|
+
target: this.config.target,
|
|
546
|
+
routeId: escapedRoutePath,
|
|
547
|
+
lazy: node._fsRouteType === "lazy",
|
|
548
|
+
verboseFileRoutes: !(this.config.verboseFileRoutes === false),
|
|
549
|
+
angularRouterPackage: this.config.target === "angular" ? this.config.angularRouterPackage ?? "@benjavicente/angular-router-experimental" : void 0
|
|
550
|
+
},
|
|
551
|
+
node
|
|
552
|
+
});
|
|
553
|
+
if (transformResult.result === "no-route-export") {
|
|
554
|
+
const fileName = node_path.default.basename(node.fullPath);
|
|
555
|
+
const dirName = node_path.default.dirname(node.fullPath);
|
|
556
|
+
const ignorePrefix = this.config.routeFileIgnorePrefix;
|
|
557
|
+
const ignorePattern = this.config.routeFileIgnorePattern;
|
|
558
|
+
const suggestedFileName = `${ignorePrefix}${fileName}`;
|
|
559
|
+
const suggestedFullPath = node_path.default.join(dirName, suggestedFileName);
|
|
560
|
+
let message = `Warning: Route file "${node.fullPath}" does not export a Route. This file will not be included in the route tree.`;
|
|
561
|
+
message += `\n\nIf this file is not intended to be a route, you can exclude it using one of these options:`;
|
|
562
|
+
message += `\n 1. Rename the file to "${suggestedFullPath}" (prefix with "${ignorePrefix}")`;
|
|
563
|
+
message += `\n 2. Use 'routeFileIgnorePattern' in your config to match this file`;
|
|
564
|
+
message += `\n\nCurrent configuration:`;
|
|
565
|
+
message += `\n routeFileIgnorePrefix: "${ignorePrefix}"`;
|
|
566
|
+
message += `\n routeFileIgnorePattern: ${ignorePattern ? `"${ignorePattern}"` : "undefined"}`;
|
|
567
|
+
this.logger.warn(message);
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
if (transformResult.result === "error") throw new Error(`Error transforming route file ${node.fullPath}: ${transformResult.error}`);
|
|
571
|
+
if (transformResult.result === "modified") {
|
|
572
|
+
updatedCacheEntry.fileContent = transformResult.output;
|
|
573
|
+
shouldWriteRouteFile = true;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
for (const plugin of this.plugins) plugin.afterTransform?.({
|
|
577
|
+
node,
|
|
578
|
+
prevNode: previousCacheEntry?.node
|
|
579
|
+
});
|
|
580
|
+
if (shouldWriteRouteFile) updatedCacheEntry.mtimeMs = (await this.safeFileWrite({
|
|
581
|
+
filePath: node.fullPath,
|
|
582
|
+
newContent: updatedCacheEntry.fileContent,
|
|
583
|
+
strategy: {
|
|
584
|
+
type: "mtime",
|
|
585
|
+
expectedMtimeMs: updatedCacheEntry.mtimeMs
|
|
586
|
+
}
|
|
587
|
+
})).mtimeMs;
|
|
588
|
+
this.routeNodeShadowCache.set(node.fullPath, updatedCacheEntry);
|
|
589
|
+
return {
|
|
590
|
+
node,
|
|
591
|
+
shouldWriteTree,
|
|
592
|
+
cacheEntry: updatedCacheEntry
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
async didRouteFileChangeComparedToCache(file, cache) {
|
|
596
|
+
const cacheEntry = this[cache].get(file.path);
|
|
597
|
+
return this.didFileChangeComparedToCache(file, cacheEntry);
|
|
598
|
+
}
|
|
599
|
+
async didFileChangeComparedToCache(file, cacheEntry) {
|
|
600
|
+
if (!cacheEntry) return { result: "file-not-in-cache" };
|
|
601
|
+
let mtimeMs = file.mtimeMs;
|
|
602
|
+
if (mtimeMs === void 0) try {
|
|
603
|
+
mtimeMs = (await this.fs.stat(file.path)).mtimeMs;
|
|
604
|
+
} catch {
|
|
605
|
+
return { result: "cannot-stat-file" };
|
|
606
|
+
}
|
|
607
|
+
return {
|
|
608
|
+
result: mtimeMs !== cacheEntry.mtimeMs,
|
|
609
|
+
mtimeMs,
|
|
610
|
+
cacheEntry
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
async safeFileWrite(opts) {
|
|
614
|
+
const tmpPath = this.getTempFileName(opts.filePath);
|
|
615
|
+
await this.fs.writeFile(tmpPath, opts.newContent);
|
|
616
|
+
if (opts.strategy.type === "mtime") {
|
|
617
|
+
const beforeStat = await this.fs.stat(opts.filePath);
|
|
618
|
+
if (beforeStat.mtimeMs !== opts.strategy.expectedMtimeMs) throw rerun({
|
|
619
|
+
msg: `File ${opts.filePath} was modified by another process during processing.`,
|
|
620
|
+
event: {
|
|
621
|
+
type: "update",
|
|
622
|
+
path: opts.filePath
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
const newFileState = await this.fs.stat(tmpPath);
|
|
626
|
+
if (newFileState.mode !== beforeStat.mode) await this.fs.chmod(tmpPath, beforeStat.mode);
|
|
627
|
+
if (newFileState.uid !== beforeStat.uid || newFileState.gid !== beforeStat.gid) try {
|
|
628
|
+
await this.fs.chown(tmpPath, beforeStat.uid, beforeStat.gid);
|
|
629
|
+
} catch (err) {
|
|
630
|
+
if (typeof err === "object" && err !== null && "code" in err && err.code === "EPERM") console.warn(`[safeFileWrite] chown failed: ${err.message}`);
|
|
631
|
+
else throw err;
|
|
632
|
+
}
|
|
633
|
+
} else if (await require_utils.checkFileExists(opts.filePath)) throw rerun({
|
|
634
|
+
msg: `File ${opts.filePath} already exists. Cannot overwrite.`,
|
|
635
|
+
event: {
|
|
636
|
+
type: "update",
|
|
637
|
+
path: opts.filePath
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
await this.fs.rename(tmpPath, opts.filePath);
|
|
641
|
+
return await this.fs.stat(opts.filePath);
|
|
642
|
+
}
|
|
643
|
+
getTempFileName(filePath) {
|
|
644
|
+
const absPath = node_path.default.resolve(filePath);
|
|
645
|
+
const hash = node_crypto.default.createHash("md5").update(absPath).digest("hex");
|
|
646
|
+
if (!this.sessionId) {
|
|
647
|
+
(0, node_fs.mkdirSync)(this.config.tmpDir, { recursive: true });
|
|
648
|
+
this.sessionId = node_crypto.default.randomBytes(4).toString("hex");
|
|
649
|
+
}
|
|
650
|
+
return node_path.default.join(this.config.tmpDir, `${this.sessionId}-${hash}`);
|
|
651
|
+
}
|
|
652
|
+
async isRouteFileCacheFresh(node) {
|
|
653
|
+
const fileChangedCache = await this.didRouteFileChangeComparedToCache({ path: node.fullPath }, "routeNodeCache");
|
|
654
|
+
if (fileChangedCache.result === false) {
|
|
655
|
+
this.routeNodeShadowCache.set(node.fullPath, fileChangedCache.cacheEntry);
|
|
656
|
+
return {
|
|
657
|
+
status: "fresh",
|
|
658
|
+
cacheEntry: fileChangedCache.cacheEntry
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
if (fileChangedCache.result === "cannot-stat-file") throw new Error(`⚠️ expected route file to exist at ${node.fullPath}`);
|
|
662
|
+
const mtimeMs = fileChangedCache.result === true ? fileChangedCache.mtimeMs : void 0;
|
|
663
|
+
const shadowCacheFileChange = await this.didRouteFileChangeComparedToCache({
|
|
664
|
+
path: node.fullPath,
|
|
665
|
+
mtimeMs
|
|
666
|
+
}, "routeNodeShadowCache");
|
|
667
|
+
if (shadowCacheFileChange.result === "cannot-stat-file") throw new Error(`⚠️ expected route file to exist at ${node.fullPath}`);
|
|
668
|
+
if (shadowCacheFileChange.result === false) {
|
|
669
|
+
if (fileChangedCache.result === true) return {
|
|
670
|
+
status: "fresh",
|
|
671
|
+
cacheEntry: shadowCacheFileChange.cacheEntry
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
if (fileChangedCache.result === "file-not-in-cache") return { status: "stale" };
|
|
675
|
+
return {
|
|
676
|
+
status: "stale",
|
|
677
|
+
cacheEntry: fileChangedCache.cacheEntry
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
async handleRootNode(node) {
|
|
681
|
+
const result = await this.isRouteFileCacheFresh(node);
|
|
682
|
+
if (result.status === "fresh") this.routeNodeShadowCache.set(node.fullPath, result.cacheEntry);
|
|
683
|
+
const rootNodeFile = await this.fs.readFile(node.fullPath);
|
|
684
|
+
if (rootNodeFile === "file-not-existing") throw new Error(`⚠️ expected root route to exist at ${node.fullPath}`);
|
|
685
|
+
const updatedCacheEntry = {
|
|
686
|
+
fileContent: rootNodeFile.fileContent,
|
|
687
|
+
mtimeMs: rootNodeFile.stat.mtimeMs,
|
|
688
|
+
routeId: node.routePath ?? "$$TSR_NO_ROOT_ROUTE_PATH_ASSIGNED$$",
|
|
689
|
+
node
|
|
690
|
+
};
|
|
691
|
+
if (!rootNodeFile.fileContent) {
|
|
692
|
+
const rootTemplate = this.targetTemplate.rootRoute;
|
|
693
|
+
const rootRouteContent = await require_template.fillTemplate(this.config, rootTemplate.template(), {
|
|
694
|
+
tsrImports: rootTemplate.imports.tsrImports(),
|
|
695
|
+
tsrPath: require_rootPathId.rootPathId,
|
|
696
|
+
tsrExportStart: rootTemplate.imports.tsrExportStart(),
|
|
697
|
+
tsrExportEnd: rootTemplate.imports.tsrExportEnd()
|
|
698
|
+
});
|
|
699
|
+
this.logger.log(`🟡 Creating ${node.fullPath}`);
|
|
700
|
+
const stats = await this.safeFileWrite({
|
|
701
|
+
filePath: node.fullPath,
|
|
702
|
+
newContent: rootRouteContent,
|
|
703
|
+
strategy: {
|
|
704
|
+
type: "mtime",
|
|
705
|
+
expectedMtimeMs: rootNodeFile.stat.mtimeMs
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
updatedCacheEntry.fileContent = rootRouteContent;
|
|
709
|
+
updatedCacheEntry.mtimeMs = stats.mtimeMs;
|
|
710
|
+
}
|
|
711
|
+
this.routeNodeShadowCache.set(node.fullPath, updatedCacheEntry);
|
|
712
|
+
}
|
|
713
|
+
async getCrawlingResult() {
|
|
714
|
+
await this.runPromise;
|
|
715
|
+
return this.crawlingResult;
|
|
716
|
+
}
|
|
717
|
+
static handleNode(node, acc, prefixMap, config) {
|
|
718
|
+
let parentRoute = require_utils.hasParentRoute(prefixMap, node, node.routePath);
|
|
719
|
+
if (node.routePath) {
|
|
720
|
+
let searchPath = node.routePath;
|
|
721
|
+
while (searchPath.length > 0) {
|
|
722
|
+
const lastSlash = searchPath.lastIndexOf("/");
|
|
723
|
+
if (lastSlash <= 0) break;
|
|
724
|
+
searchPath = searchPath.substring(0, lastSlash);
|
|
725
|
+
const candidate = acc.routeNodesByPath.get(searchPath);
|
|
726
|
+
if (candidate && candidate.routePath !== node.routePath) {
|
|
727
|
+
if (candidate !== parentRoute) {
|
|
728
|
+
if (!parentRoute || (candidate.routePath?.length ?? 0) > (parentRoute.routePath?.length ?? 0)) parentRoute = candidate;
|
|
729
|
+
}
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (node._virtualParentRoutePath !== void 0) {
|
|
735
|
+
const explicitParent = acc.routeNodesByPath.get(node._virtualParentRoutePath);
|
|
736
|
+
if (explicitParent) parentRoute = explicitParent;
|
|
737
|
+
else if (node._virtualParentRoutePath === `/__root`) parentRoute = null;
|
|
738
|
+
}
|
|
739
|
+
if (parentRoute) node.parent = parentRoute;
|
|
740
|
+
node.path = require_utils.determineNodePath(node);
|
|
741
|
+
const trimmedPath = require_utils.trimPathLeft(node.path ?? "");
|
|
742
|
+
const trimmedOriginalPath = require_utils.trimPathLeft(node.originalRoutePath?.replace(node.parent?.originalRoutePath ?? "", "") ?? "");
|
|
743
|
+
const split = trimmedPath.split("/");
|
|
744
|
+
const originalSplit = trimmedOriginalPath.split("/");
|
|
745
|
+
node.isNonPath = require_utils.isSegmentPathless(split[split.length - 1] ?? trimmedPath, originalSplit[originalSplit.length - 1] ?? trimmedOriginalPath) || split.every((part) => this.routeGroupPatternRegex.test(part));
|
|
746
|
+
node.cleanedPath = require_utils.removeGroups(require_utils.removeUnderscoresWithEscape(require_utils.removeLayoutSegmentsWithEscape(node.path, node.originalRoutePath), node.originalRoutePath));
|
|
747
|
+
if (node._fsRouteType === "layout" || node._fsRouteType === "pathless_layout") node.cleanedPath = require_utils.removeTrailingSlash(node.cleanedPath);
|
|
748
|
+
if (!node.isVirtual && [
|
|
749
|
+
"lazy",
|
|
750
|
+
"loader",
|
|
751
|
+
"component",
|
|
752
|
+
"pendingComponent",
|
|
753
|
+
"errorComponent",
|
|
754
|
+
"notFoundComponent"
|
|
755
|
+
].some((d) => d === node._fsRouteType)) {
|
|
756
|
+
acc.routePiecesByPath[node.routePath] = acc.routePiecesByPath[node.routePath] || {};
|
|
757
|
+
const pieceKey = node._fsRouteType === "lazy" ? "lazy" : node._fsRouteType;
|
|
758
|
+
acc.routePiecesByPath[node.routePath][pieceKey] = node;
|
|
759
|
+
if (!acc.routeNodesByPath.get(node.routePath) && node.routePath !== `/__root`) this.handleNode({
|
|
760
|
+
...node,
|
|
761
|
+
isVirtual: true,
|
|
762
|
+
_fsRouteType: "static"
|
|
763
|
+
}, acc, prefixMap, config);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
const isPathlessLayoutWithPath = node._fsRouteType === "pathless_layout" && node.cleanedPath && node.cleanedPath.length > 0;
|
|
767
|
+
if (!node.isVirtual && isPathlessLayoutWithPath) {
|
|
768
|
+
const immediateParentPath = require_utils.removeLastSegmentFromPath(node.routePath) || "/";
|
|
769
|
+
const immediateParentOriginalPath = require_utils.removeLastSegmentFromPath(node.originalRoutePath) || "/";
|
|
770
|
+
let searchPath = immediateParentPath;
|
|
771
|
+
while (searchPath) {
|
|
772
|
+
const candidate = acc.routeNodesByPath.get(searchPath);
|
|
773
|
+
if (candidate && !candidate.isVirtual && candidate.path !== "/") {
|
|
774
|
+
node.parent = candidate;
|
|
775
|
+
node.path = node.routePath?.replace(candidate.routePath ?? "", "") || "/";
|
|
776
|
+
const pathRelativeToParent = immediateParentPath.replace(candidate.routePath ?? "", "") || "/";
|
|
777
|
+
const originalPathRelativeToParent = immediateParentOriginalPath.replace(candidate.originalRoutePath ?? "", "") || "/";
|
|
778
|
+
node.cleanedPath = require_utils.removeGroups(require_utils.removeUnderscoresWithEscape(require_utils.removeLayoutSegmentsWithEscape(pathRelativeToParent, originalPathRelativeToParent), originalPathRelativeToParent));
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
if (searchPath === "/") break;
|
|
782
|
+
searchPath = require_utils.removeLastSegmentFromPath(searchPath) || "/";
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (node.parent) {
|
|
786
|
+
node.parent.children = node.parent.children ?? [];
|
|
787
|
+
node.parent.children.push(node);
|
|
788
|
+
} else acc.routeTree.push(node);
|
|
789
|
+
acc.routeNodes.push(node);
|
|
790
|
+
if (node.routePath) acc.routeNodesByPath.set(node.routePath, node);
|
|
791
|
+
}
|
|
792
|
+
isFileRelevantForRouteTreeGeneration(filePath) {
|
|
793
|
+
if (filePath === this.generatedRouteTreePath) return true;
|
|
794
|
+
if (filePath.startsWith(this.routesDirectoryPath)) return true;
|
|
795
|
+
if (typeof this.config.virtualRouteConfig === "string" && filePath === this.config.virtualRouteConfig) return true;
|
|
796
|
+
if (this.routeNodeCache.has(filePath)) return true;
|
|
797
|
+
if (require_getRouteNodes$1.isVirtualConfigFile(node_path.default.basename(filePath))) return true;
|
|
798
|
+
if (this.physicalDirectories.some((dir) => filePath.startsWith(dir))) return true;
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
//#endregion
|
|
803
|
+
exports.Generator = Generator;
|
|
804
|
+
|
|
805
|
+
//# sourceMappingURL=generator.cjs.map
|