Ts-node: tsconfig.json/paths not working with ts-node

Created on 19 Jun 2016  Â·  42Comments  Â·  Source: TypeStrong/ts-node

One neat trick to use tsconfig.json/paths is to pointing the module name to your source file:

// tsconfig.json of blue-tape-fixture
{
  ...
  "paths": {
    "blue-tape-fixture": [ "src/" ]
  }
}

This way, your test files can reference the source as if it is a proper module:
image
You can see it is working in vscode. But it does not when I run test with ts-node:

// package.json
{
  "scripts": {
    "test": "ts-node -P tsconfig.build.json node_modules/blue-tape/bin/blue-tape \"test/**/*.ts\" | tap-spec"
  }
}

The error is:

Error: Cannot find module 'blue-tape-fixture'
    at Function.Module._resolveFilename (module.js:325:15)
    at Function.Module._load (module.js:276:25)
    at Module.require (module.js:353:17)

You can see it here: https://github.com/unional/blue-tape-fixture/tree/ts-node

# clone
npm install
npm test
external research

Most helpful comment

Instead of the PR, I have now created the tsconfig-paths package that makes it possible to automatically resolve paths during execution with ts-node, node, or mocha. Just follow the instructions in the readme.

All 42 comments

@unional That's the node.js error - TypeScript is working fine, but node will never be able to resolve an alias like that unless TypeScript emits code that rewrites paths. What do you think is the solution here?

