Absolute path in the tsconfig doesn't work
Asked Answered
H

4

13

I saw some questions about this problem, none of them does not work I have a nodejs project along with Typescript. I do not like to use a relative path.I get the following error, when I set path in tsconfig :

Cannot find module '@app/controllers/main'

// main.ts
export const fullName = "xxxx";
...

// app.ts
import { fullName } from '@app/controllers/main'
...

This is the structure of my project :

-node_modules
-src
----controllers
---------------main.ts
----app.ts
-package.json
-tsconfig.json

tsconfig:

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "strict": true,
        "baseUrl": ".",
        "paths": {
            "@app/*": ["src/*"]
        },
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
    }
}

Where is my problem?

Thanks in advance.

Hamachi answered 4/9, 2020 at 16:31 Comment(3)
How did you receive the error? As you built your code or TSC complier showing on IDE?Beforehand
When I want to run the project with this command : ts-node-dev src/index.tsHamachi
Is there any reason why you don't use pure tsc instead?Beforehand
W
19

Update 2023

  1. Install development dependencies
npm install --save-dev \
  ts-patch \
  typescript-transform-paths \ 
  tsconfig-paths
  • ts-patch
    Directly patch typescript installation to allow custom transformers (plugins).
    The main difference why I prefer ts-patch over ttypescript is that there is no need to change the compiler (ttsc) because (hence the name) tsc is patched.

  • typescript-transform-paths
    Transforms absolute imports to relative from paths in your tsconfig.json.

  • tsconfig-paths
    Load modules whose location is specified in the paths section of tsconfig.json. Both loading at run-time and via API are supported.

  1. Update tsconfig.json

Note: See paths and plugins

{
   "compilerOptions":{
      /* A series of entries which re-map imports to lookup locations relative to the baseUrl */
      "paths":{
         "~/*":[
            "./src/*"
         ]
      },
      /* List of language service plugins */
      "plugins":[
         /* Transform paths in output .js files */
         {
            "transform":"typescript-transform-paths"
         },
         /* Transform paths in output .d.ts files */
         {
            "transform":"typescript-transform-paths",
            "afterDeclarations": true
         }
      ]
   }
}
  1. Patch Typescript

Note: This is NOT persistent

npx ts-patch install
  1. Edit/Add prepare script in package.json to patch Typescript persistently

Note: This IS persistent

{
  // ...
  "scripts": {
    "prepare": "npx ts-patch install -s"
  }
}
  1. Usage in import
import { hello } from '~/world';
  1. Compile as always
npx tsc

Old Answer

Unfortunately (and I don't know why) the Typescript compiler currently does not support the paths transformation very well.

Here is my solution:

I used the solution with this project.

Install devDependencies

  1. ttypescript -> npm install ttypescript --save-dev -> TTypescript (Transformer TypeScript) solves the problem by patching on the fly the compile module to use transformers from tsconfig.json.
  2. typescript-transform-paths -> npm install typescript-transform-paths --save-dev -> Transforms absolute imports to relative from paths in your tsconfig.json.
  3. tsconfig-paths -> npm install tsconfig-paths --save-dev -> Use this to load modules whose location is specified in the paths section of tsconfig.json. Both loading at run-time and via API are supported.
  4. ts-node-dev -> npm install ts-node-dev --save-dev -> It restarts target node process when any of required files changes (as standard node-dev) but shares Typescript compilation process between restarts

tsconfig.json

Update the tsconfig.json file with the following options:

{
    "compilerOptions": {
        /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
        "paths": {
            "@app/*": [
                "./src/*"
            ]
        },
        /* Advanced Options */
        "plugins": [
            {
                "transform": "typescript-transform-paths"
            }
        ],
    }
}

Build

For the compilation phase use ttsc instead of tsc with the configuration file. See the snippet below:

npx ttsc --p ./tsconfig.json

Development mode with autoreload

When you are in dev mode use the following script (put it in the scripts options in package.json) to automatically reload the project with the correct paths. The src/app.ts is the "entry point" of your application located under the src folder.

npx ts-node-dev --prefer-ts true --no-notify -r tsconfig-paths/register --watch src --transpileOnly src/app.ts

PS: Using ts-node-dev increase the speed significantly.

Wail answered 4/9, 2020 at 17:2 Comment(6)
The reason why is that paths was never intended to transform module specifiers in the output. Rather, it is meant to describe such transformations, as performed by other tools to the language for typechecking purposes. Poster child use cases for paths include RequireJS config.paths, SystemJS config.{packages|paths|map}, and Webpack module.resolve.{alias|modules}. Furthermore, TypeScript avoids rewriting module specifiers in the output for a whole host of reasonsMackle
compilerOptions + plugins finally arrived ??!!!!! how long did we wait for that. cool. great answer although i wished there would be a simple solution to sometimes wanted absolute paths ;)Rhesus
When I try to run my code with node dist/app.js I'm getting this error: Error: Cannot find moduleUnthankful
@NikhilWagh can you add more information. ThanksWail
The absolute import statements are not properly transformed into relative import statements in the compiled javascript, hence I'm getting Cannot find module for the modules I have written in the app.Unthankful
@NikhilWagh Strange... Can you share the code? Thanks :)Wail
P
7

