4

Node error Error: Cannot find module 'hello' even though Typescript compiled successfully.

Dir structure (note this is almost identical to tsconfig docs for baseUrl)

main
| src
| ├─ index.ts
| └─ hello
|      └── index.ts
└── tsconfig.json
└── package.json

I want to only use non-relative imports (so no /, ./, ../). I've encountered this problem in the past and have always resorted to using relative imports, but I do not want to continue doing this.

In the top level index.ts there is import { helloWorld } from 'hello'.

tsc compiles successfully and the output dir dist looks like this:

main
├─ src
├─ tsconfig.json
├─ package.json
└─ dist
    ├─ index.js
    └─ hello
        └── index.js

Running node dist/index.js outputs the error Error: Cannot find module 'hello'. The import statement gets compiled in dist/index.js to require("hello") which is where the error is thrown.

From this answer, it seems like require() will only look at modules in the current directory if the path begins with ./. So when I change it to require("./hello") it works completely fine!

I've been playing around with baseUrl and paths in tsconfig.json but I cannot get it to output require("./hello"). Would really appreciate some help on this issue and to finally get to the bottom of it, thanks!

// tsconfig.json
{
  "compilerOptions": {
    // from extending off "@tsconfig/node16/tsconfig.json"
    "lib": ["es2021"],
    "module": "commonjs",
    "target": "es2021",

    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    // end of @tsconfig/node16/tsconfig.json

    "baseUrl": "src",
    "outDir": "dist"
  },
  "include": ["src"],
  "exclude": ["node_modules"]
}

user14629378
  • 61
  • 1
  • 6
  • 1
    if you `import { ... } from './hello'` it should compile to `const ... = require('./hello')`. Importing (either via `require` or `import` from `'hello'` will make node look for the module in `node_modules`. I *think* this is down to now node resolves modules and not a TS specific thing – apokryfos Apr 29 '22 at 08:29
  • Thanks @apokryfos, but I want to avoid using relative imports. I've seen and worked in projects that only use non-relative imports so this should be possible. – user14629378 Apr 29 '22 at 08:35
  • Paste the content of hello/index.ts – Rohit Choudhary Apr 29 '22 at 08:41
  • @RohitChoudhary It's just `export const helloWorld = "hello world"`. Node can't find the module so it has not loaded the contents of that file yet - the contents of the file should be irrelevant. – user14629378 Apr 29 '22 at 08:57
  • 1
    If you look at tsconfig.json, the `compilerOptions.paths` key, you'll see how you can map module names into your directory structure. – Dave Meehan Apr 29 '22 at 09:12
  • @DaveMeehan Following the docs for `compilerOptions.paths`, if I have an entry `"hello/*": ["hello/*"]`, with the `baseUrl: "src"` it still gives me the same error - tsc compiles, but node errors. I've tried many variants of it as well and still cannot get it to work. If possible, could you provide a working example? – user14629378 Apr 29 '22 at 18:49
  • Check the generated JS by typescript, you may also need to configure module resolution in node – Dave Meehan Apr 29 '22 at 21:32

1 Answers1

1

I found that this is topic has received a lot of attention in Typescript's Github issues. A few of many issues raised about this very problem:

From the TS Lead himself:

Our general take on this is that you should write the import path that works at runtime, and set your TS flags to satisfy the compiler's module resolution step, rather than writing the import that works out-of-the-box for TS and then trying to have some other step "fix" the paths to what works at runtime.

So it seems like we either use relative imports, or use some tool like tsc-alias.

user14629378
  • 61
  • 1
  • 6