简体   繁体   中英

Typescript: How to resolve absolute modules paths for node.js?

I need modules to be resolved basing on baseUrl so output code is usable for node.js

this is my src/server/index.ts

import express = require('express');
import {port, databaseUri} from 'server/config';

...

and this is my src/server/config/index.ts

export const databaseUri: string = process.env.DATABASE_URI || process.env.MONGODB_URI;
export const port: number = process.env.PORT || 1337;

Running tsc I'm able to compile all files without erros, but output: dist/server/index.js is

"use strict";
var express = require("express");
var config_1 = require("server/config");

...

Resulting with Cannot find module 'server/config' if I'm trying to use it with node dist/sever/index.js .

Why server/config path is not resolved in any way so it would be possible to use compiled code or how to make it resolve it. Or what am I doing or thinking wrong way?

My tsc --version is 2.1.4

This is my tsconfig.json :

{
  "compileOnSave": true,
  "compilerOptions": {
      "baseUrl": "./src",
      "rootDir": "./src",
      "module": "commonjs",
      "target": "es5",
      "typeRoots": ["./src/types", ".node_modules/@types"],
      "outDir": "./dist"
  },
  "include": [
      "src/**/*"
  ],
  "exclude": [
      "node_modules",
      "**/*.spec.ts"
  ]
}

Note I don't want to use ../../../../relative paths.

This post on Microsoft's typescript github explains their module resolution process. In the comments they explain that what you're trying to do can't be done.

this feature, along with the rest of the module resolution capabilities, are only to help the compiler find the module source given a module name. no changes to the output js code. if you require "folder2/file1" it will always be emitted this way. you might get errors if the compiler could not find a folder2/file1.ts, but no change to the output. https://github.com/Microsoft/TypeScript/issues/5039#issuecomment-206451221

and

The compiler does not rewrite module names. module names are considered resource identifiers, and are mapped to the output as they appear in the source https://github.com/Microsoft/TypeScript/issues/5039#issuecomment-232470330

So, the emitted JS from typescript does NOT rewrite the module path for the discovered modules you give to require . If you run your app in node after compilation (which it looks like you are with express), then it will use the node module system to resolve the module references after typescript compilation. This means that it will only respect relative paths in your module and then it will fall back to node_modules to find dependencies.

that is how it is meant to work. the compiler needs the paths to find the declaration of your module. module names are resource identifiers and should be emitted as is and not altered. https://github.com/Microsoft/TypeScript/issues/5039#issuecomment-255870508

You have basically confirmed this for yourself in the emitted output in your question.

One way to do this is to publish your server "module" in node_modules .

On Unixen (Linux, MacOS, Solaris etc.) you can simply symlink your server folder to node_modules :

ln -s /path/to/your/project/dist/server /path/to/your/project/node_modules

This basically makes your code a library. Node will use the usual node_modules resolution when requiring and importing files.

I'm not sure how to do this on Windows because I don't use Windows.

Alternatively you can just copy the generated code to node_modules after compiling with typescript. This would work with any OS:

# Unix shell example:
cp -r dist/server node_modules

With TypeScript, this is not possible directly.

However, if you install ttypescript (with a double t, that's not a typo) and @zerollup/ts-transform-paths , you can.

ttypescript is invoked by ttsc instead of tsc .

Then, add the ts-transform-paths plugin to your tsconfig by adding it to the compiler options:

"plugins": [
      {
        "transform": "@zerollup/ts-transform-paths",
        "exclude": ["*"]
      }
    ]

Your final tsconfig would look like this:

{
  "compileOnSave": true,
  "compilerOptions": {
      "baseUrl": "./src",
      "rootDir": "./src",
      "module": "commonjs",
      "target": "es5",
      "typeRoots": ["./src/types", ".node_modules/@types"],
      "outDir": "./dist",
      "plugins": [
        {
          "transform": "@zerollup/ts-transform-paths",
          "exclude": ["*"]
        }
      ]
  },
  "include": [
      "src/**/*"
  ],
  "exclude": [
      "node_modules",
      "**/*.spec.ts"
  ]
}

Edit: This does not work for TypeScript 4.5+.

A workaround can be found here .

I've researched the topic as well and seems that as of today there is still no out of the box solution :(

Though I've managed to achieve more or less the desired result using module-alias npm package - see this blog post for details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM