NPM Workspaces Typescript unable to find local modules
Asked Answered
A

3

21

I have a NodeJS monorepo set up using NPM workspaces and Typescript. It works fine without Typescript, however introducing TS brings some dependency resolving errors with it. Current directory setup:

+-- node_modules
+-- package.json
+-- tsconfig.json
+-- tsconfig.build.json
+-- packages
    +-- core
    |   +-- package.json
    |   +-- tsconfig.json
    |   +-- src
        |   +-- index.ts
    +-- shared
    |   +-- package.json
    |   +-- tsconfig.json
    |   +-- src
        |   +-- helper.ts

Running npm ls confirms that everything has been symlinked up correctly:

[email protected] C:\Users\<user>\Documents\Temp\monorepoDemo
├─┬ @my-packages/[email protected] -> .\packages\core
│ └── @my-packages/[email protected] deduped -> .\packages\shared
└── @my-packages/[email protected] -> .\packages\shared

I've got a dummy helper function being exported from helper.ts in shared. Which is being imported into index.ts in core. The root package.json, tsconfig.ts and tsconfig.build.json:

{
    "name": "monorepoDemo",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "start": "node packages/core/src/index.ts",
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "tsc -b --verbose tsconfig.build.json"
    },
    "author": "",
    "license": "ISC",
    "workspaces": [
        "packages\\core",
        "packages\\shared"
    ]
}
{
    "compilerOptions": {
        "composite": true,
        "target": "es2021",
        "module": "commonjs",
        "declaration": true,
        "declarationMap": true,
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true
    }
}
{
    "files": [],
    "references": [
      {
          "path": "packages/core"
      },
      {
          "path": "packages/shared"
      }
    ]
}

The package.json and tsconfig.json of core:

{
  "name": "@my-packages/core",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@my-packages/shared": "^1.0.0"
  }
}
{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
      "rootDir": "./src",
      "outDir": "./dist",
      "baseUrl": ".",
      "paths": {
        "@my-packages/shared": ["../shared"]
        }
    },
    "references": [{ "path": "../shared" }],
    "exclude": ["dist"]
}

The package.json and tsconfig.json of shared:

{
  "name": "@my-packages/shared",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
      "rootDir": "./src",
      "outDir": "./dist"
    },
    "exclude": ["dist"]
}

And the source code of index.ts and helper.ts:

import { helper } from "@my-packages/shared"

(async () => {
    console.log("Bootstrapping from core package")

    helper()
})()
export function helper(): void {
    console.log("Running from shared package")
}

The error I get in index.ts: Cannot find module '@my-packages/shared' or its corresponding type declarations.ts(2307)

I assumed it was something related to a missing declaration file, so tried to create one for helper.ts but didn't get anywhere

Alizaalizarin answered 29/4, 2022 at 8:57 Comment(1)
I'm having a similar problem. I noticed in your package.json files you have index.js listed as your main entry point to both packages but your files are typescript. I'm pretty sure the main file is how imports are resolved for packages so that might be why? You could making your main file "./src/index.ts" insteadProvitamin
P
15

The main field of package.json file is the entry point when other packages (like core) imports the package:

import { helper } from "@my-packages/shared"

This code will try to look for @my-packages/shared/index.js, which does not exist.

When you build the shared package, the built file is at dist/helper.js.

Changing the main field to "main": "dist/helper.js" made it work. If you are using vscode, don't forget to restart the language server.

has error before change

no error after change

For other considerations, like specific compiler options or esm support, consult the document ECMAScript Modules in Node.js.

Punchdrunk answered 26/7, 2022 at 0:15 Comment(3)
This works with the single top level helper.[ts|js] file, but what about whole structured shared app or library? Is it necessary to introduce barrel files?Girlfriend
Nice name. I don't know what a "barrel file" is, but I got this working by creating an index.ts for my project at the root and doing multiple export * from './someFile.ts'; inside. It's incredibly annoying that all examples of doing this just have a single index.ts in the root of each project, so you have to guess the rest.Choreographer
In my case, the main path I had specified was relative using ./, removing the unneeded prefix ./ solved the issueBlynn
M
4

The project monorepoDemo should use the project @my-packages/core that is built, in your case in the (sharedDir)/dist directory.

In order for typescript to find the package @my-packages/core when parsing monorepoDemo typescript files, you have to specify where this package is located in the monorepoDemo's tsconfig.json file:

"compilerOptions": {
  "allowJs": true,
  "paths": {
    "@my-packages/shared": ["(sharedDir)/dist"],

After that make sure the library is built and the (sharedDir)/dist folder created.

If there are several projects that use @my-packages/shared, you can create a single tsconfig.libs.ts file that contains all libraries locations, and add an extends property into tsconfig.ts of projects to tsconfig.libs.ts:

{
  "extends": "../../tsconfig.lib.json",
Mallett answered 17/8, 2022 at 12:58 Comment(0)
Y
-2

You're missing a src at the ends of your paths in references and compilerOptioms.paths.

Yecies answered 29/4, 2022 at 12:9 Comment(1)
The path in references needs to resolve to a tsconfig.json file, which works. (if I ctrl click on it, it brings me to the shared tsconfig.json file. Tried adding src, src/*.ts etc but unfortunately nothing seemed to work. I was also restarting the TS server in between these changes to double checkAlizaalizarin

© 2022 - 2024 — McMap. All rights reserved.