94

I am trying to setup aliases for my mock server. Whenever I try to compile ts files, it returns error that it couldn't find proper modules even though those are defined in tsconfig,json->paths

Folder structure:

├── server
│   └── src
│       └──/json
├── src
│   └──/modules
├── tsconfig.json

Here is my tsconfig.json

{
    "compilerOptions": {
        "baseUrl": "./src",
        "experimentalDecorators": true,
        "jsx": "react",
        "lib": [
            "dom",
            "es2015",
            "es2015.promise"
        ],
        "module": "commonjs",
        "moduleResolution": "node",
        "noImplicitAny": true,
        "noUnusedLocals": true,
        "esModuleInterop": true,
        "paths": {
            "@project/app/modules/*": [
                "modules/*"
            ],
            "@project/server/data/*": [
                "../server/src/json/*"
            ]
        },
        "sourceMap": true,
        "target": "es5"
    },
    "exclude": [
        "node_modules",
        "tools"
    ]
}

Error: Error: Cannot find module '@project/server/data/accounts/accountsList'

Jamil Alisgenderov
  • 1,448
  • 2
  • 12
  • 23

15 Answers15

45

well, after suffering a few hours, I got a solution, I am using 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

Here is my tsconfig.json file look like

{
    "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/**/*"]
}

and here is my package.json

"_moduleAliases": {
        "@modules": "build/modules",
        "@core": "build/core"
    }

You need to install this package as a dependency of module alias npm i module-alias

And don't forget to add import 'module-alias/register'; in your entry file of the application as module alias dependency.

Md Alamin
  • 1,084
  • 11
  • 19
  • 1
    this is the finest solution, tsconfig is a different beast than the resolution of node itself, so you need a thing to allow node recognize your shiny aliased paths, as it is shared in this answer: https://stackoverflow.com/a/69514947/1778979 – Jesús Franco Oct 12 '22 at 23:08
  • 1
    Adding the `tsconfig-paths` dev dependency and `tsconfig-paths/register` allowed the path "aliases" to work under `ts_node` execution. No need for `module-alias` if that is all that is desired. – CortexCompiler Dec 05 '22 at 19:21
  • 3
    For this to work you will need to import the module-alias dependency in you entry file `import 'module-alias/register';` as stated in this article https://dev.to/larswaechter/path-aliases-with-typescript-in-nodejs-4353. – Sarcadass Jan 24 '23 at 14:59
  • Nailed it man, thank you very much! With `ts-node` indeed it works differently – Vitz Jan 27 '23 at 08:56
  • Huge thank you for this solution, now I can sleep – callmenikk Feb 06 '23 at 00:33
  • Following integration process of _module-alias_ from https://dev.to/larswaechter/path-aliases-with-typescript-in-nodejs-4353 worked for me – Eric Mar 02 '23 at 17:26
  • My man (sorry for assuming your gender), your solution works best. Stay blessed. – Shalom Effiom Apr 19 '23 at 00:17
  • thanks, I needed the "include": ["src/**/*"] – Markus Aug 19 '23 at 08:38
20

After battling it for some time I figured it out you need to include all directories using tsconfig-paths in your main tsconfig.json. Working version of your tsconfig.json file could be

{
    "compilerOptions": {
        "baseUrl": ".",
...
        "paths": {
            "@project/app/modules/*": [
                "src/modules/*"
            ],
            "@project/server/data/*": [
                "server/src/json/*"
            ]
        },
    },

    "include": [
        "./src",
        "./server"
    ],
}
Konrad Gałęzowski
  • 1,761
  • 1
  • 20
  • 29
  • 2
    But this way it'll include all folders in src even without the paths. in my project I had the same name for a folder in src like a library I included. And it was complaining as it tried to read the local folder when imported, not the package. – marcellsimon Apr 27 '21 at 17:26
  • 1
    it worked for me with dev mode, but not with tsc build and then running `node dist/index.js` so I added "dist/*" to paths along with "src/*" and key is "@/*" and now it works. – Oleg Abrazhaev Jul 02 '21 at 09:02
  • One thing to note is that the @ symbol is a convention to indicate the root of the project. – Remi Jan 09 '22 at 22:51
