NodeJS:在同一个项目中加载 ES 模块和原生插件

雷兰佩克

在实际问题之前(见文末),请让我通过一个例子展示导致该问题的步骤:

创建项目

tests$ mkdir esm && cd esm
tests/esm$ nvm -v
0.37.2
tests/esm$ nvm use v15
Now using node v15.6.0 (npm v7.5.6)
tests/esm$ node -v
v15.6.0
tests/esm$ npm -v
7.5.6
tests/esm$ npm init
package name: (esm) test-esm
entry point: (index.js)

安装nodehun

tests/esm$ npm install nodehun
added 2 packages, and audited 3 packages in 11s
tests/esm$ npm ls
[email protected] tests/esm
└── [email protected]

index.js

import { suggest } from './checker.js'
suggest("misspeling");

checker.js

import Nodehun  from 'nodehun'
import fs from 'fs';

const affix       = fs.readFileSync('dictionaries/en_NZ.aff')
const dictionary  = fs.readFileSync('dictionaries/en_NZ.dic')
const nodehun     = new Nodehun(affix, dictionary)

export const suggest = (word) => hun_suggest(word);

async function hun_suggest(word) {
  let suggestions = await nodehun.suggest(word);
  console.log(suggestions);
}

要获取所需的Hunspell 词典文件(affix 和dictionary):

tests/esm$ mkdir dictionaries && cd dictionaries
tests/esm/dictionaries$ curl https://www.softmaker.net/down/hunspell/softmaker-hunspell-english-nz-101.sox > en_NZ.sox
tests/esm/dictionaries$ unzip en_NZ.sox en_NZ.aff en_NZ.dic

运行项目

根据nodejs文档(确定模块系统)支持import/ export

Node.js的将把以下作为ES模块传递到当node作为初始输入时,或者当通过引用importES模块代码内的语句:•文件中结束.js时,最近的父package.json文件包含一个顶层的“类型”与值字段"module".

我们在项目"type": "module"package.json文件中添加字段

package.json

{
  ...
  "main": "index.js",
  "type": "module",
  ...
}

第一次失败的运行

tests/esm$ node index.js
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".node" for tests/esm/node_modules/nodehun/build/Release/Nodehun.node
... omitted ...
at async link (node:internal/modules/esm/module_job:64:9) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

挖掘一下上述错误的原因:

  • 在关于如何加载插件的文档中,它指的是使用require

已编译的插件二进制文件的文件扩展名是.node(与.dll相对.so)。编写 require() 函数以查找具有.node文件扩展名文件并将它们初始化为动态链接库。

  • 一旦您将节点项目定义为"type": "module"require它就不再受支持(如Interoperability with CommonJS 中所述):

使用require加载的ES模块不支持,因为ES模块具有异步执行。相反,使用 import() 从 CommonJS 模块加载 ES 模块。

临时解决方案

经过一段时间搜索文档,我找到了一个临时解决方案自定义 ESM 说明符解析算法

当前的说明符解析不支持 CommonJS 加载器的所有默认行为行为差异之一是文件扩展名的自动解析以及导入具有索引文件的目录的能力。--experimental-specifier-resolution=[mode]标志可用于自定义扩展解析算法。要启用自动扩展解析并从包含索引文件的目录导入,请使用该node模式。

tests/esm$ node --experimental-specifier-resolution=node index.js
(node:XXXXX) ExperimentalWarning: The Node.js specifier resolution in ESM is experimental.
(Use `node --trace-warnings ...` to show where the warning was created)
[
  'misspelling',
  'misspending',
  'misspeaking',
  'misspell',
  'dispelling',
  'misapplier',
  'respelling'
]

有一些帖子达到了相同的分辨率(参考 1参考 2)。但是,使用实验性标志似乎不是在生产中运行应用程序的正确方法。

失败的esm替代方案

