Skip to main content

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:

  1. Barrel exports generator (scripts/generate-barrel-exports.mjs) now auto-detects flat .ts files at each package's src/ root and generates explicit publishConfig.exports entries. This makes extensionless imports valid under strict ESM.

  2. Codemod (packages/codemods/transforms/esm-migration/index.ts) now reads the package's exports map and skips adding .js when an explicit barrel entry exists — producing the shortest valid import for each specifier.

Barrel export counts

PackageBarrel dirsFlat filesTotal exports
@webbloqs/elements57057
@webbloqs/react33942
@webbloqs/css-runtime415

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.

RepoFiles changedBuildPrevious run
mchweb5PASSwas 55
cpq-cds3PASS (webpack)was 119+58
ewz-webportal-components14n/a (codemod only)was 107
ewz-kus-portal7n/a (codemod only)was 60
Total29was 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 build exit 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