5

I've set up a monorepo using yarn workspaces for a typescript Node.JS project. Building the project works fine, however, I'm running into issues during local development.

I need to manually run yarn build before running yarn dev. Otherwise I'm receiving the following error:

Error: Cannot find module '/Users/benedikt/code/monorepo-build/node_modules/@bhirmer/utils/dist/index.js'. Please verify that the package.json has a valid "main" entry

Here's the sample repo that reproduces the issue.

Alternatively, the important files.

Project Structure

packages/
  utils
services/
  api

Root package.json

{
  "name": "@bhirmer/monorepo",
  "description": "Workspace",
  "private": true,
  "workspaces": [
    "packages/*",
    "services/*"
  ],
  "scripts": {},
  "devDependencies": {
    "eslint": "~7.14.0",
    "typescript": "^4.0.5"
  }
}

Root tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "ES2015",
    "lib": ["ES2019", "dom"],
    "types": ["node"],
    "esModuleInterop": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "noEmitOnError": true,
    "allowUmdGlobalAccess": true,
    "allowJs": false,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": false,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noImplicitThis": true,
    "strictNullChecks": true,
    "noImplicitReturns": true,
    "preserveConstEnums": true,
    "suppressImplicitAnyIndexErrors": true,
    "composite": true,
    "baseUrl": "../..",
    "paths": {
      "@bhirmer/*": ["packages/*/src"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Utils package.json

{
  "name": "@bhirmer/utils",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
    "files": [
        "dist"
    ],
  "scripts": {
    "build": "yarn run clean && yarn run compile",
    "clean": "rimraf ./dist && rimraf ./tsconfig.buildinfo",
    "compile": "tsc --build",
    "test": "yarn run build"
  },
  "dependencies": {
    "@types/node": "^14.14.12"
  },
  "devDependencies": {},
  "sideEffects": false
}

Utils tsconfig.json

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "baseUrl": "../../",
    "rootDir": "./src",
    "outDir": "./dist",
    "composite": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

API package.json

{
  "name": "@bhirmer/api",
  "description": "api",
  "author": "Benedikt Hirmer <[email protected]",
  "license": "UNLICENSED",
  "private": true,
  "version": "1.0.0",
  "main": "server/index",
  "scripts": {
    "dev": "PROJECT_ID=test-dev PORT=3010 nodemon src/server/index.ts",
    "clean": "rimraf ./dist",
    "compile": "NODE_ENV=production tsc --build",
    "build": "yarn run compile",
    "start": "PROJECT_ID=test-prod NODE_ENV=production node dist/server/index.js",
  },
  "dependencies": {
    "@bhirmer/utils": "^1.0.0",
    "@types/compression": "^1.7.0",
    "@types/express": "^4.17.11",
    "@types/node": "^14.14.12",
    "compression": "^1.7.4",
    "express": "^4.17.1",
    "typescript": "^4.2.4"
  },
  "devDependencies": {
    "nodemon": "^2.0.7",
    "ts-node": "^9.1.1"
  }
}

API tsconfig.json

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "dist",
    "baseUrl": "../../",
    // "rootDir": "src", // Getting `TypeError: src/server/index.ts: Emit skipped` if not commented out
    "composite": true
  },
  "include": ["src/**/*"],
  "types": ["typePatches"],
  "references": [{ "path": "../../packages/utils" }]
}

Sample usage of packages/utils in services/api

import { getProjectID } from '@bhirmer/utils';

...

const projectID = getProjectID();

...

Another point to note is that once I set "rootDir": "src" in services/api/tsconfig.json, then I'm hitting the following error:

TypeError: src/server/index.ts: Emit skipped if not commented out

4
  • did you end up solving? Commented Dec 3, 2022 at 19:33
  • My workaround has been to transpile the TS to JS and then debug/run nodemon from the transpiled JS. Commented Dec 4, 2022 at 21:22
  • Why is that a "workaround"? Sounds like a solution as ts will be transpiled to run. Can you please share the solution? Commented Dec 5, 2022 at 9:28
  • 1
    Since posting we've added rollup to the build process as well. To run the nodejs project locally, we run the following script: tsc --build && rollup -c && nodemon --inspect --watch ./ --watch ../../packages dist/services/api/dist/tsc/server/index.js. Rollup is used to bring in all the other local dependencies from the workspace. I still see this as a workaround, because changes in the TS files don't result in immediate updates in the code. When debugging you either have to restart the script or change the transpiled JS directly. Commented Dec 7, 2022 at 17:57

2 Answers 2

1

NPM workspaces still need you to run npm install inside the root directory (where the package.json with workspaces definition/location is), in order to make relevant symlinks to local packages.

When you do that, you should end up with:

node_modules/
    @bhirmer/
        utils/ <-- symlink to /packages/utils/
        api/ <-- symlink to /services/api/
packages/
    utils/
        ...
services/
    api/
        ...
package.json <-- root package.json with "workspaces"

You'll have to re-run it every time you make a new local package folder with package.json inside, that you want to access from elsewhere.

Sign up to request clarification or add additional context in comments.

3 Comments

I'm using Yarn workspaces. The symlinks are set correctly.
Nodemon watches for file changes and rebuilds when a change is detected, but does not trigger build automatically when you start it. Consider something like this: "build": "NODE_ENV=production tsc --build", "dev": "PROJECT_ID=test-dev PORT=3010 nodemon -e ts,js . --exec \"yarn build && yarn start\"", "start": "node dist/index.js"
Yep, that's been my workaround so far. It just means that we have to use the js files for debugging.
0

Recently had this issue, this was the fix for us:

  "ts-node": {
    "require": ["tsconfig-paths/register"]
  },

For ts-node to see other monorepo packages it needed this entry

2 Comments

Where did you add this Xolv.io?
top level of tsconfig.json

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.