Skip to content

fix(build): emit every per-component entry point - #3237

Merged
interactivellama merged 2 commits into
masterfrom
fix/build-per-component-entries
Oct 26, 2026
Merged

interactivellama merged 2 commits into
masterfrom
fix/build-per-component-entries

Conversation

@Niznikr

@Niznikr Niznikr commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #

Additional description

Symptom: In 11.0.0-alpha.4, importing @salesforce/design-system-react/components/color-picker, .../filter or .../tree fails. Importing from the package root works.

Root cause: The library build had a single entry, the components/index.js barrel. With preserveModules, Rollup does not emit an index that only re-exports (export { default } from './tree') if the barrel is the only thing that reaches it. Rollup links the barrel's import straight to ./tree instead. Because there was no built index.js, generate-entry-shims skipped the folder. That meant dist/es/components/tree.js and dist/cjs/components/tree.cjs, the files the ./components/* export points to, were never created. tsc still emitted index.d.ts, so types resolved and the import failed only at runtime.

The same cause broke more than the three reported entries:

Entry point Problem in alpha.4
components/color-picker, filter, tree No JS output: ESM and CJS imports fail
components/grid, utilities/hooks Not reachable from the barrel, so no JS was emitted at all (types only)
components/menu-dropdown Missing at runtime: DropdownNubbinPositions, ListItem, ListItemLabel
components/page-header Missing at runtime: Info, Title, DetailRow, DetailBlock
components/popover Missing at runtime: PopoverNubbinPositions
components/progress-ring Missing at runtime: THEME_OPTIONS

The missing named exports were declared in each entry's .d.ts. The barrel doesn't use them, so they were tree-shaken out of the deep-import module.

Fix:

  • vite.config.ts: every components/*/index and utilities/*/index is now an explicit build entry. Rollup always emits entries in full.
  • scripts/generate-entry-shims.mjs: the shim re-exports default only when the index has one. utilities/hooks has only named exports, and re-exporting a default that doesn't exist is an ESM link error. Without this change, the hooks shim the build now generates would fail to load.

Build output changes: I diffed dist/ before and after.

  • New files: only the missing index files and shims for the seven entry points in the table.
  • Changed files:
    • The restored named exports in the four affected modules.
    • CJS entry files now set __esModule (standard interop marker for entry chunks).
  • No other output changed.

Regression test: scripts/check-package-exports.mjs (npm run test:exports) runs as the last step of npm run build, so both CI (test:ci) and the publish workflow run it. It lists every directory with an index in the source tree, so an entry the build fails to emit is reported, not skipped. It imports each one through the package's own name, which applies the real exports map. Each entry must:

  • resolve its types under both the import and require conditions (moduleResolution: bundler, as in our tsconfig);
  • load with ESM import() and CJS require();
  • provide at runtime every value export its types declare.

Result: 57/57 entry points pass. On the unfixed build it reports 18 failures, which cover every row in the table above.

Not breaking. This only restores entry points and exports that the types already declared. Note that components/grid now ships JS as well as types. It isn't in the barrel, but its .d.ts was already published.


CONTRIBUTOR checklist (do not remove)

Please complete for every pull request.

Green gate — all of these must pass locally (CI runs the same):

  • npm run lint passes with 0 errors (npm run lint:fix to auto-fix). Warnings are the tracked gradual-migration backlog; do not add new errors.
  • npm run typecheck is clean (tsc --noEmit).
  • npm test -- --run passes — this runs both Vitest projects (jsdom unit + real-browser browser) and the story HTML snapshots. If you changed rendered markup on purpose, update snapshots with npm test -- --run -u and review the diff.
  • npm run build succeeds (type-check + Vite library build + .d.ts + entry shims).

