Conditional Exports for Development vs Production
Map NODE_ENV to Node.js conditional exports, resolve ERR_UNSUPPORTED_DIR_IMPORT, configure Webpack and Vite conditionNames, and validate both environments with publint in CI.
Mapping NODE_ENV to Node.js Conditional Resolution
Node.js does not automatically map NODE_ENV to development or production export conditions. The native resolver requires explicit condition flags passed at runtime via CLI or environment variables. Without explicit directives, packages fall back to standard resolution paths, frequently triggering ERR_PACKAGE_PATH_NOT_EXPORTED when a development key is omitted from the fallback chain. Unlike legacy dual-package resolution patterns that relied on environment variable sniffing, modern conditional exports require explicit resolver configuration. Refer to Module System Fundamentals & Dual-Package Resolution for architectural context on environment switching. Always terminate fallback chains with a "default" condition to prevent resolution failures.
Resolving ERR_UNSUPPORTED_DIR_IMPORT in Dev Toolchains
Dev-only utilities exposed via conditional exports frequently trigger Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '...' is not supported resolving ES modules. This occurs when ESM directory resolution lacks explicit file extensions or when bundlers strip conditional paths due to misconfigured conditionNames. Isolate development exports from production type definitions to prevent declaration pollution. Ensure all conditional paths resolve to explicit .js or .mjs files rather than directory roots. Bundlers will silently drop unresolved directory imports during tree-shaking, causing silent runtime failures.
Niche package.json Exports Configuration Flags
Correctly structuring package.json requires strict condition ordering. The "types" condition must always precede runtime conditions ("development", "production", "import", "require") to satisfy TypeScript’s resolution algorithm. Misordering triggers TS2307: Cannot find module or its corresponding type declarations when 'types' is nested incorrectly. Consult Mastering the package.json Exports Field for the complete structural hierarchy of nested conditions and type resolution precedence. Avoid overlapping paths between development and production exports to prevent ambiguous resolution during static analysis.
Bundler & Runtime Condition Overrides
Bundlers ignore Node.js runtime conditions unless explicitly overridden. Webpack requires a strict resolve.conditionNames array ordering to prevent Module not found: Can't resolve './src/dev-utils' in '...'. Vite’s resolve.conditions must explicitly list 'development' before 'default' to prioritize dev tooling during local server runs. Rollup requires @rollup/plugin-node-resolve with the customConditions option enabled. Misordered arrays cause dev-only modules to resolve to production stubs or fail entirely during local development.
CI/CD Validation & Pre-Publish Verification
Pre-publish validation must simulate both environments. Run publint --strict to catch structural violations; it will flag publint: Missing 'default' fallback in exports field may cause runtime failures. Execute node --conditions=production and node --conditions=development in CI pipelines to verify runtime resolution paths. Validate type exports by running tsc --noEmit under both condition sets to ensure declaration files align with conditional paths. Automate these checks to prevent broken consumer builds post-publish.
Step-by-Step Resolution
- Define explicit dev/prod conditions in exports
"exports": {
"./utils": {
"development": "./src/dev-utils.js",
"production": "./dist/prod-utils.mjs",
"types": "./dist/types.d.ts",
"default": "./dist/prod-utils.mjs"
}
}
- Configure Node.js runtime to test conditions
NODE_OPTIONS="--conditions development" node -e "import('./utils')"
- Set Webpack conditionNames for dev server
resolve: { conditionNames: ['development', 'import', 'module', 'require', 'default'] }
- Validate exports with publint in CI
npx publint --strict && node --conditions=production test/resolver-check.js