Skip to content

Solid 2: port to solid-js 2 + @solidjs/vite-plugin start mode (SSG, router 2, meta 1.0) - #174

Draft
devagrawal09 wants to merge 12 commits into
kobaltedev:mainfrom
devagrawal09:solid2
Draft

devagrawal09 wants to merge 12 commits into
kobaltedev:mainfrom
devagrawal09:solid2

Conversation

@devagrawal09

Copy link
Copy Markdown

Summary

Ports SolidBase to Solid 2 (solid-js / @solidjs/web 2.0.0-rc.13) and to @solidjs/vite-plugin start mode, which replaced SolidStart. The library, both apps (dev/, docs/), the test suite, static site generation and the docs content are all ported. Breaking major for consumers (they have to move to Solid 2 anyway); public config/theme API and MDX authoring are kept as-is wherever possible.

Draft because every upstream dependency is a prerelease (pinned exactly — see first commit) and a couple of design choices below deserve a look.

Commits (reviewable one by one)

Commit What
1 chore: pin Solid 2 dependency set solid-js/@solidjs/web rc.13, router 2.0.0-next.35 + filesystem-routing, meta 1.0.0-next.2, @solidjs/vite-plugin 3.0.0-next.47 (replaces @solidjs/start + nitro), Kobalte 2.0.0-alpha.2, @solid-primitives/* next
2 feat: port src/ mechanical + semantic migration, tsc clean
3 test: port harness vitest projects (node / jsdom+browser condition), lazy-JSX mount() helper, flush() for batching; 128/128
4 feat: apps boot under start mode Document.tsx, createRouter + fileRoutes(pageRoutes), entry files removed
5 feat: SSG + generated assets without Nitro; docs solidbase:prerender buildApp hook, generateBundle asset emission, guides rewritten

What changed in the library

Mechanical: solid-js/web → @solidjs/web; JSX/ComponentProps from the renderer package; jsxImportSource (tsconfig and the MDX compiler + twoslash); merge/omit; Loading; onSettled; context-is-provider; classList → class arrays; boolean aria-* → enumerated strings; local ambient types for ~icons/* (upstream types target 1.x JSX).

Semantic (please review):

  • createEffect split into compute/apply at 10 sites; createResource → async createMemo in page-data (deferStream kept as a no-op prop).
  • Real bug found by v2: getRawTheme() wrote setTheme() inside an effect's compute phase (REACTIVE_WRITE_IN_OWNED_SCOPE). The signal is now seeded from the cookie.
  • Fixes default-theme: module-scope mobileLayout watcher leaks a computation and breaks hydration on viewports ≤ 1100px #152: globals.ts created a createRoot per Layout; now one module-level detached root.
  • @solidjs/meta 1.0 is provider-less: MetaProvider gone; useHead script injection → <Script> components rendered by SolidBaseRoot. meta.provider prop kept, deprecated.
  • Solid Router 2: <A end activeClass> → native <a> + useLinkState; useCurrentMatches → useRouteMatches; page module resolved via the lazy route component's preload()/moduleUrl (1.x route.key.$component kept as a fallback).
  • DefaultThemeComponentsProvider → createLayeredContext (v2 throws on reading a missing context).
  • resolve.dedupe for solid-js/@solidjs/web/router/meta in the Vite plugin — pnpm produced two @solidjs/router peer variants (root vs docs) → two contexts. Any consumer can hit this.

Routing: src/config/file-routes.ts — filesystem-routing (the Start convention, router-neutral) already treats md/mdx as pages; SolidBase only widens the pick so $$SolidBase_page_data survives code-splitting. Wired into plugin(); fileRoutes: false opts out. startConfig() now returns @solidjs/vite-plugin options (ssr, start, dotted .md/.mdx extensions).

SSG: solidbase:prerender (buildApp post-hook) finishes the environment builds, then renders / + the markdown route index + extras + crawled links through the production handleRequest(req, { renderMode: "async" }) (head tags inlined, httpStatus honored) into dist/client/**/index.html + 404.html. On by default (prerender: false for SSR deploys). Non-200 paths are logged — this already surfaced five broken links in the docs content.

Generated assets (sitemap/robots/llms) are emitted from generateBundle instead of Nitro publicAssets.

Consumer migration (apps)