Behavior & docs:

  • First-time contributors: sign the CLA (a bot will comment with instructions). — N/A
  • Added/updated Vitest + React Testing Library tests for the change (template: components/button/__tests__). New props and bug fixes should get a regression test. Tests that genuinely need a real browser (focus traversal, layout/measurement, canvas) go in a *.browser.test.jsx file (the browser project); everything else is jsdom. — The regression test is a Node script, not Vitest. It has to run against the built dist/ with real Node and TypeScript module resolution, which the Vite resolver would bypass.
  • Reviewed the component's Storybook stories: npm run storybook → http://localhost:6007/. Confirm the story actually renders and behaves (deprecated Storyshots/Karma are gone; there is no separate browser-tests server). — N/A, build-only change
  • Story/example code uses the component's real prop API (e.g. render-props like onRenderContent / onRenderVisualPicker, icon props, events), not assumptions — a common source of "component looks broken" reports has been stories authored against the wrong API. Cross-check against components/<name>/__examples__. — N/A
  • Storybook Accessibility (a11y) panel shows no new violations for the affected stories. — N/A
  • If you added or changed a type, remember runtime PropTypes were removed in the TS migration — types are the contract. Ship accurate .d.ts (they're generated by the build); JS consumers rely on them via editor type-acquisition. — No type changes. The runtime now matches the published types.

REVIEWER checklist (do not remove)

  • CI is green (GitHub Actions CI workflow: lint, typecheck, test, build). Chromatic visual review, if configured, has been checked.
  • Change has adequate test coverage; bug fixes include a regression test.
  • Reviewed the affected Storybook stories and their a11y panels.
  • Rendered markup still conforms to SLDS — check the story HTML snapshot diff (components/__tests__/__snapshots__/story-snapshots.test.jsx.snap).
  • Public API / package surface: if exports changed, the deep-import contract still holds (@salesforce/design-system-react/components/<name> and nested paths resolve for ESM, CJS, and types). Removing/renaming a public component or export is a breaking change → feat!:/BREAKING CHANGE: and a major version bump.

Niznikr and others added 2 commits October 21, 2026 16:18
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`components/color-picker`, `components/filter` and `components/tree` failed
to import in 11.0.0-alpha.4 (the package root worked).

Root cause: the library build used only the `components/index.js` barrel as
its entry. Under `preserveModules`, an `index` that only re-exports
(`export { default } from './tree'`) is not emitted when it is merely
reachable from the barrel; Rollup binds the barrel's import straight through
to `./tree`. With no built `index.js`, `generate-entry-shims` skipped the
folder, so `dist/es/components/tree.js` (the target of the `./components/*`
export) did not exist. `tsc` still emitted `index.d.ts`, so types resolved
and the import failed only at runtime.

The same root cause also affected:
- `components/grid` and `utilities/hooks`: not reachable from the barrel,
  so they had no JS at all (types only).
- `components/menu-dropdown`, `page-header`, `popover`, `progress-ring`:
  named exports the barrel doesn't use (`DropdownNubbinPositions`,
  `ListItem`, `ListItemLabel`, `Info`, `Title`, `DetailRow`, `DetailBlock`,
  `PopoverNubbinPositions`, `THEME_OPTIONS`) were tree-shaken from the
  deep-import module despite being declared in its types.

Fix: make every `components/*/index` and `utilities/*/index` an explicit
build entry. Rollup always emits entries in full. The shim generator now
re-exports `default` only when the index has one; `utilities/hooks` is
named-only, and a missing `default` re-export is an ESM link error.

Regression test: `scripts/check-package-exports.mjs` (`npm run
test:exports`, run at the end of `npm run build`, so CI and publish both
run it) lists every directory index in the source tree and imports it
through the package's own `exports` map. Each must resolve its types under
the import and require conditions, load via ESM `import()` and CJS
`require()`, and supply at runtime every value export its types declare.
Against the previous build it reports all of the above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Niznikr
Niznikr force-pushed the fix/build-per-component-entries branch from 5b281b3 to e5a94ab Compare October 21, 2026 20:29
@interactivellama
interactivellama merged commit abe2351 into master Oct 26, 2026
10 checks passed
@interactivellama
interactivellama deleted the fix/build-per-component-entries branch October 26, 2026 20:43
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.

2 participants