46

I am trying to use TypeScript's paths functionality so that I don't need to use relative imports any more.

Here is my tsconfig.json file:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./dist",
    "rootDir": ".",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "baseUrl": ".",
    "allowJs": true,
    "paths": {
      "*": ["node_modules/*", "src/*"],
      "@config/*": ["src/config/*"],
      "@controllers/*": ["src/controllers/*"],
      "@middlewares/*": ["src/middlewares/*"],
      "@models/*": ["src/models/*"],
      "@routes/*": ["src/routes/*"],
      "@types/*": ["src/types/*"],
      "@utils/*": ["src/utils/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "firebase-config.json", "webpack.config.js"]
}

Here is my package.json file:

{
  "name": "express-ts-boilerplate",
  "version": "0.1.0",
  "description": "Express Typescript Boilerplate",
  "main": "src/server.js",
  "author": "Sriram R",
  "scripts": {
    "start": "NODE_ENV=production node dist/src/app.js",
    "dev": "nodemon src/app.ts",
    "build": "tsc -p .",
    "test": "mocha --exit -r ts-node/register src/tests/*.spec.ts"
  },
  "dependencies": {
    // Dependencies here
  },
  "devDependencies": {
    // Dependencies here
  },
}

So now in one of my files, I try @config/typeConfig but I just get cannot find module error.

Maybe it's because of nodemon but it didn't work with ts-node too. How can I get this to work?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sriram R
  • 2,109
  • 3
  • 23
  • 40

6 Answers6

102

Note: for a working example with nodemon, skip to the second section of my answer.

If you mean that once you compiled the files and run the application, the modules are not found, then have a look at this thread: Module path maps are not resolved in emitted code

"paths" is designed for use with loaders that allow remapping

Say I have this path in my tsconfig.json:

"paths": {
      "@config/*": ["src/config/*"]
    }

And I require a file using that path in a file

import test from '@config/test';

Looking into the compiled file, I end up with

var test_1 = __importDefault(require("@config/test"));

As you can see, paths have not been resolved, it's still @config/test. The same thing will happen when testing your app with nodemon and ts-node.

In addition, you need to use a Typescript path alias resolver, like for exampletspath.

The TypeScript compiler will be able to resolve the paths so this will compile without problems, however the JavaScript output will not be possible to execute by Node nor a Web Browser, why? the reason is simple!

The JavaScript engine does not know anything about the compile time TypeScript configuration.

In order to run your JavaScript code, the path aliases now needs to be made into relative paths again, here is when TSPath comes into play.


That being said, if you want to make things work with nodemon, the following configuration will do. Beforehand, make sure you installed tsconfig-paths.

npm i tsconfig-paths

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. (...) If you require this package's tsconfig-paths/register module it will read the paths from tsconfig.json and convert node's module loading calls into to physcial file paths that node can load.

Perfect, we will execute node with -r tsconfig-paths/register to convert paths into physical file paths and -r ts-node/register to execute ts files on the fly and nodemon will restart the app upon changes.

In your package.json, you need to add this (modify it as needed):

    "nodemonConfig": {
          "ignore":
            [
              "**/*.test.ts",
              "**/*.spec.ts",
              ".git",
              "node_modules"
            ],
          "watch": [
            "src"
          ],
          "exec": "node -r tsconfig-paths/register -r ts-node/register ./src/server.ts",
          "ext": "ts, js"
        },
    "scripts": {
          "dev": "nodemon"
        }

Note the added configuration for nodemon.

And finally

npm run dev

And things should be running smoothly.

jperl
  • 4,662
  • 2
  • 16
  • 29
28

jperl's answer is completely correct.

In case you want a one-line solution:

nodemon -e ts,js --exec ts-node -r tsconfig-paths/register ./src/server.ts

Just remember to install tsconfig-paths/register:

npm i -D tsconfig-paths

In case instead of requiring -r tsconfig-paths/register in a CLI command or an npm script you would like to require it in ts-node configuration, you could do it in tsconfig.json as follows:

// in tsconfig.json
{
  // ... other TypeScript configuration
  "ts-node": {
    "require": ["tsconfig-paths/register"]
  }
}
Valentine Shi
  • 6,604
  • 4
  • 46
  • 46
Moh682
  • 793
  • 1
  • 7
  • 11
1

In my particular case use "tsc-alias" these were my steps:

  1. Install package npm install --save-dev tsc-alias
  2. Change the command with which I compile in package.json to "build": "tsc && tsc-alias"
  3. Modify my tsconfig.json because I did not have the outDir, so that it would look like this:
{
   "compilerOptions": {
      "outDir": "miniprogram",
      "baseUrl": "miniprogram",
      "paths": {
       "lib / *": ["./lib/*"]
     },
...
}

Note: If I don't add the outDir it gives me the following error: "compilerOptions.outDir is not set"

Then when importing it I do: import {Util} from "lib/my/path/util";

Jaime Roman
  • 749
  • 1
  • 11
  • 26
1

first step. we need to install tsconfig-paths

npm i -D tsconfig-paths

second step. Create the nodemon.json file in the root path. same level with package.json

third step. Copy and paste the code below into the nodemon.json file. In the code, ./src/app.ts This part is an express execution point. Modify it according to your situation.

{
  "ignore": ["**/*.test.ts", "**/*.spec.ts", ".git", "node_modules"],
  "watch": ["src"],
  "exec": "node -r tsconfig-paths/register -r ts-node/register ./src/app.ts",
  "ext": "ts, js"
}

last step. write executing script code in the package.json file

"script": {
  "dev": "nodemon"
}
gogoonline
  • 11
  • 3
0

Development

npm i -save-dev tsconfig-paths/register

tsconfig.json

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

package.json

"scripts": {
  dev: "ts-node -r tsconfig-paths/register src/index.ts"
}

Build

npm i -save-d ttypescript @zerollup/ts-transform-paths

tsconfig.json

{
 "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@src/*": ["src/*"],
    },   
  }
 "plugins": [
      {
          "transform": "@zerollup/ts-transform-paths",
      }
  ],
}

package.json

"scripts": {
  build: "ttsc -P ./tsconfig.json"
}
Jethro91
  • 537
  • 4
  • 7
0

TL;DR: delete all .js files

For those who didnt manage the with correct solutions above:

With my project I had 'leftover' .js files created (at some point in the past) under each .ts file. running nodemon (which in turn should use tsc) did not overwrite those files, and I kept getting the same require stack errors with Cannot resolve module.

Removing all .js files fixed it for me.

Refer to this answer for more information on how to actually do it in a safe(ish) way: Find (and delete) all files with a specified extension. make sure you actually review the list of files before deleting them.

Maor Barazani
  • 680
  • 3
  • 12
  • 31