Mostly deletion: remove entry-client.tsx, entry-server.tsx, app.config.ts; vite.config.ts uses solid(solidBase.startConfig()); add src/Document.tsx (getHtmlProps() + <HydrationScript/>); app.tsx becomes createRouter({ routes: fileRoutes(pageRoutes) }) wrapping SolidBaseRoot. See the rewritten Adding to Existing Project guide.

Verification

  • vite dev for dev/ and docs/: all routes, zero SSR errors, 404 status correct.
  • vite build: dev/ → 16 pages; docs/ → 41 pages + 404.html + sitemap.xml (41) + robots.txt + llms.txt; <title>/<meta> inlined; lang per locale.
  • vitest 128/128 (main: 126/128 — the two pre-existing preview.test failures now pass), tsc -b 0, biome clean.

Known / follow-ups

  • data-theme="ystem" in server HTML: pre-existing ("system".replace("s","")), corrected client-side by the inline script; left as-is since it changes server semantics.
  • @solid-mediakit/og has no Solid 2 release — docs OG images disabled.
  • Kobalte 2.0.0-alpha.2 declares rc.3 peers; works against rc.13.
  • with-solidbase CLI template is still SolidStart-based; quickstart points to the guide until updated.
  • App-project tsc can't see src/virtual.d.ts (pre-existing).
  • Broken docs links surfaced by prerender: /md-extensions, /customization, /extending-themes, /guide/custom-themes, /fr/guide/getting-started.
  • Upstream nits: @solid-primitives/scroll@next loses the useWindowScrollPosition generic; unplugin-icons/types/solid targets 1.x JSX; filesystem-routing doesn't append &lang.ext for non-JS pick ids.
  • Out of scope here: add basic repl support #173 (REPL — solid-repl is a Solid 1 library), solid-docs' osmium theme.

- solid-js / @solidjs/web 2.0.0-rc.13
- @solidjs/router 2.0.0-next.35 (+ filesystem-routing 0.4.0)
- @solidjs/meta 1.0.0-next.2
- @solidjs/vite-plugin 3.0.0-next.47 replaces @solidjs/start + nitro
- @kobalte/core 2.0.0-alpha.2
- @solid-primitives/* v2-targeting next prereleases
Mechanical:
- solid-js/web -> @solidjs/web; JSX/ComponentProps types from the renderer package
- jsxImportSource -> @solidjs/web (tsconfig + MDX compiler + twoslash)
- mergeProps/splitProps -> merge/omit; Suspense -> Loading; onMount -> onSettled
- Context.Provider -> context-is-provider; useCurrentMatches -> useRouteMatches
- classList -> class object/array form; aria-* booleans -> enumerated strings
- unplugin-icons: local ambient types (upstream types target solid-js 1.x JSX)

Semantic:
- createEffect split into compute/apply everywhere (10 sites)
- createResource -> async createMemo in page-data (deferStream kept as no-op prop)
- startTransition removed in locale switching (transitions are built in)
- @solidjs/meta 1.0: MetaProvider dropped; useHead script injection -> <Script>
  components (ThemeCookieScript / PreferredLanguageCookieScript) rendered by Root
- Solid Router 2: <A> -> native <a> + useLinkState; no useCurrentMatches
- globals.ts: module-level detached root fixes per-Layout computation leak (kobaltedev#152)
- startConfig() now targets @solidjs/vite-plugin start mode instead of SolidStart
- vitest projects: node (server build) + jsdom with resolve.conditions=[browser]
  for client-rendering tests; replaces 1.x hard-coded dist/*.cjs imports
- tests/helpers/solid.ts mount(): evaluate Solid 2 lazy JSX when calling
  components/providers directly (children getters no longer run eagerly)
- flush() where tests read synchronously after a write (microtask batching)
- theme.test: Script component assertion replaces useHead mock; two-arg
  createEffect mock; reactive write moved out of the owned scope
- page-data.test: useRouteMatches mock + async createMemo shim
- preview.test: esbuild strips TS, @solidjs/babel-plugin compiles JSX
  (fixes the pre-existing missing @babel/preset-typescript failure on main)

src/client/theme.ts: seed the theme signal from the cookie instead of writing
it during getRawTheme() — Solid 2 forbids reactive writes inside computations.
Library:
- src/config/file-routes.ts: solidBaseFileRoutes() on filesystem-routing (the SolidStart
  convention, router-neutral). md/mdx are pages out of the box; SolidBase only widens the
  pick to keep 57480SolidBase_page_data through route code-splitting. Wired into plugin()
  (opt out with fileRoutes: false).
- startConfig(): extensions need their leading dot for @solidjs/vite-plugin (.md/.mdx)
- page-data: resolve the page module via the lazy route component (preload/moduleUrl);
  the 1.x route.key. shape is kept as a fallback
- vite plugin: resolve.dedupe for solid-js/@solidjs/web/router/meta — Solid 2 context
  identity breaks when pnpm installs two peer-variants of @solidjs/router
- default-theme context: createLayeredContext instead of reading our own (absent)
  context — Solid 2 throws on a missing provider
- client: HttpStatusCode component (httpStatus from @solidjs/web) for MDX pages

Apps (dev/, docs/):
- vite.config: solid(solidBase.startConfig()) replaces solidStart() + nitro()
- src/Document.tsx (document shell w/ getHtmlProps + HydrationScript), src/app.tsx with
  createRouter + fileRoutes(pageRoutes) from virtual:file-routes; entry-*.tsx and
  app.config.ts removed (generated by the plugin)
- docs: @solid-mediakit/og plugin + og-image route disabled (no Solid 2 release)
- dev: ec-file.mdx examples point at Document.tsx/app.tsx; theme ComponentProps from web

Verified: vite dev serves all routes in both apps with no SSR errors (404 status works),
vite build of dev/ produces client + server bundles, vitest 128/128, tsc clean.
…; docs for the new setup

Prerender (src/config/vite-plugin/prerender.ts):
- buildApp (post) hook: finishes the environment builds (client first, then the rest —
  @solidjs/vite-plugin defers to a plugin that declares buildApp), then renders every
  markdown route from routes-index plus /, configured extras and crawled same-origin links
  through the production handleRequest() with renderMode: 'async' (head tags inlined,
  httpStatus honored) and writes dist/client/**/index.html + 404.html.
- Enabled by default (prerender: false to deploy the server bundle); options: routes,
  crawlLinks, notFound, origin. Skipped (non-200) paths are logged — which already
  surfaced five broken links in the docs content.

Generated assets (sitemap/robots/llms): emitted into the client bundle from
generateBundle instead of Nitro publicAssets; dev middleware unchanged.

Docs: guides and reference snippets rewritten for vite.config.ts + @solidjs/vite-plugin
start mode (Document.tsx, createRouter + fileRoutes, startConfig()); 'for SolidStart'
copy updated; netlify.toml publishes docs/dist/client as a static site.

Verified: dev/ -> 16 pages, docs/ -> 41 pages + 404.html + sitemap (41) + robots + llms,
titles/meta inlined, vitest 128/128, tsc clean.
@netlify

netlify Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for solidbase ready!

Name Link
🔨 Latest commit cfae258
🔍 Latest deploy log https://app.netlify.com/projects/solidbase/deploys/6ac285f711be6300080a4a69
😎 Deploy Preview https://deploy-preview-174--solidbase.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 99 (🟢 up 3 from production)
Accessibility: 100 (no change from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Importing dist/server/server.js into Vite's process left the event loop alive after
prerendering (the production server runtime holds handles), so vite v7.3.6 building client environment for production...
✓ 0 modules transformed. never
returned. Attribution: building the environments from the buildApp hook alone exits
fine; the in-process import + render hangs.

The buildApp hook now spawns prerender-worker.mjs (node, JSON over stdio), which
imports the server bundle, renders the seed/crawl set with renderMode: 'async',
writes dist/client/**/index.html + 404.html and exits. Same output; both apps'
builds return in ~5s.
@devagrawal09

devagrawal09 commented Oct 3, 2026 •

Copy link
Copy Markdown
Author

Pushed a fix for a build hang found while verifying: vite build never exited after the prerender step because importing dist/server/server.js into Vite's process keeps the event loop alive (attribution: building the environments from the buildApp hook alone exits fine; the in-process import + render hangs).

The prerender now runs in a short-lived child process (src/config/vite-plugin/prerender-worker.mjs, JSON over stdio) — see e064d29. Output is identical (dev: 16 pages, docs: 41 + 404.html, sitemap/robots/llms, titles inlined); both builds exit in ~5s. Tests still 128/128.

publishConfig.exports resolve to dist/, where tsc does not emit the .mjs worker;
scripts/build.js now copies **/*.{js,mjs,css} from src so the published package can
spawn it (verified with pnpm pack --dry-run).
- Pages are requested at base + path (crawled links un-prefixed before queueing);
  output stays relative to the client out dir. Verified with base: '/sub/'.
- solidBaseToRoute is exported so apps that set fileRoutes: false and wire
  fileRoutes() themselves keep 59015SolidBase_page_data through code-splitting.
@devagrawal09

Copy link
Copy Markdown
Author

Verification pass — findings

Ran everything beyond the SSR/curl checks: frozen-lockfile install, full pnpm check, pnpm build + pnpm pack --dry-run, static-serve + vite preview of the SSG output, prerender edge cases (base, fileRoutes: false, prerender: false), and a headless-browser smoke (Playwright, local only) against vite dev and the prerendered docs/dist/client.

Fixed in this push

  • Packaging: publishConfig.exports resolve to dist/, where the .mjs prerender worker was not copied → prerender would fail for consumers of the released package. scripts/build.js now copies **/*.{js,mjs,css} (verified in the tarball).
  • Prerender honors Vite base: pages are requested at base + path and crawled links are un-prefixed before queueing.
  • fileRoutes: false users keep page data: the convention hook is exported as solidBaseToRoute (without it, $$SolidBase_page_data is tree-shaken out of code-split MDX routes — observed: 62 → 2 occurrences in the server bundle).

⚠️ Blocker found: client hydration fails on every page (Kobalte 2.0.0-alpha.2)

The browser smoke shows hydration key misses inside every Kobalte trigger (Dialog.Trigger in the Header, Popover.Trigger in the project/version selectors), followed by Hydration Mismatch → [REACTIVITY_HALTED]. After that nothing on the client reacts: SPA navigation, the theme Select, mobile nav, locale switch. SSR output is correct; it is purely a hydration failure.

Attribution with a standalone 3-case app (@solidjs/vite-plugin start mode, solid-js/@solidjs/web rc.13, no SolidBase):

Case SSR hydration keys Hydrates Click works
native <button> with <span> + <svg> consistent ✅ ✅
dynamic(() => "button") button@140 span@141 svg@143 ✅ ✅
Kobalte Dialog.Trigger server button@…060 span@…064 svg@…066, client expects span@…061 svg@…063 ❌ ❌

i.e. Kobalte's trigger consumes 3 extra hydration ids on the server before its children. This is kobaltedev/kobalte#717 (open; not fixed in alpha.2; the thread's <Dynamic> theory is ruled out above — dynamic() alone is fine, and a local patch of Polymorphic to dynamic() changed nothing).

