Skip to content

Commit c07a044

Browse files
ryansolidClaude via Cursorcursoragent
committed
build(signals): development CJS build so server.dev.cjs gets a live DEV
The `require` branch of @solidjs/signals' exports had only the prod dist/node.cjs (__DEV__ false). A CJS host that resolved solid-js's dist/server.dev.cjs under `development` therefore required a prod signals and got DEV === undefined from a dev artifact — a lie that goes unnoticed until P1 makes the server emit into DEV.diagnostics. - dist/node.dev.cjs: flat, unmangled twin of dist/dev.js; `development` condition on the `require` branch selects it - signals dist test pins DEV per CJS artifact and equal export surface - web resolution test walks the CJS hops (@solidjs/web -> solid-js -> @solidjs/signals) under `development` and pins all three flipping together - turbo: solid-js#test now depends on solid-js#build — it had no dist-based tests until this branch, so it was the only package whose test task didn't wait for its own build Co-authored-by: Claude via Cursor <[email protected]> Co-authored-by: Cursor <[email protected]>
1 parent bd22ac8 commit c07a044

7 files changed

Lines changed: 122 additions & 7 deletions

File tree

‎.changeset/signals-dev-cjs.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solidjs/signals": patch
3+
---
4+
5+
Add a development CJS build, `dist/node.dev.cjs`, selected by the `development` condition on the `require` branch of `exports`. Previously `require` always resolved to the production `dist/node.cjs` (`__DEV__` false), so a CJS host that resolved `solid-js`'s dev server artifact would get `DEV === undefined` from its `@solidjs/signals` dependency — a dev server runtime whose diagnostics channel was silently absent. Dev CJS is the unmangled twin of `dist/dev.js`; the production CJS is unchanged.

‎documentation/plans/server-dev-build-plan.md‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,17 @@ false`), `exports-server-conditions.spec.tsx`; solid
141141
`solid.dev.*`, `web.dev.*`, `universal.dev.*`. Signals keeps `dev.js` vs
142142
`prod/` (chunked dir for the mangle pass; a restructure, not a rename).
143143
The resolution test now pins the client pairing under `browser` too.
144-
- Known gap, not P0: `@solidjs/signals`' `require` branch has no
145-
`development` condition (`dist/node.cjs` is `__DEV__: false` only), so CJS
146-
hosts loading `server.dev.cjs` still get signals' prod object and `DEV`
147-
comes back `undefined`. Needs a dev CJS build of signals; file separately.
144+
- `@solidjs/signals`' `require` branch had no `development` condition
145+
(`dist/node.cjs` is `__DEV__: false` only), so CJS hosts loading
146+
`server.dev.cjs` got signals' prod object and `DEV` came back `undefined` —
147+
a dev artifact lying about the one export P1 will emit through. Fixed in
148+
the same PR: `dist/node.dev.cjs` (unmangled twin of `dev.js`), selected by
149+
`require.development`. The resolution test walks the CJS hops
150+
(`@solidjs/web` → `solid-js` → `@solidjs/signals`) and pins all three
151+
flipping together; a signals dist test pins `DEV` per CJS artifact.
152+
- `solid-js#test` now depends on `solid-js#build` in `turbo.json` — it had
153+
no dist-based tests until this work, so it was the one package whose test
154+
task didn't wait for its own build.
148155

149156
### P1 — A server diagnostics channel
150157

