Skip to content

fix(debarrel): Respect exports boundaries and preserve non-static consumers - #37

Open
alexbit-codemod wants to merge 4 commits into
mainfrom
debarrel-codemod-bug-fixes
Open

fix(debarrel): Respect exports boundaries and preserve non-static consumers#37
alexbit-codemod wants to merge 4 commits into
mainfrom
debarrel-codemod-bug-fixes

Conversation

@alexbit-codemod

@alexbit-codemod alexbit-codemod commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens the debarrel codemod against real-world monorepo gaps found while applying it to Cal.com (404 files). This PR adds regression fixtures and fixes across export-shape handling, aliased re-exports, test mocks, and barrel rename safety.

  • Add regression fixtures for package "exports" boundaries, re-export rewrites, dynamic import() consumers, namespace re-exports, and single-pass transitive barrel chains.
  • Stop rewriting imports past public package export subpaths while still allowing tsconfig alias debarreling.
  • Rewrite export { … } from "…" edges, walk named re-export chains in one pass, and preserve barrels required by namespace re-exports or dynamic imports.
  • Resolve local import-then-reexport barrels, including import aliases (import { cache as unstable_cache }; export { unstable_cache }).
  • Rewrite parent barrel exports to default as Foo when the leaf module is default-only.
  • Normalize consumer imports against actual export shapes (named vs default vs sole named export).
  • Update factory vi.mock/jest.mock and dynamic import() paths when imports are fully debarreled to a single direct module.
  • Allow barrel rename when dynamic imports are co-located with rewritable static imports in the same file.
  • Add a benchmark harness (codemods/debarrel/benchmark/) to measure build/test wall-clock time on a target repo before and after debarreling.

New fixtures

  • aliased-import-then-reexport — import alias + local re-export barrel
  • parent-barrel-default-only-leaf — parent export { Foo } where leaf is export default
  • named-import-default-only-barrelimport { Cal } from export { default as Cal }
  • update-test-mocks-factory-path — factory mock + dynamic import path rewrites

Benchmark harness

codemods/debarrel/benchmark/bench.sh runs repeatable build and test timing on any target repo (defaults: yarn build --force, yarn test). Intended workflow:

  1. Baseline run on untouched repo
  2. Apply debarrel
  3. Post-codemod run
  4. Diff bench-results/ output

See codemods/debarrel/benchmark/README.md for flags and examples (e.g. Cal.com).

Test plan

  • pnpm test in codemods/debarrel (283 passed)
  • pnpm check-types in codemods/debarrel
  • validate_codemod_package reports ready: true
  • Review new fixtures under codemods/debarrel/tests/
  • Re-apply debarrel on Cal.com and confirm build/typecheck/test health
  • Run codemods/debarrel/benchmark/bench.sh on Cal.com before/after debarrel

alexbit-codemod and others added 2 commits July 17, 2026 00:25
Capture package exports boundaries, re-export rewrites, dynamic imports,
namespace re-exports, and single-pass completeness so fixes can be driven
by failing tests. Also bump workspace catalog deps for the local toolchain.

Co-authored-by: Cursor <cursoragent@cursor.com>
…sumers

Stop deepening past package.json exports, rewrite re-export edges, walk
named re-export chains in one pass, and keep barrels used by dynamic
imports or namespace re-exports.

Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves the codemods/debarrel codemod’s correctness around package "exports" boundaries and non-static barrel consumers, and adds regression fixtures to cover these scenarios.

Changes:

  • Add regression fixtures covering "exports" subpath boundaries, type-only alias imports at export boundaries, transitive single-pass barrel chains, parent re-export updates, namespace re-exports, dynamic import() consumers, mixed default+named imports, and CRLF line endings.
  • Update debarrel rewriting logic to (a) avoid rewriting past public "exports" subpaths, (b) rewrite export { … } from "…" edges, (c) follow transitive named re-export chains in one pass, and (d) preserve barrels required by namespace re-exports or dynamic imports.
  • Bump workspace catalog dependency versions (including TypeScript).

Reviewed changes

Copilot reviewed 105 out of 106 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Bumps catalog dependency versions (incl. TypeScript).
pnpm-lock.yaml Updates lockfile to reflect catalog dependency bumps.
codemods/debarrel/scripts/codemod.ts Adds re-export edge rewriting and preserves barrels with non-static consumers.
codemods/debarrel/scripts/utils/specifiers.ts Adds "exports" boundary guard for candidate rewrites and follows re-export chains.
codemods/debarrel/scripts/utils/paths.ts Adds helpers to detect package "exports" subpaths and preserve export boundaries.
codemods/debarrel/scripts/utils/exportStar.ts Adds transitive named re-export walking and broader barrel preservation checks.
codemods/debarrel/scripts/utils/barrel.ts Tightens namespace re-export parsing to only match the intended namespace binding.
codemods/debarrel/tests/type-import-alias-at-export-boundary/input/tsconfig.json Fixture: tsconfig paths alias for export-boundary scenario.
codemods/debarrel/tests/type-import-alias-at-export-boundary/input/packages/ui/package.json Fixture: package "exports" map defining a public subpath.
codemods/debarrel/tests/type-import-alias-at-export-boundary/input/packages/ui/components/form/index.ts Fixture: type re-export through an export boundary.
codemods/debarrel/tests/type-import-alias-at-export-boundary/input/packages/ui/components/form/fields/MultiSelect.ts Fixture: leaf type definition.
codemods/debarrel/tests/type-import-alias-at-export-boundary/input/packages/app/src/App.ts Fixture: type-only import using alias at export boundary.
codemods/debarrel/tests/type-import-alias-at-export-boundary/input/packages/app/package.json Fixture: workspace consumer package setup.
codemods/debarrel/tests/type-import-alias-at-export-boundary/expected/tsconfig.json Expected output for export-boundary type-only alias case.
codemods/debarrel/tests/type-import-alias-at-export-boundary/expected/packages/ui/package.json Expected output (unchanged) for package "exports".
codemods/debarrel/tests/type-import-alias-at-export-boundary/expected/packages/ui/components/form/index.ts Expected output (unchanged) for type re-export.
codemods/debarrel/tests/type-import-alias-at-export-boundary/expected/packages/ui/components/form/fields/MultiSelect.ts Expected output (unchanged) for leaf type.
codemods/debarrel/tests/type-import-alias-at-export-boundary/expected/packages/app/src/App.ts Expected output (unchanged) for boundary-preserving import.
codemods/debarrel/tests/type-import-alias-at-export-boundary/expected/packages/app/package.json Expected output (unchanged) for consumer package.
codemods/debarrel/tests/transitive-barrel-single-pass/metrics.json New metrics baseline for transitive single-pass scenario.
codemods/debarrel/tests/transitive-barrel-single-pass/input/tsconfig.json Fixture: basic project config for transitive barrels.
codemods/debarrel/tests/transitive-barrel-single-pass/input/src/utils/index.ts Fixture: top-level barrel re-exporting a nested barrel.
codemods/debarrel/tests/transitive-barrel-single-pass/input/src/utils/helpers/index.ts Fixture: nested barrel re-exporting a leaf module.
codemods/debarrel/tests/transitive-barrel-single-pass/input/src/utils/helpers/format.ts Fixture: leaf implementation.
codemods/debarrel/tests/transitive-barrel-single-pass/input/src/metrics.json Fixture: metrics input.
codemods/debarrel/tests/transitive-barrel-single-pass/input/src/App.ts Fixture: consumer importing from top-level barrel.
codemods/debarrel/tests/transitive-barrel-single-pass/expected/tsconfig.json Expected output config.
codemods/debarrel/tests/transitive-barrel-single-pass/expected/src/utils/index.barrel.bak.ts Expected: renamed barrel with rewritten re-export edge.
codemods/debarrel/tests/transitive-barrel-single-pass/expected/src/utils/helpers/index.barrel.bak.ts Expected: renamed nested barrel preserved as backup.
codemods/debarrel/tests/transitive-barrel-single-pass/expected/src/utils/helpers/format.ts Expected: leaf module unchanged.
codemods/debarrel/tests/transitive-barrel-single-pass/expected/src/metrics.json Expected metrics output.
codemods/debarrel/tests/transitive-barrel-single-pass/expected/src/App.ts Expected: consumer import debarreled to the leaf module.
codemods/debarrel/tests/parent-barrel-reexport-update/metrics.json New metrics baseline for parent re-export updates.
codemods/debarrel/tests/parent-barrel-reexport-update/input/tsconfig.json Fixture config.
codemods/debarrel/tests/parent-barrel-reexport-update/input/src/metrics.json Fixture metrics input.
codemods/debarrel/tests/parent-barrel-reexport-update/input/src/form/index.ts Fixture: parent barrel with mixed exports.
codemods/debarrel/tests/parent-barrel-reexport-update/input/src/form/checkbox/index.ts Fixture: nested barrel.
codemods/debarrel/tests/parent-barrel-reexport-update/input/src/form/checkbox/Checkbox.ts Fixture: leaf implementation.
codemods/debarrel/tests/parent-barrel-reexport-update/input/src/App.ts Fixture: consumer import from parent barrel.
codemods/debarrel/tests/parent-barrel-reexport-update/expected/tsconfig.json Expected config.
codemods/debarrel/tests/parent-barrel-reexport-update/expected/src/metrics.json Expected metrics output.
codemods/debarrel/tests/parent-barrel-reexport-update/expected/src/form/index.ts Expected: parent re-export edge rewritten to direct leaf module.
codemods/debarrel/tests/parent-barrel-reexport-update/expected/src/form/checkbox/index.barrel.bak.ts Expected: nested barrel renamed to backup.
codemods/debarrel/tests/parent-barrel-reexport-update/expected/src/form/checkbox/Checkbox.ts Expected: leaf unchanged.
codemods/debarrel/tests/parent-barrel-reexport-update/expected/src/App.ts Expected: consumer partially debarreled while preserving remaining imports.
codemods/debarrel/tests/package-exports-subpath-boundary/input/tsconfig.json Fixture: tsconfig paths for package export subpath boundary.
codemods/debarrel/tests/package-exports-subpath-boundary/input/packages/ui/package.json Fixture: package "exports" subpath declaration.
codemods/debarrel/tests/package-exports-subpath-boundary/input/packages/ui/components/button/index.ts Fixture: barrel at an exported subpath.
codemods/debarrel/tests/package-exports-subpath-boundary/input/packages/ui/components/button/Button.ts Fixture: leaf implementation.
codemods/debarrel/tests/package-exports-subpath-boundary/input/packages/app/src/App.ts Fixture: consumer importing the public export subpath.
codemods/debarrel/tests/package-exports-subpath-boundary/input/packages/app/package.json Fixture: workspace consumer package.
codemods/debarrel/tests/package-exports-subpath-boundary/expected/tsconfig.json Expected config.
codemods/debarrel/tests/package-exports-subpath-boundary/expected/packages/ui/package.json Expected output (unchanged) for "exports" boundary.
codemods/debarrel/tests/package-exports-subpath-boundary/expected/packages/ui/components/button/index.ts Expected output (unchanged) for exported subpath barrel.
codemods/debarrel/tests/package-exports-subpath-boundary/expected/packages/ui/components/button/Button.ts Expected output (unchanged) for leaf.
codemods/debarrel/tests/package-exports-subpath-boundary/expected/packages/app/src/App.ts Expected output (unchanged) for boundary-preserving import.
codemods/debarrel/tests/package-exports-subpath-boundary/expected/packages/app/package.json Expected output (unchanged) for consumer package.
codemods/debarrel/tests/namespace-reexport-nested-barrel/metrics.json New metrics baseline for namespace re-export scenario.
codemods/debarrel/tests/namespace-reexport-nested-barrel/input/tsconfig.json Fixture config.
codemods/debarrel/tests/namespace-reexport-nested-barrel/input/src/plugin/metadata.ts Fixture: direct export used by consumer.
codemods/debarrel/tests/namespace-reexport-nested-barrel/input/src/plugin/index.ts Fixture: barrel with namespace re-export (export * as …).
codemods/debarrel/tests/namespace-reexport-nested-barrel/input/src/plugin/api/index.ts Fixture: nested barrel behind namespace re-export.
codemods/debarrel/tests/namespace-reexport-nested-barrel/input/src/plugin/api/handler.ts Fixture: leaf implementation.
codemods/debarrel/tests/namespace-reexport-nested-barrel/input/src/metrics.json Fixture metrics input.
codemods/debarrel/tests/namespace-reexport-nested-barrel/input/src/App.ts Fixture: consumer importing from barrel.
codemods/debarrel/tests/namespace-reexport-nested-barrel/expected/tsconfig.json Expected config.
codemods/debarrel/tests/namespace-reexport-nested-barrel/expected/src/plugin/metadata.ts Expected output (unchanged) for metadata module.
codemods/debarrel/tests/namespace-reexport-nested-barrel/expected/src/plugin/index.ts Expected output: barrel preserved due to namespace re-export.
codemods/debarrel/tests/namespace-reexport-nested-barrel/expected/src/plugin/api/index.ts Expected output (unchanged) for nested barrel.
codemods/debarrel/tests/namespace-reexport-nested-barrel/expected/src/plugin/api/handler.ts Expected output (unchanged) for leaf.
codemods/debarrel/tests/namespace-reexport-nested-barrel/expected/src/metrics.json Expected metrics output.
codemods/debarrel/tests/namespace-reexport-nested-barrel/expected/src/App.ts Expected: consumer import rewritten to direct metadata module.
codemods/debarrel/tests/mixed-default-named-import/metrics.json New metrics baseline for mixed default+named import case.
codemods/debarrel/tests/mixed-default-named-import/input/tsconfig.json Fixture: tsconfig paths alias.
codemods/debarrel/tests/mixed-default-named-import/input/src/widgets/Widget.ts Fixture: default export leaf.
codemods/debarrel/tests/mixed-default-named-import/input/src/widgets/index.ts Fixture: barrel re-exporting default + named symbols.
codemods/debarrel/tests/mixed-default-named-import/input/src/widgets/helpers.ts Fixture: named export leaf.
codemods/debarrel/tests/mixed-default-named-import/input/src/metrics.json Fixture metrics input.
codemods/debarrel/tests/mixed-default-named-import/input/src/App.ts Fixture: mixed default+named import from barrel.
codemods/debarrel/tests/mixed-default-named-import/input/package.json Fixture package definition.
codemods/debarrel/tests/mixed-default-named-import/expected/tsconfig.json Expected config.
codemods/debarrel/tests/mixed-default-named-import/expected/src/widgets/Widget.ts Expected output (unchanged) for leaf default export.
codemods/debarrel/tests/mixed-default-named-import/expected/src/widgets/index.barrel.bak.ts Expected: barrel renamed to backup.
codemods/debarrel/tests/mixed-default-named-import/expected/src/widgets/helpers.ts Expected output (unchanged) for helper leaf.
codemods/debarrel/tests/mixed-default-named-import/expected/src/metrics.json Expected metrics output.
codemods/debarrel/tests/mixed-default-named-import/expected/src/App.ts Expected: mixed import split into direct default + named imports.
codemods/debarrel/tests/mixed-default-named-import/expected/package.json Expected package definition unchanged.
codemods/debarrel/tests/dynamic-import-barrel-consumer/input/tsconfig.json Fixture: config for dynamic import preservation case.
codemods/debarrel/tests/dynamic-import-barrel-consumer/input/src/registry.ts Fixture: dynamic import() consumer of a barrel path.
codemods/debarrel/tests/dynamic-import-barrel-consumer/input/src/plugin/api/index.ts Fixture: barrel that must be preserved for dynamic import.
codemods/debarrel/tests/dynamic-import-barrel-consumer/input/src/plugin/api/handler.ts Fixture: leaf implementation.
codemods/debarrel/tests/dynamic-import-barrel-consumer/expected/tsconfig.json Expected config.
codemods/debarrel/tests/dynamic-import-barrel-consumer/expected/src/registry.ts Expected output (unchanged) for dynamic import consumer.
codemods/debarrel/tests/dynamic-import-barrel-consumer/expected/src/plugin/api/index.ts Expected output: barrel preserved (not renamed).
codemods/debarrel/tests/dynamic-import-barrel-consumer/expected/src/plugin/api/handler.ts Expected output (unchanged) for leaf.
codemods/debarrel/tests/crlf-line-endings/metrics.json New metrics baseline for CRLF handling.
codemods/debarrel/tests/crlf-line-endings/input/tsconfig.json Fixture config.
codemods/debarrel/tests/crlf-line-endings/input/src/metrics.json Fixture metrics input.
codemods/debarrel/tests/crlf-line-endings/input/src/components/index.ts Fixture: barrel file (CRLF-related regression).
codemods/debarrel/tests/crlf-line-endings/input/src/components/Button.ts Fixture: leaf implementation.
codemods/debarrel/tests/crlf-line-endings/input/src/App.ts Fixture: consumer import from barrel.
codemods/debarrel/tests/crlf-line-endings/expected/tsconfig.json Expected config.
codemods/debarrel/tests/crlf-line-endings/expected/src/metrics.json Expected metrics output.
codemods/debarrel/tests/crlf-line-endings/expected/src/components/index.barrel.bak.ts Expected: barrel renamed to backup.
codemods/debarrel/tests/crlf-line-endings/expected/src/components/Button.ts Expected output (unchanged) for leaf.
codemods/debarrel/tests/crlf-line-endings/expected/src/App.ts Expected: consumer import rewritten to direct leaf module.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 255 to +257
export function barrelHasNamespaceImporters(barrelFile: string): boolean {
return barrelMustBePreserved(barrelFile);
}
Comment thread pnpm-workspace.yaml
alexbit-codemod and others added 2 commits July 17, 2026 02:06
Resolve local import-then-reexport barrels (including import aliases),
rewrite parent exports to `default as` when the leaf is default-only,
normalize consumer imports against actual export shapes, and update
factory vi.mock/jest.mock plus dynamic import() paths. Allow barrel
rename when dynamic imports are co-located with rewritable static imports.

Adds four regression fixtures covering Cal.com-style gaps.

Co-authored-by: Cursor <cursoragent@cursor.com>
Adds a dependency-free shell harness to measure build and test wall-clock
time on a target repo before and after running debarrel.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants