Skip to content

Switch build from tsc to tsup (ESM + CJS + IIFE)#1896

Open
mikedotexe wants to merge 9 commits intonear:masterfrom
mikedotexe:pr/tsup-build
Open

Switch build from tsc to tsup (ESM + CJS + IIFE)#1896
mikedotexe wants to merge 9 commits intonear:masterfrom
mikedotexe:pr/tsup-build

Conversation

@mikedotexe
Copy link
Copy Markdown
Contributor

@mikedotexe mikedotexe commented Mar 11, 2026

the big kahuna. closes #1876
first, I know it's kind of rude or perhaps a bigger deal to suggestion replacing tsc with tsup, but it came after long and grueling hours. Originally with FastNearJS I had a combo of tsc using no-emit and esbuild, but found tsup is a superset of esbuild and just did everything in what felt like a bulletproof way.

What's not as obvious from the summary below, is that the IIFE bundle means near-api-js can and will work on a static HTML site. No yarn start it can just use a <script> tag and be pulled from a CDN if desired. Quite portable while still allowing for regular NodeJS apps to use the EcmaScript build.

This change allows these three target ouputs EcmaScript, CommonJS, and browser bundle IIFE to come from the same code, and leverage ESM's safety/efficiencies. So someone can build a React app like normal and another person can (literally) run near-api-js on a tiny IoT sensor talking to the blockchain.

I say this not to sway, but to celebrate as we look into this: we'll have the most efficient web3 library in the world, I'm willing to debate. the most efficient web3 lib for the most efficient blockchain in the world 💪🏻

Summary

Replace the tsc-only build with tsup producing three output formats, matching the FastNear JS Monorepo build pattern:

  • ESM (unbundled) — tree-shakeable, preserves module structure
  • CJS (unbundled) — Node.js CommonJS with .cjs extensions
  • IIFE (bundled) — 252KB browser bundle for <script> tags, with lightweight shims

Adds browser shims to keep the IIFE bundle small (~116KB saved):

  • error-parse-lite.ts — lightweight error parser replacing ~62KB error class hierarchy
  • json-validator-noop.ts — no-op ABI validator replacing ~44KB is-my-json-valid
  • util.ts — browser shim for node:util

Depends on: #1894 (inline borsh) and #1895 (crypto utils). Merge those first, then rebase this.

Changes

File Change
tsup.config.ts New — build config for ESM, CJS, IIFE
scripts/fix-cjs-imports.js New — post-build CJS .js→.cjs path fixer
src/shims/error-parse-lite.ts New — lightweight IIFE error parser
src/shims/json-validator-noop.ts New — no-op IIFE validator shim
src/shims/util.ts New — browser shim for node:util
package.json exports (lib/→dist/), main/module/browser/types, build script (tsup), tsup devDep, files (dist), keywords, updated clean/autoclave/check-exports

Build output

Format Size Notes
ESM ~200KB Unbundled, tree-shakeable
CJS ~300KB Unbundled, .cjs extensions
IIFE 252KB Single bundled file, browser-ready

Test plan

  • pnpm build (tsup) — no errors, all 3 formats produced
  • pnpm test — all 27 test files, 346 tests pass
  • pnpm check-exports (attw) — all 7 subpath exports green across node10/node16/bundler

🤖 Generated with Claude Code

Replace the borsh npm dependency with a lean 289-line zero-dep
serializer/deserializer tailored to NEAR's schema needs. Move
is-my-json-valid to optionalDependencies and dynamically import it
with a try/catch fallback, so environments without it still work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 11, 2026

🦋 Changeset detected

Latest commit: e70716e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
near-api-js Minor

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Volya and others added 7 commits March 11, 2026 08:02
This allows downstream consumers (e.g. near-connect) to import
serialize, deserialize, and the Schema type directly from near-api-js
instead of depending on the borsh package separately.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New public API surface for downstream consumers (near-connect wallets)
to import crypto helpers (sha256, signHash, privateKeyFromRandom, etc.)
and plain JSON transaction types (PlainTransaction, mapTransaction,
mapAction) directly from near-api-js. Also adds parseRpcError re-export
and actionCreators backwards-compat alias.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the tsc-only build with tsup producing three output formats:
ESM (unbundled), CJS (unbundled with .cjs extension fix), and IIFE
(fully bundled browser build at ~252KB). Adds lightweight browser shims
for error parsing, JSON validation, and node:util to keep the IIFE
bundle small. Updates package.json exports to dist/ with proper
import/require/types conditions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap IIFE globalThis.defineProperty in try/catch to match the
js-monorepo pattern and prevent silent failures in strict
environments. Fix biome import ordering in crypto/index.ts and
index.ts, and normalize package.json array formatting.

Build verified: 346 tests pass, all 7 attw export checks green,
IIFE 252KB (unchanged effective size).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop nested types conditions from package.json exports -- TypeScript
resolves co-located .d.ts/.d.cts files automatically. Matches the
js-monorepo pattern. Verified with attw (all 7 subpath exports green)
and full test suite (346 pass).

Document root cause in fix-cjs-imports.js: tsup/esbuild with
bundle:false preserves source import specifiers verbatim, so .js
extensions in ESM source become broken require("./foo.js") in .cjs
output. The js-monorepo has the same latent bug.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gagdiez
Copy link
Copy Markdown
Contributor

gagdiez commented Mar 17, 2026

I like the idea of bringing ESM + CJS back, we used to have it and some users reported that they need it back, would be nice if we could have a PR that only does that (i.e. it only changes tsc for tscup - though some community members suggested that maybe we should look for tsdown which is the one being maintained)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: NEW❗

Development

Successfully merging this pull request may close these issues.

Consider using web standard APIs, swap EcmaScript-only approaches (like Buffer) for native bindings

3 participants