Repository navigation
Solid 2: port to solid-js 2 + @solidjs/vite-plugin start mode (SSG, router 2, meta 1.0) - #174
devagrawal09 wants to merge 44 commits into
Conversation
- 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.
✅ Deploy Preview for solidbase ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
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.
|
Pushed a fix for a build hang found while verifying: The prerender now runs in a short-lived child process ( |
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.
Verification pass — findingsRan everything beyond the SSR/curl checks: frozen-lockfile install, full Fixed in this push
|
| 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:
- Wait for a Kobalte alpha with #717 fixed (upstream owner has said Polymorphic internals will be refactored for Solid 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). - 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 previewserves unmatched paths as 200 (preview-mode quirk; static hosts serve404.htmlwith 404, andhttpStatusis correct undervite devand in the SSG output).- No unit tests added yet for
file-routes/prerender; the Playwright smoke scripts are local for now and could become atests/e2eproject if wanted.
Hydration blocker resolved — root cause is upstream and already fixedThe hydration failure reported above is solidjs/solid#3741, fixed by solidjs/solid#3757 ( Mechanism: for a component prop the compiler emits Verified with
How to consume the fix (maintainer call):
Remaining dev-only diagnostics seen in |
…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.
|
Decision: wait for the next
|
…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.
Independent review (Codex) — findings and fixesHad 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,
Also: biome warning (unused helper) resolved; EOF newline in Known, pre-existing, not addressed here: SolidBase's link helpers ( |
…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.
|
Follow-up from the Codex re-check: Fixed by restoring |
…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/.
|
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:
Also from that review, informational: Kobalte |
commit: |
|
Thanks for the PR! Still need to review the rewritten documentation, but from the details and comments here everything seems fine. So we are waiting for a Solid rc release? |
jer3m01
left a comment
There was a problem hiding this comment.
Few things to change but looks good.
Still waiting on solid and kobalte release.
|
Updated in cff3c8d. Solid and @solidjs/web are pinned to 2.0.0-rc.14, with Router 2.0.0-next.37. The official web release resolves the hydration mismatch; no local runtime patch is included. All three review requests are addressed. Verification: frozen install, library build/typecheck, Biome, 29 test files / 128 tests, dev prerender (15 pages + 404), docs prerender (41 pages + 404), and root/non-root base crawler fixtures pass. Static-browser checks pass hydration, SPA forward navigation, theme selection and cookie persistence, mobile menu, and locale switching. Browser scripts and fixtures remain outside the repo. Still keeping this draft: an additional Back/Forward check changes the URL but can leave the previous route rendered (also reproduced independently). This is not just a stale html lang attribute. The cause still needs isolation; I have not added a runtime workaround or marked the interactive migration fully verified. The six existing broken docs links remain unchanged. |
|
Investigation paused at user request. Detailed continuation handoff: https://github.com/devagrawal09/solidbase/blob/solid2/SOLID2-HANDOFF.md Correction to the previous blocker update: Claude Opus found a concrete SolidBase/Kobalte interaction in jsdom. Recreated locale options trigger an unchanged-selection callback; during Back this calls setLocale again, pushes a duplicate history entry, and destroys Forward. A selector guard plus route-driven lang effect fixed the jsdom flows. The exact stale-content production-browser symptom is not yet verified against that candidate fix, because native sandbox browser access was blocked and the main harness lacked the matching Chromium revision. No candidate fix or runtime patch has been committed. The handoff records fixtures, candidate code, verification evidence, and the exact next steps. |
|
Status update (
Verified: 138 tests, build/typecheck/Biome, dev 15 + docs 41 prerendered pages, browser checks on direct loads, tabs, history, locale, theme and mobile nav, including this deploy preview. Open questions for maintainers: merge timing, versioning/dist-tag, and whether router/meta should be peer deps. |
|
Kobalte can be bumped to |
Kobalte 2.0.0-alpha.4 requires solid-js/@solidjs/web exactly 2.0.0-rc.16 and pins the fixed event-listener/keyboard/scroll primitives; align our direct pins with it so one copy is installed. storage 5.0.0-next.7 adds explicit dispose and the hydrated option.
…sistence Kobalte 2.0.0-alpha.4 no longer shifts hydration keys on direct loads, so go back to Kobalte Tabs in tab groups and Accordion in the dev preview. Named groups persist via makePersisted (cookieStorage + messageSync over a client-only BroadcastChannel) with hydrated: true, disposing the subscription and channel on cleanup.
…solidjs/solid#4013) solid-js rc.15+ no longer leaves lazy() preloads started under the hydration Promise stub unsettled, so remove the await undefined yield and its stub-based test. Page data keeps registering under Vite-root-relative module keys.
|
Status update (
Verified: 137 tests, build, typechecks, Biome, pack; dev 15 pages + 404, docs 41 pages + 404. Production-build browser checks: direct loads of tab pages with no hydration warnings, keyboard navigation, same-page and cross-tab sync, cookie restore on reload, Back/Forward to the lazily loaded route, locale, theme, copy, and mobile nav. Notes: arrow-key direction now follows Kobalte's locale rather than the page's — Claude (Opus 5.5), posting on behalf of @devagrawal09 |
… of duplicating
Keyless inline <Script>s get a per-runtime identity from @solidjs/web useHead
("u:N" on the server, "u:cN:i" on the client), so the client never matches the
prerendered tag and appends a second copy, which also re-executes. Give the theme
and preferred-language scripts a stable key so both runtimes share the identity
"script:key:<id>" and the server-rendered element is claimed.
…ange
Kobalte's Select re-emits onChange for the kept selection whenever its
options change, and `locales` is rebuilt on every navigation, so the
LocaleSelector called setLocale(currentLocale) after each route change.
After Back on a trailing-slash URL (Netlify pretty URLs: /x -> /x/), that
navigate('/x') differed from the current '/x/' and pushed a new entry,
dropping the Forward history.
Only navigate when the selected locale actually differs, and derive
<html lang> from the current location via an effect instead of relying on
those spurious setLocale calls (which kept lang in sync on links and
Back/Forward). setLocale is a plain function again, matching main and the
documented `setLocale(locale): void`.
Solid 2's compiler (@solidjs/compiler) no longer aliases className to class, but MDX defaults to React attribute casing (className, htmlFor). Every class from markdown (Expressive Code frames/copy buttons/line marks, the TS/JS language groups, GFM task lists, footnotes) rendered as a literal className attribute. Set elementAttributeNameCase: 'html'.
…e on static hosts A single 404.html rendered for /__solidbase_404__ was served for every missing URL, but the client hydrates against the real URL: /fr/* matches fr/[...404] (French, lang=fr-FR) and /guide/* renders the guide sidebar, so hydration failed with 'Unable to find DOM nodes'. Render the probe inside each directory and keep <dir>/404.html where it differs from its parent's; route Netlify's fallback to them with 404-status rewrites.
…form SolidBase always compiles MDX with jsx: true so Solid's compiler handles the JSX; the esbuild es2019/classic-pragma pass (inherited from the React vite-plugin-mdx) only ran for jsx: false, where MDX already emits plain jsx() calls. Removing it means the published package no longer needs esbuild (Vite 8 doesn't ship it, and it needs an approved postinstall under pnpm 11).
resolve, vfile and @types/mdast are imported by published dist code/types but were only devDependencies, so a clean consumer failed to build. The published ./config/route export pointed at nonexistent src/config/route-config.js/.d.ts.
Both apps import @solidjs/meta directly but relied on the workspace root node_modules to resolve it.
… the build The prerender worker imports and runs the app's server bundle, so any console.log in consumer code (e.g. Document.tsx) landed on the stdout the parent parsed as JSON, failing vite build after rendering succeeded. Pass input and result over an IPC channel and let the worker's stdout/stderr through to the user.
Directory discovery dropped each page's final segment, so a directory with only an index page (fr/index.mdx -> /fr) was never probed and got no fr/404.html; hosts then served the root 404 under /fr/* and hydration mismatched. Probe every page path too; probes identical to their parent's 404 are still skipped.
…ter base
With a Vite base (eg. --base /docs/), pages rendered with lang="en", English
navigation and no section sidebar, and internal links left the base. Solid
Router 2's location includes the base, while SolidBase routes, locales and
sidebars are base-relative; the port also replaced <A> (which resolved hrefs
against the base) with plain anchors.
- Strip the router base (useResolvedPath("/")) before locale, route and
prev/next matching; getLocale() strips Vite's base for the document shell.
- Resolve app-absolute hrefs on plain anchors (sidebar, header nav and logo,
prev/next, hero actions, markdown links, Link, version/project selectors)
with the router's useResolvedPath.
- Pass the router base without Vite's trailing slash: resolvePath keeps it,
producing /docs//guide hrefs that also broke active link state.
Same-origin links that resolve outside the base were remapped into the app and crawled, so a build whose links escape the deployment still reported every page as successful. Skip them and warn once per link instead.
…idbase/types The guide only installed SolidBase. An existing app also needs the Solid 2 packages at the pinned versions, the SolidStart files removed (start mode picks up src/entry-*.tsx), jsxImportSource @solidjs/web, and types for virtual:file-routes / virtual:solidbase/* / ~icons/*. The workspace got those from ../src/**/*.d.ts includes that consumers don't have. Add a ./types export (src/types.d.ts, copied to dist on build) bundling filesystem-routing/types with SolidBase's virtual and icon declarations, document it, and dogfood it in docs/ and dev/.
- CurrentPageDataProvider: deferStream is a creation-time memo option; untrack it. - DirectiveContainer: keep the children() memo and read it in JSX instead of snapshotting toArray() in the component body. - useThemeText: return a merge() view so route/locale text stays reactive. - DocSearch: read the options in the effect's compute and destroy the instance on re-run/cleanup instead of reading props in the callback.
Solid Router 2 claims every same-origin anchor and sets aria-current="page" when its path and query match the location, ignoring the hash. The skip link, table of contents and heading/markdown #anchors therefore all announced themselves as the current page (75 on /guide/features/markdown). Links with a fragment now render an explicit aria-current="false", which the router leaves untouched, so only the sidebar link for the page is current, as on main.
Static hosts (eg. Netlify pretty URLs) serve pages at /guide/, while sidebar links, routes and locales are written without a trailing slash. usePrevNext compared the location with the flattened sidebar links exactly, so on /guide/ the current page wasn't found (index -1) and "Next" pointed at the first sidebar item, often the current page itself. - stripBasePath (behind useAppPathname and getLocale) now also trims trailing slashes, so prev/next, route and locale matching see one canonical path. - Legacy locale detection matches /fr as well as /fr/ against a /fr/ link.
|
Status update (
139 tests, typechecks, Biome, builds (docs 41 pages, dev 15) and a full browser pass on the deploy preview are green. The PR description is updated, including known limitations and follow-ups filed upstream (kobaltedev/kobalte#753, kobaltedev/kobalte#754, #176). — Claude (Opus 5.5), posting on behalf of @devagrawal09 |

Summary
Ports SolidBase to Solid 2 (
solid-js/@solidjs/web2.0.0-rc.16) and to@solidjs/vite-pluginstart 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; public config/theme API and MDX authoring are kept wherever possible.Draft because every upstream dependency is a prerelease (pinned exactly).
Pins
solid-js/@solidjs/web2.0.0-rc.16 (the exact peer of Kobalte alpha.4),@solidjs/router2.0.0-next.37 +filesystem-routing,@solidjs/meta1.0.0-next.2,@solidjs/vite-plugin3.0.0-next.47 (replaces@solidjs/start+nitro),@kobalte/core2.0.0-alpha.4,@solid-primitives/storage5.0.0-next.7,prerender-crawler0.2.0.Commits (grouped)
src/, tests, apps on start mode, SSG, docsbase, 404s, relative links,prerender-crawlerclassfileRouteshandling, context diagnostics, preferred-language race, font preloads/deploy docs, docs links404.htmlso missing URLs hydrate (+ Netlify 404 rewrites)./typesexport + migration guide, untracked-read fixes, in-page links don't claimaria-current, trailing-slash-safe prev/next/sidebar matchingWhat changed in the library
Mechanical:
solid-js/web→@solidjs/web;JSX/ComponentPropsfrom the renderer package;jsxImportSource(tsconfig and the MDX compiler + twoslash);merge/omit;Loading;onSettled; context-is-provider;classList→classarrays; booleanaria-*→ enumerated strings.Semantic (please review):
createResource→ asynccreateMemoinpage-data, withdeferStreampassed through (defaulttrue) so[...404]pages still set their status under streaming.getRawTheme()wrotesetTheme()inside a computation. The signal is now seeded from the cookie.globals.tsinstead of one per Layout.@solidjs/meta1.0 is provider-less:MetaProviderand themeta.providerprop are removed. The theme and preferred-language cookie scripts are keyed<Script>s, so hydration takes over the prerendered tag instead of adding (and re-running) a second copy.<a>+useLinkState;useRouteMatches; page module resolved via the lazy route'spreload()/moduleUrl. Page data is registered under Vite-root-relative keys; the old absolute keys never matched, which stalled Back/Forward to the hydrated route in production builds.setLocalestays a plain function (as onmain);<html lang>follows the current locale through an effect, so it is also right after plain links and Back/Forward. The localeSelectonly navigates on an actual locale change: Kobalte re-firesonChangewhenever its options change, and re-navigating to the current locale without the host's trailing slash pushed a history entry and dropped Forward.elementAttributeNameCase: "html"): Solid 2's compiler no longer rewritesclassNametoclass, which had dropped every class from markdown output (Expressive Code, task lists, footnotes).SolidBaseRootsetshttpStatus(404)when no route matched (Router 2 would return 200).resolve.dedupefor Solid/router/meta (pnpm peer variants → two contexts).Tabsagain, and the dev preview uses KobalteAccordion. (alpha.2's pinned@solid-primitivesshifted hydration keys, hence the interim native markup). Named groups persist viamakePersisted(cookie storage +messageSyncover a client-onlyBroadcastChannel,hydrated: true, explicitdispose). Groups lacking the saved tab fall back to the first.Routing:
src/config/file-routes.tsbuilds onfilesystem-routing, which already treats md/mdx as pages. It is wired intoplugin();fileRoutes: falseopts out (solidBaseToRouteis exported for manual wiring).startConfig()returns@solidjs/vite-pluginoptions. A customfileRoutes.diris honored by prerender, sitemap and llms.SSG:
solidbase:prerender(buildApppost-hook) renders/, the markdown route index, extras and crawled links through the productionhandleRequest(req, { renderMode: "async" })intodist/client. Link extraction, queueing and output writes are handled byprerender-crawler; SolidBase addsbasemapping and the 404 probe. It runs in a child process: the server bundle's runtime holds handles that keptvite buildfrom exiting. On by default (prerender: falsefor SSR); non-200s are logged. The 404 probe is also rendered per directory and kept where it differs (e.g.fr/,guide/,reference/), so a missing URL is served a 404 page rendered for its own locale and section and hydrates cleanly; hosts that only fall back to the root404.htmlneed rewrites (Netlify rules innetlify.toml, documented in the Deploy guide).Generated assets (sitemap/robots/llms) are emitted from
generateBundleinstead of NitropublicAssets.Consumer migration (apps)
Remove
entry-*.tsxandapp.config.ts.vite.config.tsusessolid(solidBase.startConfig()). Addsrc/Document.tsx(getHtmlProps()+<HydrationScript/>).app.tsxbecomescreateRouter({ routes: fileRoutes(pageRoutes) })wrappingSolidBaseRoot. Dropmeta.provider. Add/// <reference types="@kobalte/solidbase/types" />(virtual modules) andjsxImportSource: "@solidjs/web". See the rewritten Adding to Existing Project and Deploy guides.Upstream fixes from this work
solidjs-community/solid-primitives#1069, #1070, #1071 · kobaltedev/kobalte#746 · solidjs-community/solid-primitives#1079 · solidjs/solid#4013 (fixed by #4019 in rc.15; our
lazy()workaround is removed). Filed: kobaltedev/kobalte#753 (Tabsaria-labelledby), kobaltedev/kobalte#754 (Tabs RTL direction), #176 (locale picker → 404).Verification
pnpm test: 139 passed / 29 files.pnpm build(tsc + lib) clean.pnpm build:docs→ 41 pages +404.html, 41 sitemap URLs,robots.txt,llms.txt, 0 non-200 links.dev/build → 15 pages +404.html.Browser: direct loads hydrate cleanly; tabs, Back/Forward, locale switch OK.
Netlify deploy preview (
9530003, Chromium): direct loads (en/fr) with clean consoles; tabs (click, arrows/Home/End, same-page and cross-tab sync, cookie restore, fallback); real 404s with correctlang/sidebar and no hydration errors for/nope-xyz,/guide/…,/reference/…,/fr/…; Back/Forward/Back (en/fr, trailing slash kept); locale picker; theme cookie; copy page; mobile nav; code blocks (0className, styling and copy buttons); each head script present and executed once.TS/JS toggle (dev app with
languageSwitcher: true): labels, cookie, reload, and only the selected language's code shown.Packaging: every bare import in
dist/is declared; a fresh app outside the workspace installs the packed tarball, builds (with prerender) and runsvite dev. The unused esbuild MDX downlevel step is removed.--base /docs/build:lang, localized nav, sidebars, 404s and all internal links stay under the base; links that escape the base are reported by the prerender.One
aria-current="page"per page (in-page#links opt out; Router 2 otherwise marks every same-path link).Known / follow-ups
data-theme="ystem"in server HTML: pre-existing (getTheme()does"system".replace("s","")), corrected client-side by the inline script.@solid-mediakit/oghas no Solid 2 release, so docs OG images are disabled.with-solidbaseCLI template is still SolidStart-based; the quickstart points to the guide.dev/build prints Vite'sconfigLoader: 'native'warning (vite.config.mtsimports../src/configas a directory or without an extension).dir="rtl"([Tabs] Arrow-key direction follows locale, ignores dir="rtl" — intended? kobalte#754). Kobalte leavesaria-labelledbyoff tab panels on first render/SSR ([Tabs] Tabs.Content missing aria-labelledby on initial render and in SSR (solid2 / 2.0.0-alpha.4) kobalte#753).data-activeto every managed link and marks same-path linksaria-current; in-page links setaria-current="false"explicitly to opt out.netlify.tomlare listed per section/locale; a new docs section or locale needs a rule.usePrevNextignores the locale prefix).@solid-primitives/scroll@nextloses theuseWindowScrollPositiongeneric;unplugin-icons/types/solidtargets 1.x JSX.solid-replis a Solid 1 library), solid-docs'osmiumtheme.