Skip to content

De-barrel internal imports + lint/CI ratchet #4644

Description

@amcclain

Part of #4640 - the mechanical repo-wide rewrite. Must land after #4643, verified by #4641's harness.

Goal

Internal code imports concrete modules; barrel index.ts files become public API surface only. This removes the amplifier that turns any two-module relationship into a whole-package cycle, and is what makes barrel elision (tree-shaking) safe to re-enable.

Sequencing constraint (important)

Today, internal barrel imports are load-bearing: inside a boot cycle the barrel is always already mid-evaluation, so re-entering it is a benign back-edge. Rewriting import {XH} from '@xh/hoist/core' to a deep import turns that back-edge into a forward edge into a not-yet-evaluated module - introducing the TDZ crashes this project exists to fix, even under sideEffects: false. The upward edges must be severed first (#4643), with the guard harness green, before this lands.

This is the mechanical, obvious-looking phase, which is exactly why someone will be tempted to take it first.

Scope (counts from develop, 88.0.0-SNAPSHOT)

Roughly 824 internal barrel imports to rewrite, against 1,941 deep module imports already in the target style - so this finishes a convention the codebase mostly follows rather than inventing one. Breakdown:

Import form Count Disposition
from '@xh/hoist/core' 562 rewrite
from '@xh/hoist/data', /format, /promise, /exception, /svc 229 rewrite
from './' / from '../' (own package barrel) 33 rewrite - includes both crash sites
from '@xh/hoist/icon' 213 keep - side-effectful barrel
from '@xh/hoist/mobx' 170 keep - side-effectful barrel

98 index.ts files in the package overall.

Work

  • Codemod internal barrel imports to concrete module paths (ts-morph or similar).
  • Preserve intentionally side-effectful barrels - the sideEffects array in package.json is the authority (icon/index.ts, mobx/index.ts, core/persist/index.ts, kit/blueprint/index.ts, the platform register.ts files, and the rest) - adding explicit side-effect imports where registration must still run.
  • Land the ESLint guard (no-restricted-imports scoped to internal files) in the same PR, or IDE auto-import regresses it immediately. Note this sits alongside the foundation-set rules introduced in Sever upward eval-time edges: late-bind XH out of low-level modules #4643 and should be written as one coherent config, not two competing ones.
  • Add cycle-detection ratchet to CI: existing cycles grandfathered, new ones fail.
  • Coordinate timing against in-flight branches - this conflicts with everything open, and unlike Convert type-only imports/exports to import type / export type, with enforcement #4642 the conflicts are not regenerable by re-running a formatter. Wants a deliberately chosen quiet window and a fast merge, not a long-lived PR.

Done when

The #4641 harness is green across all barrels, the lint guard is on, and the CI ratchet holds the line.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions