@rangojs/router 0.0.0-experimental.130 → 0.0.0-experimental.132
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/bin/rango.js +69 -24
- package/dist/vite/index.js +182 -41
- package/package.json +6 -3
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/event-controller.ts +5 -4
- package/src/browser/partial-update.ts +32 -16
- package/src/browser/react/NavigationProvider.tsx +6 -83
- package/src/browser/react/filter-segment-order.ts +17 -0
- package/src/browser/react/use-link-status.ts +10 -2
- package/src/browser/react/use-navigation.ts +10 -2
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/include-resolution.ts +109 -21
- package/src/build/route-types/per-module-writer.ts +15 -2
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cf/cf-cache-store.ts +129 -5
- package/src/decode-loader-results.ts +11 -1
- package/src/encode-kv.ts +49 -0
- package/src/handles/meta.ts +5 -1
- package/src/host/cookie-handler.ts +2 -21
- package/src/prerender/param-hash.ts +6 -5
- package/src/regex-escape.ts +8 -0
- package/src/route-definition/dsl-helpers.ts +6 -2
- package/src/router/error-handling.ts +32 -1
- package/src/router/handler-context.ts +6 -1
- package/src/router/instrument.ts +56 -14
- package/src/router/intercept-resolution.ts +16 -1
- package/src/router/loader-resolution.ts +49 -19
- package/src/router/match-middleware/background-revalidation.ts +6 -0
- package/src/router/match-middleware/cache-store.ts +6 -0
- package/src/router/middleware.ts +67 -27
- package/src/router/pattern-matching.ts +3 -9
- package/src/router/revalidation.ts +65 -23
- package/src/router/router-context.ts +1 -0
- package/src/router/router-options.ts +3 -3
- package/src/router/segment-resolution/fresh.ts +8 -9
- package/src/router/segment-resolution/helpers.ts +11 -10
- package/src/router/segment-resolution/loader-cache.ts +13 -0
- package/src/router/segment-resolution/revalidation.ts +4 -4
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/trie-matching.ts +74 -20
- package/src/router.ts +2 -2
- package/src/rsc/progressive-enhancement.ts +20 -0
- package/src/rsc/server-action.ts +124 -47
- package/src/search-params.ts +8 -6
- package/src/segment-system.tsx +7 -1
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/handle-store.ts +14 -14
- package/src/server/request-context.ts +5 -26
- package/src/ssr/index.tsx +5 -4
- package/src/testing/render-handler.ts +11 -0
- package/src/vite/plugins/expose-id-utils.ts +77 -2
- package/src/vite/plugins/expose-ids/export-analysis.ts +30 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +82 -12
- package/src/vite/utils/prerender-utils.ts +1 -3
package/dist/bin/rango.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res
|
|
5
|
-
|
|
6
|
-
try {
|
|
7
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
-
} catch (e) {
|
|
9
|
-
throw err = [e], e;
|
|
10
|
-
}
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
6
|
};
|
|
12
7
|
var __export = (target, all) => {
|
|
13
8
|
for (var name in all)
|
|
@@ -64,8 +59,8 @@ var init_ast_helpers = __esm({
|
|
|
64
59
|
|
|
65
60
|
// src/build/route-types/ast-route-extraction.ts
|
|
66
61
|
import ts2 from "typescript";
|
|
67
|
-
function extractRoutesFromSource(code) {
|
|
68
|
-
const sourceFile = ts2.createSourceFile(
|
|
62
|
+
function extractRoutesFromSource(code, sourceFileArg) {
|
|
63
|
+
const sourceFile = sourceFileArg ?? ts2.createSourceFile(
|
|
69
64
|
"input.tsx",
|
|
70
65
|
code,
|
|
71
66
|
ts2.ScriptTarget.Latest,
|
|
@@ -247,6 +242,33 @@ var init_scan_filter = __esm({
|
|
|
247
242
|
import { readFileSync, existsSync } from "node:fs";
|
|
248
243
|
import { dirname, resolve } from "node:path";
|
|
249
244
|
import ts3 from "typescript";
|
|
245
|
+
function createScanMemo() {
|
|
246
|
+
return { files: /* @__PURE__ */ new Map(), blockSourceFiles: /* @__PURE__ */ new Map() };
|
|
247
|
+
}
|
|
248
|
+
function parseBlock(memo, block) {
|
|
249
|
+
if (memo) {
|
|
250
|
+
const cached = memo.blockSourceFiles.get(block);
|
|
251
|
+
if (cached) return cached;
|
|
252
|
+
}
|
|
253
|
+
const sf = ts3.createSourceFile(
|
|
254
|
+
"input.tsx",
|
|
255
|
+
block,
|
|
256
|
+
ts3.ScriptTarget.Latest,
|
|
257
|
+
true,
|
|
258
|
+
ts3.ScriptKind.TSX
|
|
259
|
+
);
|
|
260
|
+
if (memo) memo.blockSourceFiles.set(block, sf);
|
|
261
|
+
return sf;
|
|
262
|
+
}
|
|
263
|
+
function readSourceMemoized(memo, realPath) {
|
|
264
|
+
if (memo) {
|
|
265
|
+
const cached = memo.files.get(realPath);
|
|
266
|
+
if (cached !== void 0) return cached;
|
|
267
|
+
}
|
|
268
|
+
const source = readFileSync(realPath, "utf-8");
|
|
269
|
+
if (memo) memo.files.set(realPath, source);
|
|
270
|
+
return source;
|
|
271
|
+
}
|
|
250
272
|
function extractNamePrefixFromInclude(node) {
|
|
251
273
|
if (node.arguments.length >= 3) {
|
|
252
274
|
const thirdArg = node.arguments[2];
|
|
@@ -262,8 +284,8 @@ function extractNamePrefixFromInclude(node) {
|
|
|
262
284
|
}
|
|
263
285
|
return null;
|
|
264
286
|
}
|
|
265
|
-
function extractIncludesWithDiagnostics(code) {
|
|
266
|
-
const sourceFile = ts3.createSourceFile(
|
|
287
|
+
function extractIncludesWithDiagnostics(code, sourceFileArg) {
|
|
288
|
+
const sourceFile = sourceFileArg ?? ts3.createSourceFile(
|
|
267
289
|
"input.tsx",
|
|
268
290
|
code,
|
|
269
291
|
ts3.ScriptTarget.Latest,
|
|
@@ -354,8 +376,8 @@ function resolveImportPath(importSpec, fromFile) {
|
|
|
354
376
|
}
|
|
355
377
|
return null;
|
|
356
378
|
}
|
|
357
|
-
function extractUrlsBlockForVariable(code, varName) {
|
|
358
|
-
const sourceFile = ts3.createSourceFile(
|
|
379
|
+
function extractUrlsBlockForVariable(code, varName, sourceFileArg) {
|
|
380
|
+
const sourceFile = sourceFileArg ?? ts3.createSourceFile(
|
|
359
381
|
"input.tsx",
|
|
360
382
|
code,
|
|
361
383
|
ts3.ScriptTarget.Latest,
|
|
@@ -377,16 +399,20 @@ function extractUrlsBlockForVariable(code, varName) {
|
|
|
377
399
|
visit(sourceFile);
|
|
378
400
|
return result;
|
|
379
401
|
}
|
|
380
|
-
function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSchemasOut, diagnosticsOut) {
|
|
402
|
+
function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSchemasOut, diagnosticsOut, memo) {
|
|
381
403
|
const routeMap = {};
|
|
382
|
-
const
|
|
404
|
+
const blockSourceFile = parseBlock(memo, block);
|
|
405
|
+
const localRoutes = extractRoutesFromSource(block, blockSourceFile);
|
|
383
406
|
for (const { name, pattern, search } of localRoutes) {
|
|
384
407
|
routeMap[name] = pattern;
|
|
385
408
|
if (search && searchSchemasOut) {
|
|
386
409
|
searchSchemasOut[name] = search;
|
|
387
410
|
}
|
|
388
411
|
}
|
|
389
|
-
const { resolved: includes, unresolvable } = extractIncludesWithDiagnostics(
|
|
412
|
+
const { resolved: includes, unresolvable } = extractIncludesWithDiagnostics(
|
|
413
|
+
block,
|
|
414
|
+
blockSourceFile
|
|
415
|
+
);
|
|
390
416
|
if (diagnosticsOut) {
|
|
391
417
|
for (const entry of unresolvable) {
|
|
392
418
|
diagnosticsOut.push({ ...entry, sourceFile: filePath });
|
|
@@ -413,12 +439,15 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
413
439
|
targetFile,
|
|
414
440
|
imported.exportedName,
|
|
415
441
|
visited,
|
|
416
|
-
diagnosticsOut
|
|
442
|
+
diagnosticsOut,
|
|
443
|
+
void 0,
|
|
444
|
+
memo
|
|
417
445
|
);
|
|
418
446
|
} else {
|
|
419
447
|
const sameFileBlock = extractUrlsBlockForVariable(
|
|
420
448
|
fullSource,
|
|
421
|
-
variableName
|
|
449
|
+
variableName,
|
|
450
|
+
parseBlock(memo, fullSource)
|
|
422
451
|
);
|
|
423
452
|
if (!sameFileBlock) {
|
|
424
453
|
if (diagnosticsOut) {
|
|
@@ -436,7 +465,9 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
436
465
|
filePath,
|
|
437
466
|
variableName,
|
|
438
467
|
visited,
|
|
439
|
-
diagnosticsOut
|
|
468
|
+
diagnosticsOut,
|
|
469
|
+
void 0,
|
|
470
|
+
memo
|
|
440
471
|
);
|
|
441
472
|
}
|
|
442
473
|
if (namePrefix === null) {
|
|
@@ -460,8 +491,9 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
460
491
|
}
|
|
461
492
|
return routeMap;
|
|
462
493
|
}
|
|
463
|
-
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock) {
|
|
494
|
+
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock, memo) {
|
|
464
495
|
visited = visited ?? /* @__PURE__ */ new Set();
|
|
496
|
+
memo = memo ?? createScanMemo();
|
|
465
497
|
const realPath = resolve(filePath);
|
|
466
498
|
const key = variableName ? `${realPath}:${variableName}` : realPath;
|
|
467
499
|
if (visited.has(key)) {
|
|
@@ -471,7 +503,7 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
471
503
|
visited.add(key);
|
|
472
504
|
let source;
|
|
473
505
|
try {
|
|
474
|
-
source =
|
|
506
|
+
source = readSourceMemoized(memo, realPath);
|
|
475
507
|
} catch {
|
|
476
508
|
return { routes: {}, searchSchemas: {} };
|
|
477
509
|
}
|
|
@@ -479,7 +511,11 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
479
511
|
if (inlineBlock) {
|
|
480
512
|
block = inlineBlock;
|
|
481
513
|
} else if (variableName) {
|
|
482
|
-
const extracted = extractUrlsBlockForVariable(
|
|
514
|
+
const extracted = extractUrlsBlockForVariable(
|
|
515
|
+
source,
|
|
516
|
+
variableName,
|
|
517
|
+
parseBlock(memo, source)
|
|
518
|
+
);
|
|
483
519
|
if (!extracted) return { routes: {}, searchSchemas: {} };
|
|
484
520
|
block = extracted;
|
|
485
521
|
} else {
|
|
@@ -492,7 +528,8 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
492
528
|
realPath,
|
|
493
529
|
visited,
|
|
494
530
|
searchSchemas,
|
|
495
|
-
diagnosticsOut
|
|
531
|
+
diagnosticsOut,
|
|
532
|
+
memo
|
|
496
533
|
);
|
|
497
534
|
visited.delete(key);
|
|
498
535
|
return { routes, searchSchemas };
|
|
@@ -537,8 +574,16 @@ function writePerModuleRouteTypesForFile(filePath) {
|
|
|
537
574
|
let routes;
|
|
538
575
|
if (varNames.length > 0) {
|
|
539
576
|
routes = [];
|
|
577
|
+
const memo = createScanMemo();
|
|
540
578
|
for (const varName of varNames) {
|
|
541
|
-
const { routes: routeMap, searchSchemas } = buildCombinedRouteMapWithSearch(
|
|
579
|
+
const { routes: routeMap, searchSchemas } = buildCombinedRouteMapWithSearch(
|
|
580
|
+
filePath,
|
|
581
|
+
varName,
|
|
582
|
+
void 0,
|
|
583
|
+
void 0,
|
|
584
|
+
void 0,
|
|
585
|
+
memo
|
|
586
|
+
);
|
|
542
587
|
for (const [name, pattern] of Object.entries(routeMap)) {
|
|
543
588
|
const params = extractParamsFromPattern(pattern);
|
|
544
589
|
routes.push({
|
package/dist/vite/index.js
CHANGED
|
@@ -10,6 +10,13 @@ import fs from "node:fs";
|
|
|
10
10
|
// src/vite/plugins/expose-id-utils.ts
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import crypto from "node:crypto";
|
|
13
|
+
|
|
14
|
+
// src/regex-escape.ts
|
|
15
|
+
function escapeRegExp(input) {
|
|
16
|
+
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/vite/plugins/expose-id-utils.ts
|
|
13
20
|
function normalizePath(p) {
|
|
14
21
|
return p.split(path.sep).join("/");
|
|
15
22
|
}
|
|
@@ -187,8 +194,59 @@ function findStatementEnd(code, pos) {
|
|
|
187
194
|
}
|
|
188
195
|
return i;
|
|
189
196
|
}
|
|
190
|
-
function
|
|
191
|
-
|
|
197
|
+
function findCallParenAfterGenerics(code, afterCalleeIndex) {
|
|
198
|
+
let i = afterCalleeIndex;
|
|
199
|
+
while (i < code.length) {
|
|
200
|
+
const skipped = skipStringOrComment(code, i);
|
|
201
|
+
if (skipped > i) {
|
|
202
|
+
i = skipped;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (/\s/.test(code[i])) {
|
|
206
|
+
i++;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
if (i >= code.length) return -1;
|
|
212
|
+
if (code[i] === "(") return i;
|
|
213
|
+
if (code[i] === "<") {
|
|
214
|
+
let depth = 0;
|
|
215
|
+
while (i < code.length) {
|
|
216
|
+
const skipped = skipStringOrComment(code, i);
|
|
217
|
+
if (skipped > i) {
|
|
218
|
+
i = skipped;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const ch = code[i];
|
|
222
|
+
if (ch === "<") {
|
|
223
|
+
depth++;
|
|
224
|
+
} else if (ch === ">") {
|
|
225
|
+
depth--;
|
|
226
|
+
if (depth === 0) {
|
|
227
|
+
i++;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
i++;
|
|
232
|
+
}
|
|
233
|
+
if (depth !== 0) return -1;
|
|
234
|
+
while (i < code.length) {
|
|
235
|
+
const skipped = skipStringOrComment(code, i);
|
|
236
|
+
if (skipped > i) {
|
|
237
|
+
i = skipped;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (/\s/.test(code[i])) {
|
|
241
|
+
i++;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
if (i < code.length && code[i] === "(") return i;
|
|
247
|
+
return -1;
|
|
248
|
+
}
|
|
249
|
+
return -1;
|
|
192
250
|
}
|
|
193
251
|
|
|
194
252
|
// src/vite/debug.ts
|
|
@@ -850,13 +908,23 @@ function isExportOnlyFile(code, bindings) {
|
|
|
850
908
|
return true;
|
|
851
909
|
}
|
|
852
910
|
function createCallPattern(fnNames) {
|
|
853
|
-
return new RegExp(
|
|
854
|
-
|
|
855
|
-
|
|
911
|
+
return new RegExp(`\\b(?:${fnNames.map(escapeRegExp).join("|")})\\b`, "g");
|
|
912
|
+
}
|
|
913
|
+
function createCallStartIndices(code, fnNames) {
|
|
914
|
+
return codeMatchIndices(code, createCallPattern(fnNames)).filter(
|
|
915
|
+
(index) => findCallParenAfterGenerics(
|
|
916
|
+
code,
|
|
917
|
+
index + matchedNameLength(code, index)
|
|
918
|
+
) !== -1
|
|
856
919
|
);
|
|
857
920
|
}
|
|
921
|
+
function matchedNameLength(code, index) {
|
|
922
|
+
let i = index;
|
|
923
|
+
while (i < code.length && /[A-Za-z0-9_$]/.test(code[i])) i++;
|
|
924
|
+
return i - index;
|
|
925
|
+
}
|
|
858
926
|
function countCreateCallsForNames(code, fnNames) {
|
|
859
|
-
return
|
|
927
|
+
return createCallStartIndices(code, fnNames).length;
|
|
860
928
|
}
|
|
861
929
|
function offsetToLineColumn(code, index) {
|
|
862
930
|
let line = 1;
|
|
@@ -872,7 +940,7 @@ function offsetToLineColumn(code, index) {
|
|
|
872
940
|
}
|
|
873
941
|
function findUnsupportedCreateCallSites(code, fnNames, supportedBindings) {
|
|
874
942
|
const supported = new Set(supportedBindings.map((b) => b.callExprStart));
|
|
875
|
-
return
|
|
943
|
+
return createCallStartIndices(code, fnNames).filter((index) => !supported.has(index)).map((index) => offsetToLineColumn(code, index));
|
|
876
944
|
}
|
|
877
945
|
function getImportedFnNames(code, importedName) {
|
|
878
946
|
const importPattern = /import\s*\{([^}]*)\}\s*from\s*["']@rangojs\/router(?:\/[^"']*)?["']/g;
|
|
@@ -1181,14 +1249,31 @@ import MagicString2 from "magic-string";
|
|
|
1181
1249
|
import path3 from "node:path";
|
|
1182
1250
|
import { createHash } from "node:crypto";
|
|
1183
1251
|
var debug2 = createRangoDebugger(NS.transform);
|
|
1184
|
-
function
|
|
1252
|
+
function skipLeadingTrivia(code, start, end) {
|
|
1253
|
+
let i = start;
|
|
1254
|
+
while (i < end) {
|
|
1255
|
+
const skipped = skipStringOrComment(code, i);
|
|
1256
|
+
if (skipped > i) {
|
|
1257
|
+
i = skipped;
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
if (/\s/.test(code[i])) {
|
|
1261
|
+
i++;
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
return i;
|
|
1267
|
+
}
|
|
1268
|
+
function transformRouter(code, filePath, routerFnNames, absolutePath, warn) {
|
|
1185
1269
|
const pat = new RegExp(
|
|
1186
|
-
`\\b(?:${routerFnNames.map(
|
|
1270
|
+
`\\b(?:${routerFnNames.map(escapeRegExp).join("|")})\\b`,
|
|
1187
1271
|
"g"
|
|
1188
1272
|
);
|
|
1189
1273
|
let match;
|
|
1190
1274
|
const s = new MagicString2(code);
|
|
1191
1275
|
let changed = false;
|
|
1276
|
+
const unsupportedSites = [];
|
|
1192
1277
|
const basename2 = path3.basename(filePath).replace(/\.(tsx?|jsx?)$/, "");
|
|
1193
1278
|
const routeNamesImport = `./${basename2}.named-routes.gen.js`;
|
|
1194
1279
|
const routeNamesVar = `__rsc_rn`;
|
|
@@ -1197,23 +1282,35 @@ function transformRouter(code, filePath, routerFnNames, absolutePath) {
|
|
|
1197
1282
|
while ((match = pat.exec(code)) !== null) {
|
|
1198
1283
|
if (!codeOffsets.has(match.index)) continue;
|
|
1199
1284
|
const callStart = match.index;
|
|
1200
|
-
const
|
|
1285
|
+
const calleeEnd = match.index + match[0].length;
|
|
1286
|
+
const parenPos = findCallParenAfterGenerics(code, calleeEnd);
|
|
1287
|
+
if (parenPos === -1) continue;
|
|
1201
1288
|
const closeParen = findMatchingParen(code, parenPos + 1);
|
|
1202
1289
|
const callArgs = code.slice(parenPos + 1, closeParen);
|
|
1203
1290
|
if (callArgs.includes("$$id")) continue;
|
|
1291
|
+
const sourceFilePath = absolutePath ?? filePath;
|
|
1204
1292
|
const lineNumber = code.slice(0, callStart).split("\n").length;
|
|
1205
1293
|
const hash = createHash("sha256").update(`${filePath}:${lineNumber}`).digest("hex").slice(0, 8);
|
|
1206
|
-
changed = true;
|
|
1207
|
-
const sourceFilePath = absolutePath ?? filePath;
|
|
1208
1294
|
const injected = ` $$id: "${hash}", $$sourceFile: "${sourceFilePath}", $$routeNames: ${routeNamesVar},`;
|
|
1209
|
-
const
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1295
|
+
const argsContentStart = skipLeadingTrivia(code, parenPos + 1, closeParen);
|
|
1296
|
+
const firstArgChar = code[argsContentStart];
|
|
1297
|
+
if (firstArgChar === "{") {
|
|
1298
|
+
changed = true;
|
|
1299
|
+
s.appendRight(argsContentStart + 1, injected);
|
|
1300
|
+
} else if (argsContentStart >= closeParen - 1) {
|
|
1301
|
+
changed = true;
|
|
1214
1302
|
s.appendRight(parenPos + 1, `{${injected} }`);
|
|
1303
|
+
} else {
|
|
1304
|
+
const lastNl = code.lastIndexOf("\n", callStart - 1);
|
|
1305
|
+
const column = callStart - (lastNl + 1) + 1;
|
|
1306
|
+
unsupportedSites.push({ line: lineNumber, column });
|
|
1215
1307
|
}
|
|
1216
1308
|
}
|
|
1309
|
+
if (unsupportedSites.length > 0 && warn) {
|
|
1310
|
+
warn(
|
|
1311
|
+
buildUnsupportedShapeWarning(filePath, "createRouter", unsupportedSites)
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1217
1314
|
if (!changed) return null;
|
|
1218
1315
|
s.prepend(
|
|
1219
1316
|
`import { NamedRoutes as ${routeNamesVar} } from "${routeNamesImport}";
|
|
@@ -1247,11 +1344,13 @@ function exposeRouterId() {
|
|
|
1247
1344
|
try {
|
|
1248
1345
|
const filePath = normalizePath(path3.relative(projectRoot, id));
|
|
1249
1346
|
const routerFnNames = getImportedFnNames(code, "createRouter");
|
|
1347
|
+
const warn = typeof this.warn === "function" ? (message) => this.warn(message) : void 0;
|
|
1250
1348
|
return transformRouter(
|
|
1251
1349
|
code,
|
|
1252
1350
|
filePath,
|
|
1253
1351
|
routerFnNames,
|
|
1254
|
-
normalizePath(id)
|
|
1352
|
+
normalizePath(id),
|
|
1353
|
+
warn
|
|
1255
1354
|
);
|
|
1256
1355
|
} finally {
|
|
1257
1356
|
counter?.record(id, performance.now() - start);
|
|
@@ -2133,7 +2232,7 @@ import { resolve } from "node:path";
|
|
|
2133
2232
|
// package.json
|
|
2134
2233
|
var package_default = {
|
|
2135
2234
|
name: "@rangojs/router",
|
|
2136
|
-
version: "0.0.0-experimental.
|
|
2235
|
+
version: "0.0.0-experimental.132",
|
|
2137
2236
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
2138
2237
|
keywords: [
|
|
2139
2238
|
"react",
|
|
@@ -2296,7 +2395,7 @@ var package_default = {
|
|
|
2296
2395
|
tag: "experimental"
|
|
2297
2396
|
},
|
|
2298
2397
|
scripts: {
|
|
2299
|
-
build: "pnpm
|
|
2398
|
+
build: "pnpm exec esbuild src/vite/index.ts --bundle --format=esm --outfile=dist/vite/index.js --platform=node --packages=external && mkdir -p dist/vite/plugins && cp src/vite/plugins/cloudflare-protocol-loader-hook.mjs dist/vite/plugins/cloudflare-protocol-loader-hook.mjs && pnpm exec esbuild src/testing/vitest.ts --bundle --format=esm --outfile=dist/testing/vitest.js --platform=node --packages=external && pnpm exec esbuild src/bin/rango.ts --bundle --format=esm --outfile=dist/bin/rango.js --platform=node --packages=external --banner:js='#!/usr/bin/env node' && chmod +x dist/bin/rango.js",
|
|
2300
2399
|
prepublishOnly: "pnpm build",
|
|
2301
2400
|
typecheck: "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit && tsc -p tsconfig.augment-check.json --noEmit",
|
|
2302
2401
|
test: "playwright test",
|
|
@@ -2311,7 +2410,7 @@ var package_default = {
|
|
|
2311
2410
|
"@vitejs/plugin-rsc": "^0.5.26",
|
|
2312
2411
|
debug: "^4.4.1",
|
|
2313
2412
|
"magic-string": "^0.30.17",
|
|
2314
|
-
picomatch: "^4.0.
|
|
2413
|
+
picomatch: "^4.0.4",
|
|
2315
2414
|
"rsc-html-stream": "^0.0.7",
|
|
2316
2415
|
tinyexec: "^0.3.2"
|
|
2317
2416
|
},
|
|
@@ -2357,6 +2456,9 @@ var package_default = {
|
|
|
2357
2456
|
vitest: {
|
|
2358
2457
|
optional: true
|
|
2359
2458
|
}
|
|
2459
|
+
},
|
|
2460
|
+
engines: {
|
|
2461
|
+
node: "^20.19.0 || >=22.12.0"
|
|
2360
2462
|
}
|
|
2361
2463
|
};
|
|
2362
2464
|
|
|
@@ -2459,8 +2561,8 @@ function extractObjectStringProperties(node) {
|
|
|
2459
2561
|
}
|
|
2460
2562
|
|
|
2461
2563
|
// src/build/route-types/ast-route-extraction.ts
|
|
2462
|
-
function extractRoutesFromSource(code) {
|
|
2463
|
-
const sourceFile = ts2.createSourceFile(
|
|
2564
|
+
function extractRoutesFromSource(code, sourceFileArg) {
|
|
2565
|
+
const sourceFile = sourceFileArg ?? ts2.createSourceFile(
|
|
2464
2566
|
"input.tsx",
|
|
2465
2567
|
code,
|
|
2466
2568
|
ts2.ScriptTarget.Latest,
|
|
@@ -2586,6 +2688,33 @@ import ts4 from "typescript";
|
|
|
2586
2688
|
import { readFileSync, existsSync as existsSync2 } from "node:fs";
|
|
2587
2689
|
import { dirname, resolve as resolve2 } from "node:path";
|
|
2588
2690
|
import ts3 from "typescript";
|
|
2691
|
+
function createScanMemo() {
|
|
2692
|
+
return { files: /* @__PURE__ */ new Map(), blockSourceFiles: /* @__PURE__ */ new Map() };
|
|
2693
|
+
}
|
|
2694
|
+
function parseBlock(memo, block) {
|
|
2695
|
+
if (memo) {
|
|
2696
|
+
const cached = memo.blockSourceFiles.get(block);
|
|
2697
|
+
if (cached) return cached;
|
|
2698
|
+
}
|
|
2699
|
+
const sf = ts3.createSourceFile(
|
|
2700
|
+
"input.tsx",
|
|
2701
|
+
block,
|
|
2702
|
+
ts3.ScriptTarget.Latest,
|
|
2703
|
+
true,
|
|
2704
|
+
ts3.ScriptKind.TSX
|
|
2705
|
+
);
|
|
2706
|
+
if (memo) memo.blockSourceFiles.set(block, sf);
|
|
2707
|
+
return sf;
|
|
2708
|
+
}
|
|
2709
|
+
function readSourceMemoized(memo, realPath) {
|
|
2710
|
+
if (memo) {
|
|
2711
|
+
const cached = memo.files.get(realPath);
|
|
2712
|
+
if (cached !== void 0) return cached;
|
|
2713
|
+
}
|
|
2714
|
+
const source = readFileSync(realPath, "utf-8");
|
|
2715
|
+
if (memo) memo.files.set(realPath, source);
|
|
2716
|
+
return source;
|
|
2717
|
+
}
|
|
2589
2718
|
function extractNamePrefixFromInclude(node) {
|
|
2590
2719
|
if (node.arguments.length >= 3) {
|
|
2591
2720
|
const thirdArg = node.arguments[2];
|
|
@@ -2601,8 +2730,8 @@ function extractNamePrefixFromInclude(node) {
|
|
|
2601
2730
|
}
|
|
2602
2731
|
return null;
|
|
2603
2732
|
}
|
|
2604
|
-
function extractIncludesWithDiagnostics(code) {
|
|
2605
|
-
const sourceFile = ts3.createSourceFile(
|
|
2733
|
+
function extractIncludesWithDiagnostics(code, sourceFileArg) {
|
|
2734
|
+
const sourceFile = sourceFileArg ?? ts3.createSourceFile(
|
|
2606
2735
|
"input.tsx",
|
|
2607
2736
|
code,
|
|
2608
2737
|
ts3.ScriptTarget.Latest,
|
|
@@ -2693,8 +2822,8 @@ function resolveImportPath(importSpec, fromFile) {
|
|
|
2693
2822
|
}
|
|
2694
2823
|
return null;
|
|
2695
2824
|
}
|
|
2696
|
-
function extractUrlsBlockForVariable(code, varName) {
|
|
2697
|
-
const sourceFile = ts3.createSourceFile(
|
|
2825
|
+
function extractUrlsBlockForVariable(code, varName, sourceFileArg) {
|
|
2826
|
+
const sourceFile = sourceFileArg ?? ts3.createSourceFile(
|
|
2698
2827
|
"input.tsx",
|
|
2699
2828
|
code,
|
|
2700
2829
|
ts3.ScriptTarget.Latest,
|
|
@@ -2716,16 +2845,20 @@ function extractUrlsBlockForVariable(code, varName) {
|
|
|
2716
2845
|
visit(sourceFile);
|
|
2717
2846
|
return result;
|
|
2718
2847
|
}
|
|
2719
|
-
function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSchemasOut, diagnosticsOut) {
|
|
2848
|
+
function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSchemasOut, diagnosticsOut, memo) {
|
|
2720
2849
|
const routeMap = {};
|
|
2721
|
-
const
|
|
2850
|
+
const blockSourceFile = parseBlock(memo, block);
|
|
2851
|
+
const localRoutes = extractRoutesFromSource(block, blockSourceFile);
|
|
2722
2852
|
for (const { name, pattern, search } of localRoutes) {
|
|
2723
2853
|
routeMap[name] = pattern;
|
|
2724
2854
|
if (search && searchSchemasOut) {
|
|
2725
2855
|
searchSchemasOut[name] = search;
|
|
2726
2856
|
}
|
|
2727
2857
|
}
|
|
2728
|
-
const { resolved: includes, unresolvable } = extractIncludesWithDiagnostics(
|
|
2858
|
+
const { resolved: includes, unresolvable } = extractIncludesWithDiagnostics(
|
|
2859
|
+
block,
|
|
2860
|
+
blockSourceFile
|
|
2861
|
+
);
|
|
2729
2862
|
if (diagnosticsOut) {
|
|
2730
2863
|
for (const entry of unresolvable) {
|
|
2731
2864
|
diagnosticsOut.push({ ...entry, sourceFile: filePath });
|
|
@@ -2752,12 +2885,15 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
2752
2885
|
targetFile,
|
|
2753
2886
|
imported.exportedName,
|
|
2754
2887
|
visited,
|
|
2755
|
-
diagnosticsOut
|
|
2888
|
+
diagnosticsOut,
|
|
2889
|
+
void 0,
|
|
2890
|
+
memo
|
|
2756
2891
|
);
|
|
2757
2892
|
} else {
|
|
2758
2893
|
const sameFileBlock = extractUrlsBlockForVariable(
|
|
2759
2894
|
fullSource,
|
|
2760
|
-
variableName
|
|
2895
|
+
variableName,
|
|
2896
|
+
parseBlock(memo, fullSource)
|
|
2761
2897
|
);
|
|
2762
2898
|
if (!sameFileBlock) {
|
|
2763
2899
|
if (diagnosticsOut) {
|
|
@@ -2775,7 +2911,9 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
2775
2911
|
filePath,
|
|
2776
2912
|
variableName,
|
|
2777
2913
|
visited,
|
|
2778
|
-
diagnosticsOut
|
|
2914
|
+
diagnosticsOut,
|
|
2915
|
+
void 0,
|
|
2916
|
+
memo
|
|
2779
2917
|
);
|
|
2780
2918
|
}
|
|
2781
2919
|
if (namePrefix === null) {
|
|
@@ -2799,8 +2937,9 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
2799
2937
|
}
|
|
2800
2938
|
return routeMap;
|
|
2801
2939
|
}
|
|
2802
|
-
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock) {
|
|
2940
|
+
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock, memo) {
|
|
2803
2941
|
visited = visited ?? /* @__PURE__ */ new Set();
|
|
2942
|
+
memo = memo ?? createScanMemo();
|
|
2804
2943
|
const realPath = resolve2(filePath);
|
|
2805
2944
|
const key = variableName ? `${realPath}:${variableName}` : realPath;
|
|
2806
2945
|
if (visited.has(key)) {
|
|
@@ -2810,7 +2949,7 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
2810
2949
|
visited.add(key);
|
|
2811
2950
|
let source;
|
|
2812
2951
|
try {
|
|
2813
|
-
source =
|
|
2952
|
+
source = readSourceMemoized(memo, realPath);
|
|
2814
2953
|
} catch {
|
|
2815
2954
|
return { routes: {}, searchSchemas: {} };
|
|
2816
2955
|
}
|
|
@@ -2818,7 +2957,11 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
2818
2957
|
if (inlineBlock) {
|
|
2819
2958
|
block = inlineBlock;
|
|
2820
2959
|
} else if (variableName) {
|
|
2821
|
-
const extracted = extractUrlsBlockForVariable(
|
|
2960
|
+
const extracted = extractUrlsBlockForVariable(
|
|
2961
|
+
source,
|
|
2962
|
+
variableName,
|
|
2963
|
+
parseBlock(memo, source)
|
|
2964
|
+
);
|
|
2822
2965
|
if (!extracted) return { routes: {}, searchSchemas: {} };
|
|
2823
2966
|
block = extracted;
|
|
2824
2967
|
} else {
|
|
@@ -2831,7 +2974,8 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
2831
2974
|
realPath,
|
|
2832
2975
|
visited,
|
|
2833
2976
|
searchSchemas,
|
|
2834
|
-
diagnosticsOut
|
|
2977
|
+
diagnosticsOut,
|
|
2978
|
+
memo
|
|
2835
2979
|
);
|
|
2836
2980
|
visited.delete(key);
|
|
2837
2981
|
return { routes, searchSchemas };
|
|
@@ -4437,9 +4581,6 @@ import {
|
|
|
4437
4581
|
writeFileSync as writeFileSync2
|
|
4438
4582
|
} from "node:fs";
|
|
4439
4583
|
import { resolve as resolve5 } from "node:path";
|
|
4440
|
-
function escapeRegExp2(str) {
|
|
4441
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4442
|
-
}
|
|
4443
4584
|
function encodePathParam(value) {
|
|
4444
4585
|
return String(value).split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
4445
4586
|
}
|
|
@@ -4447,7 +4588,7 @@ function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
|
|
|
4447
4588
|
let result = pattern;
|
|
4448
4589
|
let hadOmittedOptional = false;
|
|
4449
4590
|
for (const [key, value] of Object.entries(params)) {
|
|
4450
|
-
const escaped =
|
|
4591
|
+
const escaped = escapeRegExp(key);
|
|
4451
4592
|
if (value === "") {
|
|
4452
4593
|
result = result.replace(
|
|
4453
4594
|
new RegExp(`:${escaped}(\\([^)]*\\))?(?!\\?)`),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rangojs/router",
|
|
3
|
-
"version": "0.0.0-experimental.
|
|
3
|
+
"version": "0.0.0-experimental.132",
|
|
4
4
|
"description": "Django-inspired RSC router with composable URL patterns",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
"@vitejs/plugin-rsc": "^0.5.26",
|
|
168
168
|
"debug": "^4.4.1",
|
|
169
169
|
"magic-string": "^0.30.17",
|
|
170
|
-
"picomatch": "^4.0.
|
|
170
|
+
"picomatch": "^4.0.4",
|
|
171
171
|
"rsc-html-stream": "^0.0.7",
|
|
172
172
|
"tinyexec": "^0.3.2"
|
|
173
173
|
},
|
|
@@ -214,8 +214,11 @@
|
|
|
214
214
|
"optional": true
|
|
215
215
|
}
|
|
216
216
|
},
|
|
217
|
+
"engines": {
|
|
218
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
219
|
+
},
|
|
217
220
|
"scripts": {
|
|
218
|
-
"build": "pnpm
|
|
221
|
+
"build": "pnpm exec esbuild src/vite/index.ts --bundle --format=esm --outfile=dist/vite/index.js --platform=node --packages=external && mkdir -p dist/vite/plugins && cp src/vite/plugins/cloudflare-protocol-loader-hook.mjs dist/vite/plugins/cloudflare-protocol-loader-hook.mjs && pnpm exec esbuild src/testing/vitest.ts --bundle --format=esm --outfile=dist/testing/vitest.js --platform=node --packages=external && pnpm exec esbuild src/bin/rango.ts --bundle --format=esm --outfile=dist/bin/rango.js --platform=node --packages=external --banner:js='#!/usr/bin/env node' && chmod +x dist/bin/rango.js",
|
|
219
222
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit && tsc -p tsconfig.augment-check.json --noEmit",
|
|
220
223
|
"test": "playwright test",
|
|
221
224
|
"test:ui": "playwright test --ui",
|