Cannot find modules when deploying express + typescript to heroku

Viewed 1498

First, My current setup works locally. I've been stuck on this problem for days.

Second, I'll describe it by sections

  • directory structure
  • typescript setup (tsconfig + path mappings)
  • setup related to module-alias package + package.json
  • step how i deploy to heroku
  • the error log by heroku

Directory structure

├── client/
│   ├── src
│   └── package.json
├── server/
│   ├── tsconfig.json
│   └── package.json
└── shared/
    ├── helpers/validators.ts
    ├── constants/tags.ts
    └── models/bookmark.ts

tsconfig setup

Note: I'm using path mappings/alias set in tsconfig.json. I installed the package module-alias for the compiled files to be able to find the path mappings.

On my tsconfig, i'm importing a file that is outside the typescript project where tsconfig.json is located

As you can see on the directory structure, the tsconfig.json is under server folder and i'm importing files from the shared folder

So here's my tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@models/*": ["../shared/models/*"],
      "@constants/*": ["../shared/constants/*"],
      "@helpers/*": ["../shared/helpers/*"]
    },
    "target": "es2018",
    "module": "commonjs",
    "moduleResolution": "node",
    "rootDir": "../",
    "outDir": "./build",
    "esModuleInterop": true,
    "strict": true,
    "typeRoots": ["node_modules/@types"],
    "sourceMap": false,
    "removeComments": true
  },
  "include": ["**/*", "../shared/**/*"]
}

package.json + setup related to module alias package

Note: the path for _moduleAliases is based on the path of the compiled javascript file

{
  "name": "pocketlite-server",
  "version": "1.0.0",
  "engines": {
    "node": "14.x",
    "npm": "6.x"
  },
  "description": "",
  "main": "index.js",
  "_moduleAliases": {
    "@models": "build/shared/models",
    "@constants": "build/shared/constants",
    "@helpers": "build/shared/helpers"
  },
  "scripts": {
    "start:prod": "node build/server/index.js",
    "start:staging": "export NODE_ENV=staging && nodemon -r tsconfig-paths/register index.ts",
    "start:dev": "export NODE_ENV=development && nodemon -r tsconfig-paths/register index.ts",
    "build": "tsc -p .",
    "heroku-prebuild": "npm i --only=dev",
    "lint": "eslint . --ext .ts",
    "csrf": "npx http-server -c-1 -d false -o /csrf-test"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/cookie-parser": "^1.4.2",
    "@types/cors": "^2.8.7",
    "@types/express": "^4.17.7",
    "@types/jsdom": "^16.2.3",
    "@types/mongoose": "^5.7.36",
    "@types/mongoose-delete": "^0.5.1",
    "@types/mozilla-readability": "^0.2.0",
    "@types/node": "^14.0.27",
    "@types/passport": "^1.0.4",
    "@types/passport-google-oauth20": "^2.0.3",
    "@types/passport-jwt": "^3.0.3",
    "@types/puppeteer": "^3.0.1",
    "@types/turndown": "^5.0.0",
    "@typescript-eslint/eslint-plugin": "^3.8.0",
    "@typescript-eslint/parser": "^3.8.0",
    "dotenv": "^8.2.0",
    "eslint": "^7.6.0",
    "nodemon": "^2.0.4",
    "ts-node": "^8.10.2",
    "tsconfig-paths": "^3.9.0"
  },
  "dependencies": {
    "@mozilla/readability": "^0.3.0",
    "@types/cookie-session": "^2.0.41",
    "@types/csurf": "^1.9.36",
    "cookie-parser": "^1.4.5",
    "cookie-session": "^1.4.0",
    "cors": "^2.8.5",
    "csurf": "^1.11.0",
    "express": "^4.17.1",
    "isomorphic-dompurify": "^0.6.0",
    "jsdom": "^16.4.0",
    "jsonwebtoken": "^8.5.1",
    "module-alias": "^2.2.2",
    "mongoose": "^5.9.28",
    "mongoose-delete": "^0.5.2",
    "passport": "^0.4.1",
    "passport-google-oauth20": "^2.0.0",
    "passport-jwt": "^4.0.0",
    "puppeteer": "^5.2.1",
    "puppeteer-extra": "^3.1.15",
    "puppeteer-extra-plugin-adblocker": "^2.11.6",
    "puppeteer-extra-plugin-stealth": "^2.6.1",
    "turndown": "^6.0.0",
    "turndown-plugin-gfm": "^1.0.2",
    "typescript": "^4.0.3"
  }
}

