KernelRanch
返回文章列表

文件明明在包里,Node.js 为什么报 ERR_PACKAGE_PATH_NOT_EXPORTED?

2026-11-13 #nodejs#npm#modules

碰到 ERR_PACKAGE_PATH_NOT_EXPORTED,先看消费方实际装到的 package.json,检查 exports 有没有声明报错里的子路径。exports 定义了哪些包名入口能被解析。文件存在只能说明它被安装到了磁盘。

1. 同一个文件,公开前后怎么解析

保存为 package_exports_lab.py,运行时需 Python 和 Node.js。脚本只写临时目录,不使用 npm registry。

import json
from pathlib import Path
import subprocess
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    root = Path(tmp)
    package = root / "node_modules" / "demo-exports"
    package.mkdir(parents=True)
    config = {"name": "demo-exports", "type": "commonjs",
              "exports": {".": "./index.cjs"}}
    manifest = package / "package.json"
    (package / "index.cjs").write_text("module.exports = 'root';", encoding="utf-8")
    (package / "internal.cjs").write_text("module.exports = 'helper';", encoding="utf-8")
    manifest.write_text(json.dumps(config), encoding="utf-8")

    def node(mode, script):
        result = subprocess.run(
            ["node", "--input-type=" + mode, "-e", script],
            cwd=root, capture_output=True, text=True, check=True,
        )
        return result.stdout.strip()

    assert node("commonjs", "console.log(require('demo-exports'))") == "root"
    assert node("commonjs", "console.log(require('./node_modules/demo-exports/internal.cjs'))") == "helper"
    for mode, operation in (
        ("commonjs", "require('demo-exports/internal.cjs')"),
        ("module", "await import('demo-exports/internal.cjs')"),
    ):
        result = node(mode, f"try {{ {operation} }} catch (err) {{ console.log(err.code) }}")
        assert result == "ERR_PACKAGE_PATH_NOT_EXPORTED"
        print(mode, result)

    config["exports"]["./helper"] = "./internal.cjs"
    manifest.write_text(json.dumps(config), encoding="utf-8")
    assert node("commonjs", "console.log(require('demo-exports/helper'))") == "helper"
    assert node("module", "import h from 'demo-exports/helper'; console.log(h)") == "helper"
    print("demo-exports/helper works in both loaders")
commonjs ERR_PACKAGE_PATH_NOT_EXPORTED
module ERR_PACKAGE_PATH_NOT_EXPORTED
demo-exports/helper works in both loaders

2. 按维护者和消费方各自的入口修复

消费方先找包的文档,确认同一功能是否从根入口或其他公开子路径导出。代码依赖旧版深层路径时,查清版本边界,再把调用迁到受支持的入口。直接拼安装路径虽然在样本中可行,但会依赖目录布局,升级或切换包管理器后容易失效。

维护者给旧包增加 exports 时,要盘点已有的公开入口。旧版曾承诺 pkg/feature 和 pkg/feature.js 的话,需要明确保留对应键,或在迁移说明里指出路径变更。发布前用打包产物建一个干净的消费方项目,分别运行实际支持的 import 与 require。两种加载器可能因条件导出命中不同文件,本例特意让它们共用一个 CJS 目标来隔离子路径因素。

小结

把包名和子路径当作发布接口。复现时应从消费方导入,直接执行包内文件无法验证公开入口。

数据来源与检索时间

来源URL检索日期对应内容
Node.js 24.19.0 Packageshttps://nodejs.org/download/release/v24.19.0/docs/api/packages.html#package-entry-points2026-11-13exports 的入口优先级、封装及兼容性
Node.js 24.19.0 Subpath exportshttps://nodejs.org/download/release/v24.19.0/docs/api/packages.html#subpath-exports2026-11-13显式公开子路径及相关错误

核验说明

  • Windows、Python 3.11.9、Node.js 24.19.0 实测 CommonJS 和 ESM。实验使用本地临时包,未测试 bundler 或发布流程。