chore: verify packaging from the tarball, then migrate build to tsdown - #401
chore: verify packaging from the tarball, then migrate build to tsdown#401hyesungoh wants to merge 21 commits into
Conversation
…ssing diagnostics
🦋 Changeset detectedLatest commit: a6ccc4e The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
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 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #401 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 58 58
Lines 1639 1639
Branches 499 499
=========================================
Hits 1639 1639 🚀 New features to boost your workflow:
|
|
Size Change: +49.2 kB (+100.83%) 🆘 Total Size: 97.9 kB 📦 View Changed
|
Yarn 4.10.2's PnP require patch rejects any option Node passes beyond `paths`/`plugnplay`, so a `createRequire(import.meta.url)` call fails with "Some options passed to require() aren't supported by PnP yet (conditions)". rolldown and its oxc/yuku toolchain load their native `.node` bindings exactly that way, which makes them unloadable under 4.10.2 on any Node version. 4.18.0 allows `conditions` and auto-unplugs the platform bindings, so no dependenciesMeta overrides are needed. `enableScripts: true` is pinned because 4.18 flipped the default to false; it preserves the install-script behaviour the repo already relied on. The `approvedGitRepositories` and `npmMinimalAgeGate` keys the upgrade offered to write are deliberately left out: there are no git-protocol dependencies, and keeping the default 1440-minute age gate retains the new supply-chain guard.
tsup emitted one flat bundle per format, so a consumer importing a single hook also paid for unrelated top-level side effects elsewhere in that bundle — a forwardRef call, a displayName assignment, storage singletons — because a bundler cannot prove those statements are removable. Importing only useToggle cost 5363B minified. tsdown's unbundle mode mirrors the source tree as per-module output, so together with the existing `sideEffects: false` a consumer's bundler skips whole files it never imports and what is inside them stops mattering. useToggle now costs 102B and useNetworkStatus 570B, both under the 1024B budget, so verify-pack no longer needs --skip-size in CI. tsdown emits .mjs/.d.mts for ESM and .cjs/.d.cts for CJS into a single dist/, so both packages point module at ./dist/index.mjs and the import condition at ./dist/index.d.mts; core drops its separate esm/ directory entirely. Relative .ts specifiers are rewritten to the emitted extensions and the "use client" banner reaches every emitted file, not just the entry. The useStorageState barrel now marks Serializable as a type-only export, which rolldown requires to resolve the re-export. It changes no emitted code and matches the inline-type pattern the mobile barrel already uses.
… changes Adds a verify-examples job that packs core/mobile, points with-vite and with-nextjs at the tarballs, and rebuilds them. The Next.js example gains a core demo page without a 'use client' directive so a missing banner in the build output fails the RSC build rather than only failing a grep. Also corrects the compressed-size pattern: tsdown emits .mjs/.cjs under packages/*/dist, not the old top-level dist/esm .js/.cjs layout.
Source imports use explicit .ts/.tsx extensions, and tsdown now emits .mjs/.cjs rather than the plain .js this line described. AGENTS.md and .github/copilot-instructions.md already state it correctly; CLAUDE.md was stale from before the tsdown migration.
@react-simplikit/mobile never had an esm/ directory, so the shared changeset text was misrepresenting its changelog.
No package emits an esm/ directory and no tsup config file exists after the tsdown migration.
A transitive dependency bump changes what gets packed but wasn't in the paths-filter, so verify-examples would silently skip it. This branch's own yarn upgrade and enableScripts change would have been missed by the same gap.
Ignore the Compressed Size 🆘 — the base was measured at half its outputNothing doubled. The action applies this branch's pattern (
The arithmetic confirms it: current output is 128.7 kB CJS + 121.6 kB ESM, near enough an even split, and the reported base (97.9 − 49.2 ≈ 48.7 kB) is half the head total. The Same cause for the This self-corrects after merge, once What actually changes for consumers
Install footprint grows modestly — per-module output carries per-file overhead — while what reaches an application's bundle drops sharply. Public API, runtime behavior, and type resolution are unchanged. |
Overview
Closes #388
Adds a tarball-based packaging verification pipeline (
yarn verify:pack) and migrates both published packages' build output from tsup flat bundles to tsdown per-module ("unbundle") output.Why the verification comes first
exportspins exact filenames, and workspace links can't catch afilesorexportsmismatch because they connect the whole package directory rather than whatnpm publishactually ships. #318 was that class of mistake. Since the build change replaces the output layout entirely, the gate needed to exist and be proven against the current build before anything moved.What
yarn verify:packdoesBuilds both packages, packs each with
npm pack, then against the real tarball:.js/.cjsstarts with the"use client";banner (RSC compatibility)exports/main/module/typesexists inside the tarballpublint --strictandattw(node10 / node16-cjs / node16-esm / bundler resolution matrix)require, ESMimport, andtscunder bothbundlerandnode16resolutionChecks fail rather than pass when they verify nothing — an empty candidate list, a tool that never produced a judgment, or a consumer install that failed all surface as failures with the underlying tool's real output.
The build migration
tsup produced one flat bundle per package, so importing a single hook still pulled in every other top-level statement in that bundle — a bundler can't prove those statements are side-effect free, so it can't remove them. Switching to tsdown's per-module output means each hook/util compiles to its own file; a consumer's bundler now only includes the files actually imported.
The public API is unchanged: the export surface was verified identical name-for-name (core 38 exports, mobile 17 values plus 6 types), and runtime behavior is unchanged. What changed is packaging — output file names and extensions (
.mjs/.cjsinstead of a flat bundle,.d.mts/.d.ctsdeclarations), and theesm/directory is gone.exportsonly ever exposed.and./package.json, so no supported import path is affected; only deep imports into internal build paths (never part of the public API) would break.Before / after: cost of importing a single export
react-simplikituseToggle@react-simplikit/mobileuseNetworkStatusBoth numbers come from
yarn verify:pack's tree-shaking size gate, now enforced in CI with no size-check bypass.Checklist
yarn verify:pack; no package source behavior changedyarn run fixto format and lint the code and docs?yarn run test:coverageto make sure there is no uncovered line?