I see. In order for it to work, a module-path-rewriter need to be written and loaded by ts-node.
Alternatively is to load the transpiled code dist/*, which kind of defeat the purpose of ts-node (I said kind of because the test files, in this use case, can remain as ts files).

This module uses the emitted code though - it's TypeScript itself that doesn't do any rewriting right?

Just check. Nope it does not do any rewriting. Seems like the baseUrl and paths is only used during compilation. By design or bug on TS side?

Updated https://github.com/unional/blue-tape-fixture/tree/ts-node to demo the issue.
run npm run build-test and see under dist/test/index.ts. It is still requiring blue-tape-fixture

@unional Yeah, it's tricky. I can understand TypeScript's stance, if they don't have a solution for it built-in I think we can investigate adding a plugin once they implement the pluggable transforms in TypeScript 2.0. This is definitely more of a user-land feature.

any news? i want to use 'paths' option in my tests

No, it does not work and is not expected to work. Someone can still implement a plugin (which would be very valuable to others too!), but I am not currently working on it.

Maybe this could be solved by augmenting node's loading rather than having typescript re-write the paths. There are some alternatives mentioned here. In particular I'm thinking that wrapping the global require and just changing the path according to tsconfig would be the simplest solution.

When I looked closer I realised that wrapping global require is not possible in a way that replaces it (the example above installs a separate require function). However it is possible to patch Module._load like is done in mock-require. I did some experiments with this and it seems doable. I'll try to put together a PR to demonstrate.

For those interested I did a PR to enable execution of projects with tsconfig paths in #254.

Instead of the PR, I have now created the tsconfig-paths package that makes it possible to automatically resolve paths during execution with ts-node, node, or mocha. Just follow the instructions in the readme.

Note to all using @jonaskello 's tsconfig-paths:

The errror "Missing paths in compilerOptions. tsconfig-paths will be skipped" means that you don't have any paths property specified in your tsconfig.json. I had been able to use imports relative to my src directory just by specifying "baseUrl": ".", but to get ts-node/node/mocha to understand the same imports, I also needed to specify a paths property in my tsconfig.json, eg:

"paths": {
  "src/*" : ["./src/*"]
}

Hope this helps another overwhelmed soul.

@shirakaba Just a note, you can set your "baseUrl" to "./src/" and "paths" like this

"paths": {
  "*" : ["./*"]
}

I will be the same, but you are able to add more paths with the same root more easily.

I've tried nearly all the above solutions and I'm still getting Module not found: Error: Can't resolve ....

What's the solution here? I have updated my tsconfig.json webpack.config.js and even tried tsconfig-paths. None of these work.

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

@ashok-sc ts-node has no support for paths in tsconfig.json, and no plans to add it. I think your best bet to get it working is using tsconfig-paths. If you have trouble using it feel free to file an issue at that repo.

If you are using webpack the scenario is quite different. If you are using ts-loader you might want to try tsconfig-paths-webpack-plugin altough it is still early in development. If you are using awesome typescript loader then it has this type of plug-in built-in.

@ashok-sc As @jonaskello said, there is no plan to support path, as you need to replace the path call with the proper full path in order to be understandable for Webpack. You could use Awesome Typescript Loader that have a plugin out-the-box for that, but still, it needs to be a plugin.

Hey @jonaskello and @michaeljota thanks for the help! I am in fact using awesome-typescript-loader with webpack. However, the instructions are quite poor. I understand that you may not have the answers here, but I thought I'd give it a shot anyway.

The instructions state to add the TsConfigPathsPlugin, but it's not clear as to what it needs in the constructor. Here's what I tried:

new TsConfigPathsPlugin({
    configFileName: path.join(__dirname, './tsconfig.json'),
    compiler: 'typescript'
  })

This doesn't seem to work though and I still get module not found.

Anyway, if you don't have the answer of the top of your head feel free to ignore.

Here's a gist of what a webpack config should look like with awesome-typescript-loader's tsconfig paths plugin. I don't pass anything in the constructor in my config, but maybe you need it if your tsconfig.json is in a non-standard location.

var TsConfigPathsPlugin = require('awesome-typescript-loader').TsConfigPathsPlugin;

module.exports = {
    entry: ...,
    output: ...,
    resolve: {
        extensions: ['...'],
        plugins: [
            new TsConfigPathsPlugin()
        ]
    }
}

@ashok-sc If it does not work, you can try version 0.2.0 of tsconfig-paths-webpack-plugin. I have added code that prints the path that it tries to load the tsconfig.json from.

Note, tsconfig-paths-webpack-plugin is used instead of the one in awesome-typscript-loader. You can also use it with ts-loader.

@ashok-sc I use ATL as @jonaskello describe and I have not faced any problems.

Hey so I've actually got the plugin to work!

Unfortunately now I still have an issue where index.ts files don't work.

For example an import like this:
import config from '@src/server/config';

where the module is ./src/server/config/index.ts is not working. I'm getting the following error:

Error: Cannot find module '@src/server/config'
    at Function.Module._resolveFilename (module.js:527:15)
    at Function.Module._load (module.js:476:23)
    at Module.require (module.js:568:17)
    at require (internal/module.js:11:18)

I've also tried import config from '@src/server/config/index' and nothing. Is this expected to be broken. Support for path resolution on all fronts feels quite broken.

Try it with tsc. If the project compiles with tsc then it should also work with the plugins. If you get errors with tsc you can try tsc --traceResolution. See more here under heading "Tracing module resolution".

@ashok-sc can you post the content of your tsconfig?

Here's my tsconfig.json:

{
  "compilerOptions": {
    "outDir": "./compiled/",
    "sourceMap": true,
    "noImplicitAny": false,
    "module": "commonjs",
    "lib": ["es6", "dom", "esnext"],
    "target": "es6",
    "jsx": "react",
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "baseUrl": "src",
    "paths": {
      "@src/*": [
        "*"
      ]
    }
  },
  "include": [
    "./src/core/*",
    "./src/client/*",
    "./src/server/*"
  ]
}

@jonaskello will try this with tsc in a bit.

Had you try to add a slash to the end of the baseUrl? Or maybe do otherwise, set baseUrl to ./ and set @src/* to src, or src/?

with baseUrl enabled it seems @jonaskello's plugin will also use this for npm_modules that otherwise would load normally. When removing baseUrl and paths all together and using relative paths the project compiles again.

$ npm run test

> [email protected] test /Users/xvilo/projects/xxxxxx
> mocha -r ts-node/register -r tsconfig-paths/register --require tests/testHelper.js tests/js/**/*.spec.ts

