[BUG] /design-sync story previews ignore package.json "browser": false, so builds fail on Node built-ins
Summary
/design-sync story previews ignore "browser": false entries in a dependency's package.json, so a story whose import graph reaches such a package fails to compile:
! preview build failed: <Component>: Could not resolve "stream" (node_modules/@saulx/utils/dist/src/readStream.js:1:28)
Plain esbuild with the same options builds the same import fine.
Claude Code 2.1.282, macOS, storybook shape.
Cause
@saulx/utils declares:
"browser": { "./dist/src/readStream.js": false }
esbuild honours that and resolves the file to an empty module. In lib/story-imports.mjs, however, the ds-import-policy plugin resolves every import itself with b.resolve(...) and then, for third-party files, returns that result:
if (p.includes('/node_modules/')) return r; // third-party stays put
The returned object carries only path and namespace, so esbuild's "disabled" marker is lost and the real readStream.js is bundled. That file does import { PassThrough } from 'stream', which fails for platform: 'browser'.
Verified fix
I bundled a one-line story (import * as B from '@based', which reaches @saulx/utils through the Based SDK client) with the converter's own storyImportPlugins:
| Plugins | Result |
|---|---|
| Stock story-imports.mjs | Could not resolve "stream" |
| Same file, with that line changed to return null; | builds |
| Plain esbuild, no plugins | builds |
Returning null for node_modules paths hands resolution back to esbuild, which already keeps third-party files bundled as-is, and preserves the browser field handling. The same pattern is worth checking wherever a plugin returns a raw b.resolve() result.
cfg.storyImports offers no workaround that keeps the module's real contents. shim can only redirect the file to the design system's global.