Steps how I deploy in hero

  1. heroku create pocketlite-server
  2. heroku buildpacks:set heroku/nodejs
  3. heroku git:remote -a pocketlite-server
  4. git subtree push --prefix server heroku master

The error log in heroku

relevent npm scripts for heroku

  "scripts": {
    "start:prod": "node build/server/index.js",
    "start:staging": "export NODE_ENV=staging && nodemon -r tsconfig-paths/register index.ts",
    "start:dev": "export NODE_ENV=development && nodemon -r tsconfig-paths/register index.ts",
    "build": "tsc -p .",
    "heroku-prebuild": "npm i --only=dev"
  }

Error log

-----> Node.js app detected
       
-----> Creating runtime environment
       
       NPM_CONFIG_LOGLEVEL=error
       NPM_CONFIG_PRODUCTION=false
       NODE_ENV=production
       NODE_MODULES_CACHE=true
       NODE_VERBOSE=false
       
-----> Installing binaries
       engines.node (package.json):  14.x
       engines.npm (package.json):   6.x
       
       Resolving node version 14.x...
       Downloading and installing node 14.13.0...
       Bootstrapping npm 6.x (replacing 6.14.8)...
       npm 6.x installed
       
-----> Prebuild
       Running heroku-prebuild
       
       > pocketlite-server@1.0.0 heroku-prebuild /tmp/build_8e06db38
       > npm i --only=dev
       
       
       > nodemon@2.0.4 postinstall /tmp/build_8e06db38/node_modules/nodemon
       > node bin/postinstall || exit 0
       
       Love nodemon? You can now support the project via the open collective:
        > https://opencollective.com/nodemon/donate
       
       added 194 packages from 194 contributors and audited 665 packages in 7.23s
       
       19 packages are looking for funding
         run `npm fund` for details
       
       found 2 high severity vulnerabilities
         run `npm audit fix` to fix them, or `npm audit` for details
       
-----> Installing dependencies
       Installing node modules
       
       > puppeteer@5.2.1 install /tmp/build_8e06db38/node_modules/puppeteer
       > node install.js
       
       
       
       > nodemon@2.0.4 postinstall /tmp/build_8e06db38/node_modules/nodemon
       > node bin/postinstall || exit 0
       
       Love nodemon? You can now support the project via the open collective:
        > https://opencollective.com/nodemon/donate
       
       added 661 packages in 19.572s
       
-----> Build
       Running build
       
       > pocketlite-server@1.0.0 build /tmp/build_8e06db38
       > tsc -p .
       
       helpers/link-scraper.ts(12,26): error TS2307: Cannot find module '@models/bookmark.model' or its corresponding type declarations.
       helpers/validators.ts(1,23): error TS2307: Cannot find module '@constants/patterns' or its corresponding type declarations.
       models/bookmark-favorite.ts(2,34): error TS2307: Cannot find module '@models/bookmark-favorite.model' or its corresponding type declarations.
       models/bookmark.ts(7,26): error TS2307: Cannot find module '@models/bookmark.model' or its corresponding type declarations.
       models/tag.ts(2,21): error TS2307: Cannot find module '@models/tag.model' or its corresponding type declarations.
       models/user.ts(2,22): error TS2307: Cannot find module '@models/user.model' or its corresponding type declarations.
       routes/bookmarks.ts(4,32): error TS2307: Cannot find module '@constants/tags' or its corresponding type declarations.
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! pocketlite-server@1.0.0 build: `tsc -p .`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the pocketlite-server@1.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR!     /tmp/npmcache.tNTfH/_logs/2020-10-04T01_44_27_692Z-debug.log
-----> Build failed
       
       We're sorry this build is failing! You can troubleshoot common issues here:
       https://devcenter.heroku.com/articles/troubleshooting-node-deploys
       
       Some possible problems:
       
       - A module may be missing from 'dependencies' in package.json
         https://devcenter.heroku.com/articles/troubleshooting-node-deploys#ensure-you-aren-t-relying-on-untracked-dependencies
       
       Love,
       Heroku
       
 !     Push rejected, failed to compile Node.js app.
 !     Push failed

Update:

  • I have also tried to dockerize my app, and the same error still occurs.
  • I've discovered that it is related to typescript not resolving path mappings to their corresponding paths without a module bundler that rewrites path like webpack (w/c i don't use currently). Here's the relevant discussion in typescript's github issue
0 Answers
Related