gbro3n
gbro3n

Reputation: 6967

MODULE_NOT_FOUND trying to share code by directory between modules in Node JS / TypeScript projects

I have set up an example project to demonstrate the issue:

https://github.com/garethrbrown/node-share-code

In this example there are two projects, example-api (a mini express project) and example-shared (a class library), both using Node JS / TypeScript. I want example-api to be able to use classes from example-shared to avoid code duplication. Having followed this example, I have referenced the example-shared project from package.json in example-api.

"dependencies": {
    "example-shared": "file:..\\example-shared",
    "express": "^4.17.1"
}

Having done this, and following running npm install, intellisense in VSCode sees ApiClass from the example-shared project and assists with the import.

enter image description here

I can then run by build command tsc --build via NPM, which succeeds.

I can also see that the sym link has been created in the example-api node_modules directory.

enter image description here

However, when I try to run the example-api project using the npm start script from under example-api, I get an error along the lines of:

  Error: Cannot find module 'example-shared/apiClass'
  Require stack:

  ...

  code: 'MODULE_NOT_FOUND',
  requireStack: [
    ...
  ]

I have tried running commands from different locations such as described here, but with no luck so far.

I'm using current stable versions of Node (14+) and NPM (7+).

I don't want to share via NPM or git repositories as I feel it will slow down development.

What am I doing wrong?

Upvotes: 2

Views: 2241

Answers (2)

R-D
R-D

Reputation: 634

As per @mtbno's answer, the standard behavior without "outDir" is all js and map files will just be created next to each of your TypeScript files, which is super gross.

Without using some extra npm package or webpack or any of that, you can solve this by adding that "outDir" in your tsconfig, and then making a couple of tweaks to your package.json.

For argument's sake, say your root TypeScript file is called app.ts and your outDir folder is lib.

Relevant sections in your package.json can look like this:

  "scripts": {
    "build": "tsc --build",
    "clean": "tsc --build --clean",
    "start": "npm run build && node ./lib/app"
  },

and

"main": "./lib/app",

And here's an example tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "lib": ["es6"],
    "sourceMap": true,
    "outDir": "./lib",
  },
  "exclude": [
    "node_modules"
  ]
}

After those 2 files are updated, then running npm run start should build out your lib folder with the compiled JavaScript, and your server should start up successfully. Note that if you previously did a build and you have "old" .js and .map files next to your TypeScript files, you may have to delete those manually because npm run clean won't do it for you.

Upvotes: 0

mtbno
mtbno

Reputation: 600

Seems like this is a discussed problem, see this post Typescript: How to resolve absolute modules paths for node.js?

I did not investigate further, but in the example-shared folder you can remove "outDir" from your example-shared/tsconfig.json and then run npm run build. Unfortunately, this will emit the javascript files next to typescript files instead of placing them in a separate directory.

Finally, in the example-api run npm i, npm run build and npm start.

Now, Express will run because Node is using the javascript file instead of typescript file.

Upvotes: 1

Related Questions