I tweak @Carlo Corradini answer a little bit, here is my approach to fix this issue.

  1. install required plugins: npm i -D ttypescript typescript-transform-paths ts-node tsconfig-paths. these are packages that will help us to transform the paths.
    ttypescript-> https://www.npmjs.com/package/ttypescript

  2. then, on tsconfig.json, I put:

    {
    
     "ts-node": {
       "transpileOnly": true,
       "require": [  // set this so you dont need to use ts-node -r 
         "typescript-transform-paths/register",
         "tsconfig-paths/register"
        ]
     },         
     "compilerOptions": {
        "composite": true,
        "rootDir": ".", // must define
        "baseUrl": "src", // must define, the paths will relative to this
        "outDir": "lib", 
        "skipLibCheck": false,
        "paths": {
         "@app/*": ["./*"],
         "@controllers/*": ["./controllers/*"],
        },
         "plugins": [
           { "transform": "typescript-transform-paths" }
         ]
     }
    }
    
  3. then on your codes you can use:

    import myControllers from "@controllers/main.ts"
    
  4. now you can compile, npx ttsc -p tsconfig.json

  5. and to run on ts-node use npx ts-node -p tsconfig.json src/app.ts

Petasus answered 24/9, 2021 at 3:48 Comment(1)
i think this solution is deprecated nowPetasus
O
6

Update (as of March 2023)

The previous answers were helpful for development but I was still facing issues in production when I would run my build file.

The way I solved it once and for all is by using the tsc-alias package in build and the tsconfig-paths in development.

Setup instructions:

  1. Install tsc-alias & tsconfig-paths as a dev dependency with:
npm i -D tsc-alias tsconfig-paths
  1. Use tsconfig-paths in your dev or start script like so:
{
  "scripts": {
    "dev": "ts-node-dev -r tsconfig-paths/register src/index.ts", // here I am using ts-node-dev
    "start": "ts-node -r tsconfig-paths/register src/index.ts" // works with node too, see tsconfig-paths doc
  },
  "devDependencies": {
    "tsconfig-paths": "^4.1.2",
    "typescript": "^4.9.5"
  }
}
  1. Append tsc-alias in the build script. In your package.json:
{
  "scripts": {
    "build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json"
  },
  "devDependencies": {
    "tsc-alias": "^1.8.3",
    "typescript": "^4.9.5"
  }
}

And that's it. You are done. You can now use both path alias AND absolute imports and build your app without running into module not found issues.

Here's my tsconfig.json for reference:

{
  "compilerOptions": {
    "module": "commonjs",
    "baseUrl": "src", // notice my base url is set to ./src folder
    "resolveJsonModule": true,
    "outDir": "./build",
    "paths": {
      "@middlewares": ["_globals/middlewares"], // referring to ./src/_globals/middlewares folder 
      "@exceptions": ["_globals/exceptions"],
      "@utils": ["_globals/utils"]
    }
  },
  "include": [ "src/**/*" ], // if you need absolute path or path alias outside src then include the directory accordingly
  "exclude": ["src/test.ts"] // import won't work in this file/directory
}

Usage

You can now use both absolute import and path alias:

import { sth } from "_globals/sth"; // using absolute imports
import { sth } from "@utils"; // using path alias

Your build file should also run without any module not found errors.

Oldfashioned answered 21/3, 2023 at 11:36 Comment(0)
J
5

Your syntax is correct.

It appears that, at the time of writing, ts-node-dev does not support the paths entry of tsconfig.json.

This github issue discusses the problem and presents workaround options.

Jabalpur answered 4/9, 2020 at 16:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.