Skip to content

Commit c802695

Browse files
authored
docs: add circular import rules to namespace treeshake spec (#22754)
1 parent 225a769 commit c802695

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

packages/opencode/specs/effect/namespace-treeshake.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,3 +442,58 @@ Going forward:
442442
- If a file grows large enough that it's hard to navigate, split by concern
443443
(errors.ts, schema.ts, etc.) for readability. Not for tree-shaking — the
444444
bundler handles that.
445+
446+
## Circular import rules
447+
448+
Barrel files (`index.ts` with `export * as`) introduce circular import risks.
449+
These cause `ReferenceError: Cannot access 'X' before initialization` at
450+
runtime — not caught by the type checker.
451+
452+
### Rule 1: Sibling files never import through their own barrel
453+
454+
Files in the same directory must import directly from the source file, never
455+
through `"."` or `"@/<own-dir>"`:
456+
457+
```ts
458+
// BAD — circular: index.ts re-exports both files, so A → index → B → index → A
459+
import { Sibling } from "."
460+
461+
// GOOD — direct, no cycle
462+
import * as Sibling from "./sibling"
463+
```
464+
465+
### Rule 2: Cross-directory imports must not form cycles through barrels
466+
467+
If `src/lsp/lsp.ts` imports `Config` from `"../config"`, and
468+
`src/config/config.ts` imports `LSPServer` from `"../lsp"`, that's a cycle:
469+
470+
```
471+
lsp/lsp.ts → config/index.ts → config/config.ts → lsp/index.ts → lsp/lsp.ts 💥
472+
```
473+
474+
Fix by importing the specific file, breaking the cycle:
475+
476+
```ts
477+
// In config/config.ts — import directly, not through the lsp barrel
478+
import * as LSPServer from "../lsp/server"
479+
```
480+
481+
### Why the type checker doesn't catch this
482+
483+
TypeScript resolves types lazily — it doesn't evaluate module-scope
484+
expressions. The `ReferenceError` only happens at runtime when a module-scope
485+
`const` or function call accesses a value from a circular dependency that
486+
hasn't finished initializing. The SDK build step (`bun run --conditions=browser
487+
./src/index.ts generate`) is the reliable way to catch these because it
488+
evaluates all modules eagerly.
489+
490+
### How to verify
491+
492+
After any namespace conversion, run:
493+
494+
```bash
495+
cd packages/opencode
496+
bun run --conditions=browser ./src/index.ts generate
497+
```
498+
499+
If this completes without `ReferenceError`, the module graph is safe.

0 commit comments

Comments
 (0)