Barrel Exports + Shortest Valid Import Codemod
2026-03-06 by Showboat
What changed
PR #846 (CDSTLZ-193-esm) migrates webbloqs to native ESM with nodenext module resolution.
Consumer repo audits revealed that @webbloqs/react has 9 flat files at src/ root
(i18n, models, validation, etc.) imported 518+ times across ewz repos — but with no
explicit barrel exports. Under strict ESM, these require .js extensions.
Two complementary fixes:
-
Barrel exports generator (
scripts/generate-barrel-exports.mjs) now auto-detects flat.tsfiles at each package'ssrc/root and generates explicitpublishConfig.exportsentries. This makes extensionless imports valid under strict ESM. -
Codemod (
packages/codemods/transforms/esm-migration/index.ts) now reads the package's exports map and skips adding.jswhen an explicit barrel entry exists — producing the shortest valid import for each specifier.
Barrel export counts
| Package | Barrel dirs | Flat files | Total exports |
|---|---|---|---|
@webbloqs/elements | 57 | 0 | 57 |
@webbloqs/react | 33 | 9 | 42 |
@webbloqs/css-runtime | 4 | 1 | 5 |
Flat files in @webbloqs/react
create-component, global, i18n, media, models, option, properties, tracking, validation
Verification
Codemod unit tests (18 tests)
pnpm --filter @webbloqs/codemods test 2>&1 | grep -E "(PASS|FAIL|Tests:|Test Suites:)"
PASS __tests__/esm-migration.test.ts
Test Suites: 1 passed, 1 total
Tests: 18 passed, 18 total
ESM smoke test
pnpm run test:esm 2>&1 | grep -E "^(---| |===)"
--- Step 1: Building packages ---
--- Step 2: Packing tarballs ---
--- Step 3: Creating consumer project ---
--- Step 4: Testing imports via esbuild (bundler) ---
esbuild bundle: OK
--- Step 4b: Testing imports via Webpack 5 (fullySpecified) ---
Building with Webpack 5...
Webpack 5 build: OK
--- Step 4c: Testing consumer-specific import patterns ---
package.json metadata reads: OK
--- Step 5: Testing TypeScript type resolution ---
TypeScript types: OK
--- Step 6: Verifying package metadata ---
--- Step 6b: Verifying .js extensions in compiled imports ---
All relative imports have .js extensions: OK
=== ESM Smoke Test PASSED ===
Consumer repo integration tests
All 4 consumer repos tested with updated tarballs from /tmp/webbloqs-esm-tarballs/.
The codemod now produces the shortest valid import — flat file imports with barrel exports
are left extensionless, only deep subpath imports get .js added.
| Repo | Files changed | Build | Previous run |
|---|---|---|---|
| mchweb | 5 | PASS | was 55 |
| cpq-cds | 3 | PASS (webpack) | was 119+58 |
| ewz-webportal-components | 14 | n/a (codemod only) | was 107 |
| ewz-kus-portal | 7 | n/a (codemod only) | was 60 |
| Total | 29 | was 399 |
~93% reduction in codemod-modified files. The remaining 29 files are all deep subpath
imports (e.g., @webbloqs/elements/lib/icon/spritemap → @webbloqs/elements/lib/icon/spritemap.js)
that genuinely need .js extensions.
Key validation points
- mchweb: Webpack client + server builds pass with ESM tarballs
- cpq-cds: Webpack build passes (
pnpm run buildexit code 0) - ewz repos: Codemod runs without errors, all transforms correct
- Zero errors across all consumer repos
CI verification
Full CI (pnpm run ci) passed: 2189+ unit tests, 35 E2E tests, all builds green.
Commits
5c12057bd F!! Add flat file barrel exports to publishConfig.exports
fb7001d03 F!! Teach codemod to prefer shortest valid import via exports map
3620b3dd3 E: Expand ESM smoke test with flat file barrel imports
bbc8c59c4 B: Fix WbComponentProps type reference in ESM smoke test