Skip to content

build: replace webpack with Vite and ship a real ESM/CJS package - #204

Merged
Eliav2 merged 2 commits into
masterfrom
build/vite-library
Aug 8, 2026
Merged

build: replace webpack with Vite and ship a real ESM/CJS package#204
Eliav2 merged 2 commits into
masterfrom
build/vite-library

Conversation

@Eliav2

@Eliav2 Eliav2 commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Closes #118.

Why

The webpack build emitted one UMD bundle and pointed both main and module at it. UMD cannot be tree-shaken, so every consumer paid for the whole library regardless of what they imported. That is the root cause of #118, and it is why #175 and #154 (trimming lodash imports) could never have fixed it on their own.

What

  • vite.config.mts replaces webpack.config.js + webpack.libConfig.js
  • Emits ESM + CJS + UMD. UMD is kept, under unpkg/jsdelivr, so existing script-tag users are unaffected
  • Declarations bundled into one flat index.d.ts, copied to .d.mts/.d.cts so both exports conditions resolve
  • Real exports map with per-condition types
  • src is now published, so the shipped sourcemaps actually resolve

Two silent failures worth knowing about

JSX runtime. The bundler does not inherit jsx from tsconfig.json, and defaults to the automatic runtime. That inlined the entirety of react/jsx-runtime into the bundle and emitted a require("react") that threw the moment the ESM entry was imported. It would also have broken the declared react >=16.8 peer, since react/jsx-runtime does not exist before React 17. Now pinned to the classic runtime explicitly.

bundleTypes. vite-plugin-dts renamed rollupTypes to bundleTypes in v5 and ignores the old key without warning, so declarations stayed unbundled and looked fine.

Both produced a build that passed lint, type-check, tests and a filename check on the tarball. Only actually importing the artifact caught them, which is why CI now does that.

Target

ES5 to ES2015. esbuild cannot emit ES5, and browserslist already excluded IE11, so nothing supported is lost.

Results

raw gzip
UMD before 25,866 7,873
UMD after 18,874 6,880
import { useXarrow } before 26,661 8,847
import { useXarrow } after 16,408 6,349

@arethetypeswrong/cli: no problems found, green on node10 / node16-CJS / node16-ESM / bundler.

CI

Two new steps, both of which would have caught the JSX bug:

  • loads the ESM and CJS entry points out of the packed tarball and asserts the exports are callable
  • runs @arethetypeswrong/cli

Removed

