Ts-node-dev: paths option(tsconfig.json) is ignored

Created on 19 Sep 2019  路  13Comments  路  Source: wclr/ts-node-dev

Settings

tsconfig.json

Other options are omiited for brevity.

{
  "compilerOptions": {
    "baseUrl": "./src"
    "paths": {
      "#/*": ["./*"]
    }
  }
}
command
ts-node-dev --respawn --transpileOnly src/index.ts
src/index.ts
import { foo } from '#/foo'

console.log(`index.ts uses ${foo}`)

Problem

When I edit foo.ts, which index.ts depends on, ts-node-dev does not respawn. I guess it's because ts-node-dev does not consider the paths option in tsconfig.json.

Most helpful comment

For anyone finding this and want a two-liner here it is:
npm i -D tsconfig-paths

then in your package.json add a script like this:
"dev": "tsnd --respawn -r tsconfig-paths/register *pathtoyourmainfile* "

All 13 comments

Workaround

Here is a workaround for folks with the same problem.

Install concurrently (or another preferred alternative) and node-dev (or maybe another like nodemon).

npm uninstall ts-node-dev
npm install concurrently node-dev --save-dev

Then run

concurrently 'tsc -w' 'node-dev --respawn dist/index.js'

This would incrementally compile editted .ts files(tsc -w), and node-dev would respawn by detecting compiled .js files. Obviously, this is logically identical to what ts-node-env does, but now tsc respects the "paths" option.

This is strange "workaround" =)

Should this even work with node.js without special plugins?

TS compiler doesn't transform require paths like '#/foo', so it should just give the error Cannot find module '#/foo'

There is a module https://github.com/dividab/tsconfig-paths allows to handle tsconfig's paths option with node.js

@whitecolor

Thank you for the recommendation :)
I've been using link-module-alias, which automatically creates symlinks in _node_modules_ (And by this aspect, it has some other benefits, especially on plain node project, by the way). Whereas tsconfig-paths works on runtime, link-module-alias should be executed (only once) before running node.

TS compiler doesn't transform require paths like '#/foo', so it should just give the error Cannot find module '#/foo'

The compiler doesn't transform but compiles it "as-is"
(import foo from '#/foo' -> const foo = require('#/foo')),
without an error or warning.

Should this even work with node.js without special plugins?

For example, I set a symlink node_modules/# -> dist through link-module-alias. So #/foo is valid on compiled js.

So, I let ts compiler check types by looking "paths", and node find modules by symlinks. Maybe you can read https://dev.to/larswaechter/path-aliases-with-typescript-in-nodejs-4353 if you want.

What I suggest is, of course, different from tsc's behavior. Though ts compiler compiles modules aliases as-is, ts-node-dev should dynamically stitch ts files by looking "baseUrl" and "paths". This only requires reading tsconfig.json and doing some regex, so I guess It'd be not a big deal.

Sometimes, project structure gets deeper and wider, so using absolute path provides significant benefit. If ts-node-dev doesn't care them, people using aliases have to find other ways like the "workaround" I suggested. And what we should consider is, people negatively feel to fragmented setting. They prefer setting as consistent as possible through projects, no matter big or small. If ts-node-dev doesn't support, then they might just avoid using it at all even on projects without aliases.

P.S.

This is strange "workaround" =)

"strange"..? Hmm, why?
Well, I personally think it's also a quite good approach. There would be pros and cons.

