tsconfig-paths: Cannot Find Module

It seems my module paths do not get resolved correctly when using node or ts-node

src/index.ts

import cleanup from 'node-cleanup';
import ArimaClient from '@client/ArimaClient';

// ...

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2019",
    "lib": ["ES2019"],
    "moduleResolution": "node",
    "module": "commonjs",
    "strict": true,
    "alwaysStrict": true,
    "useDefineForClassFields": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "resolveJsonModule": true,
    "sourceMap": true,
    "inlineSources": true,
    "esModuleInterop": true,
    "removeComments": true,
    "skipLibCheck": true,
    "baseUrl": ".",
    "outDir": "dist/",
    "typeRoots": ["node_modules/@types", "src/typings"],
    "paths": {
      "@root/*": ["src/*"],
      "@client/*": ["src/lib/client/*"],
      "@database/*": ["src/lib/database/*"],
      "@structures/*": ["src/lib/structures/*"],
      "@structures": ["src/lib/structures"],
      "@utils/*": ["src/lib/utils/*"],
      "@utils": ["src/lib/utils"]
    }
  },
  "include": ["src"]
}

Scripts (I get the same error for both)

ts-node --files src/index.ts -r dotenv/config -r tsconfig-paths/register NODE_ENV=dev
tsc && node -r dotenv/config -r tsconfig-paths/register .

Error

internal/modules/cjs/loader.js:883
  throw err;
  ^

Error: Cannot find module '@client/ArimaClient'
Require stack:
- C:\Users\me\OneDrive\Desktop\Arima\dist\index.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
    at Function.Module._resolveFilename (C:\Users\me\OneDrive\Desktop\Arima\node_modules\tsconfig-paths\lib\register.js:75:40)
    at Function.Module._load (internal/modules/cjs/loader.js:725:27)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (C:\Users\me\OneDrive\Desktop\Arima\dist\index.js:7:39)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ 'C:\\Users\\me\\OneDrive\\Desktop\\Arima\\dist\\index.js' ]
}

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 33
  • Comments: 21

Most upvoted comments

It looks like you are having the same problem that I’m currently having with this. What seems to be happening is that tsconfig-paths is looking at tsconfig.json’s baseUrl but not taking outDir into consideration. I found this by manually editing the register function in node_modules/tsconfig-paths/lib/register.js so that it would log some debug info when trying to resolve my path mappings like this:

register.js
/**
 * Installs a custom module load function that can adhere to paths in tsconfig.
 * Returns a function to undo paths registration.
 */
function register(explicitParams) {
    // ...
    Module._resolveFilename = function (request, _parent) {
        var isCoreModule = coreModules.hasOwnProperty(request);
        if (!isCoreModule) {
            // DEBUG ___________________________
            if (request.indexOf('@src') === 0) {
              console.log(`_resolveFilename handling '${request}', configLoaderResult =`, configLoaderResult);
            }
            // DEBUG ^^^^^^^^^^^^^^^^^
            var found = matchPath(request);
            if (found) {
                var modifiedArguments = [found].concat([].slice.call(arguments, 1)); // Passes all arguments. Even those that is not specified above.
                // tslint:disable-next-line:no-invalid-this
                return originalResolveFilename.apply(this, modifiedArguments);
            }
        }
        // tslint:disable-next-line:no-invalid-this
        return originalResolveFilename.apply(this, arguments);
    };
    return function () {
        // Return node's module loading to original state.
        Module._resolveFilename = originalResolveFilename;
    };
}

The output from here was like this (build is what I’ve specified for outDir):

Failure shown in output
_resolveFilename handling '@src/util', configLoaderResult = {
  resultType: 'success',
  configFileAbsolutePath: 'C:\\project_root\\tsconfig.json',
  baseUrl: '.',
  absoluteBaseUrl: 'C:\\project_root',
  paths: { '@src/*': [ 'src/*' ] }
}
internal/modules/cjs/loader.js:883
  throw err;
  ^

Error: Cannot find module '@src/util'
Require stack:
- C:\project_root\build\src\foo.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
    at Function.Module._resolveFilename (C:\project_root\node_modules\tsconfig-paths\lib\register.js:78:40)
    at Function.Module._load (internal/modules/cjs/loader.js:725:27)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (C:\project_root\build\src\foo.js:22:16)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    'C:\\project_root\\build\\src\\foo.js'
  ]
}

If I change baseUrl to match outDir (i.e. build for my example, dist for OP) after running tsc then tsconfig-paths gets it right. In my opinion, this is a bug, but it is possible to work around by adapting the “Bootstrapping with explicit params” example in the README.md.

What I did to make it work was replace node -r tsconfig-paths/register ... with node -r ./register-paths.js ... (and create register-paths.js as shown below). Basically this tells tsconfig-paths a different baseUrl that incorporates outDir.

`register-paths.js`
const path = require('path');
const tsConfig = require('./tsconfig.json');
const tsConfigPaths = require('tsconfig-paths');

const baseUrl = tsConfig.compilerOptions.baseUrl || '.';
const outDir = tsConfig.compilerOptions.outDir || '.';
const explicitParams = {
  baseUrl: path.resolve(baseUrl, outDir),
  paths: tsConfig.compilerOptions.paths,
};
const cleanup = tsConfigPaths.register(explicitParams);

// TODO: clean-up when path registration is no longer needed
//cleanup();

well, after suffering a few hours, I got a solution I have used the ts-node package, and I got the same error Error: Cannot find module '@modules/logger'

You need to add ts-node configuration in the tsconfig.json file. You can get more infor at ts-node

{
    "ts-node": {
        // Do not forget to `npm i -D tsconfig-paths`
        "require": ["tsconfig-paths/register"]
    },
    "compilerOptions": {
        "lib": ["es5", "es6", "es7"],
        "target": "es2017",
        "module": "commonjs",
        "moduleResolution": "node",
        "rootDir": "src",
        "outDir": "build",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "esModuleInterop": true,
        "noImplicitAny": true,
        "strict": true,
        "resolveJsonModule": true,
        "allowJs": true,
        "sourceMap": true,

        "baseUrl": ".",
        "paths": {
            "@modules/*": ["src/modules/*"],
            "*": ["node_modules/*"]
        },

    },
    "include": ["src/**/*"]
}

+1 this package is useless I think, time to find a different solution

EDIT: This package seems to work good for my use case https://github.com/justkey007/tsc-alias

Actually I got it to work now using TS_NODE_BASEURL. I have a layout like

src/
  myscript.ts
  mymodule.ts
tsconfig.json

and im my tsconfig.json

"compilerOptions": {
    "outDir": "dist",
    "baseUrl": "src",
    "paths": {
      "@/*": ["./*"]
    },
  },
  "include": [
    "src"
  ],

Now when I put a script in my package.json like so

"scripts": {
    "myscript": "tsc --module 'CommonJS' && TS_NODE_BASEURL=./dist node -r tsconfig-paths/register dist/myscript.js",
}

I can run it with npm run myscript just fine with "@/mymodule" imports.

What didn’t work before was when I had baseUrl and paths switched in tsconfig.json like

    "baseUrl": "./",
    "paths": {
      "@/*": ["./src"]
    },

“ts-node”: { // Do not forget to npm i -D tsconfig-paths “require”: [“tsconfig-paths/register”] },

This doesn’t work for me