Impact: the port is complete and correct for SSR/SSG, but the interactive client is non-functional until this is resolved. Default-theme Kobalte usage: Dialog ×3, Popover ×2, Select ×2, Collapsible, Badge, Tabs across 8 files.

Options:

  1. Wait for a Kobalte alpha with #717 fixed (upstream owner has said Polymorphic internals will be refactored for Solid 2).
  2. Find the 3 server-only id consumers in Kobalte's Button/Trigger and ship a pnpm patch (workspace-only; consumers would need the same until upstream lands).
  3. Replace the Kobalte triggers (Dialog/Popover/Select) in the default theme with native elements + small primitives — removes the alpha dependency entirely; Collapsible/Badge/Tabs could follow.

I'd lean 3 for Dialog/Popover (they are thin here) and 1/2 for Select, but that's a maintainer call.

Other observations (not blockers)

  • vite preview serves unmatched paths as 200 (preview-mode quirk; static hosts serve 404.html with 404, and httpStatus is correct under vite dev and in the SSG output).
  • No unit tests added yet for file-routes/prerender; the Playwright smoke scripts are local for now and could become a tests/e2e project if wanted.

@devagrawal09

Copy link
Copy Markdown
Author

Hydration blocker resolved — root cause is upstream and already fixed

The hydration failure reported above is solidjs/solid#3741, fixed by solidjs/solid#3757 (fix(web): spread applies attributes before children, matching ssrElement, merged to next on Oct 2, not yet in @solidjs/web@2.0.0-rc.13). Not a Kobalte bug — kobaltedev/kobalte#717 is the same thing; I left the analysis there.

Mechanism: for a component prop the compiler emits get disabled() { return memo(() => cond)() ? p.disabled : undefined }. Through dynamic()/<Dynamic> (Kobalte Polymorphic), the server read attribute getters before children while the client's spread() inserted children before its attribute effect; both draw ids from the same owner counter, so the memo and the first child swapped hydration ids. ButtonRoot has several such props → every Kobalte trigger mis-hydrated → REACTIVITY_HALTED.