Error: Cannot find module '/Users/xvilo/projects/xxxxxx/src/js/util'
    at Function.Module._resolveFilename (module.js:543:15)
    at Function.Module._resolveFilename (/Users/xvilo/projects/xxxxxx/node_modules/tsconfig-paths/lib/register.js:29:44)
    at Function.Module._load (module.js:470:25)
    at Module.require (module.js:593:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/xvilo/projects/xxxxxx/node_modules/tough-cookie/lib/memstore.js:35:12)
    at Module._compile (module.js:649:30)
    at Object.Module._extensions..js (module.js:660:10)
    at Module.load (module.js:561:32)
    at tryModuleLoad (module.js:501:12)
    at Function.Module._load (module.js:493:3)
    at Module.require (module.js:593:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/xvilo/projects/xxxxxx/node_modules/tough-cookie/lib/cookie.js:36:25)
    at Module._compile (module.js:649:30)
    at Object.Module._extensions..js (module.js:660:10)
    at Module.load (module.js:561:32)
    at tryModuleLoad (module.js:501:12)
    at Function.Module._load (module.js:493:3)
    at Module.require (module.js:593:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/xvilo/projects/xxxxxx/node_modules/jsdom/lib/api.js:5:21)
    at Module._compile (module.js:649:30)
    at Object.Module._extensions..js (module.js:660:10)
    at Module.load (module.js:561:32)
    at tryModuleLoad (module.js:501:12)
    at Function.Module._load (module.js:493:3)
    at Module.require (module.js:593:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/xvilo/projects/xxxxxx/tests/testHelper.js:1:13)
    at Module._compile (module.js:649:30)
    at Object.Module._extensions..js (module.js:660:10)
    at Module.load (module.js:561:32)
    at tryModuleLoad (module.js:501:12)
    at Function.Module._load (module.js:493:3)
    at Module.require (module.js:593:17)
    at require (internal/module.js:11:18)
    at requires.forEach.mod (/Users/xvilo/projects/xxxxxx/node_modules/mocha/bin/_mocha:511:3)
    at Array.forEach (<anonymous>)
    at Object.<anonymous> (/Users/xvilo/projects/xxxxxx/node_modules/mocha/bin/_mocha:510:10)
    at Module._compile (module.js:649:30)
    at Object.Module._extensions..js (module.js:660:10)
    at Module.load (module.js:561:32)
    at tryModuleLoad (module.js:501:12)
    at Function.Module._load (module.js:493:3)
    at Function.Module.runMain (module.js:690:10)
    at startup (bootstrap_node.js:194:16)
    at bootstrap_node.js:666:3
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] test: `mocha -r ts-node/register -r tsconfig-paths/register --require tests/testHelper.js tests/js/**/*.spec.ts`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] test script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/xvilo/.npm/_logs/2018-06-21T15_25_59_483Z-debug.log

and with baseUrl and paths disabled:

$ npm run test

> [email protected] test /Users/xvilo/projects/xxxxxx
> mocha -r ts-node/register -r tsconfig-paths/register --require tests/testHelper.js tests/js/**/*.spec.ts

Missing baseUrl in compilerOptions. tsconfig-paths will be skipped


  Dom.createNodeFromString
    ✓ Div with class
    ✓ span with class
    ✓ img
    ✓ table with 1 tr and 1 td with content
    ✓ ul
    ✓ li
    ✓ ul with li


  7 passing (22ms)

part of my package.json

"scripts": {
    "test": "mocha -r ts-node/register -r tsconfig-paths/register --require tests/testHelper.js tests/js/**/*.spec.ts"
  },

tsconfig.json/paths not working if you set outDir

For production apps after a tsc build.
I have created a TS Paths replacer in Go which is pretty fast and that addresses the custom paths issue.
After it's applied you only need to run your app node main.js.

@joseluisq That doesn't address the issue in any possible way. People here want to run typescript code directly with a support of paths aliases from tsconfig.json for the dev purposes and not only.
Specifically I am facing the issue of wrong paths resolution when outDir is specified in tsconfig.json.

