feat(screens): Next.js as a Screens app — pages, route handlers, links and redirects
- frameworks/nextjs.ts (split out of react.ts): App Router app/**/page.tsx and Pages Router pages → routes named by path ((group) stripped, [slug] → :slug, [...all] → :all*), bound to the default export; app/**/route.ts exports → METHOD /api/… endpoints referencing their functions; pages/api → ANY; resolve() claims router.push/replace/prefetch, redirect/permanentRedirect and NextResponse.redirect(new URL(…)) into navigates edges via the Expo href readers, against a Next-only route table gated on the app's root
- next-router-synthesizer.ts: <Link href> and internal <a href> → dashed navigates edges from the component (next-link, registeredAt)
- expo-router.ts: href readers exported; matcher accepts :param / :all* segments
- steps.ts: a Next page's own work fires from page load; a Next page makes the project a web app; {status: 201} read off the call site (branch-guards CallSiteText.status) for response rows
- frameworks/package-deps.ts: nested package.json files probed on disk (getAllFiles lists only sources); Express/React/Expo/Nest detectors use it; routing manifest names constant handlers
- tests: nextjs.test.ts (file→route rules, extract, verbs, end to end with Screens and Steps); frameworks.test.ts Next cases moved to the Next resolver
- docs: CHANGELOG, spec §3.12 frameworks paragraph, CLAUDE.md, synthesis doc, plan P4 built, playbook rows for Next / MERN / Nest channels
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REFyW9hmNrxhwN5wxRoAkC
This commit is contained in:
co-authored by
Claude Fable 5
parent
b1f40c57dd
commit
9c6bc23b21
@@ -85,6 +85,15 @@ the call at request time from the directive. Validated on `bradtraversy/proshop_
|
||||
all correct on inspection, after Express mounts + chained `router.route()` landed) and `nestjs/nest` (`sample/26-queues`,
|
||||
`sample/30-event-emitter`); test `__tests__/ui-steps-cross-tier.test.ts`.
|
||||
|
||||
## Next.js links (`src/resolution/next-router-synthesizer.ts`, 2026-08-28)
|
||||
|
||||
`<Link href="/x">`, `<Link href={`/users/${id}`}>`, `<Link href={{ pathname }}>` and an internal `<a href>` are JSX
|
||||
attributes — no reference is ever extracted for them — so this pass reads them from the source, attributes each to the
|
||||
component it is written in, matches the href against the Next page table (`frameworks/nextjs.ts`) and synthesizes a
|
||||
`navigates` edge (`synthesizedBy:'next-link'`, `href`, `navMethod: 'link' | 'a'`, `registeredAt` = the JSX site). Only files
|
||||
under a Next app's root, never test files; ≤ 24 links per component (a navigation menu is not a decision); an external `<a>`
|
||||
is nothing. The Screens view walks back from these edges exactly as from `router.push`; they draw dashed.
|
||||
|
||||
## The hole
|
||||
|
||||
```ts
|
||||
|
||||
@@ -440,6 +440,18 @@ for a pair with several. Placement: at the FAR end of the line; first lane centr
|
||||
lane; never over a box; overflow counted in the panel. Panel row hover: that pill prints the whole condition (wraps at
|
||||
360px), its line at 1.0, the rest at 0.38; a hovered line tints its row `--press`. Legend bottom-left, remembered per browser.
|
||||
|
||||
**Frameworks.** The picture is a pure function of `route` nodes bound to the component that renders them and `navigates` edges
|
||||
from the function that pushes a path to the route it names, so any framework that produces those facts lands here. Expo Router
|
||||
(`resolution/frameworks/expo-router.ts`): `app/**` screen files, `router.push` / `navigate` / `replace` and a helper's return value.
|
||||
Next.js (`frameworks/nextjs.ts`, `next-router-synthesizer.ts`): App Router `app/**/page.tsx` and Pages Router pages (`(group)`
|
||||
stripped, `[slug]` → `:slug`, `[...all]` → `:all*`; `@slot` and `(.)intercepting` routes not modelled), bound to the default export;
|
||||
`router.push` / `replace` / `prefetch`, `redirect` / `permanentRedirect`, `NextResponse.redirect(new URL(…))` read like an Expo href
|
||||
(string, template with holes, `{ pathname }`, a conditional whose arms agree, a local `const href`) and matched against the Next
|
||||
pages only, from files under a Next app's root; `<Link href>` and an internal `<a href>` are markup, not calls, so a synthesizer
|
||||
reads them and draws a dashed `navigates` edge from the component (`synthesizedBy: 'next-link'`, the site as `registeredAt`).
|
||||
`app/api/**/route.ts` exports and `pages/api/*` are endpoints, not screens (`POST /api/users`, `ANY /api/users`), so the same
|
||||
index is a web app: pages on this tab, endpoints in Entry points, and a page's Steps picture firing from its load.
|
||||
|
||||
### 3.13 Steps (`#/steps?anchor=<id>` | `?symbol=<name>`, `&depth=`)
|
||||
What happens from an anchor — a screen, a handler, any symbol — drawn with the Screens view's machinery (§3.12's
|
||||
layout, tracks, pills, nearest-line pointer, panel) over a different node universe. `/api/steps` walks FORWARD from
|
||||
|
||||
@@ -277,6 +277,9 @@ Status legend: ✅ done+validated · 🔬 hole identified · ⬜ not started.
|
||||
|
||||
(Verify the exact supported set against `src/extraction/languages/` and
|
||||
`src/resolution/frameworks/` before starting — this table is a starting point.)
|
||||
| TypeScript/JS | Next.js (App Router + Pages Router) | page → `<Link>` / `router.push` / `redirect` → page; page load → data; client component → `'use server'` action → DB → `redirect`; `route.ts` handler → DB → response | R + S | ✅ 2026-08-28 (`frameworks/nextjs.ts`, `next-router-synthesizer.ts`, Steps `load` trigger + server-action crossing): fixture `__tests__/nextjs.test.ts` end to end (Screens `routed`, the push attributed back under its condition, the action's redirect); `leerob/next-saas-starter` indexes its pages and links. 🔬 agent A/B (`--model sonnet`, ≥2 runs/arm) not run yet |
|
||||
| TypeScript/JS | Express + React (MERN monorepo) | client `axios` / `fetch` literal path → own route → handler → Mongoose → response rows | S + R | ✅ 2026-08-28 (`tier-synthesizer.ts` `http-client` + Express mounts / chained `router.route()` / wrapped `const h = asyncHandler(…)` handlers / nested `package.json` detection): `bradtraversy/proshop_mern` 49 routes (19 pages + 30 endpoints), 23 client→route edges, every one spot-checked correct; `login → ⇢ POST /api/users/login → User.findOne → 401 rows → jwt.sign`. Node count stable across re-index. 🔬 A/B not run |
|
||||
| TypeScript/JS | NestJS queues / events / sockets | `queue.add('job')` → `@Process('job')`; `emit('x')` → `@OnEvent('x')`; `socket.emit` → `@SubscribeMessage` and `server.emit` → `socket.on` | S | ✅ 2026-08-28 (`tier-synthesizer.ts` `queue-job`, `event-bus`): `nestjs/nest` `sample/26-queues` (`transcode` → `handleTranscode`) and `sample/30-event-emitter` (`order.created` → its listener) exact, 0 wrong edges after the `e2e/` and generic-event guards; `immich-app/immich` 0 edges — a generated SDK client and a wrapped queue API carry no literal, so silence (correct). 🔬 A/B not run |
|
||||
|
||||
### Retrieval A/Bs that are not coverage work
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
**Status:** plan, written 2026-08-28 at the end of the session that built the Steps view and the
|
||||
readings it rests on (Expo + React Native app, `amniservices-mobile-app`). **Updated the same day, later
|
||||
sessions: P0, P1, P2, P3, P5 and P6 are built** (see the per-item notes marked *Built*); P4 (Next.js as a
|
||||
Screens app) and P7's agent A/B numbers are open. Every claim about what a
|
||||
sessions: P0–P6 are built** (see the per-item notes marked *Built*); P7's agent A/B numbers are open. Every claim about what a
|
||||
resolver emits *today* was verified against the source on this date — re-verify before building on it,
|
||||
the resolvers move. What was learned building it, beyond the plan: the index keeps only the LAST
|
||||
segment of a deep member call (`create` for `prisma.user.create`) and name-matches it — often to the
|
||||
@@ -335,6 +334,16 @@ prisma.user.create → response`, dashed where synthesized, with `registeredAt`
|
||||
|
||||
### P4 — Next.js as a Screens app
|
||||
|
||||
*Built* (2026-08-28, later session) — `frameworks/nextjs.ts` split out of `react.ts` (App Router pages and `route.ts` handlers,
|
||||
Pages Router pages and `pages/api`, `(group)` stripped, `[slug]` → `:slug`, `[...all]` → `:all*`, parallel / intercepting
|
||||
routes skipped; a page's `calls` ref to its default export via `defaultExportName`; `resolve()` claiming `router.push|replace|
|
||||
prefetch`, `redirect`, `permanentRedirect`, `NextResponse.redirect(new URL(…))` through the Expo href readers — now exported —
|
||||
against a Next-only route table gated on the app's root), `next-router-synthesizer.ts` (`<Link href>`, internal `<a href>` →
|
||||
dashed `navigates` from the component), `scoreMatch` accepting `:param` / `:all*`, the `load` trigger and `project: 'web'` for a
|
||||
Next page in `steps.ts`, `{ status: 201 }` read off the call site for response rows. Test `__tests__/nextjs.test.ts`.
|
||||
Route-handler references resolve by name with the same-file preference (`GET` / `POST` are common names). **Not built:** `revalidatePath`
|
||||
as a refresh, `middleware.ts` `config.matcher` as a global guard, a helper's return value as a destination (Expo has it).
|
||||
|
||||
*Where:* `resolution/frameworks/react.ts` (split a `nextjs.ts` out of it — the pages/app routing is
|
||||
already there), a `next-router-synthesizer.ts` modelled on `expo-router-synthesizer.ts`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user