ESM 和 CommonJS 中的“同构”__dirname

问题描述 投票:0回答:2

我维护一个用 Typescript 编写的库,我想同时针对 CommonJS 和 ESM。为此,我运行了两次构建。这需要我编写对两者都有效的代码(在 Typescript 中)。

一个问题是 CommonJS 有一个

__dirname
变量,我用它来相对地加载文件。
__dirname
在 ESM 中不存在,所以我尝试做这样的事情:

const _mydirname = typeof __dirname !== 'undefined'
  ? __dirname
  : path.dirname(url.fileURLToPath(import.meta.url));

// Example usage
const file =  fs.readFileSync(path.join(_mydirname, '../README.md'), 'utf-8')

不幸的是,在 Typescript 中构建它时,它会发出以下错误:

 Error: src/application.ts(22,36): error TS1343: The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node16', or 'nodenext'.

有没有办法获取相对于当前脚本的路径,该路径在两种情况下都有效?

node.js typescript es6-modules commonjs
2个回答
1
投票

我不认为有任何统一的方法来获取 CommonJS 和 ESM 的当前脚本路径。

如果你无论如何都要构建它两次,你可以添加一些条件构建时间逻辑来为每个模块系统生成适当的代码,实际的实现将取决于你的打字稿构建系统。

  • 像 Webpack/Rollup 这样的打包工具有相应的插件。您可以注入一个虚拟模块以根据目标返回目录名或使用“替换”插件来转换代码。
  • 您可以使用 Babel(在 tsc 构建之后或使用 typescript 插件)对其进行转换。像 babel-plugin-transform-import-meta 这样的 Babel 插件可以将
    import.meta.url
    转换为 cjs.
  • 如果你使用普通的 tsc,你可能需要自己实现它。

更新(如前所述,这在 cjs 中不起作用):

如果你想保持简单并依赖 __dirname/import.meta 的运行时断言,你可以告诉打字稿忽略错误:
const _mydirname = typeof __dirname !== 'undefined'
  ? __dirname
    // @ts-ignore
  : path.dirname(fileURLToPath(import.meta.url));

作为旁注,当基于源脚本位置的逻辑时要小心,它在开发过程中可能有意义,但在构建时具有不同的结构(例如,如果您使用的是捆绑器)。

0
投票

我有同样的问题,实际上我正在使用这个替代品。

// src.index.ts
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";

let dir;
try {
  dir = __dirname;
} catch (e) {
  dir = dirname(fileURLToPath(import.meta.url));
}

console.log(dir);
// tsup.config.js
import { defineConfig } from "tsup";

export default defineConfig({
  entry: ["src/index.ts"],
  dts: true,
  format: ["esm", "cjs"]
});
// tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "moduleResolution": "node",
    "module": "ESNext",
    "outDir": "dist",
    "esModuleInterop": true,
    "declaration": true,
    "sourceMap": true
  },
  "include": [
    "src"
  ],
}
// package.json
{
  "main": "dist/index.js",
  "exports": {
    "import": "./dist/index.mjs",
    "require": "./dist/index.js"
  },
  "types": "dist/index.d.ts",
  "scripts": {
    "dev": "tsup --watch",
    "build": "tsup"
  },
  "devDependencies": {
    "tsup": "^6.7.0",
    "typescript": "^4.9.4",
  }
}

这似乎适用于

tsup
。有一个警告,但构建成功。我不确定我使用了最佳实践...... 其他想法?

编辑: 好吧,看来

tsup
有一个干净的解决方案。

https://tsup.egoist.dev/#inject-cjs-and-esm-shims

// tsup.config.js
import { defineConfig } from "tsup";

export default defineConfig({
  entry: ["src/index.ts"],
  dts: true,
  format: ["esm", "cjs"],
  shims: true
});
© www.soinside.com 2019 - 2024. All rights reserved.