node.js - Menggunakan Skema Prisma dalam monorepo PNPM dengan NestJS menyebabkan isu ESM


Saya telah mencipta monorepo PNPM yang mempunyai dua aplikasi NestJS (apps/api dan apps/backend; kedua-duanya adalah sama) dan satu pakej pangkalan data Prisma (packages/database). Apabila saya menjalankan api dengan start:dev, saya mendapat ralat berikut:

(node:69498) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/username/Documents/Slug/slug-monorepo/packages/database/src/index.ts:1

export * from "@prisma/client";
^^^^^^

SyntaxError: Unexpected token 'export'
    at wrapSafe (node:internal/modules/cjs/loader:1389:18)
    at Module._compile (node:internal/modules/cjs/loader:1425:20)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1564:10)
    at Module.load (node:internal/modules/cjs/loader:1287:32)
    at Function.Module._load (node:internal/modules/cjs/loader:1103:12)
    at Module.require (node:internal/modules/cjs/loader:1310:19)
    at require (node:internal/modules/helpers:179:18)
    at Object.<anonymous> (/Users/username/Documents/Slug/slug-monorepo/apps/api/src/prisma/prisma.service.ts:2:1)
    at Module._compile (node:internal/modules/cjs/loader:1480:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1564:10)

Bagaimana saya boleh menggunakan Prisma dengan NestJS dalam monorepo PNPM?

./pnpm-workspace.yaml

packages:
  - 'apps/*'
  - 'packages/*'

apps/api/package.json

{
  "name": "@slug-monorepo/api",
  "version": "0.0.1",
  "description": "",
  "author": "",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/platform-express": "^10.0.0",
    "@slug-monorepo/database": "workspace:*",
    "reflect-metadata": "^0.2.0",
    "rxjs": "^7.8.1"
  },
  "devDependencies": {
    ...
  },
  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "rootDir": "src",
    "testRegex": ".*\\.spec\\.ts$",
    "transform": {
      "^.+\\.(t|j)s$": "ts-jest"
    },
    "collectCoverageFrom": [
      "**/*.(t|j)s"
    ],
    "coverageDirectory": "../coverage",
    "testEnvironment": "node"
  }
}

apps/api/tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ESNext",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "strictBindCallApply": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true
  }
}

apps/api/src/prisma/prisma.service.ts

import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@slug/database';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }
}

apps/api/src/slug/dto/createSlug.dto.ts

import {
  SlugType
} from '@slug/database';

export class CreateSlugDto {
  @IsOptional()
  @IsEnum(SlugType)
  slug?: SlugType;
}

pakej/pangkalan_data/pakej.json

{
  "name": "@slug-monorepo/database",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.ts",
  "scripts": {
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.14.8",
    "prisma": "^5.16.0",
    "typescript": "^5.1.3"
  },
  "dependencies": {
    "@prisma/client": "^5.16.0"
  }
}

pakej/pangkalan_data/tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "dist"
  },
  "include": ["./**/*.ts"],
  "exclude": ["dist", "node_modules"]
}

pakej/pangkalan_data/src/index.ts

export * from "@prisma/client";

Jawapan #1

Saya tidak tahu tentang hal TypeScript, jadi pengalaman anda mungkin berbeza, tetapi melihat pada fail package.json itu, saya rasa saya nampak beberapa perkara. Pertama, apps/api/package.json anda tidak mempunyai main mahupun type. Kedua, dalam packages/database/package.json anda, saya rasa anda tidak boleh menjadikan fail TypeScript sebagai titik masuk utama — saya rasa anda perlu menunjuk kepada versi yang telah ditranspilasi yang akan keluar dalam dist.

Saya mendapati artikel ringkas ini sangat berguna. Berikut adalah ramalan terbaik saya tentang apa yang anda perlukan:

 // File: apps/api/package.json
   "license": "UNLICENSED",
+  "type": "module",
+  "main": "dist/apps/api/index.js" // or wherever your transpile is
   "scripts": {
// File: packages/database/package.json
   "description": "",
-  "main": "src/index.ts",  // delete this
+  "exports": {
+    "types": "./dist/types.d.ts",
+    "require": "./dist/index.js",
+    "import": "./dist/index.mjs"
+   }, 
   "scripts": {

Semoga itu membantu, atau sekurang-kurangnya menunjukkan arah yang betul kepada anda.