总结
你不能在 CJS 中使用static import statements:没有办法。
但是,如果您只需要在异步上下文中使用模块,则可以通过dynamic import statements 使用 ES 模块。但是,TypeScript 的当前状态在这种方法方面引入了一些复杂性。
操作方法
考虑这个示例,其中我使用您提到的模块设置了 CJS TS 存储库,并配置了 npm test 脚本来编译和运行输出。我已将以下文件放入一个空目录(我在此 Stack Overflow 问题的 ID 之后将其命名为 so-70545129):
文件
./package.json
{
"name": "so-70545129",
"version": "1.0.0",
"description": "",
"type": "commonjs",
"main": "dist/index.js",
"scripts": {
"compile": "tsc",
"test": "npm run compile && node dist/index.js"
},
"author": "",
"license": "MIT",
"devDependencies": {
"@types/node": "^17.0.5",
"typescript": "^4.5.4"
},
"dependencies": {
"unified": "^10.1.1"
}
}
./tsconfig.json
{
"compilerOptions": {
"exactOptionalPropertyTypes": true,
"isolatedModules": true,
"lib": [
"ESNext"
],
"module": "CommonJS",
"moduleResolution": "Node",
"noUncheckedIndexedAccess": true,
"outDir": "dist",
"strict": true,
"target": "ESNext",
},
"include": [
"./src/**/*"
]
}
./src/index.ts
import {unified} from 'unified';
function logUnified (): void {
console.log('This is unified:', unified);
}
logUnified();
现在,运行 npm install 并运行 test 脚本:
$ npm install
--- snip ---
$ npm run test
> so-70545129@1.0.0 test
> npm run compile && node dist/index.js
> so-70545129@1.0.0 compile
> tsc
/so-70545129/dist/index.js:3
const unified_1 = require("unified");
^
Error [ERR_REQUIRE_ESM]: require() of ES Module /so-70545129/node_modules/unified/index.js from /so-70545129/dist/index.js not supported.
Instead change the require of /so-70545129/node_modules/unified/index.js in /so-70545129/dist/index.js to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (/so-70545129/dist/index.js:3:19) {
code: 'ERR_REQUIRE_ESM'
}
作为参考,输出如下:./dist/index.js:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const unified_1 = require("unified");
function logUnified() {
console.log('This is unified:', unified_1.unified);
}
logUnified();
上面的错误解释了问题(我在这个答案的顶部总结了)。 TypeScript 已将静态 import 语句转换为对 require 的调用,因为模块类型是“CommonJS”。让我们修改./src/index.ts以使用动态导入:
import {type Processor} from 'unified';
/**
* `unified` does not export the type of its main function,
* but you can easily recreate it:
*
* Ref: https://github.com/unifiedjs/unified/blob/10.1.1/index.d.ts#L863
*/
type Unified = () => Processor;
/**
* AFAIK, all envs which support Node cache modules,
* but, just in case, you can memoize it:
*/
let unified: Unified | undefined;
async function getUnified (): Promise<Unified> {
if (typeof unified !== 'undefined') return unified;
const mod = await import('unified');
({unified} = mod);
return unified;
}
async function logUnified (): Promise<void> {
const unified = await getUnified();
console.log('This is unified:', unified);
}
logUnified();
再次运行test 脚本:
$ npm run test
> so-70545129@1.0.0 test
> npm run compile && node dist/index.js
> so-70545129@1.0.0 compile
> tsc
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
Error [ERR_REQUIRE_ESM]: require() of ES Module /so-70545129/node_modules/unified/index.js from /so-70545129/dist/index.js not supported.
Instead change the require of /so-70545129/node_modules/unified/index.js in /so-70545129/dist/index.js to a dynamic import() which is available in all CommonJS modules.
at /so-70545129/dist/index.js:11:52
at async getUnified (/so-70545129/dist/index.js:11:17)
at async logUnified (/so-70545129/dist/index.js:16:21) {
code: 'ERR_REQUIRE_ESM'
}
障碍
嗯?,我们不是刚刚解决了这个问题吗?我们来看看输出:./dist/index.js:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* AFAIK, all envs which support Node cache modules,
* but, just in case, you can memoize it:
*/
let unified;
async function getUnified() {
if (typeof unified !== 'undefined')
return unified;
const mod = await Promise.resolve().then(() => require('unified'));
({ unified } = mod);
return unified;
}
async function logUnified() {
const unified = await getUnified();
console.log('This is unified:', unified);
}
logUnified();
解决方案
为什么对require 的呼叫仍然存在?这个 GitHub issue ms/TS#43329 解释了为什么 TS 仍然以这种方式编译,并提供了两种解决方案:
-
在您的 TSConfig 中,set compilerOptions.module to "node12"(或 nodenext)。
-
如果#1 不是一个选项(您没有在问题中说),请使用eval 作为a workaround
让我们探索这两个选项:
方案一:修改TSConfig
让我们修改./tsconfig.json中的compilerOptions.module值:
{
"compilerOptions": {
...
"module": "node12",
...
},
...
}
然后再次运行:
$ npm run test
> so-70545129@1.0.0 test
> npm run compile && node dist/index.js
> so-70545129@1.0.0 compile
> tsc
tsconfig.json:8:15 - error TS4124: Compiler option 'module' of value 'node12' is unstable. Use nightly TypeScript to silence this error. Try updating with 'npm install -D typescript@next'.
8 "module": "node12",
~~~~~~~~
Found 1 error.
另一个编译器错误!让我们按照诊断消息中的建议来解决它:将 TS 更新到不稳定版本typescript@next:
$ npm uninstall typescript && npm install --save-dev typescript@next
--- snip ---
$ npm ls
so-70545129@1.0.0 /so-70545129
├── @types/node@17.0.5
├── typescript@4.6.0-dev.20211231
└── unified@10.1.1
现在安装的typescript的版本是"^4.6.0-dev.20211231"
让我们再次运行:
$ npm run test
> so-70545129@1.0.0 test
> npm run compile && node dist/index.js
> so-70545129@1.0.0 compile
> tsc
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
Error [ERR_REQUIRE_ESM]: require() of ES Module /so-70545129/node_modules/unified/index.js from /so-70545129/dist/index.js not supported.
Instead change the require of /so-70545129/node_modules/unified/index.js in /so-70545129/dist/index.js to a dynamic import() which is available in all CommonJS modules.
at /so-70545129/dist/index.js:30:65
at async getUnified (/so-70545129/dist/index.js:30:17)
at async logUnified (/so-70545129/dist/index.js:35:21) {
code: 'ERR_REQUIRE_ESM'
}
还是同样的错误。这是检查的输出:./dist/index.js:
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
/**
* AFAIK, all envs which support Node cache modules,
* but, just in case, you can memoize it:
*/
let unified;
async function getUnified() {
if (typeof unified !== 'undefined')
return unified;
const mod = await Promise.resolve().then(() => __importStar(require('unified')));
({ unified } = mod);
return unified;
}
async function logUnified() {
const unified = await getUnified();
console.log('This is unified:', unified);
}
logUnified();
TS 仍在将动态 import 转换为对 require 的调用,尽管我们已遵循所有诊断消息建议并正确配置了项目。 ? 在这一点上这似乎是一个错误。
让我们尝试一下解决方法,但首先,让我们撤消刚刚所做的更改:
首先,卸载typescript的不稳定版,重新安装稳定版:
$ npm uninstall typescript && npm install --save-dev typescript
--- snip ---
$ npm ls
so-70545129@1.0.0 /so-70545129
├── @types/node@17.0.5
├── typescript@4.5.4
└── unified@10.1.1
现在安装的typescript的版本是"^4.5.4"
然后,将./tsconfig.json中的compilerOptions.module值修改回"CommonJS":
{
"compilerOptions": {
...
"module": "CommonJS",
...
},
...
}
解决方案 2:使用eval 的解决方法
让我们修改./src/index.ts,特别是函数getUnified(第16-21行):
目前,它看起来像这样:
async function getUnified (): Promise<Unified> {
if (typeof unified !== 'undefined') return unified;
const mod = await import('unified');
({unified} = mod);
return unified;
}
TS 拒绝停止转换的问题陈述在第 18 行:
const mod = await import('unified');
让我们把它变成一个字符串文字,并在运行时使用eval 对其进行评估,这样 TS 就不会对其进行转换:
// before:
const mod = await import('unified');
// after:
const mod = await (eval(`import('unified')`) as Promise<typeof import('unified')>);
所以整个函数现在看起来像这样:
async function getUnified (): Promise<Unified> {
if (typeof unified !== 'undefined') return unified;
const mod = await (eval(`import('unified')`) as Promise<typeof import('unified')>);
({unified} = mod);
return unified;
}
保存文件并再次运行:
$ npm run test
> so-70545129@1.0.0 test
> npm run compile && node dist/index.js
> so-70545129@1.0.0 compile
> tsc
This is unified: [Function: processor] {
data: [Function: data],
Parser: undefined,
Compiler: undefined,
freeze: [Function: freeze],
attachers: [],
use: [Function: use],
parse: [Function: parse],
stringify: [Function: stringify],
run: [Function: run],
runSync: [Function: runSync],
process: [Function: process],
processSync: [Function: processSync]
}
终于! ? 达到了预期的结果。让我们比较一下最后一次的输出:./dist/index.js:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* AFAIK, all envs which support Node cache modules,
* but, just in case, you can memoize it:
*/
let unified;
async function getUnified() {
if (typeof unified !== 'undefined')
return unified;
const mod = await eval(`import('unified')`);
({ unified } = mod);
return unified;
}
async function logUnified() {
const unified = await getUnified();
console.log('This is unified:', unified);
}
logUnified();
这就是我们想要的:动态的 import 语句没有转换为 require 调用。
现在,当您需要使用unified 函数时,只需在您的程序中使用以下语法:
const unified = await getUnified();