19

I faced the same issue. I tried many things and now i got a solution which works for me. I have an app and a library in my angular project. I want to use a library alias in my app.

I have following structure:

├── projects
│   └── lib
│       └── src
│           └── lib
│               └── lib-service.ts
│           └── index.ts
│   └── app
│       └──tsconfig.app.json
├── tsconfig.json

In the tsconfig.json file in the root folder I have following paths defined:

"paths": {
  "my-lib": [
    "projects/lib/src/index.ts"
  ]
}

There I access an index.ts file where I define the things I want to export. In my case the index.ts file has the following entry:

export * from './lib/lib-service';

Now I can access the LibService in components of the app with the help of the alias:

import {LibService} from 'my-lib';

It is important to mention that this soloution don't work if I add this entry to the tsconfig.app.json file. I think the IDE (in my case WebStorm) searches for aliases in the tsconfig.json file which is close to the root folder. So I extend the tsconfig.json in my tsconfig.app.json

"extends": "../../tsconfig",

Maybe you have to restart you IDE to remove the red underline of the import line.

I hope this solution works for you. You have to to a little bit more of setup work because you have to define the classes you want to export in the index.ts file. But in case of using libraries it make sense because you don't want to make everything visible for other app.

troYman
  • 1,648
  • 16
  • 18
15

My issue was that my tsconfig.app.json which extended my tsconfig.json was overriding the "baseUrl" option incorrectly. I removed it from the tsconfig.app.json so it did not have a "baseUrl" or "paths". In the end, my tsconfig.json had the following compiler options:

"baseUrl": "src/",
"paths": {
    "@shared/*": ["shared/*"],
    "@client/*": ["client/*"],
    "@server/*": ["server/*"]
}
Jake Tyler
  • 180
  • 2
  • 9
9

The following tsconfig.json worked for me, allowing import { foo } from '@models' etc for the associated index.ts barrel exports:

{
    "baseUrl": "./",
    "paths": {
      "@environment": ["./src/environments/environment.ts"],
      "@models": ["./src/app/shared/models/index.ts"],
      "@services": ["./src/app/shared/services/index.ts"]
    },
}
Andrew
  • 1,406
  • 1
  • 15
  • 23
7

TypeScript path properties can reliably resolve typings-based URLs such as an interface.

The intended behaviour is to allow TypeScript to resolve type information[1]

Most us seem to be experiencing that a path pointing to an actual dependency wont always compile as we expect. This is apparently by design despite what the doc says:

{
  "compilerOptions": {
    "baseUrl": ".", // This must be specified if "paths" is.
    "paths": {
      "jquery": ["node_modules/jquery/dist/jquery"] // This mapping is relative to "baseUrl"
    }
  }
}

Read this great breakdown by Mitchell Simoens - Why TypeScript Paths Failed Me

Found this after setting up @paths to my shared folders throughout the app. During compilation TS paths where breaking and actual builds were missing all sorts of modules. On a personal note this is a huge disappointment that TS can't reliably resolve dependencies this way.

Ben Racicot
  • 5,332
  • 12
  • 66
  • 130
6

TLDR;

npm i -D module-alias

and add the mappings from your tsconfig.json paths to _moduleAliases in your package.json

"_moduleAliases": {
  "foo": "dist"
}

As for the reasoning behind it, using "jsx": "react" in conjunction with "module": "commonjs" prevents you from being able to use absolute imports even though baseUrl and paths are configured correctly. A detailed discussion can be found in this issue.

Mateja Petrovic
  • 3,799
  • 4
  • 25
  • 40
3

When u use the bare variant with just tsconfig.json ("typescript": "^4.4.4") without any third party libraries:

  1. Add baseUrl.
"baseUrl": ".",
  1. Add paths.
"paths": {
      "@app/assets/*": ["src/assets/*"],
      "@app/components/*": ["src/components/*"],
      "@app/features/*": ["src/features/*"],
      "@app/hooks/*": ["src/hooks/*"],
      "@app/navigation/*": ["src/navigation/*"],
      "@app/services/*": ["src/services/*"],
      "@app/utils/*": ["src/utils/*"]
}
  1. Add package.json with a name of alias for each path inside the destination folder.