webpack, webpack-cli, webpack-node-externals, babel-loader, ts-loader, file-loader, the five @babel/* packages, .babelrc, and ts-node.

ts-node was only ever there to load the webpack config, so #198 (bump ts-node) can be closed rather than merged.

Not in this PR

Version stays at 2.0.2. This is consumer-visible and wants a minor bump to 2.1.0, but publishing is blocked until the npm Trusted Publisher is registered on npmjs.com.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Build & Packaging

    • Updated the package build system to generate ESM, CommonJS, UMD, and TypeScript declaration outputs.
    • Improved package entry points and conditional exports for broader tooling compatibility.
    • Added source files to the published package.
  • Quality Assurance

    • Expanded package verification to validate generated files, type resolution, and loading in React applications.
  • Maintenance

    • Updated the TypeScript compilation target and replaced the previous build configuration.

The webpack build emitted a single UMD bundle and pointed both "main" and
"module" at it. UMD cannot be tree-shaken, so every consumer paid for the whole
library no matter what they imported (#118).

Build:
- vite.config.mts replaces webpack.config.js and webpack.libConfig.js
- emits ESM, CJS and UMD; UMD is kept so existing script-tag users are unaffected
- declarations are bundled into one flat index.d.ts via vite-plugin-dts, and
  copied to .d.mts/.d.cts so both exports conditions resolve correctly
- adds an "exports" map with per-condition types
- src is now published so the shipped sourcemaps actually resolve

Two things had to be set explicitly, both of which failed silently by default:
- JSX is pinned to the classic runtime. The bundler does not inherit "jsx" from
  tsconfig.json and defaults to the automatic runtime, which inlined the whole
  of react/jsx-runtime into the bundle and emitted a require("react") that threw
  on import from ESM. It would also have broken the declared react >=16.8 peer.
- Target moves from ES5 to ES2015, because esbuild cannot emit ES5. browserslist
  already excluded IE11, so nothing supported is lost.

Sizes, production build:
  UMD          25,866 -> 18,874 raw   7,873 -> 6,880 gzip
  import { useXarrow } only
               26,661 -> 16,408 raw   8,847 -> 6,349 gzip

CI now loads both entry points from the packed tarball and runs
@arethetypeswrong/cli. A filename check would not have caught the broken ESM
output above.

Drops webpack, webpack-cli, webpack-node-externals, babel-loader, ts-loader,
file-loader, the five @babel packages, .babelrc and ts-node.

Version is deliberately left at 2.0.2; this is consumer-visible and wants a
minor bump, but publishing is blocked until the npm Trusted Publisher is
registered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codesandbox

codesandbox Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Eliav2, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2b1902d8-f8e7-4c75-ac90-e753a041f6d9

📥 Commits

Reviewing files that changed from the base of the PR and between 34508e2 and 7b26fe8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • package.json
  • vite.config.mts
📝 Walkthrough

Walkthrough

The package replaces Webpack and Babel library builds with Vite. It publishes ESM, CJS, UMD, and declaration outputs, defines conditional exports, and adds CI checks for packed-package contents and consumer resolution.

Changes

Library build and publishing

Layer / File(s) Summary
Package outputs and build commands
package.json, tsconfig.json
Package metadata now maps ESM, CJS, UMD, and declaration files. Build scripts and development dependencies now use Vite tooling. TypeScript targets ES2015. The published files include src.
Vite library output configuration
vite.config.mts
Vite builds src/index.tsx in ESM, CJS, and UMD formats. The configuration externalizes React dependencies, generates sourcemaps, and emits declaration variants.
Packed package validation
.github/workflows/ci.yml
CI checks packed output files, validates export and type resolution, and tests ESM and CJS loading from a temporary React consumer. Webpack configuration and Babel configuration are removed.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant Vite
  participant Package
  participant CI
  participant Consumer
  Developer->>Vite: run build
  Vite->>Package: emit ESM, CJS, UMD, and declaration files
  CI->>Package: inspect packed contents
  CI->>Consumer: install packed package
  Consumer->>Package: import or require entry point
  Package-->>Consumer: provide default and named exports
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: replacing Webpack with Vite and publishing ESM/CJS package outputs.
Linked Issues check ✅ Passed The PR provides tree-shakeable ESM output and reports reduced package size, addressing issue #118's lodash bundling objective.
Out of Scope Changes check ✅ Passed The changes support the build migration, package exports, artifact validation, and bundle-size objective without unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch build/vite-library

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 70-71: Add a reviewed, pinned `@arethetypeswrong/cli` version to
devDependencies and regenerate the lockfile. Update the “Verify type resolution”
workflow step to invoke the installed pinned dependency rather than npx with the
latest tag.

In `@vite.config.mts`:
- Around line 50-57: Update the Rollup configuration around external and output
handling so lodash remains bundled in the UMD build instead of requiring
window._, while preserving the existing externalization for other formats. Add a
UMD smoke test that loads the generated bundle with React dependencies but
without lodash and verifies it initializes successfully.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 213f8752-916b-4052-b7ee-f88fc13ef70d

📥 Commits

Reviewing files that changed from the base of the PR and between 6f270c8 and 34508e2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • .babelrc
  • .github/workflows/ci.yml
  • package.json
  • tsconfig.json
  • vite.config.mts
  • webpack.config.js
  • webpack.libConfig.js
💤 Files with no reviewable changes (3)
  • webpack.config.js
  • .babelrc
  • webpack.libConfig.js

Comment thread .github/workflows/ci.yml Outdated
Comment thread vite.config.mts
Review feedback on this PR.

- @arethetypeswrong/cli was invoked as npx ...@latest, so CI silently tracked
  whatever release was current. Pin it in devDependencies and run it via
  pnpm exec.
- Nothing exercised the UMD output. Load it against real React, lodash and
  prop-types globals and assert the exports, since a wrong global name yields a
  bundle that loads fine and only fails on first use.

Also documents why lodash is read as `_`. The webpack build asked for
`root["lodash"]`, a global lodash never defines, so the script-tag path was
already broken.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Eliav2
Eliav2 merged commit d67c6e9 into master Aug 8, 2026
2 checks passed
@Eliav2
Eliav2 deleted the build/vite-library branch August 8, 2026 18:53
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.

Entirety of Lodash is Bundled (over 75% of package size)

1 participant