Chris Schmitz
Chris Schmitz

Reputation: 20940

Typescript build "Cannot find module", but only on a ubuntu server

I'm working on a node script and writing it in typescript. I've had no problems with running builds on my laptop (mac) or from my raspberry pi running raspbian.

I've gotten to the point where I want to set the codebase up on a digital ocean server that's running ubuntu. I moved the codebase up and ran my build script. The build completes successfully, but when I launch the node process I get the error:

node dist/server/source-server/index.js
internal/modules/cjs/loader.js:883
  throw err;
  ^

Error: Cannot find module 'express'
Require stack:
- /var/www/binary-operations-presentation/multi-player-server/dist/server/source-server/index.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
    at Function.Module._load (internal/modules/cjs/loader.js:725:27)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (/var/www/binary-operations-presentation/multi-player-server/dist/server/source-server/index.js:6:35)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/var/www/binary-operations-presentation/multi-player-server/dist/server/source-server/index.js'
  ]
}

It's weird. I've run this codebase on multiple servers and none have had this issue. I did some searching around, but all of the answers I've found are saying "run npm install express" or "run npm install @types/express", both of which I have:

dependencies

Plus, if I didn't have them installed, the typescript compiler build would have failed.

I compared the file structure on the ubuntu server to my raspberry pi server and aside from the files that are generated when the codebase actually runs, the directories are the same:

enter image description here

Both servers have the same tsconfig.json file:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "../dist/server",
    "rootDirs": ["./", "../project-common/"],
    "strict": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true
  }
}

And both have the same dependency versions:

// from package.json
{
  ...
  "devDependencies": {
    "@types/uuid": "^8.3.0",
    "@types/ws": "^7.4.0",
    "killport": "^1.0.1",
    "nodemon": "^2.0.7",
    "tsc-watch": "^4.2.9",
    "tslint": "^6.1.3",
    "typescript": "^4.1.3"
  },
  "dependencies": {
    "@types/express": "^4.17.11",
    "@types/lodash.clonedeep": "^4.5.6",
    "@types/websocket": "^1.0.1",
    "express": "^4.17.1",
    "lodash.clonedeep": "^4.5.0",
    "uuid": "^8.3.2",
    "ws": "^7.4.2"
  }
}

Neither of the dist folders have the node_modules folder, and when I look at the transpiled index.js file for the server they both import express the same way:

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const http_1 = __importDefault(require("http"));

and both of the index.js.map files refer to the same relative source directory:

{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../source-server/index.ts"]

So I'm not sure why one server would be able to refer back to the node_modules folder but the other one wouldn't.

The only big difference (aside from the OS version, the server is running Ubuntu 20.04) that I see is that my raspberry pi is running node 15.x and the ubuntu is running node 14.x, though I don't know that that would necessarily cause this problem.

Any ideas?


UPDATE: same version of node

I tried making the version of node the same. I installed nvm on my raspberry pi, pulled in 14.6.0 (same version as the digital ocean server), rebuilt the project, and launched the server -> works fine.

enter image description here

so the node version doesn't seem to be a factor :/

Upvotes: 0

Views: 1235

Answers (1)

Chris Schmitz
Chris Schmitz

Reputation: 20940

So here's the snag and solution:

The gist is that the compiled version of the index file is firing a regular node require() function to pull in modules:

enter image description here

Which means it goes through a number of steps to find the modules. When it comes to possible node_modules modules, it looks for the node_modules folder in the current directory. If node doesn't find it, node will step up to the parent directory and look there, continuing to step back to the root directory if node_modules directories aren't found.

In my case, I previously had all of the server and client code at the same directory level (the directory where the new subfolders now live). When I separated the code into the subfolders, I forgot to blow away the old node_modules folder locally, which is why the dist folder still found the node_modules folder that now resided at the parent directory of dist. When I did a git pull on my raspberry pi, the folders updated, but because node_modules is part of my .gitignore, that folder at the parent level wasn't blown away.

enter image description here

But, in newer clones of the repository of course wouldn't have node_modules at that parent directory, or any other parent directory so they bombed out.

While talking with @cefn I realized that I did want to have the dist be a "everything needed to run this project" directory, so I added a step to my npm build script:

"scripts": {
    "build": "tsc --build tsconfig.json && npm run build:move-indexes && npm run build:move-node_modules",
    "build:move-indexes": "rsync -avz indexes ../dist/server/source-server",
    "build:move-node_modules": "rsync -avz node_modules ../dist/server/source-server",

This way, every time I rebuild the source code, the server will sync the node_modules in the dist directory with the node_modules in my source directory.

Tried it out, and it works :)

Upvotes: 1

Related Questions