node.js - Bruke Prisma-skjema i PNPM-monorepo med NestJS som forårsaker ESM-problemer


Jeg har laget et PNPM monorepo som har to NestJS-apper (apps/api og apps/backend; begge er helt like) og en Prisma databasepakke (packages/database). Når jeg kjører API-en med start:dev, får jeg følgende feil:

(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)

Hvordan kan jeg bruke Prisma med NestJS i en PNPM monorepo?

./pnpm-arbeidsområde.yaml

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

apper/api/pakke.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
  }
}

apper/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;
}

pakker/database/package.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"
  }
}

pakker/database/tsconfig.json

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

packages/database/src/index.ts

export * from "@prisma/client";

Svar #1

Jeg har ingen anelse om TypeScript-greiene, så YMMV, men når jeg ser på de package.json-filene, tror jeg jeg ser et par ting. For det første har ikke apps/api/package.json hverken en main eller en type. For det andre, i packages/database/package.json, tror jeg ikke du kan ha en TypeScript-fil som hovedinngangspunkt — jeg tror du må peke den mot den transpilte versjonen som ville komme ut i dist.

Jeg syntes denne raske artikkelen var veldig nyttig. Her er mitt beste gjetning på hva du trenger:

 // 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": {

Forhåpentligvis hjelper det, eller i det minste peker det deg i riktig retning.