Agent skill
nodejs-subpath-imports
Use when debugging subpath import errors (ERR_PACKAGE_IMPORT_NOT_DEFINED, "only allows ./"), configuring package.json imports field, migrating from tsconfig paths, or building cross-platform code with conditions. Covers Node.js spec constraints that ALL bundlers enforce.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/nodejs-subpath-imports
SKILL.md
Node.js Subpath Imports
CRITICAL
Path Restrictions Are Node.js Spec, Not Bundler Limitations
When you see errors about path formats in imports field:
| Error pattern | This is... | NOT... |
|---|---|---|
| "only allows ./" | Node.js spec | Metro limitation |
| "rejects ../ paths" | Node.js spec | Webpack limitation |
| "must be relative" | Node.js spec | Bundler bug |
Every compliant resolver enforces these rules. See Spec: Restrictions below.
Operations
Debug Subpath Import Errors
- Check error message for pattern (see CRITICAL table above)
- Verify targets start with
./(required for local files) - Check for forbidden segments:
..,.,node_modules - Verify extensions are explicit (no auto-resolution)
- Check condition order (first match wins)
Common errors:
ERR_PACKAGE_PATH_NOT_EXPORTED→ subpath not in exportsERR_PACKAGE_IMPORT_NOT_DEFINED→ key not in importsMODULE_NOT_FOUND→ missing extension or wrong path
Debug Project Config
- Check
moduleResolutionin tsconfig (bundler,node16,nodenext) - Verify
resolvePackageJsonImports: true(default with above modes) - Check bundler's condition configuration matches your targets
- Verify
type: "module"if using ESM
Migrate from tsconfig Paths
Before (compile-time only):
// tsconfig.json
{
"compilerOptions": {
"paths": { "@utils/*": ["./src/utils/*"] }
}
}
After (runtime + compile-time):
// package.json
{
"imports": {
"#utils/*": "./src/utils/*.js"
}
}
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "bundler",
"resolvePackageJsonImports": true
}
}
Key changes:
@prefix →#prefix (required by spec)- Add explicit
.jsextensions - Works at runtime without bundler
Build Cross-Platform Code
See reference/cross-platform.md for complete guide with:
- Platform abstraction patterns
- External package substitution (unique to
imports) - Conditions by runtime (Node, Deno, Bun)
- Conditions by bundler (Webpack, esbuild, Vite, Metro, etc.)
Quick pattern:
{
"imports": {
"#platform": {
"react-native": "./src/platform/native.js",
"browser": "./src/platform/browser.js",
"node": "./src/platform/node.js",
"default": "./src/platform/node.js"
},
"#fetch": {
"node": "node-fetch",
"default": "./src/fetch-native.js"
}
}
}
What This Enables
Runtime aliases - unlike tsconfig.json paths (compile-time only), subpath imports resolve at runtime:
import { helper } from "#utils/helper.js"; // Works in Node, Deno, Bun natively
Cross-platform code - same import, different implementation per environment:
{
"imports": {
"#fetch": {
"node": "node-fetch",
"browser": "./fetch-browser.js",
"default": "./fetch-polyfill.js"
}
}
}
Spec: Syntax
Source: Node.js Packages - Subpath imports
Keys Must Start with #
{ "imports": { "#dep": "./dep.js", "#internal/*": "./src/internal/*.js" } }
Future: Node.js v26+ allows
#/prefix (PR #60864).
Targets: Strings, Objects, or Patterns
{
"imports": {
"#string": "./path/to/file.js",
"#object": { "node": "pkg", "default": "./polyfill.js" },
"#pattern/*": "./src/*.js"
}
}
Relative Targets Must Start with ./
{ "imports": { "#good": "./src/file.js", "#bad": "src/file.js" } }
External packages (bare specifiers) don't need ./.
External Packages Allowed
{
"imports": {
"#dep": { "node": "dep-node-native", "default": "./dep-polyfill.js" }
},
"dependencies": { "dep-node-native": "^1.0.0" }
}
Spec: Resolution Algorithm
Source: Node.js ESM - Resolution Algorithm
- Import specifier must start with
# - Find matching key in nearest
package.jsonimportsfield - If value is object → apply condition matching (first match wins)
- If key contains
*→ perform string substitution on target - Resolve resulting path/specifier
Pattern Substitution
* is literal string replacement, not glob:
{ "imports": { "#internal/*.js": "./src/internal/*.js" } }
import z from "#internal/z.js"; // → ./src/internal/z.js
import deep from "#internal/nested/x.js"; // → ./src/internal/nested/x.js
Spec: Conditions
Source: Node.js Packages - Conditional exports
First matching condition wins. Order from most specific to least.
Built-in Conditions
| Condition | When Matched |
|---|---|
node-addons |
Node.js with native addons |
node |
Any Node.js environment |
import |
Loaded via import |
require |
Loaded via require() |
default |
Fallback (must be last) |
Community Conditions
| Condition | When Matched |
|---|---|
types |
TypeScript (should be first) |
browser |
Web browser |
react-native |
React Native |
react-server |
React Server Components |
development / production |
Build mode |
Nested Conditions
{
"imports": {
"#utils": {
"node": { "import": "./utils-node.mjs", "require": "./utils-node.cjs" },
"browser": "./utils-browser.js",
"default": "./utils.mjs"
}
}
}
Spec: Restrictions
Source: Node.js Packages - Subpath patterns
Forbidden Path Segments
After ./, these are disallowed:
| Segment | Why |
|---|---|
.. |
Path traversal |
. |
Ambiguous |
node_modules |
Injection |
Invalid Examples
{
"imports": {
"#bad1": "../outside/file.js",
"#bad2": "./dist/../dist/file.js",
"#bad3": "././file.js",
"#bad4": "./node_modules/pkg/file.js"
}
}
Spec: Scope
Source: Node.js Packages - Subpath imports
- Private to package - only code within the package can use
#imports - Cannot be consumed externally -
import x from 'pkg/#internal'is invalid - Isolated mappings - no inheritance between packages
Edge Cases & Gotchas
For full details with sources, see reference/edge-cases.md.
| Gotcha | Summary |
|---|---|
| PATTERN_KEY_COMPARE | Longer base index wins, then total length |
| No extension guessing | Must use explicit .js in CJS and ESM |
| Arrays as fallbacks | Only for validation errors, NOT file-not-found |
| Trailing slash (DEP0155) | Use #lib/* pattern instead of #lib/ |
Pattern keys need one * |
Multiple * = exact match only |
| Exact before patterns | Non-pattern keys checked first |
Tool Support
All major runtimes and bundlers support subpath imports. For versions, conditions config, and examples, see reference/tool-support.md.
Runtimes: Node.js v14.6+, Deno v1.25+, Bun v1.0+
Bundlers: esbuild, Webpack 5+, Rspack, Rollup, Rolldown, Metro v0.82+
Frameworks: Vite, Next.js, Astro, Expo
Complete Example
{
"name": "my-app",
"type": "module",
"imports": {
"#db": "./src/database/index.js",
"#db/*": "./src/database/*.js",
"#utils/*": "./src/utils/*.js",
"#platform": {
"node": "./src/platform/node.js",
"browser": "./src/platform/browser.js",
"react-native": "./src/platform/native.js",
"default": "./src/platform/node.js"
}
}
}
import { connect } from "#db"; // exact match → ./src/database/index.js
import { query } from "#db/queries.js"; // pattern → ./src/database/queries.js
import platform from "#platform"; // condition-based
Monorepo Note
Subpath imports cannot reference outside the package - ./ requirement and .. prohibition are fundamental.
Alternatives for shared code:
- Workspace dependencies - proper package
- Symlinks - link into package
- tsconfig paths - build-time fallback
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?