summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-15 23:44:08 -0400
committerGitHub <[email protected]>2026-04-15 23:44:08 -0400
commitc802695ee9555ccfd8b0a6ae2215f750bccda712 (patch)
tree69bfe20d8b0bc711725b1f2c883fd0697e2d88f0
parent225a769411f35a0e2dd357589374766dae77ae6a (diff)
downloadopencode-c802695ee9555ccfd8b0a6ae2215f750bccda712.tar.gz
opencode-c802695ee9555ccfd8b0a6ae2215f750bccda712.zip
docs: add circular import rules to namespace treeshake spec (#22754)
-rw-r--r--packages/opencode/specs/effect/namespace-treeshake.md55
1 files changed, 55 insertions, 0 deletions
diff --git a/packages/opencode/specs/effect/namespace-treeshake.md b/packages/opencode/specs/effect/namespace-treeshake.md
index 8a9cf94fd..5d1fbd07e 100644
--- a/packages/opencode/specs/effect/namespace-treeshake.md
+++ b/packages/opencode/specs/effect/namespace-treeshake.md
@@ -442,3 +442,58 @@ Going forward:
- If a file grows large enough that it's hard to navigate, split by concern
(errors.ts, schema.ts, etc.) for readability. Not for tree-shaking — the
bundler handles that.
+
+## Circular import rules
+
+Barrel files (`index.ts` with `export * as`) introduce circular import risks.
+These cause `ReferenceError: Cannot access 'X' before initialization` at
+runtime — not caught by the type checker.
+
+### Rule 1: Sibling files never import through their own barrel
+
+Files in the same directory must import directly from the source file, never
+through `"."` or `"@/<own-dir>"`:
+
+```ts
+// BAD — circular: index.ts re-exports both files, so A → index → B → index → A
+import { Sibling } from "."
+
+// GOOD — direct, no cycle
+import * as Sibling from "./sibling"
+```
+
+### Rule 2: Cross-directory imports must not form cycles through barrels
+
+If `src/lsp/lsp.ts` imports `Config` from `"../config"`, and
+`src/config/config.ts` imports `LSPServer` from `"../lsp"`, that's a cycle:
+
+```
+lsp/lsp.ts → config/index.ts → config/config.ts → lsp/index.ts → lsp/lsp.ts 💥
+```
+
+Fix by importing the specific file, breaking the cycle:
+
+```ts
+// In config/config.ts — import directly, not through the lsp barrel
+import * as LSPServer from "../lsp/server"
+```
+
+### Why the type checker doesn't catch this
+
+TypeScript resolves types lazily — it doesn't evaluate module-scope
+expressions. The `ReferenceError` only happens at runtime when a module-scope
+`const` or function call accesses a value from a circular dependency that
+hasn't finished initializing. The SDK build step (`bun run --conditions=browser
+./src/index.ts generate`) is the reliable way to catch these because it
+evaluates all modules eagerly.
+
+### How to verify
+
+After any namespace conversion, run:
+
+```bash
+cd packages/opencode
+bun run --conditions=browser ./src/index.ts generate
+```
+
+If this completes without `ReferenceError`, the module graph is safe.