Mocha: 支持 ES6 样式测试,无需使用转译器

创建于 2017-09-18  ·  75评论  ·  资料来源: mochajs/mocha

先决条件

  • [x] 通过交叉引用带有common mistake标签的问题,检查您的问题尚未提交
  • [x] 在没有 Mocha 的情况下使用相同的环境和/或转译器配置检查了下一代 ES 问题和语法问题,以确保它不仅仅是相关环境中实际不支持的功能或代码中的错误.
  • [x] '冒烟测试' 通过在真实测试套件之外运行要测试的代码来更好地了解问题是出在被测代码、您对 Mocha 的使用还是 Mocha 本身
  • [x] 确保本地和全局安装的 Mocha 版本之间没有差异。 您可以通过以下方式找到它们:
    node node_modules/.bin/mocha --version (本地)和mocha --version (全球)。 我们建议避免使用全局安装的 Mocha。

描述

在我开始之前,已经有一些关于这个主题的已解决的问题,但随着先决条件的变化,我想开始一个新的尝试。

现在该节点支持运行EMCAScript 模块(是的,我知道它是实验性的),很高兴看到 mocha 与mjs测试定义一起工作。

重现步骤

我有一个非常简单的测试

describe('Test', function () {
});

我保存为test.jstest.mjs

预期行为:我希望两个测试都显示

- test/test.js 
  0 passing (1ms)
(node:70422) ExperimentalWarning: The ESM module loader is experimental.

实际行为:虽然js测试有效,但mjs测试给了我

- test/test.mjs 
module.js:658
    throw new errors.Error('ERR_REQUIRE_ESM', filename);
    ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/dgehl/Repositories/LOreal/code/ecom-lora/test/frontend/components/global/test.mjs

重现频率: 100%

版本

node --version - v8.5.0
mocha --version - 3.5.3

附加信息

我_认为_这可能是 mocha 的运行程序正在使用 commonjs,而 nodejs 当前的实现不允许使用来自 commonjs 上下文的 ECMAScript 模块。

请不要回复“使用转译器”,我想明确不使用转译器。

编辑:在早期版本中,我不小心使用了 jsx 而不是 mjs。

feature usability

最有用的评论

我们在 Mocha v7.1.0 中实现了 Node 的原生 ESM 支持。

所有75条评论

在进行任何进一步研究之前,我的初步想法是:

  • 我似乎记得 Node 人员专门更改了标准以允许向后兼容 CommonJS 模块。 如果那(仍然?)是真的,那么我希望最终它会得到支持,而无需对 Mocha 做任何事情。 (考虑到 Node 的发布周期,“最终”我的意思是“可能比 Mocha 的变化更快”;请参阅下面强调的倒数第二个以获得更详细的解释。)
  • 我可以假设这是像node --experimental-modules node_modules/mocha/bin/_mocha一样运行吗?
  • .jsx究竟是如何参与的? 我看到显示的错误示例是指.mjs ; 从发布的内容来看,目前还不清楚jsx在哪里。
  • 我还隐约记得听说过 Node 的初始实现需要.mjs文件扩展名; 如果那(仍然?)是真的,并且如果您尝试将import / export.jsx文件一起使用( import.jsx .mjs文件或加载包含import / export.jsx文件),这可能是问题而不是 Mocha 吗?
  • 我们需要想出一种方法来确保对实验功能的更改不需要对 Mocha 进行更改,而这将不得不等待一个 semver 专业——否则你最终可能会回到我们现在的位置,除非等待更长时间(因为 Mocha 的主要发布周期不像 Node 那样锁定为一年两次)。
  • 仅代表我个人观点,不代表团队,如果新的模块格式不能与旧的模块格式互操作,而不需要修改以旧格式编写的现有库,那么新的模块格式还没有准备好

我似乎记得 Node 人员专门更改了标准以允许向后兼容 CommonJS 模块。 如果那(仍然?)是真的,那么我希望最终它会得到支持,而无需对 Mocha 做任何事情。 (考虑到 Node 的发布周期,“最终”我的意思是“可能比 Mocha 的变化更快”;请参阅下面强调的倒数第二个以获得更详细的解释。)

从我的(小)研究看来,至少现在允许使用来自 ECMAScript 模块的require ,但不允许使用来自 commonjs 模块的import

我可以假设它像 node --experimental-modules node_modules/mocha/bin/_mocha 一样运行吗?
.jsx 究竟是如何参与的? 我看到显示的错误示例是指 .mjs; 目前尚不清楚,从发布的内容来看,jsx 在哪里。

是的,它是用--experimental-modules运行的。 jsx是一个错字,我的意思是mjs ,将更新最初的帖子。

我还隐约记得听说过 Node 的初始实现需要 .mjs 文件扩展名。 如果那(仍然?)是真的,并且如果您尝试将导入/导出与 .jsx 文件一起使用(在 .mjs 文件中导入 .jsx 文件或加载包含导入/导出的 .jsx 文件),可以这样吗是问题而不是摩卡?