// src/assets/package.json
{
  "name": "@app/assets"
}
2

I put it both in webpack resolve alias and the tsconfig paths and it worked that way :)


module.exports = {   //...   resolve: {
    alias: {
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Templates: path.resolve(__dirname, 'src/templates/'),
    },   }, };
Liam
  • 27,717
  • 28
  • 128
  • 190
Ido
  • 2,034
  • 1
  • 17
  • 16
2

You can easily define simple path resolver like this:

loader.js

const Module = require("module");
const originalResolveFilename = Module._resolveFilename;
const tsConfig = require("./tsconfig.json");
const Path = require("path");
const fs = require('fs');
if (!tsConfig.compilerOptions || !tsConfig.compilerOptions.baseUrl)
    throw "Compiler options Base URL (compilerOptions.baseUrl) is required in tsconfig.json.";
const basePath = Path.resolve(tsConfig.compilerOptions.baseUrl, tsConfig.compilerOptions.outDir);

//Inspired by https://github.com/dividab/tsconfig-paths but that one does not work with VS Code
Module._resolveFilename = function (request) {
    if (tsConfig.compilerOptions && tsConfig.compilerOptions.paths[request] instanceof Array) { //Supports only without wildchars, but enough for our use
        const pathsTried = [];
        for (const reqPath of tsConfig.compilerOptions.paths[request]) {
            const modPath = Path.resolve(basePath, reqPath) + ".js";
            if (fs.existsSync(modPath)) return modPath;
            pathsTried.push(reqPath);
        }
        throw "Module " + request + " not found, paths tried: " + pathsTried.join(", ");
    }
    const ret = originalResolveFilename.apply(this, arguments);
    return ret;
};

and than reference it from VS Code's launch.json like this:

"configurations": [
{
  "type": "node",
  "request": "launch",
  "name": "Node.js",
  "runtimeArgs": ["-r", "./loader.js"],
  "program": "${workspaceFolder}/run.ts",
  "preLaunchTask": "tsc: build - tsconfig.json",
  "outFiles": [
    "${workspaceFolder}/out/**/*.js"
  ]
}]
    

It does not support patterns in paths but with this stub it can be added.

tsconfig.json should look like this:

"compilerOptions": {
    "outDir": "out", //required
    "moduleResolution": "node",
    "baseUrl": "./", //required
    "paths": {
        "InjectableStub": [ //requires 2
            "myapp/myfolder/mymodule", //used in runtime
            "./myfolder/mymodule //used in compile-time
        ]
    }
}

at least for me tsc creates sub-folder in name of the root folder in out that's why the 2 registrations.

Đonny
  • 910
  • 10
  • 11
2

If your having this issue with Jest then you can edit your jest.config to include a moduleNameMapper that would looks something like this:

{

  "moduleNameMapper": {
    "^@project/server(|/.*)$": "<rootDir>/server/$1"
  }
}

So this uses a regex to map the import @project/server... to the path "<rootDir>/server/$1"

Liam
  • 27,717
  • 28
  • 128
  • 190
0

There are some issues with path, for example if you set the path to the root let us say: @root/src and in src there is no index.ts that has a default, then this might fail...

It's a bit tricky per what I found.

piotrek1543
  • 19,130
  • 7
  • 81
  • 94
-1

Check angular.json file if its "build" -> "options" config has "tsConfig" option set as your changed tsconfig file.

"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
      "outputPath": "dist",
      "tsConfig": "src/tsconfig.app.json",
      "polyfills": "src/polyfills.ts",
      "stylePreprocessorOptions": {
        "includePaths": [
          "src/assets/styles"
        ]
      },
............

The above one is my angular.json file, and I configured it to use tsconfig.app.json. In this case, "paths" should be added in tsconfig.app.json file.

Julian W.
  • 1,501
  • 7
  • 20
-1

just add "moduleResolution" : "Node", on tsConfig

Rogerio Orioli
  • 121
  • 1
  • 2
-1

Just Use this in your tsconfig.json

"moduleResolution": "node",