‎packages/signals/package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@
3434
},
3535
"require": {
3636
"types": "./dist/types-cjs/index.d.cts",
37+
"development": "./dist/node.dev.cjs",
3738
"default": "./dist/node.cjs"
3839
}
3940
},
4041
"./package.json": "./package.json"
4142
},
4243
"scripts": {
4344
"build": "npm-run-all -nl build:* && pnpm types",
44-
"build:clean": "rimraf dist/dev dist/prod dist/node dist/dev.js dist/prod.js dist/node.cjs",
45+
"build:clean": "rimraf dist/dev dist/prod dist/node dist/dev.js dist/prod.js dist/node.cjs dist/node.dev.cjs",
4546
"build:js": "rollup -c && node ./scripts/mangle-props.mjs dist/prod dist/node.cjs && node ./scripts/check-pure.mjs dist/prod",
4647
"types": "tsc -p tsconfig.build.json && node ../../scripts/sync-dual-types.mjs ./dist/types ./dist/types-cjs",
4748
"test": "vitest run",

‎packages/signals/rollup.config.js‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import prettier from "rollup-plugin-prettier";
66
// consumed exclusively by bundlers, which can drop whole feature modules —
77
// including their top-level GlobalQueue hook installs, which statement-level
88
// shaking of a flat file can never remove (#2883) — and scope-hoist the rest
9-
// back into one module. Dev and node stay flat single files: dev bundle size
9+
// back into one module. Dev and node (prod + dev CJS) stay flat single files: dev bundle size
1010
// doesn't matter (and vitest's per-module SSR transform makes a chunked tree
1111
// ~2x slower in the flush hot path, poisoning CI benches), and CJS `require`
1212
// can't tree-shake, so a tree would charge unbundled SSR the per-module-
@@ -95,5 +95,38 @@ export default [
9595
parser: "typescript"
9696
})
9797
]
98+
},
99+
{
100+
// Dev CJS — the `require` twin of dist/dev.js, selected by the
101+
// `development` condition on the `require` branch. Without it a CJS host
102+
// that resolved solid-js's `dist/server.dev.cjs` would `require` the prod
103+
// `dist/node.cjs` (`__DEV__` false) and get `DEV === undefined` from a dev
104+
// artifact — a lie that goes unnoticed until something emits into
105+
// `DEV.diagnostics`. Flat single file like node.cjs; not mangled (dev
106+
// outputs never are — see build:js).
107+
input: "src/index.ts",
108+
output: {
109+
file: "dist/node.dev.cjs",
110+
format: "cjs",
111+
exports: "named"
112+
},
113+
plugins: [
114+
replace({
115+
__DEV__: "true",
116+
__TEST__: "false",
117+
preventAssignment: true
118+
}),
119+
typescript({
120+
declaration: false,
121+
outDir: "dist",
122+
module: "esnext",
123+
target: "esnext",
124+
moduleResolution: "bundler",
125+
verbatimModuleSyntax: true
126+
}),
127+
prettier({
128+
parser: "typescript"
129+
})
130+
]
98131
}
99132
];
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Direct coverage of the built CJS artifacts — dist/node.cjs (prod, the
3+
* `require` default) and dist/node.dev.cjs (the `development` condition on
4+
* the `require` branch). The suite otherwise imports source under vitest's
5+
* `__DEV__: true` define, so it cannot see whether each artifact's replace
6+
* ran. `DEV` is the observable: defined in dev, `undefined` in prod. The dev
7+
* CJS exists so a CJS host that resolves solid-js's `dist/server.dev.cjs`
8+
* also gets a signals with a live `DEV.diagnostics` — one dev flag per
9+
* process, not one per module format. Requires a prior build (the turbo
10+
* `test` task depends on `build`).
11+
*/
12+
import { createRequire } from "node:module";
13+
import { describe, expect, test } from "vitest";
14+
15+
const require = createRequire(import.meta.url);
16+
17+
describe("@solidjs/signals CJS artifacts", () => {
18+
test("dist/node.cjs (prod) exports DEV as undefined", () => {
19+
const prod = require("../dist/node.cjs");
20+
expect(prod.DEV).toBeUndefined();
21+
});
22+
23+
test("dist/node.dev.cjs exports a live DEV with the diagnostics channel", () => {
24+
const dev = require("../dist/node.dev.cjs");
25+
expect(dev.DEV).toBeDefined();
26+
expect(typeof dev.DEV.diagnostics.subscribe).toBe("function");
27+
expect(typeof dev.DEV.diagnostics.capture).toBe("function");
28+
});
29+
30+
test("the two artifacts export the same surface", () => {
31+
const prod = require("../dist/node.cjs");
32+
const dev = require("../dist/node.dev.cjs");
33+
expect(Object.keys(dev).sort()).toEqual(Object.keys(prod).sort());
34+
});
35+
});

‎packages/web/test/server/exports-server-conditions.spec.tsx‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,40 @@ describe("export conditions: dev/prod artifact pairing", () => {
8989
});
9090
});
9191

92+
test("CJS `require` pairs solid-js's server.dev.cjs with signals' node.dev.cjs", () => {
93+
// The web server bundle externalizes solid-js, and solid-js's server
94+
// bundle externalizes @solidjs/signals, so a CJS host resolves each in
95+
// turn. The dev server artifact is only honest if the signals it requires
96+
// is dev too — its `DEV` export is signals' object — so the `require`
97+
// branch of signals needs its own `development` entry, and this pins
98+
// both hops flipping together.
99+
const script =
100+
`const { createRequire } = require("node:module"); ` +
101+
`const r = createRequire(process.cwd() + "/"); ` +
102+
`const solid = r.resolve("solid-js"); ` +
103+
`const signals = createRequire(solid).resolve("@solidjs/signals"); ` +
104+
`const web = r.resolve("@solidjs/web"); ` +
105+
`process.stdout.write(JSON.stringify([web, solid, signals].map(p => p.replace(/^.*\\/packages\\//, ""))));`;
106+
const run = (conditions: string[]) =>
107+
JSON.parse(
108+
execFileSync(
109+
process.execPath,
110+
[...conditions.map(c => `--conditions=${c}`), "-e", script],
111+
{ cwd: process.cwd(), encoding: "utf8" }
112+
)
113+
);
114+
expect(run([])).toEqual([
115+
"web/dist/server.cjs",
116+
"solid/dist/server.cjs",
117+
"signals/dist/node.cjs"
118+
]);
119+
expect(run(["development"])).toEqual([
120+
"web/dist/server.dev.cjs",
121+
"solid/dist/server.dev.cjs",
122+
"signals/dist/node.dev.cjs"
123+
]);
124+
});
125+
92126
test("worker and deno conditions carry the same dev/prod pairing as node", () => {
93127
// Node always adds its own `node` condition; passing `worker`/`deno` on top
94128
// exercises those keys' nesting (they precede `node` in every exports map

‎turbo.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"outputs": []
8181
},
8282
"solid-js#test": {
83-
"dependsOn": ["@solidjs/signals#build"],
83+
"dependsOn": ["@solidjs/signals#build", "solid-js#build"],
8484
"outputs": []
8585
},
8686
"@solidjs/web#test": {

0 commit comments

Comments
 (0)