[GH-ISSUE #6642] [Bug] Route discovery silently drops index routes with constant or type-wrapped true flags #4023

Closed
opened 2026-10-31 00:46:08 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6642

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

The public analyze-javascript-application CLI retains / for createBrowserRouter([{index:true,Component:Home}]), but returns an empty route inventory for the equivalent immutable constant const INDEX=true; ... {index:INDEX,Component:Home}. A supported TypeScript source with {index:(true as const),Component:Home} also loses /. All three results report complete source parsing and candidate enumeration with no route interpretation gap.

Reproduction

Run from the source checkout below with its built runtime: mise exec node@24.22.1 -- node /path/to/reproduce-index.mjs. Save the following script as reproduce-index.mjs outside the checkout. It creates local source directories with isolated HOME/XDG state, checks source syntax with Node, runs the actual CLI, prints the relevant inventory, and removes its fixtures. It does not install or execute React Router, start a browser, or make network requests.

import {mkdtempSync,mkdirSync,writeFileSync,rmSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join,resolve} from 'node:path';
import {spawnSync} from 'node:child_process';
const entry=resolve('scripts/rea.mjs');
const scratch=mkdtempSync(join(tmpdir(),'rea-index-fixture-'));
try {
  for(const [name,prefix,index,extension] of [['literal','','true','js'],['alias','const INDEX=true;','INDEX','js'],['erased-wrapper','','(true as const)','ts']]) {
    const app=join(scratch,name),home=join(scratch,`${name}-home`);
    mkdirSync(app);mkdirSync(home);
    writeFileSync(join(app,'package.json'),' {"name":"route-fixture","private":true,"type":"module"}');
    const source=`import {createBrowserRouter} from 'react-router';${prefix}export const router=createBrowserRouter([{index:${index},Component:Home}]);function Home(){return null;}`;
    const file=join(app,`routes.${extension}`);writeFileSync(file,source);
    const check=spawnSync(process.execPath,['--check',file],{encoding:'utf8'});
    if(check.status!==0)throw new Error(check.stderr);
    const env={PATH:process.env.PATH,HOME:home,XDG_CONFIG_HOME:join(home,'config'),XDG_DATA_HOME:join(home,'data'),XDG_CACHE_HOME:join(home,'cache'),XDG_STATE_HOME:join(home,'state'),NO_COLOR:'1'};
    const run=spawnSync(process.execPath,[entry,'analyze-javascript-application',app,'--format','json'],{cwd:home,env,encoding:'utf8',timeout:60000,maxBuffer:8*1024*1024});
    if(run.status!==0)throw new Error(`CLI failed: ${run.status} ${run.stderr}`);
    const inv=JSON.parse(run.stdout).normalized_result.route_inventory;
    console.log(JSON.stringify({case:name,exit:run.status,routes:inv.routes.map(r=>r.path),coverage:inv.coverage,unknowns:inv.unknowns}));
  }
} finally {rmSync(scratch,{recursive:true,force:true});}

The JavaScript literal and alias cases were also run separately before this script and had the same results. The TypeScript case was executed once. node --check succeeded for all three source files.

Expected behavior

All three configurations contain the same true index flag and should retain the root / candidate. React Router documents a top-level index route as rendering at /. REA documents nested route-object discovery, stable lexical bindings, and explicit unknowns for dynamic fields; it also supports TypeScript source artifacts. Type assertions do not change the runtime value of the literal. A genuinely unsupported or unresolved flag should produce an explicit interpretation gap rather than silently become a known non-index route.

Impact and scope

An index page disappears from retained route Evidence solely because an unchanged boolean literal is expressed through a stable local binding or erased TypeScript assertion. The observed loss is bounded to the small source fixtures; real-application prevalence and other route fields are untested. This is static route discovery, not a claim about runtime rendering or whole-application route completeness. No target mutation or security consequence was observed.

REA version

Source main a281455f0b83c088ea30ebbb887398888ed167fb; package version 6.7.0, using its compiled runtime. The published npm release was not separately tested.

Runtime and platform

Node.js v24.22.1 through mise; Linux x64.

Target and artifact context

Self-authored JavaScript/TypeScript source directories with an ESM package manifest and a standard createBrowserRouter import/configuration. React Router is referenced as inert source syntax; it was not installed or run in these fixtures.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Actual output from the script:

{"case":"literal","exit":0,"routes":["/"],"coverage":{"scope":"provided-code-sources","source_parsing":"complete","candidate_enumeration":"complete","application_routes":"unknown"},"unknowns":[]}
{"case":"alias","exit":0,"routes":[],"coverage":{"scope":"provided-code-sources","source_parsing":"complete","candidate_enumeration":"complete","application_routes":"unknown"},"unknowns":[]}
{"case":"erased-wrapper","exit":0,"routes":[],"coverage":{"scope":"provided-code-sources","source_parsing":"complete","candidate_enumeration":"complete","application_routes":"unknown"},"unknowns":[]}
  • Expected source: REA's route parser contract, supported source grammars, and React Router's documented index-route behavior. The literal control is an observed known-good syntax variant on the same REA baseline.
  • Failure source: Executed public CLI, inline input script, and output above.
  • Evidence provenance: observed REA CLI results; React Router's expected route and the causal explanation below are documentation/source-derived.
  • Local verification: reproduced.
  • Reproduction completeness: complete for REA static analysis. A React Router runtime/browser was not run.

Typed outcome or recovery expected

Retain the index-route candidate for the stable true values, or preserve an explicit interpretation gap if the flag cannot be resolved. Do not silently claim complete enumeration while dropping the route. Keep application_routes unknown.

Analysis or suspected cause

Source-derived: the index-route emission branch tests the raw field node with t.isBooleanLiteral; it does not resolve its binding or erase expression wrappers there. Neighboring path-field handling resolves its selected value before interpreting it. These locators explain the observed variants without prescribing an implementation.

Additional context

The supplied open-issue snapshot had no matching index-route report. NOT_ENOUGH_INFO: last known good revision and first affected release.

Restoration check, proposed and not run after drafting: all three script cases retain / with the original literal control preserved. Keep source parsing and enumeration coverage truthful; an unresolved flag must not be silently interpreted as false. No source fix was attempted.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Supported static directory analysis silently loses an index page for two equivalent valid input expressions, despite a passing literal control and complete enumeration status.
  • Workaround status: unverified
  • Workaround basis: Inlining true restores the fixture's route but changes the analyzed source and its identity; no transparent caller-side workaround preserving the original source Evidence was exercised.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6642 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior The public `analyze-javascript-application` CLI retains `/` for `createBrowserRouter([{index:true,Component:Home}])`, but returns an empty route inventory for the equivalent immutable constant `const INDEX=true; ... {index:INDEX,Component:Home}`. A supported TypeScript source with `{index:(true as const),Component:Home}` also loses `/`. All three results report complete source parsing and candidate enumeration with no route interpretation gap. ### Reproduction Run from the source checkout below with its built runtime: `mise exec node@24.22.1 -- node /path/to/reproduce-index.mjs`. Save the following script as `reproduce-index.mjs` outside the checkout. It creates local source directories with isolated HOME/XDG state, checks source syntax with Node, runs the actual CLI, prints the relevant inventory, and removes its fixtures. It does not install or execute React Router, start a browser, or make network requests. ```js import {mkdtempSync,mkdirSync,writeFileSync,rmSync} from 'node:fs'; import {tmpdir} from 'node:os'; import {join,resolve} from 'node:path'; import {spawnSync} from 'node:child_process'; const entry=resolve('scripts/rea.mjs'); const scratch=mkdtempSync(join(tmpdir(),'rea-index-fixture-')); try { for(const [name,prefix,index,extension] of [['literal','','true','js'],['alias','const INDEX=true;','INDEX','js'],['erased-wrapper','','(true as const)','ts']]) { const app=join(scratch,name),home=join(scratch,`${name}-home`); mkdirSync(app);mkdirSync(home); writeFileSync(join(app,'package.json'),' {"name":"route-fixture","private":true,"type":"module"}'); const source=`import {createBrowserRouter} from 'react-router';${prefix}export const router=createBrowserRouter([{index:${index},Component:Home}]);function Home(){return null;}`; const file=join(app,`routes.${extension}`);writeFileSync(file,source); const check=spawnSync(process.execPath,['--check',file],{encoding:'utf8'}); if(check.status!==0)throw new Error(check.stderr); const env={PATH:process.env.PATH,HOME:home,XDG_CONFIG_HOME:join(home,'config'),XDG_DATA_HOME:join(home,'data'),XDG_CACHE_HOME:join(home,'cache'),XDG_STATE_HOME:join(home,'state'),NO_COLOR:'1'}; const run=spawnSync(process.execPath,[entry,'analyze-javascript-application',app,'--format','json'],{cwd:home,env,encoding:'utf8',timeout:60000,maxBuffer:8*1024*1024}); if(run.status!==0)throw new Error(`CLI failed: ${run.status} ${run.stderr}`); const inv=JSON.parse(run.stdout).normalized_result.route_inventory; console.log(JSON.stringify({case:name,exit:run.status,routes:inv.routes.map(r=>r.path),coverage:inv.coverage,unknowns:inv.unknowns})); } } finally {rmSync(scratch,{recursive:true,force:true});} ``` The JavaScript literal and alias cases were also run separately before this script and had the same results. The TypeScript case was executed once. `node --check` succeeded for all three source files. ### Expected behavior All three configurations contain the same true index flag and should retain the root `/` candidate. React Router documents a top-level index route as rendering at `/`. REA documents nested route-object discovery, stable lexical bindings, and explicit unknowns for dynamic fields; it also supports TypeScript source artifacts. Type assertions do not change the runtime value of the literal. A genuinely unsupported or unresolved flag should produce an explicit interpretation gap rather than silently become a known non-index route. ### Impact and scope An index page disappears from retained route Evidence solely because an unchanged boolean literal is expressed through a stable local binding or erased TypeScript assertion. The observed loss is bounded to the small source fixtures; real-application prevalence and other route fields are untested. This is static route discovery, not a claim about runtime rendering or whole-application route completeness. No target mutation or security consequence was observed. ### REA version Source main `a281455f0b83c088ea30ebbb887398888ed167fb`; package version `6.7.0`, using its compiled runtime. The published npm release was not separately tested. ### Runtime and platform Node.js v24.22.1 through mise; Linux x64. ### Target and artifact context Self-authored JavaScript/TypeScript source directories with an ESM package manifest and a standard `createBrowserRouter` import/configuration. React Router is referenced as inert source syntax; it was not installed or run in these fixtures. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Actual output from the script: ```jsonl {"case":"literal","exit":0,"routes":["/"],"coverage":{"scope":"provided-code-sources","source_parsing":"complete","candidate_enumeration":"complete","application_routes":"unknown"},"unknowns":[]} {"case":"alias","exit":0,"routes":[],"coverage":{"scope":"provided-code-sources","source_parsing":"complete","candidate_enumeration":"complete","application_routes":"unknown"},"unknowns":[]} {"case":"erased-wrapper","exit":0,"routes":[],"coverage":{"scope":"provided-code-sources","source_parsing":"complete","candidate_enumeration":"complete","application_routes":"unknown"},"unknowns":[]} ``` - Expected source: REA's [route parser contract](https://github.com/morluto/rea/blob/a281455f0b83c088ea30ebbb887398888ed167fb/docs/javascript-route-inventory.md#L60-L70), [supported source grammars](https://github.com/morluto/rea/blob/a281455f0b83c088ea30ebbb887398888ed167fb/docs/javascript-artifact-reconstruction.md#L231-L241), and React Router's [documented index-route behavior](https://reactrouter.com/start/data/routing#index-routes). The literal control is an observed known-good syntax variant on the same REA baseline. - Failure source: Executed public CLI, inline input script, and output above. - Evidence provenance: observed REA CLI results; React Router's expected route and the causal explanation below are documentation/source-derived. - Local verification: reproduced. - Reproduction completeness: complete for REA static analysis. A React Router runtime/browser was not run. ### Typed outcome or recovery expected Retain the index-route candidate for the stable true values, or preserve an explicit interpretation gap if the flag cannot be resolved. Do not silently claim complete enumeration while dropping the route. Keep `application_routes` unknown. ### Analysis or suspected cause Source-derived: the [index-route emission branch](https://github.com/morluto/rea/blob/a281455f0b83c088ea30ebbb887398888ed167fb/src/domain/javascript/javascriptRouteScan.ts#L1435-L1438) tests the raw field node with `t.isBooleanLiteral`; it does not resolve its binding or erase expression wrappers there. Neighboring path-field handling resolves its selected value before interpreting it. These locators explain the observed variants without prescribing an implementation. ### Additional context The supplied open-issue snapshot had no matching index-route report. NOT_ENOUGH_INFO: last known good revision and first affected release. Restoration check, proposed and not run after drafting: all three script cases retain `/` with the original literal control preserved. Keep source parsing and enumeration coverage truthful; an unresolved flag must not be silently interpreted as false. No source fix was attempted. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Supported static directory analysis silently loses an index page for two equivalent valid input expressions, despite a passing literal control and complete enumeration status. - Workaround status: unverified - Workaround basis: Inlining true restores the fixture's route but changes the analyzed source and its identity; no transparent caller-side workaround preserving the original source Evidence was exercised.
Author
Owner

@gongfpp commented on GitHub (Oct 23, 2026):

I plan to work on this existing static-route correctness fix. I checked the current issue timeline and public PR overlap, and will reuse the route scanner’s existing stable-binding / erased-wrapper resolver for the index flag, retain explicit interpretation gaps for unresolved flags, and preserve known child paths. Planned verification includes literal/const/TypeScript controls, false and dynamic cases, and the real compiled public CLI. No React Router execution, browser/provider, or new capability is proposed. Please let me know if someone is already working on this or a maintainer intends to take it; I will coordinate rather than duplicate work. This is coordination, not an assignment or scope/merge approval.

<!-- gh-comment-id:6366939714 --> @gongfpp commented on GitHub (Oct 23, 2026): I plan to work on this existing static-route correctness fix. I checked the current issue timeline and public PR overlap, and will reuse the route scanner’s existing stable-binding / erased-wrapper resolver for the index flag, retain explicit interpretation gaps for unresolved flags, and preserve known child paths. Planned verification includes literal/const/TypeScript controls, false and dynamic cases, and the real compiled public CLI. No React Router execution, browser/provider, or new capability is proposed. Please let me know if someone is already working on this or a maintainer intends to take it; I will coordinate rather than duplicate work. This is coordination, not an assignment or scope/merge approval.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4023
No description provided.