pros

  • Able to choose tools we prefer. concurrently, npm-run-all, parallelshell, node-dev, nodemon, forever, pm2 or whatever.
  • tsc --watch is stable, so straight-forward and beginner-friendly.
  • Able to compose various options of different tools by ourselves. It can be more fine-grained than options ts-node-dev provides.
  • More closer to production-like condition. Node process would directly and statically consume the entire file system source code is in. (While this logcally doesn't neccessarily mean ts-node-dev has a disadvantage.)
  • Incremental compilation can be enabled. (I know ts-node-dev supports "dynamic" watching compilation, but don't know if it supports "static" incremental compilation. But tsc --watch --incremental supports both ones.)

cons

  • node process could start before compilation(tsc -w) is completed, which would cause an initial error message printed. (However, it's actually not a problem as it would be restarted automatically just after .ts modules are all compiled.)
  • tsc compiles even when 'modules entrypoint (e.g. index.ts) does not depend on' (e.g. test files) changes. (But this is very unlikey because there's generally no reason to place such a file without "excluding" it on tsconfig.)
  • Maybe it looks not so "neat" ?

It works smoothly. jjangga0214/ts-boilerplate is a working example. You can clone it, run yarn/npm dev, and then edit .ts files. Then it would automatically restart the node process seamlessly.

tsc --watch creates *.js files, which ts-node (not ts-node-dev, but ts-node) was created to avoid.

If you ok with this *.js filles all around while dev, it is just another approach.

Also I prefer to use yarn workspaces instead of tsconfig paths and other "workarounds" like mentioned link-module-alias.

@whitecolor Thanks for the recommendation again.
The main point of this issue is asking if ts-node-dev would repect paths. I think it's cool and necessary. How do you think?

This is a task for another package, as I said you may use https://github.com/dividab/tsconfig-paths to achieve that if needed, setup is quite transparent.

Closing this issue. Thanks for the suggestion anyway :)

If anyone has issues with this, try the module-alias plugin. So far it's the cleanest solution I've found and it is working all right!

Given a directory structure like the following:

- build/                 // Here is the typescript outDir folder
- src/                   // Here is where we have all the app files
- src/index.ts
- src/app.ts
- package.json

Create a src/aliases.ts file like this:

import moduleAlias from 'module-alias';
import path from 'path';

const IS_DEV = process.env.NODE_ENV === 'development';
const rootPath = path.resolve(__dirname, '..', '..');
const rootPathDev = path.resolve(rootPath, 'src');
const rootPathProd = path.resolve(rootPath, 'build');
moduleAlias.addAliases({
  '@src': IS_DEV ? rootPathDev : rootPathProd,
});

Then, in the src/index.ts file, or in whatever is your entry file, add this as first import:

import './aliases'; // MUST BE FIRST!!

// Now you can use the `@src` for absolute imports
import { app } from '@src/app';

Finally, set tsconfig.json like this (mostly to make typescript and your IDE recognise the path):

{
  "compilerOptions": {
     "outDir": "./build/",
     "baseUrl": ".",
     "paths": {
       /* IMPORTANT: this must be the same of 'src/aliases.ts' */
       "@src/*": ["src/*"]
     },    
}

@whitecolor I would recommend to add a note about this (also mentioning baseUrl, since one really doesn't have to use paths for things to break) in points of notice in the README.

Because while I love ts-node-dev for the reason you mentioned in this issue, I almost just threw in the towel until I stumbled across your recommendation to use tsconfig-paths. Which is so simple to set up but... why would one even think about looking for it in the first place? At least all I perceived was an inconsistency between tsc and ts-node-dev and assumed it was a bug (hence then looking for it here).

@whitecolor tsconfig paths does not seem to work with ts-node-dev. It worked well previously with nodemon and ts-node.

Running via npx ts-node-dev --project tsconfig.json -r tsconfig-paths/register src/server/server.ts

My aliased paths do not resolve.

Not sure why it may not work, when I used it, it worked ok.

Please disregard. I was a bit confused when running a nextjs customer server, that first passed through ts-node for the server and than passed through webpack for the react code. Think I have it figured out now.

For anyone finding this and want a two-liner here it is:
npm i -D tsconfig-paths

then in your package.json add a script like this:
"dev": "tsnd --respawn -r tsconfig-paths/register *pathtoyourmainfile* "

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Derya picture Derya  路  6Comments

capc0 picture capc0  路  5Comments

Sytten picture Sytten  路  7Comments

maxpain picture maxpain  路  6Comments

Richienb picture Richienb  路  3Comments