问题似乎是,我可能在这里遗漏了一些东西,即 mocha 使用require来加载测试(这至少是我目前的假设,因为我不是 mocha 专家,更多的是用户),然后通过import包括其他模块。 这与第一点一起解释了错误。

我们需要想出一种方法来确保对实验功能的更改不需要对 Mocha 进行更改,而这将不得不等待一个 semver 专业——否则你最终可能会回到我们现在的位置,除非等待更长时间(因为 Mocha 的主要发布周期不像 Node 那样锁定为一年两次)。
纯粹是我个人的观点,不代表团队,如果不修改以旧格式编写的现有库,新的模块格式不能与旧的模块格式互操作,那么新的模块格式还没有准备好。

我担心这会是答案,我知道这不是首要任务。 如果我对上述错误原因的假设是正确的,那么像 #956 这样的东西可能会有所帮助,因为测试的入口点可能是mjs模块而不是 commonjs,否则可能很难实现。 从当前模块支持import似乎在 nodejs 团队的路线图上,但是不清楚时间表。

从我的(小)研究看来,至少现在允许使用来自 ECMAScript 模块的 require 但不能从 commonjs 模块导入。

似乎在 nodejs 团队的路线图上支持从当前模块导入,但是不清楚时间表。

澄清一下:鉴于在“较旧”(在某些情况下是当前非实验性)环境中import是语法错误,使用分支逻辑或类似的东西无法避免,Mocha 需要的不是能够使用import本身,而是能够使用require加载使用(或使用使用)新格式的模块。

jsx 是一个错字,我的意思是 mjs ,将更新最初的帖子。

谢谢,这消除了一个可能的角度!

如果我对上述错误原因的假设是正确的,那么像 #956 这样的东西可能会有所帮助,因为测试的入口点可能是 mjs 模块而不是 commonjs,否则可能很难实现。

不幸的是,如果不对一些更神秘的内部进行大量重写,就不可能让 Mocha 不创建全局变量(我自己尝试过但无法弄清楚 😿); 但是,现在可以通过“程序化”API 使用您自己的 JS 入口点(可能不会在旧 wiki 页面和源文件中的 JSDoc 注释之外记录,但得到官方支持):

// test.mjs
var Mocha = require('mocha'),
var mocha = new Mocha();

// your mission: create a file `example.mjs`
// in the `test` folder and have it `import` stuff
// as well as using `describe` and `it` to make tests
mocha.addFile("./test/example.mjs");

mocha.run(function(failures){
  process.on('exit', function () {
    process.exit(failures ? 1 : 0);
  });
});
node --experimental-modules test.mjs

我实际上并没有尝试过,看看它是否会有所作为(需要先获取最新版本的 Node),但让我知道它是否适合你......

首先感谢您对此的支持!

我试过这个

// runner.mjs
import Mocha from 'mocha';
var mocha = new Mocha();

// your mission: create a file `example.mjs`
// in the `test` folder and have it `import` stuff
// as well as using `describe` and `it` to make tests
mocha.addFile('./test/frontend/components/global/test.mjs');

mocha.run(function (failures) {
    process.on('exit', function () {
        process.exit(failures ? 1 : 0);
    });
});

所以基本上把节点入口点做成了一个 ECMAScript 模块。

我通过node --experimental-modules --harmony ./runner.mjs运行它

我明白了

(node:88620) ExperimentalWarning: The ESM module loader is experimental.
{ Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /.../test/frontend/components/global/test.mjs
    at Object.Module._extensions..mjs (module.js:658:11)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)

Mocha 需要的不是能够使用import本身,而是能够使用 require 来加载使用(或使用使用)新格式的模块。

恐怕目前在节点中是不可能的,您只能在通过import导入的模块中使用require #$ 。 有没有办法避免mocha.addFile('./test/frontend/components/global/test.mjs');而是导入测试并像这样添加导入的脚本

import test from './test';
mocha.addTest(test);

?

目前在 Mocha 中没有类似的功能,您可以按照这些方式做一些事情。 addFile只是将文件附加到稍后由run函数require d 的列表中。 run函数调用loadFilesrequire他们:

https://github.com/mochajs/mocha/blob/1cc0fc0e6153bbd746b0c2da565363570432cdf7/lib/mocha.js#L220 -L235

您想要做的是任何需要import ed 而不是require d 不要调用addFile的文件(所以 Mocha 不会尝试require它在run上),而不是在调用run之前调用一些类似于loadFiles中的代码,但使用import而不是require 。 我不记得是否有任何限制使用import可以防止这种情况发生,但如果有可能的话,我想它看起来非常接近:

modules.forEach(function (file) {
  file = path.resolve(file);
  mocha.suite.emit('pre-require', global, file, mocha);
  import fileExport from file; // fileExport is used by the exports interface, not sure if anything else; most interfaces act as a side effect of running the file
  mocha.suite.emit('require', fileExport, file, mocha);
  mocha.suite.emit('post-require', global, file, mocha);
});

