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