簡體   English   中英

無法在 Nestjs 中導入 ESM 模塊

[英]Unable to import ESM module in Nestjs

我在基於 Nest.js 的項目中導入 ESM 模塊時遇到問題。 據我了解,這個問題不僅與 Nest.js 相關,還與 typescript 相關。

我嘗試了 Node.js 和 typescript 版本的各種組合,將"type":"module"添加到package.json並更改了我的tsconfig.json文件的設置,因此它具有以下視圖,與默認值相去甚遠:

{
  "compilerOptions": {
    "lib": ["ES2020"],
    "esModuleInterop": true,
    "module": "NodeNext",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "Node",
    "target": "esnext",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false,
  }
}

我的完整環境是:

  • Node.js (19.2.1 LTS) 通過 nvm 管理
  • Typescript(4.9.4,但我也試過4.3.5)
  • @nestjs/常見:9.2.1
  • @nestjs/核心:9.2.1
  • ts-加載器:“9.4.2”,
  • ts-節點:“10.9.1”,
  • tsconfig-路徑:“4.1.0”,

但是當我嘗試在我的任何服務中導入任何 ESM 模塊時,它仍然給我一個錯誤。 例如:

import random from `random`;

export class AppService implements OnApplicationBootstrap {
  async test() {
     const r = random.int(1, 5);
     console.log(r);
  }
}

有誰知道如何解決它?

這個問題似乎更頻繁地發生,因為更多的包正在切換為作為 ES 模塊分發。

概括

  • ES 模塊可以導入 CommonJS 模塊
  • CommonJS 模塊無法同步導入 ES 模塊
  • NestJS 當前不支持編譯為 ESM - 請參閱此PR

這個問題有兩種方法。

使用動態import()異步導入 package function

CommonJS 中 ES 模塊使用import()的說明隨處可見。 但是當使用 typescript 時,附加提示丟失了如何防止編譯器將import()調用轉換為require() 我為此找到了兩個選擇:

  • tsconfig.json (變體 1)中將nodenext moduleResolution node16
  • 使用eval解決方法 - 這基於答案https://stackoverflow.com/a/70546326/13839825 (變體 2 和 3)中的“解決方案 2:使用 eval 的解決方法”部分

變體 1:在需要時await import()

這個解決方案在 NestJS 官方Discord上也經常出現

  • "moduleResolution": "nodenext""moduleResolution": "node16"添加到您的tsconfig.json
  • 需要時使用await import()調用
  • 簡單明了
  • package沒有和其他進口一起列在最前面
  • 你不能從導入的 ES 模塊中導入/使用類型(至少到目前為止我發現不可能)
  • 僅適用於async上下文
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  async getHello(): Promise<string> {
    const random = (await import('random')).default;
    return 'Hello World! ' + random.int(1, 10);
  }
}

變體 2:助手 Function

注意:從 ES 模塊導入類型不會導致require調用,因為它僅由 typescript 編譯器而不是運行時環境使用。

  • 有點亂,因為 package 總是隱藏在額外的 function 調用后面
  • 僅適用於async上下文
  • 您可以導入/使用類型
import { Injectable } from '@nestjs/common';
import { type Random } from 'random';

async function getRandom(): Promise<Random> {
  const module = await (eval(`import('random')`) as Promise<any>);
  return module.default;
}

@Injectable()
export class AppService {
  async getHello(): Promise<string> {
    return 'Hello World! ' + (await getRandom()).int(1, 10);
  }
}

變體 3:導入並保存到局部變量

  • 無需額外撥打 function
  • 導入的模塊可以在運行時未定義(不太可能但仍然是不好的做法)
  • 您可以導入/使用類型
import { Injectable } from '@nestjs/common';
import { type Random } from 'random';

let random: Random;
eval(`import('random')`).then((module) => {
  random = module.default;
});


@Injectable()
export class AppService {
  async getHello(): Promise<string> {
    return 'Hello World! ' + random.int(1, 10);
  }
}

將 NestJS 項目轉換為 ES 模塊(不推薦)

盡管不受支持,但似乎可以將 NestJS 設置為編譯為 ESM。 本指南有很好的一般說明,可以在 typescript 項目中執行此操作。

我用 NestJS 測試了它,發現這些步驟足夠了:

  • 添加"type": "module"到你的package.json
  • NodeNext的 compilerOptions 中將module更改為tsconfig.json
  • .js擴展名添加到所有相關導入

現在導入應該按預期工作

import { Injectable } from '@nestjs/common';
import random from 'random';

@Injectable()
export class AppService {
  async getHello(): Promise<string> {
    return 'Hello World! ' + random.int(1, 10);
  }
}

到目前為止沒有用的是開玩笑的單元測試。 我在那里遇到了其他導入錯誤,我敢打賭未來還會有更多問題。 我會避免這種方法,等到 NestJS 正式支持 ES 模塊。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM