UNPKG

agent-rules

Version:

Rules and instructions for agentic coding tools like Cursor, Claude CLI, Gemini CLI, Qodo, Cline and more

113 lines (85 loc) 3.69 kB
--- applyTo: '**' description: Modle Resolution and Import Strategy source: https://kashw1n.com/blog/nodejs-2025/ --- # Module Resolution & Import Strategy Goal: make imports robust, refactor-friendly, and adaptive by using **internal import maps** + **dynamic imports**. Keep internal vs external deps clearly separated. --- ## Decision Rules - **Internal modules (stable paths, refactor-safe):** Use **import maps** with `#aliases` for config, utilities, db, features. - **Conditional/optional features or env-specific adapters:** Use **dynamic `import()`** with try/fallback logic. - **Feature flags & code splitting:** Load modules lazily based on env/config to reduce startup and bundle size. - **Distinguish ownership:** `#`-prefixed imports = internal; bare/URL imports = external. --- ## Internal Imports (Import Maps) **Define aliases:** ```json { "imports": { "#config": "./src/config/index.js", "#utils/*": "./src/utils/*.js", "#db": "./src/database/connection.js" } } ```` **Use them in code:** ```js import config from '#config'; import { logger, validator } from '#utils/common'; import db from '#db'; ``` **Benefits** * Paths don’t break when files move; central remap only. * Clear boundary between **internal** (`#...`) and **external** deps. * Faster refactors; fewer deep relative paths. --- ## Dynamic Imports (Flexible Loading) **Env-driven adapter selection with safe fallback:** ```js export async function loadDatabaseAdapter() { const dbType = process.env.DATABASE_TYPE || 'sqlite'; try { const { default: adapter } = await import(`#db/adapters/${dbType}`); return adapter; } catch { console.warn(`Adapter ${dbType} missing; falling back to sqlite`); const { default: fallback } = await import('#db/adapters/sqlite'); return fallback; } } ``` **Feature flags / optional capabilities:** ```js export async function loadOptionalFeatures() { const features = []; if (process.env.ENABLE_ANALYTICS === 'true') { const { default: analytics } = await import('#features/analytics'); features.push(analytics); } if (process.env.ENABLE_MONITORING === 'true') { const { default: monitoring } = await import('#features/monitoring'); features.push(monitoring); } return features; } ``` **Benefits** * Load only what’s needed for the current environment. * Natural place to implement fallbacks and progressive enhancement. --- ## Best Practices * **Keep aliases high-level:** map domains (`#config`, `#db`, `#utils/*`), not volatile file names. * **One source of truth:** centralize mappings; don’t duplicate alias logic elsewhere. * **Validate boundaries:** internal code imports `#...`; external libs stay bare (`react`, `node:fs`). * **Guard dynamic imports:** wrap in `try/catch` with a **clear fallback** or user-facing warning. * **Log decisions:** when falling back, log context (requested adapter/feature) to aid debugging. * **Feature flags via env:** prefer explicit `ENABLE_*` env vars for optional modules. * **Test both paths:** verify primary and fallback imports in CI (set env vars to exercise each branch). --- ## Antipatterns (Avoid) * **Deep relative paths** (`../../../utils`) that break on refactors replace with `#utils/...`. * **Stringly-typed, scattered paths** don’t hardcode file locations in many places; alias once. * **Dynamic import without fallback** leaves features broken silently; always handle `catch`. * **Over-granular aliases** (`#utils/math/add.js`) alias directories or stable entry points instead. * **Mixing internal/external semantics** don’t alias third-party packages under `#...`; keep them explicit.