@rangojs/router 0.30.1 → 0.31.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/bin/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/dist/bin/rango.js +7861 -197
- package/dist/types/build/generate-route-types.d.ts +1 -1
- package/dist/types/build/route-types/include-resolution.d.ts +1 -1
- package/dist/types/build/route-types/router-processing.d.ts +3 -7
- package/dist/types/vite/temp-rsc-server.d.ts +8 -4
- package/dist/types/vite/utils/forward-user-plugins.d.ts +3 -1
- package/dist/vite/index.js +119 -36
- package/package.json +2 -2
- package/skills/rango/SKILL.md +5 -5
- package/skills/typesafety/generated-files-and-cli.md +2 -1
- package/src/bin/rango.ts +13 -8
- package/src/build/generate-route-types.ts +0 -1
- package/src/build/route-types/include-resolution.ts +2 -1
- package/src/build/route-types/router-processing.ts +213 -49
- package/src/build/runtime-discovery.ts +90 -41
- package/src/client-urls/server-projection.ts +3 -1
- package/src/vite/temp-rsc-server.ts +9 -4
- package/src/vite/utils/forward-user-plugins.ts +4 -2
|
@@ -4,5 +4,5 @@ export { generatePerModuleTypesSource, generateRouteTypesSource, } from "./route
|
|
|
4
4
|
export { DEFAULT_EXCLUDE_PATTERNS, type ScanFilter, createScanFilter, findTsFiles, isInSkippedScanDir, } from "./route-types/scan-filter.js";
|
|
5
5
|
export { writePerModuleRouteTypes, writePerModuleRouteTypesForFile, } from "./route-types/per-module-writer.js";
|
|
6
6
|
export { type UnresolvableReason, type UnresolvableInclude, extractIncludesWithDiagnostics, } from "./route-types/include-resolution.js";
|
|
7
|
-
export { extractUrlsFromRouter,
|
|
7
|
+
export { extractUrlsFromRouter, type UrlsExtractionResult, buildCombinedRouteMapForRouterFile, detectUnresolvableIncludes, detectUnresolvableIncludesForUrlsFile, findNestedRouterConflict, formatNestedRouterConflictError, findRouterFiles, findHostRouterFiles, writeCombinedRouteTypes, genFileTsPath, resolveSearchSchemas, } from "./route-types/router-processing.js";
|
|
8
8
|
export { findUrlsVariableNames } from "./route-types/per-module-writer.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
-
export type UnresolvableReason = "factory-call" | "dynamic-expression" | "unresolvable-import" | "file-not-found";
|
|
2
|
+
export type UnresolvableReason = "factory-call" | "dynamic-expression" | "unresolvable-import" | "file-not-found" | "unresolvable-basename";
|
|
3
3
|
export interface UnresolvableInclude {
|
|
4
4
|
pathPrefix: string;
|
|
5
5
|
namePrefix: string | null;
|
|
@@ -30,11 +30,6 @@ export type UrlsExtractionResult = {
|
|
|
30
30
|
* Returns either a variable name or an inline code block.
|
|
31
31
|
*/
|
|
32
32
|
export declare function extractUrlsFromRouter(code: string): UrlsExtractionResult | null;
|
|
33
|
-
/**
|
|
34
|
-
* Extract the `basename` string literal from createRouter({ basename: "..." }).
|
|
35
|
-
* Returns the basename value or undefined if not present.
|
|
36
|
-
*/
|
|
37
|
-
export declare function extractBasenameFromRouter(code: string): string | undefined;
|
|
38
33
|
export declare function genFileTsPath(sourceFile: string): string;
|
|
39
34
|
export declare function resolveSearchSchemas(publicRouteNames: string[], runtimeSchemas: Record<string, Record<string, string>> | undefined, sourceFile: string): Record<string, Record<string, string>> | undefined;
|
|
40
35
|
/**
|
|
@@ -47,8 +42,9 @@ export declare function buildCombinedRouteMapForRouterFile(routerFilePath: strin
|
|
|
47
42
|
};
|
|
48
43
|
/**
|
|
49
44
|
* Walk the full include tree starting from a router file and detect
|
|
50
|
-
* all includes that the static parser cannot resolve
|
|
51
|
-
*
|
|
45
|
+
* all includes that the static parser cannot resolve, plus an explicit
|
|
46
|
+
* createRouter `basename` it cannot read (every pattern would lack the
|
|
47
|
+
* prefix). Returns an array of diagnostics; empty means fully resolvable.
|
|
52
48
|
*/
|
|
53
49
|
export declare function detectUnresolvableIncludes(routerFilePath: string): UnresolvableInclude[];
|
|
54
50
|
/**
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The discovery temp RSC server: a minimal Vite server whose module runner
|
|
3
3
|
* imports the user's router files. Shared by dev discovery
|
|
4
|
-
* (discovery/dev-server-phase.ts)
|
|
5
|
-
* (discovery/build-discovery-phase.ts)
|
|
4
|
+
* (discovery/dev-server-phase.ts), build discovery
|
|
5
|
+
* (discovery/build-discovery-phase.ts) and `rango generate --runtime`
|
|
6
|
+
* (build/runtime-discovery.ts).
|
|
6
7
|
*
|
|
7
8
|
* Sits in src/vite/, not src/vite/discovery/: the loader hook URL below
|
|
8
|
-
* resolves against this module's import.meta.url, and the published
|
|
9
|
-
* (dist/vite/index.js)
|
|
9
|
+
* resolves against this module's import.meta.url, and the published bundles
|
|
10
|
+
* (dist/vite/index.js, dist/bin/rango.js) ship the hook at the same relative
|
|
11
|
+
* path.
|
|
10
12
|
*/
|
|
11
13
|
import type { ViteDevServer } from "vite";
|
|
12
14
|
import type { DiscoveryState } from "./discovery/state.js";
|
|
@@ -36,4 +38,6 @@ export declare function createTempRscServer(state: DiscoveryState, options?: {
|
|
|
36
38
|
* shell capture actually runs.
|
|
37
39
|
*/
|
|
38
40
|
realSsrEntry?: boolean;
|
|
41
|
+
/** Caller-specific plugins, appended after the forwarded user plugins. */
|
|
42
|
+
plugins?: import("vite").Plugin[];
|
|
39
43
|
}): Promise<ViteDevServer>;
|
|
@@ -20,6 +20,8 @@ export interface ForwardedRunnerConfig {
|
|
|
20
20
|
/**
|
|
21
21
|
* Extract the data-only config slice to mirror into the discovery temp server.
|
|
22
22
|
* `alias` is included here so callers no longer need to thread it separately.
|
|
23
|
+
* Accepts the unresolved config too: `rango generate --runtime` must not
|
|
24
|
+
* resolve it (that runs rango()'s configResolved, which writes gen files).
|
|
23
25
|
*
|
|
24
26
|
* `tsconfigPaths` is forwarded so Vite 8's native tsconfig `paths` resolution
|
|
25
27
|
* (a top-level `resolve` flag, off by default) reaches the temp server. The
|
|
@@ -34,4 +36,4 @@ export interface ForwardedRunnerConfig {
|
|
|
34
36
|
* replaced the deprecated `esbuild` transform option with `oxc`, so we read and
|
|
35
37
|
* forward `oxc` exclusively — no `esbuild` field is touched.
|
|
36
38
|
*/
|
|
37
|
-
export declare function pickForwardedRunnerConfig(config: ResolvedConfig): ForwardedRunnerConfig;
|
|
39
|
+
export declare function pickForwardedRunnerConfig(config: Pick<ResolvedConfig | UserConfig, "resolve" | "define" | "oxc">): ForwardedRunnerConfig;
|
package/dist/vite/index.js
CHANGED
|
@@ -4841,6 +4841,11 @@ function getAppVersion() {
|
|
|
4841
4841
|
}
|
|
4842
4842
|
|
|
4843
4843
|
// src/router/basename.ts
|
|
4844
|
+
function normalizeBasename(basename2) {
|
|
4845
|
+
if (!basename2) return void 0;
|
|
4846
|
+
const trimmed = basename2.replace(/^\/+|\/+$/g, "");
|
|
4847
|
+
return trimmed ? "/" + trimmed : void 0;
|
|
4848
|
+
}
|
|
4844
4849
|
function prefixBasename(path6, basename2) {
|
|
4845
4850
|
if (!basename2 || !path6.startsWith("/") || path6 === basename2 || path6.startsWith(basename2 + "/") || path6.startsWith(basename2 + "?") || path6.startsWith(basename2 + "#")) {
|
|
4846
4851
|
return path6;
|
|
@@ -5988,7 +5993,7 @@ function serializeSearchSchema(route2, value) {
|
|
|
5988
5993
|
throw projectionError(route2, 'path option "search" must be an object');
|
|
5989
5994
|
}
|
|
5990
5995
|
const entries = [];
|
|
5991
|
-
for (const key of Object.keys(value)
|
|
5996
|
+
for (const key of Object.keys(value)) {
|
|
5992
5997
|
const schemaValue = value[key];
|
|
5993
5998
|
if (typeof schemaValue !== "string" || !SEARCH_SCHEMA_VALUES.has(schemaValue)) {
|
|
5994
5999
|
throw projectionError(
|
|
@@ -7141,7 +7146,7 @@ function resolveVersionsFrom(table, routerId2) {
|
|
|
7141
7146
|
// package.json
|
|
7142
7147
|
var package_default = {
|
|
7143
7148
|
name: "@rangojs/router",
|
|
7144
|
-
version: "0.
|
|
7149
|
+
version: "0.31.0",
|
|
7145
7150
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
7146
7151
|
keywords: [
|
|
7147
7152
|
"react",
|
|
@@ -7334,7 +7339,7 @@ var package_default = {
|
|
|
7334
7339
|
tag: "latest"
|
|
7335
7340
|
},
|
|
7336
7341
|
scripts: {
|
|
7337
|
-
build: "pnpm run build:types && 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",
|
|
7342
|
+
build: "pnpm run build:types && 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 && mkdir -p dist/bin/plugins && cp src/vite/plugins/cloudflare-protocol-loader-hook.mjs dist/bin/plugins/cloudflare-protocol-loader-hook.mjs",
|
|
7338
7343
|
"build:types": "rm -rf dist/types && pnpm exec tsc -p tsconfig.types.json",
|
|
7339
7344
|
prepublishOnly: "pnpm build",
|
|
7340
7345
|
typecheck: "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit && tsc -p tsconfig.augment-check.json --noEmit",
|
|
@@ -9325,14 +9330,7 @@ function collectCreateRouterCallChain(endpoint) {
|
|
|
9325
9330
|
current = receiver;
|
|
9326
9331
|
}
|
|
9327
9332
|
}
|
|
9328
|
-
function
|
|
9329
|
-
const sourceFile = ts5.createSourceFile(
|
|
9330
|
-
"router.tsx",
|
|
9331
|
-
code,
|
|
9332
|
-
ts5.ScriptTarget.Latest,
|
|
9333
|
-
true,
|
|
9334
|
-
ts5.ScriptKind.TSX
|
|
9335
|
-
);
|
|
9333
|
+
function findRouterUrls(sourceFile) {
|
|
9336
9334
|
let result = null;
|
|
9337
9335
|
function visit(node) {
|
|
9338
9336
|
if (result) return;
|
|
@@ -9360,7 +9358,7 @@ function extractAllUrlsFromRouter(code) {
|
|
|
9360
9358
|
if (urls2) extracted.push(urls2);
|
|
9361
9359
|
}
|
|
9362
9360
|
if (extracted.length > 0) {
|
|
9363
|
-
result = extracted;
|
|
9361
|
+
result = { createRouterCall, extractions: extracted };
|
|
9364
9362
|
return;
|
|
9365
9363
|
}
|
|
9366
9364
|
}
|
|
@@ -9368,13 +9366,9 @@ function extractAllUrlsFromRouter(code) {
|
|
|
9368
9366
|
ts5.forEachChild(node, visit);
|
|
9369
9367
|
}
|
|
9370
9368
|
visit(sourceFile);
|
|
9371
|
-
return result
|
|
9372
|
-
}
|
|
9373
|
-
function extractUrlsFromRouter(code) {
|
|
9374
|
-
const extractions = extractAllUrlsFromRouter(code);
|
|
9375
|
-
return extractions[extractions.length - 1] ?? null;
|
|
9369
|
+
return result;
|
|
9376
9370
|
}
|
|
9377
|
-
function
|
|
9371
|
+
function extractAllUrlsFromRouter(code) {
|
|
9378
9372
|
const sourceFile = ts5.createSourceFile(
|
|
9379
9373
|
"router.tsx",
|
|
9380
9374
|
code,
|
|
@@ -9382,25 +9376,112 @@ function extractBasenameFromRouter(code) {
|
|
|
9382
9376
|
true,
|
|
9383
9377
|
ts5.ScriptKind.TSX
|
|
9384
9378
|
);
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9379
|
+
return findRouterUrls(sourceFile)?.extractions ?? [];
|
|
9380
|
+
}
|
|
9381
|
+
function extractUrlsFromRouter(code) {
|
|
9382
|
+
const extractions = extractAllUrlsFromRouter(code);
|
|
9383
|
+
return extractions[extractions.length - 1] ?? null;
|
|
9384
|
+
}
|
|
9385
|
+
function unwrapTypeOperators(expr) {
|
|
9386
|
+
while (ts5.isAsExpression(expr) || ts5.isSatisfiesExpression(expr)) {
|
|
9387
|
+
expr = expr.expression;
|
|
9388
|
+
}
|
|
9389
|
+
return expr;
|
|
9390
|
+
}
|
|
9391
|
+
function hasModifier(statement, kind) {
|
|
9392
|
+
return ts5.canHaveModifiers(statement) && (ts5.getModifiers(statement)?.some((m) => m.kind === kind) ?? false);
|
|
9393
|
+
}
|
|
9394
|
+
function findTopLevelConst(sourceFile, name, exported) {
|
|
9395
|
+
for (const statement of sourceFile.statements) {
|
|
9396
|
+
if (ts5.isVariableStatement(statement) && statement.declarationList.flags & ts5.NodeFlags.Const && !hasModifier(statement, ts5.SyntaxKind.DeclareKeyword) && (!exported || hasModifier(statement, ts5.SyntaxKind.ExportKeyword))) {
|
|
9397
|
+
for (const decl of statement.declarationList.declarations) {
|
|
9398
|
+
if (ts5.isIdentifier(decl.name) && decl.name.text === name) {
|
|
9399
|
+
return decl.initializer && unwrapTypeOperators(decl.initializer);
|
|
9397
9400
|
}
|
|
9398
9401
|
}
|
|
9399
9402
|
}
|
|
9400
|
-
ts5.forEachChild(node, visit);
|
|
9401
9403
|
}
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
+
return void 0;
|
|
9405
|
+
}
|
|
9406
|
+
function findNamedImport(sourceFile, name) {
|
|
9407
|
+
for (const statement of sourceFile.statements) {
|
|
9408
|
+
if (!ts5.isImportDeclaration(statement) || !ts5.isStringLiteral(statement.moduleSpecifier) || statement.importClause?.isTypeOnly) {
|
|
9409
|
+
continue;
|
|
9410
|
+
}
|
|
9411
|
+
const bindings = statement.importClause?.namedBindings;
|
|
9412
|
+
if (!bindings || !ts5.isNamedImports(bindings)) continue;
|
|
9413
|
+
for (const el of bindings.elements) {
|
|
9414
|
+
if (!el.isTypeOnly && el.name.text === name) {
|
|
9415
|
+
return {
|
|
9416
|
+
specifier: statement.moduleSpecifier.text,
|
|
9417
|
+
exportedName: el.propertyName?.text ?? name
|
|
9418
|
+
};
|
|
9419
|
+
}
|
|
9420
|
+
}
|
|
9421
|
+
}
|
|
9422
|
+
return void 0;
|
|
9423
|
+
}
|
|
9424
|
+
function resolveTopLevelConst(name, sourceFile, routerFilePath) {
|
|
9425
|
+
const local = findTopLevelConst(sourceFile, name, false);
|
|
9426
|
+
if (local) return local;
|
|
9427
|
+
const imported = findNamedImport(sourceFile, name);
|
|
9428
|
+
if (!imported) return void 0;
|
|
9429
|
+
const target = resolveImportPath(imported.specifier, routerFilePath);
|
|
9430
|
+
if (!target) return void 0;
|
|
9431
|
+
const targetSource = readFileSync4(target, "utf-8");
|
|
9432
|
+
return findTopLevelConst(
|
|
9433
|
+
ts5.createSourceFile(target, targetSource, ts5.ScriptTarget.Latest, true),
|
|
9434
|
+
imported.exportedName,
|
|
9435
|
+
true
|
|
9436
|
+
);
|
|
9437
|
+
}
|
|
9438
|
+
function isTopLevelScope(node) {
|
|
9439
|
+
for (let p = node.parent; !ts5.isSourceFile(p); p = p.parent) {
|
|
9440
|
+
if (ts5.isFunctionLike(p) || ts5.isClassLike(p) || ts5.isBlock(p) || ts5.isCaseBlock(p) || ts5.isModuleBlock(p) || ts5.isIterationStatement(p, false)) {
|
|
9441
|
+
return false;
|
|
9442
|
+
}
|
|
9443
|
+
}
|
|
9444
|
+
return true;
|
|
9445
|
+
}
|
|
9446
|
+
function resolveRouterBasename(routerSource, routerFilePath) {
|
|
9447
|
+
const sourceFile = ts5.createSourceFile(
|
|
9448
|
+
routerFilePath,
|
|
9449
|
+
routerSource,
|
|
9450
|
+
ts5.ScriptTarget.Latest,
|
|
9451
|
+
true,
|
|
9452
|
+
ts5.ScriptKind.TSX
|
|
9453
|
+
);
|
|
9454
|
+
const options = findRouterUrls(sourceFile)?.createRouterCall.arguments[0];
|
|
9455
|
+
const prop = options && ts5.isObjectLiteralExpression(options) ? options.properties.find(
|
|
9456
|
+
(p) => (ts5.isPropertyAssignment(p) || ts5.isShorthandPropertyAssignment(p)) && ts5.isIdentifier(p.name) && p.name.text === "basename"
|
|
9457
|
+
) : void 0;
|
|
9458
|
+
if (!prop) return {};
|
|
9459
|
+
const valueExpr = ts5.isPropertyAssignment(prop) ? prop.initializer : prop.name;
|
|
9460
|
+
const expr = unwrapTypeOperators(valueExpr);
|
|
9461
|
+
let value = getStringValue(expr);
|
|
9462
|
+
if (value === null && isTopLevelScope(prop)) {
|
|
9463
|
+
if (ts5.isIdentifier(expr)) {
|
|
9464
|
+
const init = resolveTopLevelConst(expr.text, sourceFile, routerFilePath);
|
|
9465
|
+
value = init ? getStringValue(init) : null;
|
|
9466
|
+
} else if (ts5.isPropertyAccessExpression(expr) && ts5.isIdentifier(expr.expression)) {
|
|
9467
|
+
const owner = resolveTopLevelConst(
|
|
9468
|
+
expr.expression.text,
|
|
9469
|
+
sourceFile,
|
|
9470
|
+
routerFilePath
|
|
9471
|
+
);
|
|
9472
|
+
value = owner ? resolveConstMember(owner, expr.name.text) : null;
|
|
9473
|
+
}
|
|
9474
|
+
}
|
|
9475
|
+
return value === null ? { unresolved: valueExpr.getText(sourceFile).replace(/\s+/g, " ") } : { value };
|
|
9476
|
+
}
|
|
9477
|
+
function resolveConstMember(owner, name) {
|
|
9478
|
+
if (!ts5.isObjectLiteralExpression(owner) || owner.properties.some(ts5.isSpreadAssignment)) {
|
|
9479
|
+
return null;
|
|
9480
|
+
}
|
|
9481
|
+
const member = owner.properties.find(
|
|
9482
|
+
(p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === name
|
|
9483
|
+
);
|
|
9484
|
+
return member ? getStringValue(unwrapTypeOperators(member.initializer)) : null;
|
|
9404
9485
|
}
|
|
9405
9486
|
function applyBasenameToRoutes(result, basename2) {
|
|
9406
9487
|
const prefixed = {};
|
|
@@ -9498,8 +9579,9 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
|
9498
9579
|
if (extractions.length === 0) {
|
|
9499
9580
|
return { routes: {}, searchSchemas: {} };
|
|
9500
9581
|
}
|
|
9501
|
-
const
|
|
9502
|
-
|
|
9582
|
+
const basename2 = normalizeBasename(
|
|
9583
|
+
resolveRouterBasename(routerSource, routerFilePath).value
|
|
9584
|
+
);
|
|
9503
9585
|
const result = { routes: {}, searchSchemas: {} };
|
|
9504
9586
|
const memo = createScanMemo();
|
|
9505
9587
|
for (const extraction of extractions) {
|
|
@@ -12693,7 +12775,8 @@ async function createTempRscServer(state, options = {}) {
|
|
|
12693
12775
|
// to resolveId/load (plus transform for import rewriters, see
|
|
12694
12776
|
// IMPORT_REWRITE_PLUGINS) and placed last so framework resolution runs first;
|
|
12695
12777
|
// Vite re-sorts by `enforce`, so `enforce: "pre"` resolvers still lead.
|
|
12696
|
-
...state.userResolvePlugins
|
|
12778
|
+
...state.userResolvePlugins,
|
|
12779
|
+
...options.plugins ?? []
|
|
12697
12780
|
]
|
|
12698
12781
|
});
|
|
12699
12782
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rangojs/router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Django-inspired RSC router with composable URL patterns",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
"node": ">=24.0.0"
|
|
260
260
|
},
|
|
261
261
|
"scripts": {
|
|
262
|
-
"build": "pnpm run build:types && 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",
|
|
262
|
+
"build": "pnpm run build:types && 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 && mkdir -p dist/bin/plugins && cp src/vite/plugins/cloudflare-protocol-loader-hook.mjs dist/bin/plugins/cloudflare-protocol-loader-hook.mjs",
|
|
263
263
|
"build:types": "rm -rf dist/types && pnpm exec tsc -p tsconfig.types.json",
|
|
264
264
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit && tsc -p tsconfig.augment-check.json --noEmit",
|
|
265
265
|
"test": "playwright test",
|
package/skills/rango/SKILL.md
CHANGED
|
@@ -445,11 +445,11 @@ npx rango generate src/urls.tsx src/api/
|
|
|
445
445
|
|
|
446
446
|
Three modes:
|
|
447
447
|
|
|
448
|
-
| Flag | Behavior
|
|
449
|
-
| ----------- |
|
|
450
|
-
| (none) | Static parser. Exits with an error, writing nothing, if an `include()` can't be resolved statically (factory or dynamic).
|
|
451
|
-
| `--static` | Static parser, but writes partial output and prints the
|
|
452
|
-
| `--runtime` | Vite-based runtime discovery (full coverage; needs `vite`; `@vitejs/plugin-rsc` comes with the router). `--config <path>` picks the config.
|
|
448
|
+
| Flag | Behavior |
|
|
449
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
450
|
+
| (none) | Static parser. Exits with an error, writing nothing, if an `include()` can't be resolved statically (factory or dynamic), or an explicit `createRouter` `basename` isn't a string literal or top-level const. |
|
|
451
|
+
| `--static` | Static parser, but writes partial output (an unreadable basename leaves patterns unprefixed) and prints the diagnostics as warnings. |
|
|
452
|
+
| `--runtime` | Vite-based runtime discovery (full coverage; needs `vite`; `@vitejs/plugin-rsc` comes with the router). `--config <path>` picks the config. |
|
|
453
453
|
|
|
454
454
|
### Auto-detection
|
|
455
455
|
|
|
@@ -111,7 +111,8 @@ imports it directly, so `Rango.Env`, `Rango.Vars`, and optional
|
|
|
111
111
|
and client helpers.
|
|
112
112
|
|
|
113
113
|
CLI flags: the default mode fails (and writes nothing) when an `include()` cannot
|
|
114
|
-
be resolved statically (factory functions, dynamic expressions)
|
|
114
|
+
be resolved statically (factory functions, dynamic expressions) or an explicit
|
|
115
|
+
`createRouter` `basename` is not a string literal or top-level const; `--static`
|
|
115
116
|
accepts partial output with warnings; `--runtime` uses Vite-based discovery for
|
|
116
117
|
full coverage (needs `vite`, optional
|
|
117
118
|
`--config <path>`). Passing a directory scans it for router and `urls()` files.
|
package/src/bin/rango.ts
CHANGED
|
@@ -189,23 +189,23 @@ function runStaticGeneration(args: string[], mode: "default" | "static") {
|
|
|
189
189
|
|
|
190
190
|
if (uniqueDiagnostics.length > 0 && mode === "default") {
|
|
191
191
|
// Hard error: no files written
|
|
192
|
-
console.error("\n[rango] Unresolvable
|
|
192
|
+
console.error("\n[rango] Unresolvable routes detected:\n");
|
|
193
193
|
formatDiagnostics(uniqueDiagnostics);
|
|
194
194
|
console.error(
|
|
195
|
-
"\nThe static parser cannot resolve these includes
|
|
196
|
-
"factory functions or dynamic expressions
|
|
195
|
+
"\nThe static parser cannot resolve these includes or basename because " +
|
|
196
|
+
"they use factory functions or dynamic expressions. A basename must be " +
|
|
197
|
+
"a string literal or a top-level const (in the router file or imported " +
|
|
198
|
+
"from a relative module).\n\n" +
|
|
197
199
|
"Options:\n" +
|
|
198
200
|
" rango generate <path> --runtime Use Vite-based discovery (requires vite)\n" +
|
|
199
|
-
" rango generate <path> --static Accept partial output (missing routes above)\n",
|
|
201
|
+
" rango generate <path> --static Accept partial output (missing routes or basename prefix above)\n",
|
|
200
202
|
);
|
|
201
203
|
process.exit(1);
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
if (uniqueDiagnostics.length > 0 && mode === "static") {
|
|
205
207
|
// Warning: partial output accepted
|
|
206
|
-
console.warn(
|
|
207
|
-
"\n[rango] Warning: partial output (unresolvable includes):\n",
|
|
208
|
-
);
|
|
208
|
+
console.warn("\n[rango] Warning: partial output (unresolvable routes):\n");
|
|
209
209
|
formatDiagnostics(uniqueDiagnostics);
|
|
210
210
|
console.warn("");
|
|
211
211
|
}
|
|
@@ -317,7 +317,12 @@ function formatDiagnostics(
|
|
|
317
317
|
diagnostics: Array<UnresolvableInclude & { routerFile: string }>,
|
|
318
318
|
) {
|
|
319
319
|
for (const d of diagnostics) {
|
|
320
|
-
const prefix =
|
|
320
|
+
const prefix =
|
|
321
|
+
d.reason === "unresolvable-basename"
|
|
322
|
+
? "createRouter basename"
|
|
323
|
+
: d.namePrefix
|
|
324
|
+
? `${d.namePrefix}.*`
|
|
325
|
+
: `${d.pathPrefix}*`;
|
|
321
326
|
console.error(` ${prefix}`);
|
|
322
327
|
console.error(` Reason: ${d.reason} -- ${d.detail}`);
|
|
323
328
|
console.error(` Source: ${d.sourceFile}`);
|