您还可以查看https://github.com/mochajs/mocha/blob/master/bin/_mocha如何使用 Mocha 的编程 API 来了解如何提供其他选项以及如何使用诸如 Mocha 的文件查找功能之类的东西。 它组织得不是很好,但命令行界面所做的一切都在那里(直接或因为其中调用了 Mocha 的编程 API 中的函数)。

我可以更进一步,但导入的测试现在抱怨它不知道描述( ReferenceError: describe is not defined )。 注入它的正确方法是什么? 如果我做

import Mocha from 'mocha';
const describe = Mocha.describe;

它抱怨TypeError: describe is not a function

所以,我的发行版终于得到了 NodeJS 8.5,我有机会玩这个并确认我有一些预感,但在我能够检查之前不想说明:

  1. 如果不将其名称硬编码到脚本/模块文件中,我找不到任何加载 ES 模块的方法。 这意味着无论我们做什么,也不管任何其他 ES 与 CommonJS 语义如何,Mocha 都无法通过命令行界面加载它们。 如果这种情况发生变化,我们会想知道的,我猜。 (如果它发生变化,ES 模块可以通过变量require加载,我们可能不需要更改任何内容,但我认为在它发生之前我们无法确定模块如何工作.)
  2. 而 Mocha 在pre-require事件发射中设置接口(不是在模块首次加载时;选择的接口特定于new Mocha实例),新的模块系统实际上解析依赖关系树并在依赖它们的模块之前加载依赖项,因此未定义describe的原因是 Mocha 直到加载测试模块后才设置它。 这意味着要让这种情况发生会很复杂(同样,除非并且直到require(file)被允许并在该特定行加载依赖项,最好是同步的,所以我们不必更改任何其他内容)。

但是,我确实发现我可以通过利用import ed 模块按import调用的顺序加载这一事实来使其工作。 (如果您使用导出接口或需要每个单独文件名称的报告器,如我将要链接的代码中所述,它会变得更加多余,但原则上它是有效的。)如果事实是在不改变上述任何其他事实的情况下进行改变,那么即使这样也根本不可能。 但现在我认为这是你必须做的。

https://gist.github.com/anonymous/caba0883254a2349c5615df8e9286665

node --experimental-modules ./run.mjs

不幸的是,考虑到 ES 模块的工作方式以及 Node 目前允许的功能,我相当肯定这是我们能做的最好的事情。

换一种方式想一想:

  • import是语法。
  • require是一个函数。

你不能动态地import任何东西,就像你不能在不使用eval()的情况下动态地运行代码一样。

这个第 3 阶段的提案允许这种行为,但我不确定是否有任何运行时正在发布它。

因此,Mocha 无法在通过mocha运行时导入.mjs文件,而无需添加@std/esm并使用其require实现带有.mjs的文件可能是一个可行的解决方案,我们可以考虑支持,但讨论(和这样的 PR)可能需要来自社区,至少在这种行为没有被标记之前。