_By the way, there is already a proven solution to the problem you referenced_ https://github.com/ilearnio/module-alias
_or_
https://github.com/dividab/tsconfig-paths
_or even for webpack_
https://github.com/dividab/tsconfig-paths-webpack-plugin

@alexeychikk this thread confirms that the maintainer is not interested in any implementation of TS paths support, but he suggests external implementations instead as you have enumerated.

Sure, if the people wants to run TS code directly via ts-node is dividab/tsconfig-paths an alternative . I use it for development.

My solution is pretty simple: replace TS path aliases using a tsconfig.json as well. But replacing those paths directly into the .js files, not on the fly like tsconfig-paths for example.

Oh boy. This looks like it's going to be fun to figure out.

@alexeychikk
1) module-alias not the best way if you use es6
https://github.com/ilearnio/module-alias/issues/59
2) If project doesn't use webpack "even webpack" is not usecase.
3) May be you can help me to configure tsconfig-paths to use it as npm script with ts-node for script.ts and extended tsconfig containing outDir?
https://github.com/dividab/tsconfig-paths/issues/39

@jonaskello You saved my ass here!

Is there any reason why this shouldn't become a default built in part of ts-node? It seems like it would make more sense to include this by default, than to not.

Thanks @jonaskello!

It also seems to work when using ts-node through nyc:

nyc.config.js

module.exports = {
  all: false,
  exclude: ['**/*.d.ts', '**/*.test*.ts', '**/index.ts'],
  extension: ['.ts'],
  include: ['src/**/*.ts'],
  reporter: ['text-summary'],
  require: ['tsconfig-paths/register', 'ts-node/register'],
};

Thank you to janaskello for tsconfig-paths a slight annoyance I have is that it seems like tsconfig-paths needs to be installed in projects themselves for ts-node -r tsconfig-paths/register to work - which is a bit of a pain for third party libraries (my use case of understanding someone else code is perhaps unusual).

I'd also like to say that on newer versions of node you get a MODULE_NOT_FOUND error (other page about this suggest deleting and reinstalling things for this). It's odd how ts-node in one says can "find" the library (because if a file does not exist you get a different error) but then fails to import.

I would vote for ts-node refusing to run if paths is set given that this error is a little cryptic (I didn't find this page until I guessed that the problem might be because ts-node is ignoring paths and googled this - working in someone elses repo).

I was running on the same issue when running webpack with ts-node (i'm using Typescript language webpack configuration) but already followed the webpack documentation and installed tsconfig-paths.

Just putting "moduleResolution": "node" into my tsconfig.webpack.json resolved the issue.

Hope this help

@jonaskello You saved my ass here!

Is there any reason why this shouldn't become a default built in part of ts-node? It seems like it would make more sense to include this by default, than to not.

Apparently I've been through this before... lol.

This really probably should become default behavior.

Okay, so I found a solution that works reasonably well for my use-case. I needed this just for Knex's knexfile.ts and couldn't find a solution

Constraints:

  • Every "path" in paths I have has only 1 item in the array
  • I am able to use module-alias as a dev dependency
  • I have only 1 file that _requires_ this fix, however putting this in a fix-paths.ts file works if it is is imported first
  • I am able to use "resolveJsonModule": true

Code

import moduleAlias from 'module-alias';
import * as path from 'path';
import { compilerOptions } from './tsconfig.json';

const root = path.join(__dirname, compilerOptions.baseUrl || '');

for (const [key, paths] of Object.entries(compilerOptions.paths)) {
  const target = path.join(root, paths[0]);
  moduleAlias.addAlias(key, target);
}

Was this page helpful?
0 / 5 - 0 ratings

Related issues

conordickinson picture conordickinson  Â·  4Comments

aj-r picture aj-r  Â·  3Comments

remojansen picture remojansen  Â·  4Comments

KiaraGrouwstra picture KiaraGrouwstra  Â·  3Comments

motss picture motss  Â·  4Comments