从那时起,已经尝试了几次失败的尝试来避免使用--experimental-*标志。进行了一些搜索,我发现了一些推荐使用该软件包的帖子(参考 1参考 2esm

  • esm每周下载 130 万次。
  • 根据GitHub 中的自述文件,它不需要任何更改。

但是,此时,当我尝试此操作时node -r esm index.js,会出现一个新错误:

tests/esm$ npm install esm
added 1 package, and audited 4 packages in 709ms
tests/esm$ npm ls
[email protected] tests/esm
├── [email protected]
└── [email protected]
tests/esm$ node -r esm index.js
tests/esm/index.js:1
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: tests/esm/index.js
    at new NodeError (node:internal/errors:329:5)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1125:13) {
  code: 'ERR_REQUIRE_ESM'
}

以上可能是由于报告的问题错误 [ERR_REQUIRE_ESM]:必须使用导入加载 ES 模块 / ES 模块的 require() 不受支持)。

  • 建议的补丁来修复它,虽然我自己不知道如何使用它。
const module = require('module');
module.Module._extensions['.js'] = function(module, filename) {
  const content = fs.readFileSync(filename, 'utf8');
  module._compile(content, filename);
};

问题

  1. 是否有一种(标准)方式来使用import/ export(ES 模块)而不会引起import 插件问题
    • 避免使用--experimental-specifier-resolution=node标志。
  2. 也许esm可以解决上述问题。使用esm有什么问题吗?
    • 如果使用正确,有没有办法自己使用建议的补丁作为解决方法?

任何帮助解决它的提示将不胜感激。

注意:示例的最终状态可以从https://github.com/rellampec/test-esm.git克隆

雷兰佩克

经过一番胡说八道,试图找出这个问题的根本原因。

使用时node -r esm index.js,该esm软件包已经为您完成了所有工作(如其他答案中所述),因此(其他答案中未提及):

  • package.json应由被更新删除"type:" "module"(因为它创造了本土之间希望的交互nodeES模块的功能和esm安装包)

旁注:如果您尝试使用nodeES 模块,然后尝试切换到esm包,则很容易错过这一点。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

NPM 链接同一个项目中的多个功能/模块?

如何在 iOS 中的同一个 UICollectionView 中加载图像和视频

Clojure和Java源代码在同一个NetBeans项目中

如何在同一个Spark项目中同时使用Scala和Python?

是否可以在同一个项目中包含 PolarSSL 和 OpenSSL?

在同一个Xcode项目中拥有iOS和macOS应用

Extjs 6和Ext.net在同一个项目中?

MVC .net Web API 和 MVC 应用程序在同一个项目中

是否可以在同一个项目中使用 Postgres 和 Oracle?

同一个命名空间和项目中的两个类是否也在同一个执行程序集中?

从另一个Angular4项目中加载功能模块

在同一个项目中添加 ASP.NET MVC 和 Web API Web 应用程序

有没有办法在同一个iOS Xcode项目中使用Storyboard和SwiftUI?

为什么不能在同一个项目中同时使用 require('foo') 和 ECMAScript 导出?

在同一个项目中使用 Vuejs 和 Reactjs 有意义吗

如何在同一个ASP.Net MVC项目中使用Unity MVC和Unity WebAPI

React-Redux 和 React Context API 可以在同一个项目中使用吗?

如何在同一个应用程序项目中多次将一个库模块包含到多个库模块中?

在同一个项目中加载多个 jquery 版本但不是相同的页面

如何在同一个项目中一起使用“Image_picker”和“shared_preferences”

有没有办法在同一个模型和同一个控制器中加入相等的表?

在同一个项目中使用 jpa 存储库和休眠(使用标准,HQL 调用查询)是否可以

有没有在同一个项目中同时使用auth0弹簧安全的API和Java的智威汤逊的方法吗?

我可以在同一个Xcode项目中拥有Swift,Objective-C,C和C ++文件吗?

有没有办法在 Apple Watch 的同一个 iOS Xcode 项目中使用故事板和 SwiftUI?

C# WPF Prism - 在不同模块(项目)之间共享同一个对象

无法在同一进程中多次使用 imp.load_module 加载同一个模块

同时在 IDEA 和 PyCharm 中为同一个项目工作

使用 IntelliJ Idea 和 Android Studio 为同一个 GitHub 项目做出贡献