import describe from 'mocha'在优先级列表中的位置非常低,不幸的是,由于这类事情的固有困难(#956)。 最好使用node运行并坚持使用全局变量。

实际上,我想到我们可以加载测试并利用vm.runInContext ,假设这样的东西支持模块。 因为 Node 的加载行为与.mjs扩展名相关联,并且vm.runInContext需要一个string ,所以看不到它是怎么做到的——而且文档中没有提到这一点。 也许某个地方有问题?

(话又说回来,这可能正是@std/esm在幕后所做的!)

在浏览器中没有转译器就可以进行 mocha 测试。 也许它有助于解决这个问题。

这无关紧要,因为您不是将 mocha 作为一个模块,而是一个脚本......

对不起让我自己困惑。 在浏览器中是不同的。

我想投票支持做一些事情以允许 Mocha 运行位于 ES 模块中的测试。 在尝试编写这样的测试并从 Node.js 模块加载器中得到一个奇怪的错误后,我来到了这里。 我正在使用 Node.js 9.5,它本机支持 ES6 模块。

就目前而言,Node.js 9.5 不允许 CommonJS 模块 require() ES6 模块。 也许他们正在朝着允许的方向努力,我不知道。

我将测试编写为带有.mjs扩展名的 ES6 模块并尝试运行它。 从加载程序得到错误——我假设mocha命令导致使用require() ,这就是它失败的原因。

使用.js扩展重新进行测试,并尝试使用require()加载要测试的模块。 这也从加载程序中得到了错误。

我认为 Node.js 世界需要考虑他们将如何迁移到和支持 ES6 模块。 由于 Mocha 是这个世界上非常流行的工具,所以 Mocha 团队最好考虑如何支持 ES6 模块。

跟进......经过一些思考和搜索,我能够让这个序列作为一种解决方法工作。

使用.js扩展名命名测试脚本(使其成为 CommonJS 脚本)

然后在测试脚本中添加:

require = require("@std/esm")(module,{"esm":"js"});

然后我可以像这样require()一个 ES 模块:

const model = require('../models/notes');

@robogeek或者从命令行使用@std/esm预加载器可能会更好,这样您就不必用变通方法弄乱规范文件,并且可以有.mjs扩展名。

mocha -r @std/esm spec.mjs

动态导入在--harmony-dynamic-import标志后面随节点 v9.6 一起提供。 动态导入允许 mocha 加载包含在 es6 模块中的测试,而无需转译器。

@harrysarson它不会开箱即用。 Mocha 使用 cjs 模块和require ,您必须使用 cjs 编写测试文件,并使用一些额外的胶水代码来处理import的异步性质。 还是我错过了什么?

我是一个观察不活动问题的机器人。
此问题最近没有任何活动,我将其标记为stale 。 14天后,如果没有进一步的评论或活动,我将关闭此问题。
感谢您为摩卡做出贡献!

该问题仍然存在,但依赖于对 ESM 的本机支持。 浏览器有,Node 还没有。

我只是在玩耍,熟悉 ESM/.mjs 并决定需要对我的玩具进行测试。 意识到 mocha 还没有正式支持.mjs文件,我通过一个快速的临时模块(直到有人有时间添加对 mocha 的完全支持):

https://www.npmjs.com/package/mocha-esm
欢迎 PR/问题: https ://github.com/stefanpenner/mocha-esm

那里可能有更好的东西,但一起度过很有趣。 所以\o/

我决定分叉mocha本身以支持动态导入(基于上面的一些想法,但我无法让它以任何其他方式运行)。

这意味着您可以使用例如node --experimental-modules --harmony-dynamic-import test.mjs运行

然后在test.mjs

import 'should';
import Mocha from 'mocha';

const mocha = new Mocha();

mocha.addFile(() => import("./some-module.spec.mjs"));

mocha.run(failures => {
  process.on('exit', function () {
    process.exit(failures ? 1 : 0);
  });
});

我保留了mocha 中的更改以支持这个最小的,我没有时间为潜在的 PR 正确集成它,也没有添加专门的 npm 模块,但你可以直接从 github 安装这个 fork "mocha": "git+https://[email protected]/odolha/mocha" .

请注意,您可以使用此方法以您可能想要的任何异步方式加载文件,而不仅仅是通过动态导入,因为 mocha 需要一个提供 Promise 的函数。

编辑

我忘了提到你的测试不能是这种方法的纯脚本,因为我们不能注入上下文,所以你需要:

// some-module.psec.mjs
export const test = ({ describe, it }) => {
  describe('Something', () => {
    it('works', () => {
      ...
}

@odolha
感谢您链接您的叉子。
已经有一个支持原生 ESM 的 PR,但由于模块支持仍处于试验阶段,它已关闭。

您的实现让我感到欣慰的是,添加对此的支持应该很容易。 我们很多人都在热切地等待这个功能:)

@demurgos :no_mouth: 是的...我在做了自己的事情后才看到那个 PR,d'oh 😃。

@harrysarson @boneskull
esm包(以前称为@std/esm )在此提交中删除了对.mjs文件的支持。
这意味着它不再可能与 Mocha 一起使用来测试.mjs文件。 本期对此进行了讨论

我仍然希望能够测试 ES 模块,以便可以在 Node 或浏览器中安全地运行它们。

关于当前的模块讨论,一致认为.mjs应该在最终结果中可用(可能不是唯一的解决方案,但至少可用)并且import("./foo.mjs")将返回一个承诺对应的 ES 命名空间。 将 CJS 模块转换为具有与 $#$ module.exports $#$ 对应的default导出的模块这一事实更具争议性,但这似乎是一个安全的假设。

是否可以重新考虑使用动态import向 Mocha 添加原生 ES 支持? 功能标志可以重命名为--experimental-es-modules (从 #3253 开始),以更好地表明这取决于 Node 支持的当前进展。
根据截止日期,最终规范要到节点 12 才能落地,因此当前的实现将在那里保留一段时间(并且是最终提案的一个相对安全的子集)。

@demurgos我个人更喜欢在提交 mocha 上的任何代码实现之前再等一下。 但也许@boneskull@ScottFreeCode不同意?

@demurgos

esm 包(以前称为 @std/esm)在此提交中删除了支持 .mjs 文件。

esm加载器没有放弃对.mjs的支持。 我们只需遵循当前的--experimental-modules实现,并在尝试使用require加载.mjs时抛出错误。 用户仍然可以将.js文件用于他们的测试条目文件,该文件使用 ESM 语法或动态import()来加载.mjs.js的后续测试文件,很多就像esm自己的测试做的。

根据截止日期,最终规范要到 Node 12 才能落地,因此当前的实现将保留一段时间

--experimental-modules没有固定的降落时间。 希望它可以在 Node 10 的支持周期中的某个时候登陆_(所以在接下来的 2 年中的某个时间)_,但没有什么是一成不变的。

(并且是最终提案的一个相对安全的子集)。

当前的--experimental-modules实现可能与最终提案不兼容。 关于 Node 中的 ESM 支持是什么样的,有几个讨论。 一些提议的方向与当前的实验实现不兼容。 取决于事情如何改变你今天为--experimental-modules编写的代码可能不适用于最终形式。

esm 加载器没有放弃对 .mjs 的支持。

我的观点是esm不再需要.mjs ,因此您不再可以将 Mocha 的测试发现用于.mjs 。 但你是对的,它没有被记录在案,所以即使其他人依赖它,它也不是一个真正的重大变化。

关于截止日期,我指的是这个问题。 似乎有针对节点 11 的尝试和针对节点 12 的最终实现,因此它可以移植到节点 10 LTS。 一些人希望它早点发生,另一些人则警告不要急于求成。

我的提议是合并#3253。 这仅提供了一种选择加入机制来使用import(...)而不是require来加载测试用例。 由于我希望它主要应用于.mjs的上下文中的--experimental-modules ,我仍然认为它是安全的。 (返回命名空间承诺的.mjs的动态导入可能会保留)。 但我会让你决定是否可以合并它并避免为它付出太多。

同样,此 PR 的主要原因是没有它,您将无法再使用 Mocha 的测试发现,而必须使用上述@jdalton 描述的解决方法。 ( .js入口点和手动导入)

我的提议是合并#3253。

3253 有一些缺陷,按原样合并绝对不是一个好主意。

@jdalton示例之后,我设置了一个小型工作流程来测试没有esm(纯 Node + Mocha)的本机 ESM。
我使用异步测试定义(使用--delay )和--experimental-modules

目前,_mocha_ 只能导入 CJS,CJS 只能使用动态import()伪函数导入 ESM。 因此,我生成了以下 CJS 入口点(其名称以.js结尾),它导入规范文件并触发测试执行:

test.esm.js

(async () => {
  await import("./test/a.spec.mjs");
  await import("./test/b.spec.mjs");
  run();
})();

(我在构建时使用导入列表生成入口点,但您可以手动编写它或在那里使用glob 。)

然后我使用以下命令执行它:

NODE_OPTIONS="--experimental-modules" mocha --delay build/test/test.esm.js

我必须通过NODE_OPTIONS因为标志不被 mocha 识别。


我仍然希望 mocha 能够为实验性 ESM 提供更好的支持,但至少很高兴知道今天有一种方法可以在没有其他工具的情况下使用它。

@demurgos这是您发现的一个不错的小工作区:+1:。

很高兴看到确实可以(如果不容易的话)将 es 模块与 mocha 一起使用: smile:。

@demurgos这个问题是关于在不使用转译器的情况下支持 ES6 样式测试。 什么是“构建时间”? 您用来生成测试入口点的代码一个转译器,只是一个专用的。

@rulatir
我提到我使用构建工具,但它们不是本期讨论的级别:Mocha 使用本机 ESM 运行,而不是由转译器降低为 CJS 的 ESM。

看我的留言:

我在构建时使用导入列表生成入口点,但您可以手动编写它或在那里使用glob

我使用了构建步骤,因为我希望我的导入是 (1) 静态定义的并且 (2) 自己不维护列表。

如果您只有几个规范文件,则删除 (2) 很好:只需有一个入口点导入您的 1-2 个规范文件。
(1) 已经是对测试文件的特定要求,所以它主要只是一个“很好”的东西,你可以在运行时使用glob (而不是像我这样的构建时间)。 这只是一个细节,一旦你理解了核心思想,最终就无关紧要了。

如果您只想要一个简单的复制粘贴解决方案,在运行时查找 mjs 规范文件,这里有一个示例:

const {sync: globSync} = require("glob");

(async () => {
  const matches = globSync("**/*.spec.mjs");
  for (const match of matches) {
    await import(match);
  }
  run();
})();

使用NODE_OPTIONS="--experimental-modules" mocha --delay test.esm.js运行它。
如您所见,根本没有构建,但代码中的噪音更多。

我是一个观察不活动问题的机器人。
此问题最近没有任何活动,我将其标记为stale 。 14天后,如果没有进一步的评论或活动,我将关闭此问题。
感谢您为摩卡做出贡献!

此问题仍然有效,不应关闭。

即将发布的mocha@6版本将具有列入白名单的--experimental-modules标志,允许更轻松地试验 ES6 模块。 是否有可能在 v6 之前发布次要版本或补丁版本? 我目前正在完成一个新的覆盖工具,它确实使用 V8 调试器而不是伊斯坦布尔,并希望使用 Mocha 和 ES6 模块对其进行测试(无需在我的 package.json 中使用 git 依赖项)。

@demurgos

当我尝试这样...

const {sync: globSync} = require("glob");
(async () => {
    const matches = globSync("**/*.spec.mjs");
    for (const match of matches) {
        await import(match);
    }
    run();
})();

我明白了

(node:4632) UnhandledPromiseRejectionWarning: Error: Cannot find module test/Sanity.spec.mjs

但是当我这样跑...

const {sync: globSync} = require("glob");
(async () => {
    await import("./Sanity.spec.mjs");
    run();
})();

它运行完美我错过了什么?

@demurgos你应该与@bcoe 协调; 见https://github.com/bcoe/c8

@demurgos要删除一个小版本,我们需要将自 v5.2.0 以来的所有非破坏性更改挑选到一个分支中并将它们编译到 CHANGELOG 中。 如果您或其他人愿意做这项工作,那么我们可以取消发布。

fwiw 我推荐esm而不是--experimental-modules直到 Node.js 有它的故事。 这将是一个相当长的等待时间。

@骨头骷髅
哈哈谢谢。 自 7 月以来,我已经在c8附近工作(我在这个 repo 上打开了一堆 PR 和问题)。 还有一些我们不同意的设计决策,因此我们尝试共享大部分依赖项(例如,我编写了合并算法)并决定我将发布另一个工具: c88 。 这个周末我让它工作了,我现在正在我的图书馆上测试它。 我可以在 CI 中将它与原生 ESM 和 mocha 一起使用。 我仍然需要一些时间来记录和修复它,但它应该在一月份准备好)。

@jrgleason
我在头顶写了上面的代码。 这里的问题似乎是globSync返回不以./../开头的相对路径。 您可能希望在它前面加上./ :它应该适用于简单的相对路径。
您还应该注意,动态导入使用相对 URL: #?和其他特殊字符的处理方式可能不同。 如果您想要一个坚如磐石的解决方案,您应该解析模块的绝对路径,然后将其转换为文件 URL。 作为覆盖工作的一部分,我编写了一个库来在绝对路径和 URL 之间进行转换:您可能希望使用fromSysPath中的furi 。 转换应该处理任何类型的路径(甚至是 Windows 命名空间和 UNC 路径......)。

下面是一个完整的例子:

const {fromSysPath} = require("furi");
const {sync: globSync} = require("glob");
const {resolve} = require("path");

(async () => {
    const matches = globSync("**/*.spec.mjs");
    for (const match of matches) {
        await import(fromSysPath(resolve(match)).href);
    }
    run();
})();

我的意思是, mocha --require esm工作吗? 我们真的需要自动检测吗? 这听起来很困难,并增加了开销......

@demurgos要删除一个小版本,我们需要将自 v5.2.0 以来的所有非破坏性更改挑选到一个分支中并将它们编译到 CHANGELOG 中。 如果您或其他人愿意做这项工作,那么我们可以取消发布。

谢谢你的提议。 仍然有可能使用NODE_OPTIONS获得--experimental-modules ,因此它不是高优先级(并且它可能会使 git 树复杂化以进行樱桃选择提交)。 如果我设法解决与其他依赖项有关的问题,我会看看我是否可以花一些时间来解决这个问题。 同时,我只关注 v6 里程碑。

fwiw 我推荐esm而不是--experimental-modules直到 Node.js 有它的故事。 这将是一个相当长的等待时间。
我的意思是,难道 mocha --require esm 不起作用吗?

我绝对同意它是目前最好的解决方案:它是最容易设置的解决方案,并且已经推出了一段时间:它有效。 就我而言,我正在维护自己的构建工具并处理原生 ESM 作为经典 CJS 构建的替代方案。 尽管非常渴望原生 ESM,但我仍然建议不要将其用作运行代码的唯一方法:它毕竟是实验性的:stuck_out_舌头:。

我最近的大部分信息都是关于分享使用本机 ESM 可以完成的工作。 这主要是实验性的工作,我预计当 Node 的 ESM 稳定时必须更改它。 从长远来看,拥有一个不需要esm包的解决方案是有好处的。 以下是我的理由:

  • 它减少了所需工具的数量(复杂性更低,需要理解的配置更少)
  • 在边缘情况(评估错误、循环、加载错误、异步/动态模块、wasm 等)周围,真正的 ESM 和esm之间可能存在一些差异。 在编写同构代码时,减少任何可能的行为分歧来源可能更安全。 这也与原生覆盖有关:使用esm ,V8 会看到转译的输出,因此您必须处理源映射( c8尚不支持,但我正在准备 PR,他们在c88上工作)。 调试时也可能出现其他差异。
  • 它避免了必须动态转换代码并有助于提高性能。

我们真的需要自动检测吗? 这听起来很困难,并增加了开销......

我不确定您指的是哪种自动检测。 和今年早些时候发的PR有关系吗?


编辑:如果你想讨论,我也在 Node tooling Slack 上(主要在#c8频道上活跃)。

@demurgos我想我对人们在这里想要什么感到有些困惑。 反正...

如果NODE_OPTIONS=--experimental-modules在 Mocha v6.0.0 支持--experimental-modules之前一直有效,那么对于这个问题还有其他工作要做吗? 这就是我所缺少的。

我希望这个问题应该保持开放,直到本机 ESM(“不使用转译器的 ES6 样式测试”)开箱即用/像 CJS 目前的工作一样容易。

我使用--delayNODE_OPTIONS=--experimental-modules发布的解决方案与其说是适当的支持,不如说是一种解决方法。 一旦您能够运行mocha **/*.spec.mjs并获得报告,我会认为此问题已解决。

不幸的是,目前我觉得我们仍然需要等待 Node 弄清楚 ESM 支持。 我必须检查一下,但我认为 PR 没有使用自动检测,而是使用动态导入简单地导入了每个模块(CJS 或 ESM)。 实现将取决于模块的互操作故事。


编辑:我指的是https://github.com/mochajs/mocha/pull/3253。 它允许将所有模块加载为 ESM(无自动检测)。

我是一个观察不活动问题的机器人。
此问题最近没有任何活动,我将其标记为stale 。 14天后,如果没有进一步的评论或活动,我将关闭此问题。
感谢您为摩卡做出贡献!

节点 12 应该包括新的 ESM 实现。 这将是检查如何在 Mocha 中支持 ES 模块的机会。

我在使用 Mocha 和 ESM 时遇到了 GC 错误,但已报告并确认,因此应该修复它: https://github.com/nodejs/node/issues/27492。

除了这个错误,我上面评论中描述的策略仍然适用于将 Mocha 与 ESM 一起使用。

嗨 Mocha 人,感谢您创建和维护好工具!

这只是仅供参考。 在过去的几个月里,我一直在使用类似下面的补丁来开发 Mocha 和* -test.mjs 。 运行mocha test/*.mjs几乎没有问题(没有转译器或esm npm 模块)。
https://gist.github.com/tadd/756d21bad38933c179f10e59bddee6b4

当然,对于 Mocha 提交者来说,这个补丁并不是“生产就绪”的; 对于想要尽快在测试代码中使用 ESM 的 Mocha 用户来说,这只是一个 hack。
我一直在使用带有--experimental-modules选项的 Node.js v11。 目前我使用 v12 并且也可以使用。

这是另一个故事,但 Node v12 在package.json中引入了.cjs扩展和"type"字段。 看来这些也是需要考虑的。

嗨 Mocha 人,感谢您创建和维护好工具!

确实,我也要感谢😄

我采用了不同的方法来使 loadFiles 保持同步,见下文。 自二月以来它一直对我有用。 与其他一些黑客不同,这仍然允许所有标志、检查/开发工具和使用c8的准确代码覆盖率。 最后一个特性是我真正需要原生 ESM 的原因,因为esm包对每个文件都有不同的偏移量。 (模块的导出被列出并混淆了伊斯坦布尔)。

https://gist.github.com/maxnordlund/a860dd67013beaf0f31ce776536f0a47

你好! 这也需要测试任何依赖于原生 ES6 项目的代码,例如lit-element 。 否则,它会像这样抛出:

node_modules/lit-element/lit-element.js:14
import { TemplateResult } from 'lit-html';
       ^

SyntaxError: Unexpected token {
    at Module._compile (internal/modules/cjs/loader.js:703:23)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:770:10)
    at Module.load (internal/modules/cjs/loader.js:628:32)
    at Function.Module._load (internal/modules/cjs/loader.js:555:12)
    at Module.require (internal/modules/cjs/loader.js:666:19)
    at require (internal/modules/cjs/helpers.js:16:16)
    ...

我不知道是否有解决方法,但目前我认为没有办法将 Mocha 与 ES6 本机框架一起使用。

@heruan我在上面的评论中发布了一个从节点 8 开始工作的解决方案。 这是一个更新版本,需要 Node 10.12 才能进行本机文件 URL 转换:

添加以下test.esm.js文件:

const {pathToFileURL} = require("url");
const {sync: globSync} = require("glob");
const {resolve} = require("path");

(async () => {
    const matches = globSync("**/*.spec.mjs"); // Change the glob to match your test files
    for (const match of matches) {
        await import(pathToFileURL(resolve(match)).href);
    }
    run();
})();

然后使用mocha --experimental-modules --delay test.esm.js运行测试。

此代码通过使用test.esm.js作为 commonjs 桥来加载 ESM 测试来工作。

节点 12 报告了 IIAFE 由 GC 收集的问题(nodejs/node#27492),它应该在下一个次要版本中修复(可能有一些解决方法,但我还没有研究)。 我建议在修复之前使用最新的 Node 10 版本。

如果有任何错误,Thar 将触发UnhandledPromiseRejectionWarning警告。 最好链接console.error或以其他方式处理错误。

import glob from "glob"
import { pathToFileURL } from "url"
import { resolve } from "path"
import { promisify } from "util"

const globAsync = promisify(glob)

async function main() {
  const matches = await glob("test/**/*.mjs")

  for (const match of matches) {
    await import(pathToFileURL(resolve(match)).href)
  }

  run()
}

main().catch(console.error)

_我知道这是在require上使用import #$ ,但是请参阅我的要点以获得让您留在 ESM 领域的解决方案_

@demurgos感谢您十天前发布的 Node 10.12 代码片段!

我正在运行 Node 12.1,它似乎工作正常。 这是否会很快添加到 Mocha 中,或者是否有更简单的方法可以使用 Node 12 来做到这一点? 另外,如何在--watch模式下使用它?

https://github.com/standard-things/esm 似乎可以使用--require esm开箱即用,但是放弃另一个依赖项会很棒:) 谢谢。

如果 Mocha 在源代码中切换到 ESM,Rollup 可以提供 ESM 分发文件以及 CommonJS 和/或 UMD 文件。

这里的另一个优点是浏览器 HTML 不需要被额外的脚本标签污染来拉入 Mocha。 测试文件(或主测试入口文件)可以进行导入(并避免测试文件中的“魔术”,只要弄清楚变量来自哪里——只需要跟踪导入路径)。

也可以将 CSS 插件用于 Rollup 以允许注入mocha.css ,进一步减少对 HTML 混乱的需求。

我是一个观察不活动问题的机器人。
此问题最近没有任何活动,我将其标记为stale 。 14天后,如果没有进一步的评论或活动,我将关闭此问题。
感谢您为摩卡做出贡献!

我认为这仍然是相关的。

是否有人能够在(编辑:~Travis~)节点 >=12.11.0 上运行带有 ES6 模块的 Mocha?
在 12.10.0 上,我似乎成功设置了它:
摩卡运行.js

(async () => {
    await import("./tests.js");
    run();
})();

然后mocha --experimental-modules --delay ./mocha-run.js就像魅力一样工作。

但由于某些未知原因,在 12.11.0 上,它的行为好像没有--delay参数:

>  mocha --experimental-modules --delay ./mocha-run.js

(node:6439) ExperimentalWarning: The ESM module loader is experimental.

internal/modules/cjs/loader.js:1007

      internalBinding('errors').triggerUncaughtException(

                                ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/travis/build/Palindrom/Palindrom/mocha-run.js

@tomalec我在节点 12.11.1 上运行带有 ES 模块的 mocha:

__mocha-run.js__

(async () => {
    await import("./tests.mjs");
    run();
})();

但是,手表模式不起作用。 mocha 等待文件更改,但在文件更改后不运行另一个测试运行。

@vanslly你很幸运;)
对我来说,使用 Node 12.12.0 (https://travis-ci.org/Palindrom/Palindrom/builds/597771311#L450) 和mocha-run.js就像你上面建议的那样 (https://github.com/Palindrom/ Palindrom/commit/49835962bdd61c849f115e271bbc6c3f82d30511#diff-24eabf03aee8844b2b4747aa95a6af7d),

mocha --experimental-modules --delay test/mocha-run.js https://travis-ci.org/Palindrom/Palindrom/builds/597771311#L643 , , 仍然抛出同样的错误

这仍然是一个问题是疯狂的! ESM 不再隐藏在 --experimental-modules 后面。 这就是未来。

呃,其实是前几天才公布的……

太晚了——我们都改用 Jest。

嘿,伙计们,只是想确保它保持活力。 请将此作为重中之重,并感谢所有出色的工作!

@luijar这正在 #4038 处进行

我们昨天发布了一个实验版本v7.0.0-esm1 :请参阅发行说明

很高兴看到这个!

不过我可以问一下——缺少对浏览器的引用是否意味着 ESM 的使用在浏览器上不可用,或者仅仅是您不需要像使用 Node.js 一样指定浏览器版本。 我认为在发行说明中提及浏览器的状态可能会有所帮助(如果不支持,可能会有什么计划支持它)。

@brettz9
NodeJs ESM 不会以任何方式影响 Mocha 浏览器。 您在浏览器中运行的测试不会由 NodeJs 加载,您必须自己在 HTML 代码中执行此操作。

如果我没记错的话,您必须将<script>标记设置为type="module"属性。 对于您的测试文件和 Mocha 脚本,以保持加载顺序。 ESM 应该已经使用浏览器多年了。

@juergba :是的,当然,但是需要一个 ESM 导出分发文件,这样就可以使用import mocha from '../node_modules/mocha/mocha-esm.js';而无需编译——对于那些使用编译的人(例如,以便能够只使用import mocha from 'mocha'; ),他们会希望module包含在package.json中,以便打包程序可以自动发现 ESM 构建。

mocha 是用 commonjs 编写的; 我们不能在 package.json 中放置“模块”字段。 Mocha 将支持在用 ESM 编写的节点中运行测试。

如果你不想在内部重构使用 ESM,你应该仍然可以使用 Rollup 及其 CommonJS 插件并指示 ESM 目标文件以支持module (如 Sinon 提供和大多数包注意我遇到过,jQuery 是唯一一个值得注意的例外,他们一直在重构以使用 ESM)。

我创建了一个示例项目来使用 ESM 测试 mocha。 我可以成功运行测试,但(_还_)无法使用 nyc/istanbul 运行覆盖范围。 欢迎您的帮助。

@concatimenyc兼容之前,您可以使用c8https ://www.npmjs.com/package/c8

c8 --all --include=lib/**/*.js --reporter=lcovonly node_modules/.bin/mocha --recursive

@cedx我已经更新了我的模板存储库。它可以工作。 整洁的!

我们在 Mocha v7.1.0 中实现了 Node 的原生 ESM 支持。

此页面是否有帮助?
0 / 5 - 0 等级