Verified with @solidjs/web built from next (e44b2e4), via a temporary pnpm patch in the workspace (not pushed yet):

  • dev/ (vite dev): 0 hydration issues on /, /about, /fr/; SPA navigation; mobile-nav Dialog opens.
  • docs/ prerendered static output, hydrated in Chromium: 0 hydration issues and zero warnings on /, /guide/, /fr/, /reference/; SPA navigation (/guide/quickstart); theme Light→Dark switches data-theme + cookie and persists across reload; locale English→Français → /fr/guide with lang="fr-FR"; mobile nav opens.
  • vitest 128/128, tsc clean with the patched runtime.

How to consume the fix (maintainer call):

  1. Wait for the next @solidjs/web rc and bump — cleanest.
  2. Ship the temporary pnpm patch of @solidjs/web@2.0.0-rc.13 (whole-dist from next, ~106 KB) on this branch with a TODO to drop it on rc.14 — makes the branch usable today. Note next also carries #3753 (fix!: lowercase on* are attributes) and #3760; SolidBase uses camelCase handlers and is unaffected.

Remaining dev-only diagnostics seen in vite dev (not in the production build), worth fixing as follow-ups: STRICT_READ_UNTRACKED in <Article> and <DirectiveContainer> (reactive children read outside a tracking scope), and an EFFECT_WRITES_OWN_SOURCE loop (an effect writing a signal it reads).

…ttled

The port replaced `startTransition(() => navigate()).then(setLang)` with
`Promise.resolve(navigate())` - but Router 2's navigate() returns void, so the
callback ran on the next microtask, before the navigation committed.

setLocale is now a generator action: the navigation runs as a transition and
onSettled() queues the lang write behind its effects, i.e. after the new route
is in the DOM (verified in Chromium: h1 changes, then lang; back/forward and
locale switching work with no errors).

Tests call setLocale from the test body - Solid 2 rejects actions invoked inside
an owned scope ([ACTION_CALLED_IN_OWNED_SCOPE]) - and flush() the effect queue.
@devagrawal09

Copy link
Copy Markdown
Author

