@tanstack/router-generator 1.132.0-alpha.9 → 1.132.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/config.cjs +6 -2
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/config.d.cts +12 -9
- package/dist/cjs/generator.cjs +264 -316
- package/dist/cjs/generator.cjs.map +1 -1
- package/dist/cjs/generator.d.cts +20 -7
- package/dist/cjs/index.cjs +0 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +4 -4
- package/dist/cjs/plugin/types.d.cts +10 -38
- package/dist/cjs/transform/transform.cjs +108 -40
- package/dist/cjs/transform/transform.cjs.map +1 -1
- package/dist/cjs/transform/transform.d.cts +1 -1
- package/dist/cjs/transform/types.d.cts +4 -18
- package/dist/cjs/types.d.cts +1 -1
- package/dist/cjs/utils.cjs +55 -39
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +5 -5
- package/dist/esm/config.d.ts +12 -9
- package/dist/esm/config.js +6 -2
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/generator.d.ts +20 -7
- package/dist/esm/generator.js +266 -318
- package/dist/esm/generator.js.map +1 -1
- package/dist/esm/index.d.ts +4 -4
- package/dist/esm/index.js +1 -2
- package/dist/esm/plugin/types.d.ts +10 -38
- package/dist/esm/transform/transform.d.ts +1 -1
- package/dist/esm/transform/transform.js +106 -38
- package/dist/esm/transform/transform.js.map +1 -1
- package/dist/esm/transform/types.d.ts +4 -18
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/utils.d.ts +5 -5
- package/dist/esm/utils.js +55 -39
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/config.ts +7 -1
- package/src/generator.ts +306 -366
- package/src/index.ts +2 -7
- package/src/plugin/types.ts +11 -44
- package/src/transform/transform.ts +118 -53
- package/src/transform/types.ts +5 -18
- package/src/types.ts +1 -1
- package/src/utils.ts +85 -70
- package/dist/cjs/plugin/default-generator-plugin.cjs +0 -94
- package/dist/cjs/plugin/default-generator-plugin.cjs.map +0 -1
- package/dist/cjs/plugin/default-generator-plugin.d.cts +0 -2
- package/dist/cjs/transform/default-transform-plugin.cjs +0 -97
- package/dist/cjs/transform/default-transform-plugin.cjs.map +0 -1
- package/dist/cjs/transform/default-transform-plugin.d.cts +0 -2
- package/dist/esm/plugin/default-generator-plugin.d.ts +0 -2
- package/dist/esm/plugin/default-generator-plugin.js +0 -94
- package/dist/esm/plugin/default-generator-plugin.js.map +0 -1
- package/dist/esm/transform/default-transform-plugin.d.ts +0 -2
- package/dist/esm/transform/default-transform-plugin.js +0 -97
- package/dist/esm/transform/default-transform-plugin.js.map +0 -1
- package/src/plugin/default-generator-plugin.ts +0 -109
- package/src/transform/default-transform-plugin.ts +0 -106
package/dist/esm/generator.js
CHANGED
|
@@ -2,15 +2,14 @@ import path from "node:path";
|
|
|
2
2
|
import * as fsp from "node:fs/promises";
|
|
3
3
|
import { existsSync, mkdirSync } from "node:fs";
|
|
4
4
|
import crypto from "node:crypto";
|
|
5
|
-
import {
|
|
5
|
+
import { rootRouteId } from "@tanstack/router-core";
|
|
6
6
|
import { logging } from "./logger.js";
|
|
7
7
|
import { getRouteNodes as getRouteNodes$1, isVirtualConfigFile } from "./filesystem/physical/getRouteNodes.js";
|
|
8
8
|
import { getRouteNodes } from "./filesystem/virtual/getRouteNodes.js";
|
|
9
9
|
import { rootPathId } from "./filesystem/physical/rootPathId.js";
|
|
10
|
-
import { multiSortBy, format,
|
|
10
|
+
import { multiSortBy, format, getImportForRouteNode, isRouteNodeValidForAugmentation, buildRouteTreeConfig, findParent, replaceBackslash, removeExt, createRouteNodesByFullPath, createRouteNodesByTo, createRouteNodesById, getResolvedRouteNodeVariableName, buildFileRoutesByPathInterface, checkRouteFullPathUniqueness, mergeImportDeclarations, buildImportString, checkFileExists, resetRegex, hasParentRoute, determineNodePath, trimPathLeft, removeGroups, removeUnderscores, removeLayoutSegments, removeLastSegmentFromPath, routePathToVariable, getImportPath } from "./utils.js";
|
|
11
11
|
import { getTargetTemplate, fillTemplate } from "./template.js";
|
|
12
12
|
import { transform } from "./transform/transform.js";
|
|
13
|
-
import { defaultGeneratorPlugin } from "./plugin/default-generator-plugin.js";
|
|
14
13
|
const DefaultFileSystem = {
|
|
15
14
|
stat: async (filePath) => {
|
|
16
15
|
const res = await fsp.stat(filePath, { bigint: true });
|
|
@@ -49,15 +48,12 @@ function rerun(opts) {
|
|
|
49
48
|
function isRerun(result) {
|
|
50
49
|
return typeof result === "object" && result !== null && "rerun" in result && result.rerun === true;
|
|
51
50
|
}
|
|
52
|
-
class
|
|
51
|
+
const _Generator = class _Generator {
|
|
53
52
|
constructor(opts) {
|
|
54
53
|
this.routeNodeCache = /* @__PURE__ */ new Map();
|
|
55
54
|
this.routeNodeShadowCache = /* @__PURE__ */ new Map();
|
|
56
55
|
this.fileEventQueue = [];
|
|
57
|
-
this.plugins = [
|
|
58
|
-
this.pluginsWithTransform = [];
|
|
59
|
-
this.transformPlugins = [];
|
|
60
|
-
this.routeGroupPatternRegex = /\(.+\)/g;
|
|
56
|
+
this.plugins = [];
|
|
61
57
|
this.physicalDirectories = [];
|
|
62
58
|
this.config = opts.config;
|
|
63
59
|
this.logger = logging({ disabled: this.config.disableLogging });
|
|
@@ -67,17 +63,9 @@ class Generator {
|
|
|
67
63
|
this.targetTemplate = getTargetTemplate(this.config);
|
|
68
64
|
this.routesDirectoryPath = this.getRoutesDirectoryPath();
|
|
69
65
|
this.plugins.push(...opts.config.plugins || []);
|
|
70
|
-
this.plugins
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
throw new Error(
|
|
74
|
-
`Plugin with name "${plugin.name}" is already registered for export ${plugin.transformPlugin.exportName}!`
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
this.pluginsWithTransform.push(plugin);
|
|
78
|
-
this.transformPlugins.push(plugin.transformPlugin);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
66
|
+
for (const plugin of this.plugins) {
|
|
67
|
+
plugin.init?.({ generator: this });
|
|
68
|
+
}
|
|
81
69
|
}
|
|
82
70
|
getGeneratedRouteTreePath() {
|
|
83
71
|
const generatedRouteTreePath = path.isAbsolute(
|
|
@@ -136,12 +124,7 @@ class Generator {
|
|
|
136
124
|
break;
|
|
137
125
|
}
|
|
138
126
|
try {
|
|
139
|
-
const start = performance.now();
|
|
140
127
|
await this.generatorInternal();
|
|
141
|
-
const end = performance.now();
|
|
142
|
-
this.logger.info(
|
|
143
|
-
`Generated route tree in ${Math.round(end - start)}ms`
|
|
144
|
-
);
|
|
145
128
|
} catch (err) {
|
|
146
129
|
const errArray = !Array.isArray(err) ? [err] : err;
|
|
147
130
|
const recoverableErrors = errArray.filter((e) => isRerun(e));
|
|
@@ -188,7 +171,7 @@ Add the file in: "${this.config.routesDirectory}/${rootPathId}.${this.config.dis
|
|
|
188
171
|
throw new Error(errorMessage);
|
|
189
172
|
}
|
|
190
173
|
this.physicalDirectories = physicalDirectories;
|
|
191
|
-
|
|
174
|
+
await this.handleRootNode(rootRouteNode);
|
|
192
175
|
const preRouteNodes = multiSortBy(beforeRouteNodes, [
|
|
193
176
|
(d) => d.routePath === "/" ? -1 : 1,
|
|
194
177
|
(d) => d.routePath?.split("/").length,
|
|
@@ -211,20 +194,23 @@ Add the file in: "${this.config.routesDirectory}/${rootPathId}.${this.config.dis
|
|
|
211
194
|
}
|
|
212
195
|
const routeFileResult = routeFileAllResult.flatMap((result) => {
|
|
213
196
|
if (result.status === "fulfilled" && result.value !== null) {
|
|
214
|
-
|
|
197
|
+
if (result.value.shouldWriteTree) {
|
|
198
|
+
writeRouteTreeFile = true;
|
|
199
|
+
}
|
|
200
|
+
return result.value.node;
|
|
215
201
|
}
|
|
216
202
|
return [];
|
|
217
203
|
});
|
|
218
|
-
routeFileResult.forEach((
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
writeRouteTreeFile = true;
|
|
204
|
+
routeFileResult.forEach((r) => r.children = void 0);
|
|
205
|
+
const acc = {
|
|
206
|
+
routeTree: [],
|
|
207
|
+
routeNodes: [],
|
|
208
|
+
routePiecesByPath: {}
|
|
209
|
+
};
|
|
210
|
+
for (const node of routeFileResult) {
|
|
211
|
+
_Generator.handleNode(node, acc);
|
|
227
212
|
}
|
|
213
|
+
this.crawlingResult = { rootRouteNode, routeFileResult, acc };
|
|
228
214
|
if (!this.routeTreeFileCache) {
|
|
229
215
|
const routeTreeFile = await this.fs.readFile(this.generatedRouteTreePath);
|
|
230
216
|
if (routeTreeFile !== "file-not-existing") {
|
|
@@ -255,10 +241,14 @@ Add the file in: "${this.config.routesDirectory}/${rootPathId}.${this.config.dis
|
|
|
255
241
|
}
|
|
256
242
|
}
|
|
257
243
|
if (!writeRouteTreeFile) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
244
|
+
if (this.routeNodeCache.size !== this.routeNodeShadowCache.size) {
|
|
245
|
+
writeRouteTreeFile = true;
|
|
246
|
+
} else {
|
|
247
|
+
for (const fullPath of this.routeNodeCache.keys()) {
|
|
248
|
+
if (!this.routeNodeShadowCache.has(fullPath)) {
|
|
249
|
+
writeRouteTreeFile = true;
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
262
252
|
}
|
|
263
253
|
}
|
|
264
254
|
}
|
|
@@ -266,11 +256,12 @@ Add the file in: "${this.config.routesDirectory}/${rootPathId}.${this.config.dis
|
|
|
266
256
|
this.swapCaches();
|
|
267
257
|
return;
|
|
268
258
|
}
|
|
269
|
-
|
|
259
|
+
const buildResult = this.buildRouteTree({
|
|
270
260
|
rootRouteNode,
|
|
271
|
-
|
|
261
|
+
acc,
|
|
272
262
|
routeFileResult
|
|
273
|
-
);
|
|
263
|
+
});
|
|
264
|
+
let routeTreeContent = buildResult.routeTreeContent;
|
|
274
265
|
routeTreeContent = this.config.enableRouteTreeFormatting ? await format(routeTreeContent, this.config) : routeTreeContent;
|
|
275
266
|
let newMtimeMs;
|
|
276
267
|
if (this.routeTreeFileCache) {
|
|
@@ -302,281 +293,258 @@ Add the file in: "${this.config.routesDirectory}/${rootPathId}.${this.config.dis
|
|
|
302
293
|
mtimeMs: newMtimeMs
|
|
303
294
|
};
|
|
304
295
|
}
|
|
296
|
+
this.plugins.map((plugin) => {
|
|
297
|
+
return plugin.onRouteTreeChanged?.({
|
|
298
|
+
routeTree: buildResult.routeTree,
|
|
299
|
+
routeNodes: buildResult.routeNodes,
|
|
300
|
+
acc,
|
|
301
|
+
rootRouteNode
|
|
302
|
+
});
|
|
303
|
+
});
|
|
305
304
|
this.swapCaches();
|
|
306
305
|
}
|
|
307
306
|
swapCaches() {
|
|
308
307
|
this.routeNodeCache = this.routeNodeShadowCache;
|
|
309
308
|
this.routeNodeShadowCache = /* @__PURE__ */ new Map();
|
|
310
309
|
}
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
310
|
+
buildRouteTree(opts) {
|
|
311
|
+
const config = { ...this.config, ...opts.config || {} };
|
|
312
|
+
const { rootRouteNode, acc } = opts;
|
|
313
|
+
const sortedRouteNodes = multiSortBy(acc.routeNodes, [
|
|
314
|
+
(d) => d.routePath?.includes(`/${rootPathId}`) ? -1 : 1,
|
|
315
|
+
(d) => d.routePath?.split("/").length,
|
|
316
|
+
(d) => d.routePath?.endsWith(config.indexToken) ? -1 : 1,
|
|
317
|
+
(d) => d
|
|
318
|
+
]);
|
|
319
|
+
const routeImports = sortedRouteNodes.filter((d) => !d.isVirtual).flatMap(
|
|
320
|
+
(node) => getImportForRouteNode(
|
|
321
|
+
node,
|
|
322
|
+
config,
|
|
323
|
+
this.generatedRouteTreePath,
|
|
324
|
+
this.root
|
|
325
|
+
)
|
|
326
|
+
);
|
|
327
|
+
const virtualRouteNodes = sortedRouteNodes.filter((d) => d.isVirtual).map((node) => {
|
|
328
|
+
return `const ${node.variableName}RouteImport = createFileRoute('${node.routePath}')()`;
|
|
329
|
+
});
|
|
330
|
+
const imports = [];
|
|
331
|
+
if (acc.routeNodes.some((n) => n.isVirtual)) {
|
|
332
|
+
imports.push({
|
|
333
|
+
specifiers: [{ imported: "createFileRoute" }],
|
|
334
|
+
source: this.targetTemplate.fullPkg
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
if (config.verboseFileRoutes === false) {
|
|
338
|
+
const typeImport = {
|
|
339
|
+
specifiers: [],
|
|
340
|
+
source: this.targetTemplate.fullPkg,
|
|
341
|
+
importKind: "type"
|
|
332
342
|
};
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
343
|
+
if (sortedRouteNodes.some(
|
|
344
|
+
(d) => isRouteNodeValidForAugmentation(d) && d._fsRouteType !== "lazy"
|
|
345
|
+
)) {
|
|
346
|
+
typeImport.specifiers.push({ imported: "CreateFileRoute" });
|
|
337
347
|
}
|
|
338
|
-
|
|
339
|
-
(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
(d) => d
|
|
343
|
-
]);
|
|
344
|
-
const pluginConfig = plugin.config({
|
|
345
|
-
generator: this,
|
|
346
|
-
rootRouteNode,
|
|
347
|
-
sortedRouteNodes
|
|
348
|
-
});
|
|
349
|
-
const routeImports2 = sortedRouteNodes.filter((d) => !d.isVirtual).flatMap((node) => getImportForRouteNode(node, exportName) ?? []);
|
|
350
|
-
const hasMatchingRouteFiles = acc.routeNodes.length > 0 || rootRouteNode.exports?.includes(exportName);
|
|
351
|
-
const virtualRouteNodes = sortedRouteNodes.filter((d) => d.isVirtual).map((node) => {
|
|
352
|
-
return `const ${node.variableName}${exportName}Import = ${plugin.createVirtualRouteCode({ node })}`;
|
|
353
|
-
});
|
|
354
|
-
if (!rootRouteNode.exports?.includes(exportName) && pluginConfig.virtualRootRoute) {
|
|
355
|
-
virtualRouteNodes.unshift(
|
|
356
|
-
`const ${rootRouteNode.variableName}${exportName}Import = ${plugin.createRootRouteCode()}`
|
|
357
|
-
);
|
|
348
|
+
if (sortedRouteNodes.some(
|
|
349
|
+
(node) => acc.routePiecesByPath[node.routePath]?.lazy && isRouteNodeValidForAugmentation(node)
|
|
350
|
+
)) {
|
|
351
|
+
typeImport.specifiers.push({ imported: "CreateLazyFileRoute" });
|
|
358
352
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
[
|
|
378
|
-
`const ${node.variableName}${exportName} = ${node.variableName}${exportName}Import.update({
|
|
353
|
+
if (typeImport.specifiers.length > 0) {
|
|
354
|
+
typeImport.specifiers.push({ imported: "FileRoutesByPath" });
|
|
355
|
+
imports.push(typeImport);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const routeTreeConfig = buildRouteTreeConfig(
|
|
359
|
+
acc.routeTree,
|
|
360
|
+
config.disableTypes
|
|
361
|
+
);
|
|
362
|
+
const createUpdateRoutes = sortedRouteNodes.map((node) => {
|
|
363
|
+
const loaderNode = acc.routePiecesByPath[node.routePath]?.loader;
|
|
364
|
+
const componentNode = acc.routePiecesByPath[node.routePath]?.component;
|
|
365
|
+
const errorComponentNode = acc.routePiecesByPath[node.routePath]?.errorComponent;
|
|
366
|
+
const pendingComponentNode = acc.routePiecesByPath[node.routePath]?.pendingComponent;
|
|
367
|
+
const lazyComponentNode = acc.routePiecesByPath[node.routePath]?.lazy;
|
|
368
|
+
return [
|
|
369
|
+
[
|
|
370
|
+
`const ${node.variableName}Route = ${node.variableName}RouteImport.update({
|
|
379
371
|
${[
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}${
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
)
|
|
396
|
-
)}'), 'loader') })` : "",
|
|
397
|
-
componentNode || errorComponentNode || pendingComponentNode ? `.update({
|
|
372
|
+
`id: '${node.path}'`,
|
|
373
|
+
!node.isNonPath ? `path: '${node.cleanedPath}'` : void 0,
|
|
374
|
+
`getParentRoute: () => ${findParent(node)}`
|
|
375
|
+
].filter(Boolean).join(",")}
|
|
376
|
+
}${config.disableTypes ? "" : "as any"})`,
|
|
377
|
+
loaderNode ? `.updateLoader({ loader: lazyFn(() => import('./${replaceBackslash(
|
|
378
|
+
removeExt(
|
|
379
|
+
path.relative(
|
|
380
|
+
path.dirname(config.generatedRouteTree),
|
|
381
|
+
path.resolve(config.routesDirectory, loaderNode.filePath)
|
|
382
|
+
),
|
|
383
|
+
config.addExtensions
|
|
384
|
+
)
|
|
385
|
+
)}'), 'loader') })` : "",
|
|
386
|
+
componentNode || errorComponentNode || pendingComponentNode ? `.update({
|
|
398
387
|
${[
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
removeExt(
|
|
405
|
-
path.relative(
|
|
406
|
-
path.dirname(this.config.generatedRouteTree),
|
|
407
|
-
path.resolve(
|
|
408
|
-
this.config.routesDirectory,
|
|
409
|
-
d[1].filePath
|
|
410
|
-
)
|
|
411
|
-
),
|
|
412
|
-
this.config.addExtensions
|
|
413
|
-
)
|
|
414
|
-
)}'), '${d[0]}')`;
|
|
415
|
-
}).join("\n,")}
|
|
416
|
-
})` : "",
|
|
417
|
-
lazyComponentNode ? `.lazy(() => import('./${replaceBackslash(
|
|
388
|
+
["component", componentNode],
|
|
389
|
+
["errorComponent", errorComponentNode],
|
|
390
|
+
["pendingComponent", pendingComponentNode]
|
|
391
|
+
].filter((d) => d[1]).map((d) => {
|
|
392
|
+
return `${d[0]}: lazyRouteComponent(() => import('./${replaceBackslash(
|
|
418
393
|
removeExt(
|
|
419
394
|
path.relative(
|
|
420
|
-
path.dirname(
|
|
421
|
-
path.resolve(
|
|
422
|
-
this.config.routesDirectory,
|
|
423
|
-
lazyComponentNode.filePath
|
|
424
|
-
)
|
|
395
|
+
path.dirname(config.generatedRouteTree),
|
|
396
|
+
path.resolve(config.routesDirectory, d[1].filePath)
|
|
425
397
|
),
|
|
426
|
-
|
|
398
|
+
config.addExtensions
|
|
427
399
|
)
|
|
428
|
-
)}')
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
400
|
+
)}'), '${d[0]}')`;
|
|
401
|
+
}).join("\n,")}
|
|
402
|
+
})` : "",
|
|
403
|
+
lazyComponentNode ? `.lazy(() => import('./${replaceBackslash(
|
|
404
|
+
removeExt(
|
|
405
|
+
path.relative(
|
|
406
|
+
path.dirname(config.generatedRouteTree),
|
|
407
|
+
path.resolve(
|
|
408
|
+
config.routesDirectory,
|
|
409
|
+
lazyComponentNode.filePath
|
|
410
|
+
)
|
|
411
|
+
),
|
|
412
|
+
config.addExtensions
|
|
413
|
+
)
|
|
414
|
+
)}').then((d) => d.Route))` : ""
|
|
415
|
+
].join("")
|
|
416
|
+
].join("\n\n");
|
|
417
|
+
});
|
|
418
|
+
let fileRoutesByPathInterface = "";
|
|
419
|
+
let fileRoutesByFullPath = "";
|
|
420
|
+
if (!config.disableTypes) {
|
|
421
|
+
fileRoutesByFullPath = [
|
|
422
|
+
`export interface FileRoutesByFullPath {
|
|
437
423
|
${[...createRouteNodesByFullPath(acc.routeNodes).entries()].filter(([fullPath]) => fullPath).map(([fullPath, routeNode]) => {
|
|
438
|
-
|
|
439
|
-
|
|
424
|
+
return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
|
|
425
|
+
})}
|
|
440
426
|
}`,
|
|
441
|
-
|
|
427
|
+
`export interface FileRoutesByTo {
|
|
442
428
|
${[...createRouteNodesByTo(acc.routeNodes).entries()].filter(([to]) => to).map(([to, routeNode]) => {
|
|
443
|
-
|
|
444
|
-
|
|
429
|
+
return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
|
|
430
|
+
})}
|
|
445
431
|
}`,
|
|
446
|
-
|
|
447
|
-
'${rootRouteId}': typeof
|
|
432
|
+
`export interface FileRoutesById {
|
|
433
|
+
'${rootRouteId}': typeof rootRouteImport,
|
|
448
434
|
${[...createRouteNodesById(acc.routeNodes).entries()].map(([id, routeNode]) => {
|
|
449
|
-
|
|
450
|
-
|
|
435
|
+
return `'${id}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
|
|
436
|
+
})}
|
|
451
437
|
}`,
|
|
452
|
-
|
|
453
|
-
|
|
438
|
+
`export interface FileRouteTypes {
|
|
439
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
454
440
|
fullPaths: ${acc.routeNodes.length > 0 ? [...createRouteNodesByFullPath(acc.routeNodes).keys()].filter((fullPath) => fullPath).map((fullPath) => `'${fullPath}'`).join("|") : "never"}
|
|
455
|
-
|
|
441
|
+
fileRoutesByTo: FileRoutesByTo
|
|
456
442
|
to: ${acc.routeNodes.length > 0 ? [...createRouteNodesByTo(acc.routeNodes).keys()].filter((to) => to).map((to) => `'${to}'`).join("|") : "never"}
|
|
457
443
|
id: ${[`'${rootRouteId}'`, ...[...createRouteNodesById(acc.routeNodes).keys()].map((id) => `'${id}'`)].join("|")}
|
|
458
|
-
|
|
444
|
+
fileRoutesById: FileRoutesById
|
|
459
445
|
}`,
|
|
460
|
-
|
|
461
|
-
${acc.routeTree.map((child) => `${child.variableName}
|
|
446
|
+
`export interface RootRouteChildren {
|
|
447
|
+
${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`).join(",")}
|
|
462
448
|
}`
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
let routeTree = "";
|
|
474
|
-
if (hasMatchingRouteFiles) {
|
|
475
|
-
routeTree = [
|
|
476
|
-
`const root${exportName}Children${this.config.disableTypes ? "" : `: Root${exportName}Children`} = {
|
|
449
|
+
].join("\n");
|
|
450
|
+
fileRoutesByPathInterface = buildFileRoutesByPathInterface({
|
|
451
|
+
module: this.targetTemplate.fullPkg,
|
|
452
|
+
interfaceName: "FileRoutesByPath",
|
|
453
|
+
routeNodes: sortedRouteNodes
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
const routeTree = [
|
|
457
|
+
`const rootRouteChildren${config.disableTypes ? "" : `: RootRouteChildren`} = {
|
|
477
458
|
${acc.routeTree.map(
|
|
478
|
-
|
|
479
|
-
|
|
459
|
+
(child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`
|
|
460
|
+
).join(",")}
|
|
480
461
|
}`,
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
virtualRouteNodes,
|
|
489
|
-
routeTreeConfig,
|
|
490
|
-
routeTree,
|
|
491
|
-
imports,
|
|
492
|
-
createUpdateRoutes,
|
|
493
|
-
fileRoutesByFullPathPerPlugin,
|
|
494
|
-
fileRoutesByPathInterfacePerPlugin
|
|
495
|
-
};
|
|
496
|
-
};
|
|
497
|
-
const routeTrees = this.pluginsWithTransform.map((plugin) => ({
|
|
498
|
-
exportName: plugin.transformPlugin.exportName,
|
|
499
|
-
...buildRouteTreeForExport(plugin)
|
|
500
|
-
}));
|
|
501
|
-
this.plugins.map((plugin) => {
|
|
502
|
-
return plugin.onRouteTreesChanged?.({
|
|
503
|
-
routeTrees,
|
|
504
|
-
rootRouteNode,
|
|
505
|
-
generator: this
|
|
506
|
-
});
|
|
507
|
-
});
|
|
508
|
-
let mergedImports = mergeImportDeclarations(
|
|
509
|
-
routeTrees.flatMap((d) => d.imports)
|
|
462
|
+
`export const routeTree = rootRouteImport._addFileChildren(rootRouteChildren)${config.disableTypes ? "" : `._addFileTypes<FileRouteTypes>()`}`
|
|
463
|
+
].join("\n");
|
|
464
|
+
checkRouteFullPathUniqueness(
|
|
465
|
+
sortedRouteNodes.filter(
|
|
466
|
+
(d) => d.children === void 0 && "lazy" !== d._fsRouteType
|
|
467
|
+
),
|
|
468
|
+
config
|
|
510
469
|
);
|
|
511
|
-
|
|
470
|
+
let mergedImports = mergeImportDeclarations(imports);
|
|
471
|
+
if (config.disableTypes) {
|
|
512
472
|
mergedImports = mergedImports.filter((d) => d.importKind !== "type");
|
|
513
473
|
}
|
|
514
474
|
const importStatements = mergedImports.map(buildImportString);
|
|
515
475
|
let moduleAugmentation = "";
|
|
516
|
-
if (
|
|
517
|
-
moduleAugmentation = routeFileResult.map((
|
|
476
|
+
if (config.verboseFileRoutes === false && !config.disableTypes) {
|
|
477
|
+
moduleAugmentation = opts.routeFileResult.map((node) => {
|
|
518
478
|
const getModuleDeclaration = (routeNode) => {
|
|
519
479
|
if (!isRouteNodeValidForAugmentation(routeNode)) {
|
|
520
480
|
return "";
|
|
521
481
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
482
|
+
let moduleAugmentation2 = "";
|
|
483
|
+
if (routeNode._fsRouteType === "lazy") {
|
|
484
|
+
moduleAugmentation2 = `const createLazyFileRoute: CreateLazyFileRoute<FileRoutesByPath['${routeNode.routePath}']['preLoaderRoute']>`;
|
|
485
|
+
} else {
|
|
486
|
+
moduleAugmentation2 = `const createFileRoute: CreateFileRoute<'${routeNode.routePath}',
|
|
487
|
+
FileRoutesByPath['${routeNode.routePath}']['parentRoute'],
|
|
488
|
+
FileRoutesByPath['${routeNode.routePath}']['id'],
|
|
489
|
+
FileRoutesByPath['${routeNode.routePath}']['path'],
|
|
490
|
+
FileRoutesByPath['${routeNode.routePath}']['fullPath']
|
|
491
|
+
>
|
|
492
|
+
`;
|
|
493
|
+
}
|
|
494
|
+
return `declare module './${getImportPath(routeNode, config, this.generatedRouteTreePath)}' {
|
|
528
495
|
${moduleAugmentation2}
|
|
529
496
|
}`;
|
|
530
497
|
};
|
|
531
498
|
return getModuleDeclaration(node);
|
|
532
499
|
}).join("\n");
|
|
533
500
|
}
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
501
|
+
const rootRouteImport = getImportForRouteNode(
|
|
502
|
+
rootRouteNode,
|
|
503
|
+
config,
|
|
504
|
+
this.generatedRouteTreePath,
|
|
505
|
+
this.root
|
|
537
506
|
);
|
|
538
|
-
|
|
539
|
-
|
|
507
|
+
routeImports.unshift(rootRouteImport);
|
|
508
|
+
let footer = [];
|
|
509
|
+
if (config.routeTreeFileFooter) {
|
|
510
|
+
if (Array.isArray(config.routeTreeFileFooter)) {
|
|
511
|
+
footer = config.routeTreeFileFooter;
|
|
512
|
+
} else {
|
|
513
|
+
footer = config.routeTreeFileFooter();
|
|
514
|
+
}
|
|
540
515
|
}
|
|
541
516
|
const routeTreeContent = [
|
|
542
|
-
...
|
|
517
|
+
...config.routeTreeFileHeader,
|
|
543
518
|
`// This file was automatically generated by TanStack Router.
|
|
544
519
|
// You should NOT make any changes in this file as it will be overwritten.
|
|
545
520
|
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.`,
|
|
546
521
|
[...importStatements].join("\n"),
|
|
547
522
|
mergeImportDeclarations(routeImports).map(buildImportString).join("\n"),
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
523
|
+
virtualRouteNodes.join("\n"),
|
|
524
|
+
createUpdateRoutes.join("\n"),
|
|
525
|
+
fileRoutesByFullPath,
|
|
526
|
+
fileRoutesByPathInterface,
|
|
552
527
|
moduleAugmentation,
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
...
|
|
528
|
+
routeTreeConfig.join("\n"),
|
|
529
|
+
routeTree,
|
|
530
|
+
...footer
|
|
556
531
|
].filter(Boolean).join("\n\n");
|
|
557
|
-
return
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
path.relative(
|
|
563
|
-
path.dirname(this.config.generatedRouteTree),
|
|
564
|
-
path.resolve(this.config.routesDirectory, node.filePath)
|
|
565
|
-
),
|
|
566
|
-
this.config.addExtensions
|
|
567
|
-
)
|
|
568
|
-
);
|
|
532
|
+
return {
|
|
533
|
+
routeTreeContent,
|
|
534
|
+
routeTree: acc.routeTree,
|
|
535
|
+
routeNodes: acc.routeNodes
|
|
536
|
+
};
|
|
569
537
|
}
|
|
570
538
|
async processRouteNodeFile(node) {
|
|
571
539
|
const result = await this.isRouteFileCacheFresh(node);
|
|
572
540
|
if (result.status === "fresh") {
|
|
573
|
-
node.exports = result.cacheEntry.exports;
|
|
574
541
|
return {
|
|
575
|
-
node,
|
|
576
|
-
shouldWriteTree:
|
|
542
|
+
node: result.cacheEntry.node,
|
|
543
|
+
shouldWriteTree: false,
|
|
577
544
|
cacheEntry: result.cacheEntry
|
|
578
545
|
};
|
|
579
546
|
}
|
|
547
|
+
const previousCacheEntry = result.cacheEntry;
|
|
580
548
|
const existingRouteFile = await this.fs.readFile(node.fullPath);
|
|
581
549
|
if (existingRouteFile === "file-not-existing") {
|
|
582
550
|
throw new Error(`⚠️ File ${node.fullPath} does not exist`);
|
|
@@ -584,13 +552,15 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
584
552
|
const updatedCacheEntry = {
|
|
585
553
|
fileContent: existingRouteFile.fileContent,
|
|
586
554
|
mtimeMs: existingRouteFile.stat.mtimeMs,
|
|
587
|
-
|
|
588
|
-
|
|
555
|
+
routeId: node.routePath ?? "$$TSR_NO_ROUTE_PATH_ASSIGNED$$",
|
|
556
|
+
node
|
|
589
557
|
};
|
|
590
558
|
const escapedRoutePath = node.routePath?.replaceAll("$", "$$") ?? "";
|
|
591
559
|
let shouldWriteRouteFile = false;
|
|
560
|
+
let shouldWriteTree = false;
|
|
592
561
|
if (!existingRouteFile.fileContent) {
|
|
593
562
|
shouldWriteRouteFile = true;
|
|
563
|
+
shouldWriteTree = true;
|
|
594
564
|
if (node._fsRouteType === "lazy") {
|
|
595
565
|
const tLazyRouteTemplate = this.targetTemplate.lazyRoute;
|
|
596
566
|
updatedCacheEntry.fileContent = await fillTemplate(
|
|
@@ -603,7 +573,6 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
603
573
|
tsrExportEnd: tLazyRouteTemplate.imports.tsrExportEnd()
|
|
604
574
|
}
|
|
605
575
|
);
|
|
606
|
-
updatedCacheEntry.exports = ["Route"];
|
|
607
576
|
} else if (
|
|
608
577
|
// Creating a new normal route file
|
|
609
578
|
["layout", "static"].some(
|
|
@@ -626,31 +595,37 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
626
595
|
tsrExportEnd: tRouteTemplate.imports.tsrExportEnd()
|
|
627
596
|
}
|
|
628
597
|
);
|
|
629
|
-
updatedCacheEntry.exports = ["Route"];
|
|
630
598
|
} else {
|
|
631
599
|
return null;
|
|
632
600
|
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
601
|
+
}
|
|
602
|
+
const transformResult = await transform({
|
|
603
|
+
source: updatedCacheEntry.fileContent,
|
|
604
|
+
ctx: {
|
|
605
|
+
target: this.config.target,
|
|
606
|
+
routeId: escapedRoutePath,
|
|
607
|
+
lazy: node._fsRouteType === "lazy",
|
|
608
|
+
verboseFileRoutes: !(this.config.verboseFileRoutes === false)
|
|
609
|
+
},
|
|
610
|
+
node
|
|
611
|
+
});
|
|
612
|
+
if (transformResult.result === "no-route-export") {
|
|
613
|
+
this.logger.warn(
|
|
614
|
+
`Route file "${node.fullPath}" does not contain any route piece. This is likely a mistake.`
|
|
615
|
+
);
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
if (transformResult.result === "error") {
|
|
619
|
+
throw new Error(
|
|
620
|
+
`Error transforming route file ${node.fullPath}: ${transformResult.error}`
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
if (transformResult.result === "modified") {
|
|
624
|
+
updatedCacheEntry.fileContent = transformResult.output;
|
|
625
|
+
shouldWriteRouteFile = true;
|
|
626
|
+
}
|
|
627
|
+
for (const plugin of this.plugins) {
|
|
628
|
+
plugin.afterTransform?.({ node, prevNode: previousCacheEntry?.node });
|
|
654
629
|
}
|
|
655
630
|
if (shouldWriteRouteFile) {
|
|
656
631
|
const stats = await this.safeFileWrite({
|
|
@@ -664,11 +639,6 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
664
639
|
updatedCacheEntry.mtimeMs = stats.mtimeMs;
|
|
665
640
|
}
|
|
666
641
|
this.routeNodeShadowCache.set(node.fullPath, updatedCacheEntry);
|
|
667
|
-
node.exports = updatedCacheEntry.exports;
|
|
668
|
-
const shouldWriteTree = !deepEqual(
|
|
669
|
-
result.cacheEntry?.exports,
|
|
670
|
-
updatedCacheEntry.exports
|
|
671
|
-
);
|
|
672
642
|
return {
|
|
673
643
|
node,
|
|
674
644
|
shouldWriteTree,
|
|
@@ -752,7 +722,6 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
752
722
|
this.routeNodeShadowCache.set(node.fullPath, fileChangedCache.cacheEntry);
|
|
753
723
|
return {
|
|
754
724
|
status: "fresh",
|
|
755
|
-
exportsChanged: false,
|
|
756
725
|
cacheEntry: fileChangedCache.cacheEntry
|
|
757
726
|
};
|
|
758
727
|
}
|
|
@@ -769,19 +738,8 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
769
738
|
}
|
|
770
739
|
if (shadowCacheFileChange.result === false) {
|
|
771
740
|
if (fileChangedCache.result === true) {
|
|
772
|
-
if (deepEqual(
|
|
773
|
-
fileChangedCache.cacheEntry.exports,
|
|
774
|
-
shadowCacheFileChange.cacheEntry.exports
|
|
775
|
-
)) {
|
|
776
|
-
return {
|
|
777
|
-
status: "fresh",
|
|
778
|
-
exportsChanged: false,
|
|
779
|
-
cacheEntry: shadowCacheFileChange.cacheEntry
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
741
|
return {
|
|
783
742
|
status: "fresh",
|
|
784
|
-
exportsChanged: true,
|
|
785
743
|
cacheEntry: shadowCacheFileChange.cacheEntry
|
|
786
744
|
};
|
|
787
745
|
}
|
|
@@ -796,9 +754,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
796
754
|
async handleRootNode(node) {
|
|
797
755
|
const result = await this.isRouteFileCacheFresh(node);
|
|
798
756
|
if (result.status === "fresh") {
|
|
799
|
-
node.exports = result.cacheEntry.exports;
|
|
800
757
|
this.routeNodeShadowCache.set(node.fullPath, result.cacheEntry);
|
|
801
|
-
return result.exportsChanged;
|
|
802
758
|
}
|
|
803
759
|
const rootNodeFile = await this.fs.readFile(node.fullPath);
|
|
804
760
|
if (rootNodeFile === "file-not-existing") {
|
|
@@ -807,8 +763,8 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
807
763
|
const updatedCacheEntry = {
|
|
808
764
|
fileContent: rootNodeFile.fileContent,
|
|
809
765
|
mtimeMs: rootNodeFile.stat.mtimeMs,
|
|
810
|
-
|
|
811
|
-
|
|
766
|
+
routeId: node.routePath ?? "$$TSR_NO_ROOT_ROUTE_PATH_ASSIGNED$$",
|
|
767
|
+
node
|
|
812
768
|
};
|
|
813
769
|
if (!rootNodeFile.fileContent) {
|
|
814
770
|
const rootTemplate = this.targetTemplate.rootRoute;
|
|
@@ -834,23 +790,13 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
834
790
|
updatedCacheEntry.fileContent = rootRouteContent;
|
|
835
791
|
updatedCacheEntry.mtimeMs = stats.mtimeMs;
|
|
836
792
|
}
|
|
837
|
-
const rootRouteExports = [];
|
|
838
|
-
for (const plugin of this.pluginsWithTransform) {
|
|
839
|
-
const exportName = plugin.transformPlugin.exportName;
|
|
840
|
-
if (rootNodeFile.fileContent.includes(`export const ${exportName}`)) {
|
|
841
|
-
rootRouteExports.push(exportName);
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
updatedCacheEntry.exports = rootRouteExports;
|
|
845
|
-
node.exports = rootRouteExports;
|
|
846
793
|
this.routeNodeShadowCache.set(node.fullPath, updatedCacheEntry);
|
|
847
|
-
const shouldWriteTree = !deepEqual(
|
|
848
|
-
result.cacheEntry?.exports,
|
|
849
|
-
rootRouteExports
|
|
850
|
-
);
|
|
851
|
-
return shouldWriteTree;
|
|
852
794
|
}
|
|
853
|
-
|
|
795
|
+
async getCrawlingResult() {
|
|
796
|
+
await this.runPromise;
|
|
797
|
+
return this.crawlingResult;
|
|
798
|
+
}
|
|
799
|
+
static handleNode(node, acc) {
|
|
854
800
|
resetRegex(this.routeGroupPatternRegex);
|
|
855
801
|
let parentRoute = hasParentRoute(acc.routeNodes, node, node.routePath);
|
|
856
802
|
if (parentRoute?.isVirtualParentRoute && parentRoute.children?.length) {
|
|
@@ -964,7 +910,9 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
964
910
|
}
|
|
965
911
|
return false;
|
|
966
912
|
}
|
|
967
|
-
}
|
|
913
|
+
};
|
|
914
|
+
_Generator.routeGroupPatternRegex = /\(.+\)/g;
|
|
915
|
+
let Generator = _Generator;
|
|
968
916
|
export {
|
|
969
917
|
Generator
|
|
970
918
|
};
|