Reputation: 203
Here's what a real simple monorepo with nestjs using turborepo looks like:
.
├── README.md
├── apps
│ └── nest
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── src
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts <---- importing class here
│ │ └── main.ts
│ ├── test
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── package.json
├── packages
│ └── lib
│ ├── index.ts <------- exporting class here
│ ├── package.json
│ └── tsconfig.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── turbo.json
A class is defined in packages/lib/index.ts
and is then imported in apps/nest/src/app.service.ts
.
But doing so leads to the following error when trying to import this index.ts
:
nest:dev:
nest:dev: [1:40:25 AM] Found 0 errors. Watching for file changes.
nest:dev:
nest:dev: /Users/hercule/Workspace/monorepo-nestjs-package/packages/lib/index.ts:3
nest:dev: public hello() {
nest:dev: ^^^^^
nest:dev:
nest:dev: SyntaxError: Unexpected identifier
nest:dev: at Object.compileFunction (node:vm:360:18)
nest:dev: at wrapSafe (node:internal/modules/cjs/loader:1088:15)
nest:dev: at Module._compile (node:internal/modules/cjs/loader:1123:27)
nest:dev: at Object.Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
nest:dev: at Module.load (node:internal/modules/cjs/loader:1037:32)
nest:dev: at Function.Module._load (node:internal/modules/cjs/loader:878:12)
nest:dev: at Module.require (node:internal/modules/cjs/loader:1061:19)
nest:dev: at require (node:internal/modules/cjs/helpers:103:18)
nest:dev: at Object.<anonymous> (/Users/hercule/Workspace/monorepo-nestjs-package/apps/nest/src/app.service.ts:2:1)
nest:dev: at Module._compile (node:internal/modules/cjs/loader:1159:14)
Therefore, How do we import a typescript package into nestjs that will be compiled / parsed properly?
Note 1: I tried importing this lib (index.ts
) to a nextjs app and angular app, it works without any issue. The problem only arises with nestjs
Note 2: Above's example can be reproduced using following repository: https://github.com/beneccli/monorepo-nestjs-package, once cloned, at root of the project, simply run pnpm i
and then pnpm dev
(or npm or yarn).
Upvotes: 8
Views: 10578
Reputation: 135
So, Nextjs and Angular uses bundlers configured to understand typescript source code from packages in node_modules
directory (this is where your internal packages are symlinked and resolved to by node) While NestJS, by default, uses tsc
for compilation and tsc
doesn't expect typescript source code from packages under node_modules
directory.
You can enable this behavior in NestJS apps by configuring it to use webpack
instead of the default tsc
for compilation with a little bit of setup - I would not recommend this, instead, pre-compile your internal packages to javascript before it’s imported into the NestJS application (this strategy is known as “compiled packages”. Learn more here https://turbo.build/repo/docs/core-concepts/internal-packages#compilation-strategies)
Next.js and Angular apps (though I'm not certain about Angular) use just-in-time packages that export typescript source code because their respective bundlers can handle it. NestJS's default compiler, tsc
, isn't set up for that.
You can resolve this by either pre-compiling your internal packages or switching to a Webpack-based setup in your NestJS app package.
I prefer this method because it speeds up your build time significantly in a large codebase, as your package's build step will hit cache if its source code is unchanged.
package.json
Add build
and start:dev
scripts to your internal package's package.json
:
// packages/helloworld/package.json
{
"name": "@packages/helloworld",
"scripts": {
"build": "tsc --build",
"start:dev": "tsc --watch"
},
"exports": {
"./*": {
"types": "./src/*.ts",
"default": "./dist/*.js"
}
}
}
tsconfig.json
to your internal package// packages/helloworld/tsconfig.json
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"module": "CommonJS",
"target": "ES2020"
}
}
// apps/nestjs/src/app.module.ts
import { MyInternalPackage } from "@packages/helloworld/dir-under-src-dir";
Alternatively, you can configure your NestJS app to use Webpack with swc-loader
, which allows TypeScript code to be transpiled on the fly, similar to the Next.js and Angular apps.
Learn more about using webpack in NestJS here (https://docs.nestjs.com/recipes/swc#monorepo).
package.json
for the new webpack config package:// packages/webpack-config/package.json
{
"name": "@packages/webpack-config",
"files": [
"webpack.config.js"
],
"peerDependencies": {
"@nestjs/cli": "^10.1.8"
}
}
npm i -D swc-loader —workspace @packages/webpack-config
// packages/webpack-config/webpack.config.js
const swcDefaultConfig = require("@nestjs/cli/lib/compiler/defaults/swc-defaults").swcDefaultsFactory()
.swcOptions;
module.exports = {
node: {
// required for __dirname to properly resolve
// Also required for `bull` to work, see https://github.com/OptimalBits/bull/issues/811
__dirname: true,
},
module: {
rules: [
{
test: /\.ts$/,
exclude: (_) =>
/node_modules/.test(_) && !/node_modules\/(@package)/.test(_),
use: {
loader: "swc-loader",
options: swcDefaultConfig,
},
},
],
},
};
Update your NestJS application package's nest-cli.json
:
// apps/nestjs/nest-cli.json
{
"compilerOptions": {
"builder": "webpack"
}
}
webpack.config.js
in your NestJs application package and extend the internal webpack package:// apps/nestjs/webpack.config.js
module.exports = require("@packages/webpack-config/webpack.config");
Upvotes: 2
Reputation: 51
I clone a repo and see the following things in your packages/lib/package.json :
Final lib's package.json is here
{
"name": "lib",
"version": "0.0.0",
"main": "./dist/index.js",
"scripts": {
"build": "tsc"
},
"types": "./index.ts",
"devDependencies": {
"typescript": "^4.5.2"
}
}
After those are done, I run the script pnpm run build
in the root.
Then I run pnpm dev
, and your apps should start working on http://localhost:3000/
with the text Test: Hello World!
The thing behind that is you should have a build script to compile your Typescript code into JS, then have the main to point exactly the compiled index. So that when NestJs use your lib, it should have all the functions and classes in it.
Upvotes: 1
Reputation: 203
The solution is actually simple.
Issue was that the dependency was not transpiled from typescript to javascript.
To solve this it is required to tell how to build the dependency and where to find the result files. This is done using the package.json
of the lib
dependency:
// packages/lib/package.json
{
"name": "lib",
"version": "0.0.0",
"main": "./dist/index", <----- NEW
"types": "./dist/index", <----- NEW
"scripts": {
"build": "tsc --build --force tsconfig.json" <----- NEW
},
"devDependencies": {
"typescript": "^4.5.2"
}
}
I updated the previously given repository accordingly.
What is important here is to not forget to run pnpm build
after pnpm install
in order to get the proper built dependency. Then nestjs can be started in either dev or prod.
Upvotes: 0