chore: Node >=22.12 floor to match the rc runtime; dedupe @solidjs/signals - #377
Merged
Merged
Conversation
…gnals `engines.node` moves from `^20.19.0 || >=22.12.0` to `>=22.12.0`. Since Solid 2.0.0-rc.8 the runtime packages (`solid-js`, `@solidjs/web`, `@solidjs/signals`) are ESM-only with that same floor, so the plugin's Node 20 lane was already unusable with the peer range it declares. The README requirement line follows. CI already runs Node 24 in every workflow (cr, e2e, release, vitest); no matrix change. No `.npmrc`, and no example declares its own `engines`. Under `serve`, `@solidjs/signals` joins `solid-js` and `@solidjs/web` in `resolve.dedupe` — gated on the app root reaching a copy of it (`node_modules/@solidjs/signals`, walking up: an npm-style hoist or a direct dependency). `@solidjs/diagnostics/browser` and `solid-js/attribution` import the signals core directly, so a nested/duplicated install yields a second engine beside the one `solid-js` loads — the same two-engines symptom #374 addressed for the pre-bundle path with `optimizeDeps.include`. Why the gate: `resolve.dedupe` resolves the listed package from the root. Vite 8's native (rolldown) resolver falls back to the importer when that misses, but the JS `tryNodeResolve` used by the SSR externalize decision and by `fetchModule` (the module runner's resolution of externalized bare imports) does not. Under pnpm's isolated layout signals exists only as `solid-js`'s transitive dependency, so an unconditional entry turned `import "@solidjs/signals"` from the inlined `solid-js/dist/server.dev.js` into `ERR_MODULE_NOT_FOUND` as soon as anything externalized it — and vitefu does exactly that once a semi-framework package such as `@solidjs/diagnostics` lists signals under `dependencies`. Reproduced in a strict-pnpm app (solid-js + @solidjs/web + @solidjs/diagnostics) through the SSR module runner: baseline OK, unconditional dedupe throws, dedupe with a root copy OK. With no root copy there is nothing to dedupe to, so the gate loses nothing. Signals stays out of `optimizeDeps.include`: the optimizer already reaches it through `solid-js`, and an include entry that doesn't resolve from the root logs `Failed to resolve dependency` on every start (observed in this repo's own examples). Tests: start-ssr gains test/dedupe.mjs (pure resolveConfig): in the example's own pnpm layout signals is absent from `resolve.dedupe` and `optimizeDeps.include`; in a temp root with a stub `node_modules/@solidjs/signals` it is present in `dedupe` and still absent from `include`; under `build` the list is empty. Co-authored-by: Claude via Cursor <noreply@cursor.com>
🦋 Changeset detectedLatest commit: da7c347 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two maintainer-approved housekeeping changes for the 2.0 line. No plugin option is added, removed, or renamed.
1. Node engines floor:
>=22.12.0What.
package.jsonengines.nodemoves from^20.19.0 || >=22.12.0to>=22.12.0; the README requirement line (## Requirements) says the same and notes it is the runtime's floor.Why. Since Solid 2.0.0-rc.8 the runtime packages are ESM-only with
engines.node: ">=22.12.0"— verified onsolidorigin/nextinpackages/solid/package.json,packages/web/package.json,packages/signals/package.json. With the peer range this plugin declares (solid-js ^2.0.0-rc.10), the Node 20 lane was already unusable; the manifest now says so.Where.
package.json:7,README.md:31-32.Swept.
rg '20\.19|Node 20'outsidenode_modules/lockfile: only the two sites above. No.npmrc(so noengine-strict). Noexamples/*/package.jsondeclaresengines.CI matrix — before / after
Unchanged. Every workflow already runs Node 24 and nothing runs Node 20:
cr.ymle2e.ymlrelease.ymlvitest.yml2.
@solidjs/signalsinresolve.dedupe(gated on a root copy)What. Under
serve,@solidjs/signalsjoinssolid-jsand@solidjs/webinresolve.dedupewhen the app root can reach a copy (node_modules/@solidjs/signals, walking up — an npm-style hoist or a direct dependency). It does not joinoptimizeDeps.include.Why.
@solidjs/diagnostics/browser(packages/diagnostics/src/browser.ts) andsolid-js/attribution(export * from "@solidjs/signals/attribution") import the signals core directly, so a nested/duplicated install yields a second engine beside the onesolid-jsloads — the same two-engines symptom #374 addressed for the pre-bundle path.Where.
src/index.ts:1290-1313(the gateddedupelist, using the existingfindPackageDirroot walk-up),src/index.ts:1388(resolve.dedupe).The brief asked for an unconditional entry. That is a regression for strict-pnpm apps, reproduced before shipping:
resolve.deduperesolves the listed package from the root. Vite 8's native rolldown resolver (vite_resolve_plugin.rs) falls back to the importer when that misses — which is why the example suites passed with the unconditional entry. But the JStryNodeResolvestill used by the SSR externalize decision (createIsConfiguredAsExternal) and byfetchModule(module-runner resolution of externalized bare imports) has no fallback.@solidjs/signalsexists only assolid-js's transitive dependency (this repo's own examples included). vitefu externalizes the non-Solid dependencies of semi-framework packages, and@solidjs/diagnosticslists@solidjs/signalsunderdependencies→ signals lands inssr.external→ the bare import from the inlinedsolid-js/dist/server.dev.jsreachesfetchModule→ root-only resolve misses →ERR_MODULE_NOT_FOUND: Cannot find module '@solidjs/signals'.solid-js+@solidjs/web+@solidjs/diagnostics, plugin linked,createServerModuleRunner(ssr).import(entry)): baseline ok; unconditional dedupe throws as above; dedupe with a root copy ok (and the resolver check showssolid-js's import landing on the root copy — dedupe honored).Why not
optimizeDeps.include.nestedDepsalso feedsoptimizeDeps.include. The optimizer already reaches signals throughsolid-js, and an include entry that doesn't resolve from the root logsFailed to resolve dependency: @solidjs/signals, present in client 'optimizeDeps.include'on every dev start (observed in this repo's own examples). So signals goes todedupeonly, per the brief's fallback instruction.Other consumers checked.
ssr.noExternaland the vitefu crawl are not fed bynestedDeps(they useSOLID_RUNTIME_PKGS/ the crawl's own classification); untouched.pnpm why @solidjs/signals: present in the plugin's install as a transitive dependency ofsolid-js(and a direct dependency of@solidjs/diagnostics), never hoisted to the root.Tests
examples/start-ssr/test/dedupe.mjs(pureresolveConfig, wired into the example'stestscript): in the example's own pnpm layout signals is absent fromresolve.dedupeandoptimizeDeps.include; in a temp root with a stubnode_modules/@solidjs/signalsit is present indedupeand still absent frominclude; underbuildthe list is empty. 8/8.pnpm build✓,pnpm check✓ (100/100), rootpnpm test(Cypress vite-8) ✓ 1/1.examples/start-ssrfull suite ✓ (607/607 run, 10/10 http-bridge, 11/11 components-warning, 12/12 webworker-warning, 8/8 dedupe);examples/start-client✓ 65/65;examples/start-env✓ 47/47.Public API changes
engines.nodechanges from^20.19.0 || >=22.12.0to>=22.12.0. Installing the plugin under Node 20 now failsengineschecks (a hard failure only withengine-strict; otherwise a warning). This is the one user-visible effect of the PR.resolve.dedupeaddition is a config-shape change visible to apps only via their resolved Vite config.Co-authored-by: Claude via Cursor