Search code examples
node.jses6-modules

Local ESM module dependencies not resolving


I am attempting to import a local ESM package into a node 16 application. The application is unable to resolve the local package dependencies outputting the following error.

    Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@prismicio/client' imported from /Users/***/Workspace/wisdom-store/lib/prismic-utils/index.mjs
    at packageResolve (internal/modules/esm/resolve.js:664:9)
    at moduleResolve (internal/modules/esm/resolve.js:705:18)
    at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:819:11)
    at Loader.resolve (internal/modules/esm/loader.js:89:40)
    at Loader.getModuleJob (internal/modules/esm/loader.js:242:28)
    at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:73:40)
    at link (internal/modules/esm/module_job.js:72:36) {
  code: 'ERR_MODULE_NOT_FOUND'

The consuming node application is declaring the local package in the devDependencies using the file option.

{
  "name": "node-application",
  "devDependencies" : {
      "prismic-utils": "file:../lib/prismic-utils"
  }
}

The local package (pismic-utils) defines @prismicio/client as a dependency.

{   
  "name": "prismic-utils",   
  "version": "0.0.0",  
  "private": true,  
  "main": "index.mjs",   
  "dependencies": {
    "@prismicio/helpers": "^2.3.9",
    "@prismicio/client": "^6.7.3"
  },   
  "devDependencies": {
    "eslint": "^8.35.0",
     "eslint-plugin-node": "^11.1.0",
     "msw": "^1.1.0",
     "node-fetch": "^3.3.0",
     "vitest": "^0.28.5"
  } 
}

I am at a complete loss as to why the consuming node application is having trouble resolving the @prismicio/client and @prismicio/helpers inherited dependencies. If I populate the node_modules folder inside the local package the node application resolves the dependencies. The local prismic-utils package dependencies are being installed to the consuming application node_modules folder.

Any help is greatly appreciated.

Thanks


Solution

  • This happens because using file: in a dependency's version results in a symbolic link. It acts like npm link (see npm link docs here).

    npm treats linked packages differently than those installed from a registry like npmjs.com. npm assumes the linked package has its own dependencies installed, so it does not install them.

    There are a few ways to resolve your issue:

    1. Best Option: Use npm workspaces to automatically handle linked dependency installation.

      Update your package.json file to include a workspaces property:

      // package.json
      
      {
        "workspaces": [
          "./relative/path/to/prismic-utils",
          "./relative/path/to/app"
        ]
      }
      
    2. Install file: dependencies like registry dependencies using npm's --install-links option.

      # In the Node.js app:
      npm install --install-links
      

      Changes to prismic-utils will not be reflected in the app until it is re-installed, just like non-file: dependencies.

      Note: Dependencies' devDependencies are not installed using this method. You will need to move node-fetch from devDependencies into dependencies.

    3. Install dependencies in prismic-utils before starting your app. Once they are installed, prismic-utils will have access to its own dependencies via its node_modules directory.

    Because you are using Node.js 16, which includes npm >=7 and support for workspaces, Option 1 is the best choice.