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

3

21

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.

Jeminah answered 15/12, 2016 at 19:30 Comment(2)
My problem resolved when i removed outDir from tsconfig.jsonIchang
but than you have cluttered src folder polluted with generated .js, .map .d.ts, instead of a common folder for all the generated files.Cornel
C
12

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.

Crosslink answered 13/4, 2017 at 18:57 Comment(1)
Although you are correct, you don't really answer the question. "module names are resource identifiers and should be emitted as is and not altered." Relative paths lead to situation where we can have unlimited number of variations of a resource identifier. Absolute paths, on the other hand, are unique identifiers, and should be the preferred way in my opinion. It seems that the only way to achieve custom (i.e. outside node_modules) absolute paths is by adding another step to the build, e.g. Webpack.Kwasi
E
2

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
Empanel answered 27/5, 2022 at 1:16 Comment(0)
S
2

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.

Spann answered 30/5, 2022 at 14:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.