Typescript path aliases not resolved correctly at runtime
Asked Answered
C

8

32

I'm running VS Code and I am currently trying to set up some aliases on my typescript project.

My dev setup rest on nodemon and ts-node, the code gets compiled to a dist folder.

So far, I succeeded to get Typescript Hero to manage the import with aliases:

alias resolution vscode

So far, my folder structure is:

.
└─┬ src
  ├──modules
  ├────Category
  ├────Ressource
  ├──shared
  ├────debug

// tsconfig.json
{
    "compilerOptions": {
        "module": "commonjs",
        "moduleResolution": "node",
        "pretty": true,
        "sourceMap": true,
        "target": "es6",
        "outDir": "./dist",
        "baseUrl": "./src",
        "paths": {
            "@shared/*": [
                "shared/*"
            ],
            "@modules/*": [
                "modules/*"
            ]
        },
        "resolveJsonModule": true,
        "esModuleInterop": true
    },
    "include": [
        "src/**/*.ts"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts",
        "**/*.test.ts",
    ]
}

And this is the first alias import that fails.

//Server.ts file
import Print from '@shared/debug/Print.class';
import App from './App';

const MyApp: App = new App();

MyApp.ExpressApp.listen(MyApp.Config.ExpressPort, () => {
    Print.Log('Express server listening on port ' + MyApp.Config.ExpressPort);
});

However, I get an error: "Cannot find module '@shared/debug/Print.class'" on "cross-env NODE_ENV=development nodemon ts-node ./src/server.ts". CMD logs

And this is where I stand.

Now, I've read some Q&A on SO, and it seems that even if I managed to make the aliases works while in dev, it would fail in production, as I'm running from Typescript src folder and my deliverable are built in dist ? If so, is there any way to remediate ? Many thanks

Campanulate answered 5/2, 2020 at 0:5 Comment(2)
can you confirm you have a Print.class.ts file in the shared/debug folderHaphtarah
I do have such file, for now it’s only doing a console log on dev/staging environments.Campanulate
C
52

The problem was situated on node path aliases resolution on runtime. Even if the typescript was executed on runtime by ts-node, the aliases couldn't be resolved by node as-is. (I think)

But this was only the tip of the iceberg. I'd encounter it then later with my jest setup, and on JS runtime.

I had to find a way, for every runtime I had, to interpret my aliases. There was a few npm packages but a lot of them required more declarations.

And I didn't want to declare my aliases in every config files I'd have and only depend on my tsconfig file.

After much testings, there was only two node modules to install tsconfig-paths for typescript runtime execution on ts-node. And @ef-carbon/tspm to convert my aliases to the build destination.

npm i -D tsconfig-paths @ef-carbon/tspm

For ts-node, the script was modified as :

ts-node -r tsconfig-paths/register ./src/server.ts

For your js compiled, you only have to run :

ef-tspm

For jest, ts-jest is needed, I already had it but it wasn't properly configured. I used the built-in helper to set up my paths. My jest config file now looks like this :

//jest.config.js
const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig');
module.exports = {
    roots: ['<rootDir>/src'],
    globals: {
        'ts-jest': {
            tsConfig: 'tsconfig.json',
            diagnostics: {
                warnOnly: true,
            },
        },
    },
    clearMocks: true,
    coverageDirectory: 'coverage',
    testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
    moduleFileExtensions: ['js', 'json', 'jsx', 'node', 'ts', 'tsx'],
    testEnvironment: 'node',
    moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/src/' }),
    pathToJest: 'npm test',
    preset: 'ts-jest',
    testMatch: null,
};

Here is, how my scripts look like in my package.json

  "scripts": {
    "dev:ts": "cross-env NODE_ENV=development nodemon",
    "dev:js": "cross-env NODE_ENV=development npm run start:js",
    "staging": "cross-env NODE_ENV=staging npm run start:js",
    "production": "cross-env NODE_ENV=production npm run start:js",

    "test": "cross-env NODE_ENV=testing jest --runInBand",
    "test:debug": "npm run test --detectOpenHandles",

    "start:js": "npm run build && nodemon --config nodemon-js.json",
    "build": "npm run compile && npm run post:compile && npm run copyAssets",
    "compile": "tsc",
    "post:compile": "ef-tspm",
    "copyAssets": "copyfiles -e ./src/**/*.ts -e ./src/**/*sample* -e ./src/**/*.json -u 1 ./src/**/* ./dist/"
  },

Seeing how it goes, I'll probably add a grunt/gulp solution afterward. But for now, this is good enough.

Campanulate answered 8/2, 2020 at 21:21 Comment(3)
ef-tspm looked promising, but it's now archived. might not be best option for those in 2022+Caesaria
Follow this PR for integrated ts-node path mappingSpherical
Both OP and @Micah's suggestions didn't work for me. What did work was the build time tool tsc-alias. After installing, I changed my build script to this and it just worked: tsc -p tsconfig.json && tsc-alias -p tsconfig.json. For more details see my other answer.Rexfourd
H
33

I faced the same problem and CrazyYoshi solution is not worked properly in my case.

I fixed it with putting this code in tsconfig.json:

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

After it do not forget to install tsconfig-paths module:

npm i -D tsconfig-paths

Documentation: https://typestrong.org/ts-node/docs/paths/

Huai answered 3/10, 2022 at 12:22 Comment(2)
didn't work in my caseOtherworldly
This does not work, when I do build with tsc command using ts-node in production is not advisable. It works in dev.Monotint
T
17

You're using ts-node and nodemon to run your code, so you should add this to your tsconfig.json file:

{
    "ts-node": {
        "require": ["tsconfig-paths/register"]
    }
}

And then install tsconfig-paths/register as a dev dependency.

npm install --save-dev tsconfig-paths

References

Tropophilous answered 16/11, 2022 at 12:56 Comment(0)
C
14

March 2023

Just spent literally hours trying to get this to work. In the end, I ended up ditching ts-node entirely and installing tsx which just worked straight out of the box.

Apparently, this is coming to ts-node eventually, although the PR kind of looks dead so who knows:

https://github.com/TypeStrong/ts-node/pull/1585

In the mean time, if you really want to stick with ts-node you may find the following useful:

https://www.npmjs.com/package/@bleed-believer/path-alias

https://github.com/TypeStrong/ts-node/issues/1007

https://github.com/TypeStrong/ts-node/discussions/1450#discussioncomment-1806115

Cinchonize answered 12/3, 2023 at 19:42 Comment(4)
This is the right solution, tsx worked out of the box!Oilcloth
Side note: decorators are not supported by tsx: github.com/esbuild-kit/tsx/issues/216. This is a big limitation if you're using libraries like routing-controllers and nestjs.Oilcloth
+1 ... saved me a lot of headache. i'm not using nestjs, just straightforward express.js low resource consuming app. thanks!Brinker
After hours of frustration with ts-node-dev and tsconfigs-path, this is the absolutely right solution which is not conflict with moduleAliases.Paola
G
5

I think you can use the package called module-alias to solve this problem.

First, install it with yarn, npm or pnpm, choose one what you use.

Then, add same alias to your package.json just like:

{
  "_moduleAliases": {
    "@shared": "./src/shared",
    "@modules": "./src/modules"
  }
}

Finally, to import module-alias/register in your first line(that's important)!

import 'module-alias/register'
// ... your other code

This is the address of module-alias You can refer to it.

I hope my answer will help you to solve it.

Guillory answered 22/5, 2022 at 7:2 Comment(1)
This answer is actually the best in 2022. Additionaly, you can also check this very nice tutorialIndeclinable
K
1

Let's simplify it:

  • tsc-alias will fix tsc
  • tsconfig-paths/register will deal with ts-node and nodemon.

The usage, as the others said, is simple. First, install it:

npm i -D tsc-alias tsconfig-paths

Then you can update your scripts to use them

{
  "scripts": {
    "start": "node dist/app.js",
    "dev": "ts-node -r tsconfig-paths/register src/app.ts",
    "build": "tsc && tsc-alias",
    "watch": "nodemon --watch ./src --exec 'ts-node -r tsconfig-paths/register src/app.ts'"
  }
}

Other options are:

  • tsx: replaces tsc.
  • ts-node-dev: replaces nodemon
  • node --watch flag: node v18.11 and above
Klinger answered 8/3 at 8:20 Comment(0)
T
0

I installed tsc-alias in my express js project to handle aliases paths with nodemon:

and it works for me correctly :).

tsconfig.json file:

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

and this is my nodemon.json file :

{
  "watch": ["src"],
  "ext": "ts",
  "exec": "concurrently \"npx tsc && tsc-alias && ts-node dist/main.js\""
}

and this is my package.json:

{
  "name": "rest-mongoose",
  "version": "1.0.0",
  "description": "",
  "main": "./dist/main.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "npx tsc && tsc-alias",
    "start": "npm run build && node dist/main.js",
    "dev": "nodemon"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^16.3.2",
    "express": "^4.18.2",
    "mongodb": "^6.3.0",
    "mongoose": "^8.1.0"
  },
  "devDependencies": {
    "@types/cors": "^2.8.17",
    "@types/express": "^4.17.21",
    "@types/node": "^20.11.5",
    "concurrently": "^8.2.2",
    "nodemon": "^3.0.3",
    "ts-node": "^10.9.2",
    "tsc-alias": "^1.8.8",
    "typescript": "^5.3.3"
  }
}

You can run your project on development mode with bottom command: npm run dev

Teleprinter answered 22/1 at 14:8 Comment(0)
L
-1

Easy: Just use tsx, works perfectly and requires zero setup.

npx tsx ./path-to-your-script
Lunatic answered 15/12, 2023 at 4:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.