Decision: wait for the next @solidjs/web release rather than vendor the fix.

  • The local pnpm patch used for verification is not committed; the branch pins rc.13 as-is.
  • Until @solidjs/web ships fix(web): spread applies attributes before children, matching ssrElement (#3741) solidjs/solid#3757, the client in this PR will mis-hydrate on every page with a Kobalte trigger (see the comment above). SSR output, prerendered HTML, build, and tests are unaffected.
  • To unblock when it lands: bump @solidjs/web (and solid-js to the matching rc) in package.json, dev/package.json, docs/package.json, re-run pnpm install, and re-verify in a browser (theme switch, locale switch, mobile nav, SPA navigation on the prerendered docs/dist/client).
  • To test interactively before then, the exact verified build is @solidjs/web from solidjs/solid@e44b2e4 (next): pnpm --filter @solidjs/web build and drop dist/ over the installed rc.13 (or pnpm patch it). Only the 6 bundled runtime files differ (+344/−156).

@devagrawal09

Copy link
Copy Markdown
Author

Rewrote the branch to fix commit authorship (same content, dates preserved). Hashes cited in earlier comments map as follows:

old new
a97d0e1 976fca4
9d4e14c d28e850
e979216 ee5514f
e52d111 559fe47
3afbc4f f0fe7fb
e064d29 ed404c2
2a69167 422f501
92ff5cc 2389fa5
2bf459a f978aa3

…outes

Review findings (independent Codex pass on the PR):

- prerender-worker received Vite base but a half-applied patch never used it:
  requests now go to base + path and crawled links are normalized back to app
  paths. Verified discriminatingly with the router base set to /sub/: fixed
  worker prerenders all 15 dev pages, the previous one skipped all 15.
- SolidStart answered unmatched paths with 404 implicitly; Router 2 start mode
  renders an empty shell with 200. SolidBaseRoot now sets httpStatus(404) on the
  server when no route matched (an app's own [...404] route still wins).
- dev/docs routers pass base: import.meta.env.BASE_URL.
- biome: unused helper warning gone; EOF newline in structure.mdx.

Known, pre-existing: SolidBase's link helpers emit root-relative hrefs and are
not base-aware.
@devagrawal09

Copy link
Copy Markdown
Author

Independent review (Codex) — findings and fixes

Had a Codex agent verify the branch from scratch (install, typecheck, biome, tests, library build/pack, both app builds incl. exit behaviour, dev-server probes, diff review, setLocale action audit). Everything in the PR description reproduced. It found three real issues, now fixed:

  1. Prerender ignored Vite base — the worker received base but a half-applied patch never used it. Now requests base + path and normalizes crawled links back to app paths. Verified discriminatingly: with the router's base set to /sub/, the fixed worker prerenders all pages; the previous worker skips them.
  2. Unmatched routes returned 200 in apps without a [...404] route (SolidStart used to answer 404 implicitly; Router 2 start mode renders an empty shell). SolidBaseRoot now sets httpStatus(404) on the server when nothing matched; an app's own 404 route still wins.
  3. Apps' routers now pass base: import.meta.env.BASE_URL.

Also: biome warning (unused helper) resolved; EOF newline in structure.mdx.

Known, pre-existing, not addressed here: SolidBase's link helpers (applyPathPrefix etc.) emit root-relative href="/…" and are unaware of Vite base — a site deployed under a sub-path still needs that work.

…status

The Milestone 1 port turned the page-data createResource into an async createMemo
and dropped deferStream as a no-op. It was not: in streaming render mode the shell
(and the response head) is committed as soon as SolidBaseRoot's <Loading> boundary
suspends on page data, so a [...404] page's httpStatus(404) ran after commit and
was ignored. Only the async render path (prerender, crawler UAs) got 404.

The server createMemo honors deferStream at runtime (dynamic() relies on it) though
it is not on the public MemoOptions type yet; pass it through a typed cast and keep
the 1.x default of true. Verified: docs /nope and /fr/nope -> 404 under streaming,
dev unmatched -> 404, 200s unchanged, prerender unchanged (41 pages + 404.html),
tests 128/128.
@devagrawal09

Copy link
Copy Markdown
Author

Follow-up from the Codex re-check: docs/ answered unknown paths with 200 under the dev server's streaming mode even though the [...404] page rendered. Root cause was a Milestone-1 regression, not the new Root check: the page-data createResource had deferStream: true in 1.x and I had dropped it as a no-op when porting to an async createMemo. Without it the shell (and status) is committed as soon as the <Loading> boundary suspends, so the page's httpStatus(404) arrived too late. Only the async render path (prerender, crawler UAs) was correct — which is why the SSG output always had a proper 404 and I had seen 404 once by timing.

Fixed by restoring deferStream on the page-data memo (props.deferStream ?? true; the server memo honors it at runtime, it just isn't on the public MemoOptions type yet). Verified: docs /nope and /fr/nope → 404 under streaming, dev unmatched → 404, prerender unchanged (41 pages + 404.html), tests 128/128.

…eats the

exact base path as the app root

Found by an independent review (Codex GPT-6-Astra) with a scratch server fixture:
- relative hrefs were resolved against the origin, so <a href="next"> on
  /guide/first was crawled as /next instead of /guide/next;
- a link to exactly the Vite base (no trailing slash) was not stripped and was
  requested as /base/base.

extractLinks now resolves against the page URL (base + path) and toAppPath maps
base and base/ to /. Verified with the fixture under base /review-base/ and /,
the dev build under base /sub/ (15 pages, 0 skipped), and the docs build, whose
skipped-link count dropped from 7 to 6 because one relative link now resolves
correctly. Biome is diagnostic-free on src/.
@devagrawal09

Copy link
Copy Markdown
Author

Second independent review, this time on Codex GPT-6-Astra (the model originally requested; it is in the Codex catalogue even though the subagent driver's advertised list omits it). It reproduced the full PASS set at e3ae444 and found two real crawler bugs via a scratch server fixture, now fixed in the latest commit:

  1. Relative links resolved against the origin — <a href="next"> on /guide/first was crawled as /next. Now resolved against the page URL. Side effect: the docs build's skipped-link count went from 7 to 6 (one of those was exactly this).
  2. Exact-base link (/base without trailing slash) was requested as /base/base. toAppPath now maps base/base/ to /.

Also from that review, informational: Kobalte Select may invoke onChange from an options-change effect, so setLocale (a Solid action) can run from an effect rather than a click; Astra tested action-from-effect in isolation and it does not trip ACTION_CALLED_IN_OWNED_SCOPE, and the real locale switch was verified in a browser earlier. Root-relative hrefs under a non-root base remain the known pre-existing limitation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

default-theme: module-scope mobileLayout watcher leaks a computation and breaks hydration on viewports ≤ 1100px

1 participant