Jest: Meta:对 ES 模块的原生支持

创建于 2020-01-19  ·  131评论  ·  资料来源: facebook/jest

编辑:快速入门指南: https :

ESM 支持将在 Node 12 的未来版本中取消标记(可能不会在 4 月之前 https://github.com/nodejs/node/pull/29866#issuecomment-574055057)并且它已经在 Node 13.2 中取消标记,所以我认为它是是时候评估我们如何在 Jest 中添加原生支持了。 我将尝试列出 Jest 当前提供的受 ESM 支持影响的哪些功能,以及我们如何解决/调查它们。

有问题 #4842,但我认为这更像是一个讨论问题,而这个问题将面向实际实施支持,更适合跟踪那些只想了解当前实施状态的人。 添加到此问题的任何评论_不_与我们如何实现对以下列举功能的支持有关,都将被标记为垃圾邮件 - 请将任何解决方法/讨论指向单独的问题。 如果列表中缺少与 ESM 功能相关的任何内容,请随时告诉我们!

请注意,Jest 将使用vm API (https://nodejs.org/api/vm.html) 并且在撰写本文时(节点 v13.6)该 API 的 ESM 部分仍然被标记( --experimental-vm-modules )。 因此,目前说 ESM 未标记有点用词不当。 但我认为我们应该开始试验并可能向模块工作组提供反馈。

最后,我主要是为那些将实施支持的人写这个问题,所以它会有点低级和特定于 Jest 的工作原理。 对于_只是_想知道支持是否已登陆的人,我建议使用 GH 精彩的“自定义通知”,并且只订阅关闭/重新打开的通知。


  • [x] 在正确的上下文中运行模块

我们通过在给定的vm.Context (由 JSDOM 或节点核心 API 提供)中运行脚本来实现沙箱。 我们需要对 ESM 做同样的事情,但我们需要在模块构建期间访问context ,而不仅仅是在执行模块时。 我已经打开了 #9428,它将必要的 API 添加到JestEnvironment

  • [x] 全局变量

expecttestbeforeEach等仍将作为全局变量添加,此处不应有任何更改。 jasmine global 也将在这里。

  • [x] jest “全局”属性

这不是真正的全局 - 它被注入到模块范围内。 由于模块作用域在 ESM 中消失了,我们需要将它移到某个地方。 将它添加到import.meta似乎很自然 - 我们可以使用一个名为initializeImportMeta的选项。

编辑:这里的解决方案是通过import {jest} from '@jest/globals'获取它。 将来我们可能仍然通过import.meta添加它,但现在应该足够了。

  • [ ] jest.(do|un)mock

由于 ESM 在评估模块时具有不同的“阶段”,因此jest.mock不适用于静态导入。 不过它可以用于动态导入,所以我认为我们只需要在文档中明确它支持什么,不支持什么。

jest.mock调用已挂起,但这对 ESM 没有帮助。 我们可能会考虑将import 'thing'import('thing') ,这应该允许提升工作,但它是异步的。 使用顶级await可能是这种方法的必要条件。 我也认为它的侵入性足以保证一个单独的选择。 有什么要讨论的 - 我们不需要为初始版本支持jest.mock所能提供的一切。

  • [ ] jest.requireActual

不确定它是否应该在 ESM 中表现。 我们是否应该提供jest.importActual并让requireActual CJS始终在

  • [x] import.meta

Node 有url作为它唯一的属性(至少现在是这样)。 我们需要确保它也填充在 Jest 中。 我们在构建模块时提供identifier而不是filename所以我认为它不会自动发生,但是url本质上是filename pathToFileURL

还有一个import.meta.resolve的公开公关: https :

  • [x] import thing from 'thing'

这实际上应该相当简单,我们只需要实现一个linker ,我们还可以在返回之前转换源,这意味着我们不需要加载器 API(尚不存在)。 这也允许我们返回模拟(尽管它们必须来自__mocks__目录)。

  • [x] import('thing')

本质上与上面相同,但在构建模块时作为importModuleDynamically传递。 还将更干净地支持jest.mockjest.resetModules等,因此可能会被大量使用。

这也可以通过相同的选项为vm.Script

  • [ ] 在评估期间处理错误

现在这是一个运行时错误(例如未找到模块),但 ESM 不一定是这样。 对我们来说重要吗? 我们应该验证错误仍然看起来不错。

  • [x] module.createRequire

对于想要从 ESM 使用 CJS 的人,我们需要处理这个问题。 我已经打开 #9426 来单独跟踪它,因为实现它与 ESM 支持并没有真正的关系。

编辑:在#9469 中实施

  • [ ] module.syncBuiltinESMExports

https://nodejs.org/api/modules.html#modules_module_syncbuiltinesmexports。 我们关心它,还是仅仅让它成为一个空操作就足够了? 不确定 Jest 中的用例是什么。 使用内置函数已经破坏了沙箱,我认为这无关紧要。

编辑:#9469 使它成为一个空操作。 我觉得可以吗?

  • [] 检测文件是 ESM 还是 CJS 模式

检查模块的package.json type字段似乎是合理的: https :

https://github.com/nodejs/modules/issues/393

  • [x] moduleNameMapper

不确定这是否有任何影响。 我_认为_不会,因为我们将自己将模块链接在一起。 不过需要调查。

编辑:这是我们控制的所有解析逻辑。 所以这里没有变化。

  • [x] jest.config.mjs

通过#9291,我们支持jest.config.cjs - 我们需要为.mjs做任何特别的事情吗? 可能使用import('path/to/configFile.mjs')这意味着它必须是异步的。 这是一个问题吗? 可能值得在 Jest 25 中进行配置解析async ,因此它不会阻碍 Jest 25 中 ESM 的增量支持。

编辑:#9431

  • [ ] 包导出

Node 支持包导出,它有点映射到 Jest 的moduleNameMapper ,但也提供了封装功能。 希望resolve将实现这一点,但如果他们不这样做,我们将需要做一些事情。 可能足以使用pathFilter选项吗? 不确定。

  • [] JSON/WASM 模块

https://nodejs.org/api/esm.html#esm_experimental_json_modules。 我们需要关心吗? 可能,尤其是对于json 。 支持import thing from './package.json'对我们来说是微不足道的,因为我们控制链接阶段,但我们可能不应该默认这样做,因为它与默认节点不同。 我们应该强迫人们为它定义一个变换吗?

  • [x] 代码覆盖率

有关系吗? 我不认为它会受到影响,因为我们仍然可以使用 babel 转换源(也许它会被import语句混淆,可能不会)并且 V8 覆盖绝对不应该关心。 不过我们应该验证一下。

  • [] 异步代码解析

这绝对不是阻止程序,因为同步分辨率可以正常工作。 但是我们现在_可以_使用异步分辨率,这很棒。 我想知道我们是否应该再次使用 npm 的resolve模块,因为它已经支持异步。 见#9505。

  • [] 异步代码转换

与上面类似,不阻塞,但支持它会很好。 也可能使@jest/transformer在其他环境中更可用。 见#9504。

  • [] 访问全局变量时性能不佳

由于#5163,我们有extraGlobals选项作为变通方法 - 该变通方法在 ESM 中不再可行。 我在这里打开并发布节点问题: https :

ES Modules

最有用的评论

我已经通过 #9772 获得了非常基本的支持。 我只测试了最简单的情况,并且有许多已知的限制(最明显的是没有jest对象支持,并且在混合 CJS 和 ESM 时语义被破坏),但至少它是 _something_。 它将在 Jest 的下一个版本中发布(希望很快,仅被 #9806 阻止)

所有131条评论

我已经通过 #9772 获得了非常基本的支持。 我只测试了最简单的情况,并且有许多已知的限制(最明显的是没有jest对象支持,并且在混合 CJS 和 ESM 时语义被破坏),但至少它是 _something_。 它将在 Jest 的下一个版本中发布(希望很快,仅被 #9806 阻止)

25.4.0 已发布,并带有第一批支持。 除了上面提到的#9772,我还包括了#9842。 在 _theory_ 混合 CJS 和 ESM 现在应该可以正常工作(🤞)。

缺少的一项主要功能是支持jest对象。 我还没有决定是否应该坚持使用import.meta或要求人们通过import {jest} from '@jest/globals'导入它。 反馈赞赏!

我还没有为此编写文档,但是要激活它,您需要做 3 件事

  1. 确保您没有运行转换掉import语句(在配置中设置transform: {}或以其他方式确保babel不会将文件转换为 CJS,例如避免modules选项预设环境)
  2. 使用--experimental-vm-modules标志运行node@^12.16.0 || >=13.2.0
  3. 使用jest-environment-nodejest-environment-jsdom-sixteen运行您的测试

请尝试并提供反馈! 如果报告错误,如果您还可以包含如何在 Node.js 中运行相同的代码(减去任何特定于测试的代码),那就太好了。 在过去的几周里,我阅读了https://nodejs.org/api/esm.html _很多_,但我可能错过了一些东西。

缺少的一项主要功能是支持 jest 对象。 我还没有决定我们是否应该坚持使用 import.meta 或要求人们通过 import {jest} from '@jest/globals' 来导入它。

对于打字稿用例,最好有一个显式导入。

是的,我已经添加(并且暂时恢复了)一个支持此功能的@jest/globals包,因此无论如何都可以使用。 我想知道 _also_ 在import.meta上公开它是否有意义。 目前倾向于不这样做,主要是因为添加比以后删除更容易(而且我个人不喜欢全局变量)

+1 对于显式导入,它有点冗长但更易于理解

我在 Node 13.2 和 Jest 25.4 中得到了这个: ES Modules are only supported if your test environment has the getVmContext function我错过了什么?

@zandaqo哦对不起,忘记了这一点。 在上面添加,但它是

使用jest-environment-nodejest-environment-jsdom-sixteen运行您的测试

ReferenceError: jest is not defined我猜这是由于缺少@jest/globals

是的,如前所述,这仅在您不使用jest对象时才有效。
Mock 也可能坏了,没测试过😃

我已经根据我在 e2e 测试目录 ( e2e/native-esm/__tests__/native-esm.test.js ) 和本期中看到的内容编译了一个非常基本的项目。 不幸的是,我仍然无法让它工作🙃任何人都可以检查一下吗?

https://drive.google.com/file/d/1vyDZjsVKOTu6j55QA11GjO9E7kM5WX8_/view?usp=sharing

  • [x] 开玩笑版本 25.4.0
  • [x] 节点版本 v13.12.0
  • [x] package.json 包含推荐的 jest 选项并且似乎没有 babel 转换到位

运行示例脚本(只需导入double函数并打印double(2) ):

npm run main

> [email protected] main /Users/ilya/Projects/jest-esm
> node src/main.js

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

只用一项测试来运行 jest 以实现双重功能:

npm run test

> [email protected] test /Users/ilya/Projects/jest-esm
> jest

 FAIL  __tests__/native-esm.test.js
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /Users/ilya/Projects/jest-esm/__tests__/native-esm.test.js:8
    import {double} from '../src/index.js';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      at Runtime._execModule (node_modules/jest-runtime/build/index.js:1074:58)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.542s
Ran all test suites.

您需要使用--experimental-vm-modules运行节点,并在package.json命名您的文件.mjs"type": "module" package.json

编辑:您可能看到后者在 Jest 之外为您工作,只是缺少 Node 的实验标志

@SimenB哦,哇,有--experimental-vm-modules--experimental-modules 。 从某些节点 13 版本开始,不需要--experimental-modules的事实让我感到困惑。 谢谢!

TLDR: node --experimental-vm-modules node_modules/jest/bin/jest.js对我有用

是的,来自 OP

请注意,Jest 将使用vm API (https://nodejs.org/api/vm.html) 并且在撰写本文时(节点 v13.6)该 API 的 ESM 部分仍然被标记( --experimental-vm-modules )。 因此,目前说 ESM 未标记有点用词不当。

不过,它对你有用,真是太棒了!

(我会将这些评论标记为已解决)

@SimenB谢谢! 到目前为止我看到的两个问题。

第 1 期

  • ESM 单元测试文件从 ESM 文件导入默认值(fn 正在测试)
  • 正在测试的 ESM 文件从包中导入默认值,该包仅导出 CJS
  • 结果报错:CJS包文件中的ReferenceError: module is not defined

所以我在想可能没有正确实施?

import 语句可以引用 ES 模块或 CommonJS 模块

这在运行应用程序时工作正常。 CJS 的命名导出只能通过使用 createRequire 导入,但只能导入默认导出。

第二期

当我没有遇到上述错误时,我会遇到这个错误:

TypeError: _vm(...).SyntheticModule is not a constructor

      at Runtime._importCoreModule (node_modules/jest-runtime/build/index.js:1198:12)

项目详情

  • 节点 12.14.1
  • Jest 和 babel-jest 25.4.0
  • 带有targets: { node: 12 }和 2 个插件的最新 Babel: babel-plugin-transform-import-metarewire-exports 。 (尝试删除import-meta插件,但出现错误提示将其添加回来。)
  • testEnvironment: "node"几乎是唯一的配置
  • node --experimental-vm-modules node_modules/jest/bin/jest.js

如果复制回购有帮助,请告诉我。

谢谢@aldeed!

我会研究问题 1,这确实看起来像一个错误。 编辑:应该通过 #9850 修复

问题 2 需要节点 12.16.0: https ://nodejs.org/docs/latest-v12.x/​​api/vm.html#vm_class_vm_syntheticmodule

我将更改 Jest 中的检查(现在它检查vm.SourceTextModule ,这在更多版本中可用,但我们也需要SyntheticModule )。

确认使用 12.16.0 运行修复了我的问题 2。将在该修复发布后重新测试问题 1。 否则等待jest对象进行进一步测试,我同意它应该需要导入。

很棒的工作,@SimenB! 我正在一个小项目中尝试这个,但遇到了动态导入的问题。 这是我看到的错误:

Module status must not be unlinked or linkingError [ERR_VM_MODULE_STATUS]: Module status must not be unlinked or linking

如果我删除动态导入,则测试将运行(但由于其他原因而失败,当然)。 相同的测试目前正在与 Mocha(最近提供 ESM 支持)一起使用。

如果有帮助,可以在这里看到有问题的动态导入: https :

测试文件在这里: https :

让我知道是否还有其他有用的信息。

谢谢@beejunk! 我一直想知道在完全链接之前导入相同模块的import之间是否可能存在竞争条件。 _似乎_这就是你在这里打的。 我今天会解决这个问题,谢谢你的报告!

编辑:在#9858 中修复。 将修复程序复制到您的仓库中:
image

有没有人设法让它与 TypeScript 一起工作? node --experimental-vm-modules node_modules/jest/bin/jest.js返回相同的SyntaxError: Cannot use import statement outside a module ,即使我的package.json确实有"type": "module"

babel.config.cjs

module.exports = {
  presets: [
    '@babel/preset-typescript',
  ],
};

jest.config.cjs

module.exports = {
  testEnvironment: 'jest-environment-node',
  transform: {},
};

@dandv您实际上是在遇到这种情况: https :

你能开一个单独的问题吗? 需要弄清楚如何处理非 js 扩展

@SimenB :#9860。 谢谢参观。

@aldeed @beejunk 25.5.0 已发布,修复了您的问题。 继续提交错误报告 😀

哦,另外它还包括对import { jest } from '@jest/globals'支持,供等待的人使用👍

感谢您对这一切的快速处理,@SimenB! 我想我遇到了另一个问题。

我一直在尝试在导入路径中使用查询参数作为破坏开发服务器上的模块缓存的一种方式。 这个想法是有一个文件观察器来检测组件中的更改,然后使用任意查询参数更新导入路径,以便在下一个页面加载时立即拉入新代码,而无需重新启动服务器。 这在运行开发服务器时有效。 但是,Jest 抛出以下错误:

Cannot find module '/path/to/project/components/BasePage.js?cache=0' from 'renderPage.js'

这是使用查询参数的示例: https :

如果我删除查询参数,则测试将通过,尽管不一致。 如果我运行单个套件(例如npm test -- test/build.test.js ),测试会通过,但是如果我一次运行所有测试,它们大部分时间都会失败,并出现不明确的错误。 我仍在深入研究不一致测试的问题,但我想我会先报告查询参数问题。

感谢@beejunk 的报告。 #6282 应该处理这个问题,但它也想将查询传递给转换器和其他东西,我们在这里不需要。 所以现在只在运行时内部支持查询可能是有意义的,让 #6282 只处理传递该查询。

我添加了一些代码来使模块缓存因查询而异: https :

但是没有代码会通过查询调用它。 我认为我们应该能够只做resolvePath.split('?')并且一切都应该有效。

关于不一致的错误,我会看看那个 repo 是否重现了它。 我没有用并行测试测试 ESM 代码,只有一个测试。 我不确定为什么它会影响事情,但谁知道呢😀

@beejunk查询问题已在 25.5.1 中修复。 我还没有时间调查另一个问题

我有一个我认为可能与此有关的问题,但是没有在25.X得到解决。

我将尝试总结以下场景:

  • 你有一个设置脚本的测试
  • 该安装脚本将动态需要一个文件,如下所示:
    { default: generator } = require(path.resolve(f))
  • f内容都未转译,导致“意外的标识符导入错误”。

如果我尝试使用 import() 也会发生这种情况

既然你提到了转译; 如果您的设置将importrequire这个问题不是正确的地方 - 这个问题是关于本机支持的。


也就是说 - 你不能require ESM,而且我还没有能够从 CJS 添加对import()支持,因为 Node 没有它的 API。 对此的支持已登陆 Node master,但尚未发布: https :

不过,您应该可以使用.mjs安装文件。

@SimenB这太棒了,它现在似乎在几个测试文件中工作! 解决每个文件中 Babel 和 Node 导入之间的差异、添加 jest 导入等是一个有点过程,所以当我在数百个测试文件中这样做时,我可能会遇到更多问题。

比较多的几个问题:

  • 您在之前关于 cjs 支持import()评论中所说的话,是否也允许将 Jest 配置文件命名为jest.config.js ? 它目前仅适用于命名为jest.config.cjs并使用module.exports = (错误为TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified
  • 由于不是package-lock列出的包,名称“@jest/globals”不符合 eslint 规则node/no-extraneous-import package-lock 。 这个命名约定有什么原因吗? 是否可以在没有@情况下使用jest @ ? 忽略规则很容易,但我只是想知道。
  • 相关问题,为什么jest全局与testdescribebefore等魔法 fns 不同? 现在所有这些都应该进口吗?
  • 当这最终确定时,Jest 是否可以设置--experimental-vm-modules标志? 如果它不只是与jest命令一起工作,那么它看起来有点不标准。 如果您无法直接传递标志,是否可以设置/修改NODE_OPTIONS变量?
  • 在较旧的 Node 上尝试此操作时,我现在似乎遇到了与以前略有不同的错误,但是这两个错误都不清楚最低 Node 版本是问题所在。 使用更有用的错误消息添加对最低版本的检查是否容易?

这是否也允许将 Jest 配置文件命名为 jest.config.js?

如果它最近的package.jsontype: 'module'它可以被命名为.js type: 'module' 。 否则,要在配置文件中使用 ESM,需要将其命名为.mjs 。 请参阅https://nodejs.org/api/esm.html#esm_enabling。 请注意,配置文件在 Jest 之外运行,因此我们没有太多控制权。 目前,我们不支持异步配置,但是await导出的 promise 是微不足道的,欢迎 PR 🙂 #8357 已停止。

我很惊讶你得到A dynamic import callback was not specified但我们没有在 vm 中加载配置文件......你能打开一个单独的问题吗?

由于不是package-lock列出的包,名称“@jest/globals”不符合 eslint 规则node/no-extraneous-import package-lock 。 这个命名约定有什么原因吗? 是否可以在没有@情况下使用jest @ ? 忽略规则很容易,但我只是想知道。

您应该在@jest/globals上添加devDependency @jest/globals 。 包本身只是类型定义。 它与jest是一个单独的包,因此类型定义可以正常工作,因此如果它在 Jest 运行时之外加载,我们可能会抛出错误。

我目前没有任何改变它的计划,但我们可能会弃用该包并从jest导出类型。 不过,这是一个重大的突破性变化,所以让我们稍后再看 🙂

相关问题,为什么jest全局与testdescribebefore等魔法 fns 不同? 现在所有这些都应该进口吗?

jest就像 CJS 中的requiremodule对象,因为它每个文件都是唯一的,它实际上不是全局的(即globalThis.jest === undefined )。 这允许jest.mock('../../file.js')等相对于它被注入的文件正常工作。您也可以选择导入实际的全局变量,但它们仍然可以作为globalThis.expect等使用。

当这最终确定时,Jest 是否可以设置--experimental-vm-modules标志?

我认为我们将等待节点取消标记它们而不是静默设置它们 - 它们被标记是有原因的,API 可能会在某个时候改变。 我们可以添加一个设置NODE_OPTIONS的选项,我猜,不确定是否会改变当前的运行时? 无论如何,目前没有这方面的计划。

在较旧的 Node 上尝试此操作时,我现在似乎遇到了与以前略有不同的错误,但是这两个错误都不清楚最低 Node 版本是问题所在。 使用更有用的错误消息添加对最低版本的检查是否容易?

是的,一旦实现稍微稳定下来,我将添加更好的错误消息和一些文档。 看到实现由实验标志保护,我认为没有人会绊倒它。

@SimenB ,我将 Jest 更新到 25.5.2,现在所有测试都通过了。 查询参数正在运行,我之前看到的间歇性错误不再发生。 再次感谢您的所有工作!

好的,我在上次运行测试时再次看到错误,所以这种情况仍在发生。 我会看看我是否能找到一种方法来始终如一地重现和报告。

我相信我已经找到了重现该问题的一致方法。 这是我工作的分支: https :

重现:

  1. 删除 Jest 缓存(如果存在)。 我只是手动删除了/tmp/jest_rs
  2. 运行npm test三次。 前两次运行应该会成功。 但是,第三次运行应该会失败。

这是发生错误时我看到的堆栈跟踪:

    Error: 
        at invariant (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:1866:11)
        at Runtime.loadEsmModule (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:480:7)
        at Runtime.linkModules (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:548:19)
        at importModuleDynamicallyWrapper (internal/vm/module.js:397:21)
        at htmPreactPath (internal/process/esm_loader.js:31:14)
        at renderPage (/home/brian/Projects/firn.js/lib/renderPage.js:53:15)
        at map (/home/brian/Projects/firn.js/lib/build.js:43:12)
        at Array.map (<anonymous>)
        at build (/home/brian/Projects/firn.js/lib/build.js:36:43)
        at Object.<anonymous> (/home/brian/Projects/firn.js/test/build.test.js:57:5)

该错误似乎确实源于动态导入。 没有错误消息,所以我不完全确定发生了什么。

附加说明:如果我清除 Jest 缓存并通过添加--no-cache更新测试脚本,那么我将无法重现该问题。

呵呵,我在那里很懒,没有提供正确的错误信息。 问题是测试环境已经被拆除了,所以我猜在某个地方缺少了await 。 没有看到任何东西通过你的代码,所以我得再挖一些

@SimenB这是 ESM 配置问题的复制: https :

@SimenB我已经创建了一个最小示例,用于在使用动态导入时重现我上面提到的 Jest 错误:

https://github.com/beejunk/jest-esm-dynamic-import-error

感谢@beejunk 的精彩再现! 我花了更多的时间在这里承认,但没有真正理解这是 Jest 还是 Node 中的错误。 我仅使用节点核心模块重现了该行为并将其报告给上游,所以让我们看看他们怎么说: https :

感谢您的调查,@SimenB。 如果我添加--no-cache标志,测试似乎始终通过,这对我的用例来说很好。 我感谢所有的工作!

是的,我也注意到了。 我认为这是某种计时问题 - 没有缓存的东西足够慢以致于它可以工作。

@SimenB感谢您解决 #9935。 我在那里提到了第二个问题,我认为它仍然有效。 当type: "module"jest --init仍在生成包含module.exports的配置文件。 如果您知道自己在做什么,那么手动更改相对较小,但我认为如果 ESM 在 Node 中未标记并且很多人开始做 ESM 项目,它会开始看起来更像是一个令人困惑的错误(即, jest --init && jest在新的 ESM 项目上会抛出错误)。 我应该提交一个专门关于改进初始化逻辑的不同问题吗?

@aldeed你确定吗? 现在测试给了我一个mjs文件,其中包含export default 。 我想我们可以生成js而不是mjs ,但仍然如此。 它使用 ESM 语法

@SimenB好吧,直到你问我才确定。 😄我试过了,你是对的。 也许我最初是用旧版本的 Node 或 Jest 做的? 漠视。

这太棒了! 刚刚在我的一个库中重新编写了测试以使用 ES 模块并放弃了 Babel。 谢谢@SimenB!

检测文件是否应该是 ESM 或 CJS 模式

说到这里,有很多面向浏览器/捆绑器的包使用"module":"<path to es module>"语法来表示它们的 ES 模块导出。 无论包自身的设置如何,使用某种方式指定如何解析给定包可能是谨慎的做法。 类似于moduleNameMapper但要指定它是 CJS 还是 ESM。

@SimenB ,一旦此问题关闭,这意味着ts-jest也可以取消强制commonjs对吗? 从变压器端更改为使用 esm 是否需要文件扩展名?

例如,现在ts-jestts ts-jest编译ts jscommonjsesm mjs在编译时是否需要文件扩展名tsjs

@zandaqo我们将不支持modules字段,我们将遵循节点的规范并使用exports :#9771。 如果需要,您可以插入自己的解析器以支持modules ,不过: https: //jestjs.io/docs/en/configuration#resolver -string。 我们可能会添加一些其他选项( mainFields ,可能像 webpack 一样?),但是当实现稳定并且我们有更少的未知未知数时,这将进一步下降🙂


@ahnpnl #9860

小伙子们!
只是一个问题:博客文章提到,由于 ES6 模块是静态的,它们不能被模拟; 所以,实际上,没有办法模拟 ES6 中模块 B 导入的模块 A?

@gabrieledarrigomoduleNameMapper ,例如:

    "moduleNameMapper": {
      "moduleA": "<rootDir>/test/moduleA-mock.js"
    },

@gabrieledarrigo你可以做到

jest.mock('the-thing-i-want-to-mock', () => /* do whatever in here */);

let importedThing;

beforeAll(async () => {
  importedThing = await import('thing-that-imports-mocked-module');
});

所以你只需要使导入非静态并且模拟就可以工作。

我不确定它现在是否有效,因为我还没有在 ESM 代码路径中设置模拟分辨率。 很快就会在某个时候这样做。 但这可能是我们为原生 ESM 记录模块模拟的方式。

正如博客中提到的,我们将在某个时候记录这些模式,我们只需要弄清楚它们🙂

我们的一个想法是等待顶级等待,然后我们可以使用 babel 插件来做到这一点。

@SimenB首先,感谢您在这里的出色工作:)

当我想创建从jest-environment-node扩展的 customEnvironment 时,我实际上面临一个问题。 我需要在那里导入我的服务器实现,它写为 esm。 但看起来,环境必须定义为cjs
我的问题是,是否可以选择将自定义 testEnvironment 定义为 esm 以便能够导入我的服务器模块? 谢谢你的任何建议。

@kuka-radovan 你能为此打开一个单独的功能请求吗?

更新:此问题现已在https://github.com/facebook/jest/issues/10025 中跟踪

@SimenB感谢上面的jest.mock建议。 碰巧我正在转换一些今天需要的文件。 当模拟模块是node_modules包时,我可以确认您的示例有效,但它不适用于我模拟同一项目中的模块。

这是一个简单的例子:

// main.js
import secondary from "./secondary.js";

export default function main() {
  return secondary();
}

// secondary.js
export default function secondary() {
  return true;
}

// test.js
import { jest } from "@jest/globals";

jest.mock("./secondary.js");

let main;
let secondary;
beforeAll(async () => {
  ({ default: main } = await import("./main.js"));
  ({ default: secondary } = await import("./secondary.js"));
});

test("works", () => {
  secondary.mockReturnValueOnce(false); // TypeError: Cannot read property 'mockReturnValueOnce' of undefined
  expect(main()).toBe(false);
});

"./secondary.js"是包名时,完全相同的模式起作用。 (我认为我尝试导出 CommonJS 的包,如果这很重要的话。)

Jest 26.0.1 带节点 12.16.3

任何想法,或者我应该提交一个完整的单独问题?

编辑: transform: {}在配置中,所以根本没有 Babel

编辑 2:这也不起作用:

jest.mock("./secondary.js", () => ({
  default: jest.fn()
}));

这方面的惊人工作。

但是,除非我做错了什么,似乎还不能在 CJS 测试文件中使用import()

我正在用node --experimental-vm-modules node_modules/jest/bin/jest.js运行 Jest 并且在testEnvironment: 'node', transform: {}中有jest.config.js 。 这是在节点 14.2.0 上。

导入表达式产生错误:

TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]:
A dynamic import callback was not specified.

这是目前已知的限制吗? 我看到https://github.com/nodejs/node/pull/32985现在已经登陆 Node 14.1.0。

是的,我还没有开始实施它。 我可能会在这个周末降落。

@aldeed你能开一个单独的问题吗? 我需要通过并确保模拟是解决方案的一部分,你的例子似乎是一个很好的测试用例🙂

@SimenB感谢您的迅速回复。 我会留意它什么时候降落。

由于回归(https://github.com/nodejs/node/issues/33166),脚本中的import可能会被恢复,让我们推迟直到解决

我在尝试将它与.mjs测试文件一起使用时遇到问题。 如果我有__tests__/my-test.mjs ,我得到

$ yarn test
yarn run v1.22.4
$ node --experimental-vm-modules node_modules/jest/bin/jest.js
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In C:\Users\Domenic\Dropbox\Programming\WIP\remember-to-eat
  1 file checked.
  testMatch: **/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x) - 0 matches
  testPathIgnorePatterns: \\node_modules\\ - 1 match
  testRegex:  - 0 matches
Pattern:  - 0 matches
error Command failed with exit code 1.

如果我添加

"testMatch": ["**/__tests__/**/*.mjs"]

到我的 package.json,我得到

$ yarn test
yarn run v1.22.4
$ node --experimental-vm-modules node_modules/jest/bin/jest.js
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In C:\Users\Domenic\Dropbox\Programming\WIP\remember-to-eat
  1 file checked.
  testMatch: **/__tests__/**/*.mjs - 0 matches
  testPathIgnorePatterns: \\node_modules\\ - 1 match
  testRegex:  - 0 matches
Pattern:  - 0 matches
error Command failed with exit code 1.

但是,如果我删除"testMatch"然后将我的文件重命名为__tests__/my-test.js ,它就可以工作。

我希望能够在我的项目中始终使用 .mjs 扩展名。 用 Jest 可以吗?

@domenic我也遇到了这个。 解决方案是添加到配置"moduleFileExtensions": ["js", "mjs"] (除了"testMatch" )。

看了一下,确实需要moduleFileExtensions

Jest 通过在此处运行hasteFS.getAllFiles()来获取项目中所有文件的列表:

https://github.com/facebook/jest/blob/2460c059ad1dbf124466ac25c8d5ccfd74ae9f25/packages/jest-core/src/SearchSource.ts#L159 -L164

hasteFS作为HasteMap一部分创建,具有以下extensions配置:

https://github.com/facebook/jest/blob/2460c059ad1dbf124466ac25c8d5ccfd74ae9f25/packages/jest-runtime/src/index.ts#L291


但是,我认为在这种情况下没有必要指定moduleFileExtensions 。 我们已经强制找到.snap ,我们是否也应该强制使用众所周知的 JS 扩展? 那些(在我的脑海中) jsmjscjsjsxtstsx ? 它会使抓取速度变慢,但我认为它不会产生巨大影响。 不过我可能错了? 作为默认设置,它应该不会慢很多,因为只有cjsmjs已经不是默认模式的一部分,但是对于拥有自定义模式的人来说,它可能会减慢速度?

是的,如果至少在 ES 模块模式下,.mjs 可以正常工作,而无需添加 moduleFileExtensions 或修改默认的 testMatch,那将是理想的。

如果我可以排除 .js 文件也很好; 当我尝试我得到

 Validation Error:

  moduleFileExtensions must include 'js':
  but instead received:
    ["mjs"]
  Please change your configuration to include 'js'.

我想知道是否有一些“ESM 模式”可以添加节点 esm 文件扩展名并帮助使用 esm 选择加入的 compile-to-js (#9860)。


如果没有js ,我们在沙箱中加载的一些内容会在内部中断(因为它使用相同的require实现等)。 我们可能应该解决这个问题,这样用户就不会破坏我们。

关于慢下来,在大型项目上已经很慢了,但我不知道扩展的数量影响那么大。 但我同意 mjs 和 cjs 应该添加为默认值。 指定moduleFileExtensions: ['js']会覆盖默认值并加快速度,对吗? 所以也许只是将其记录为性能调整。

感谢所有这些工作! 这当然是惊人的。 我跟着3个步骤( "type": "module"我的package.json, "testEnvironment": "jest-environment-node"我开玩笑的配置和--experimental-vm-modules的CLI),它似乎也运作良好🎉

但后来我试图阅读和使用import.meta如 Node.js 文档中所述(从复选框来看似乎已经实现)来创建__dirname ,但它似乎像import.meta失败:

console.log(import.meta);

SyntaxError: [PATH]/files.test.js: Support for the experimental syntax 'importMeta' isn't currently enabled (31:20):
    Add @babel/plugin-syntax-import-meta (https://git.io/vbKK6) to the 'plugins' section of your Babel config to enable parsing.

我没有任何 babel,我认为 babel 被这项工作抛在了后面。 如果我可以在不安装 babel 的情况下以某种方式修复它,我会回来报告。

Node.js v14.3.0、Jest v25.5.4

我现在找到了解决方法。 由于我从我的文件在我的库中的同一目录运行脚本,我可以这样做:

const __dirname = process.cwd();
const __filename = __dirname + "/files.test.js";

如果有任何更新,我会关注这个 repo,再次感谢你这样做!

您需要通过使用transform: {}作为配置来明确选择退出 Babel

@SimenB我可以确认添加transform: {}有效,谢谢! 我将这一点误解为“不要添加转换”,而不是像预期的那样“取消转换”。

顺便说一句,测试时间1.3 秒,而且他们始终感觉更快。

Node 12 已发布,带有未标记的 ESM (https://nodejs.org/en/blog/release/v12.17.0/)。 不过,正如 OP 中所述,Jest 使用的 API _not_ 未标记

@SimenB我多次浏览此线程,但仍然卡住(使用节点 12.17)。

运行 Jest 26.0.1 时出现此错误:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /app/tests/setup.js
require() of ES modules is not supported.
require() of /app/tests/setup.js from /app/node_modules/@jest/transform/build/ScriptTransformer.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename setup.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /app/package.json.

我有transform: {},并使用node --experimental-vm-modules node_modules/jest/bin/jest.js

我错过了什么?

@aldarund不确定,你能把最小的复制放在一起吗?

@SimenB这是一个最小的 repo 来重现,只需运行yarn test https://github.com/aledalgrande/jest-example - 我已经尝试过 Node 13/14,结果是一样的。 在我看来,全局设置的流程尚未更新以与 ESM 一起使用。

还有,你提到了别人😆

~不是@simenB ,而是@aledalgrande ,从我的尝试来看,您似乎在这里的所有内容都是正确的,请查看我的项目在 ESM 上完全运行以进行比较( package.json中的玩笑配置)。~

~如果可能的话,为了调试它,我建议将你的 jest 配置简化为 _only_ 具有两个相关属性,甚至可能首先在package.json 。 然后添加您当前必须查看的每个其他属性,看看哪个有效/无效。~

啊第二条评论提到了globalSetup而不是正常的测试,然后 nvm 我的评论。 如果我在 Jest 中删除globalSetup键,那么该示例中的测试会按预期运行,但是globalSetup键不会像您说的那样工作。

啊哈,我忘记了全局设置和拆卸。 可以修复👍

@SimenB ,我又来了。 是否支持命名导出? 使用 Node.js,我可以导入和使用这样的包:

import { customAlphabet } from "nanoid";

但是,当尝试进行测试时,相同的代码会出现此错误:

SyntaxError: The requested module 'nanoid' does not provide an export named 'customAlphabet'

对于测试,我可以将代码更改为此,它可以工作:

import nanoid from "nanoid";
const { customAlphabet } = nanoid;

但是随后 Node.js 版本停止工作,因为实际上没有默认运动(但由于某种原因,默认导出适用于 Jest):

SyntaxError: The requested module 'nanoid' does not provide an export named 'default'

已发布的(repo 现在似乎在不断变化) nanoid代码以这样的方式结束,没有默认导出:

export { nanoid, customAlphabet, customRandom, urlAlphabet, random }

Jest 仅消耗“主要”入口点。 尚未考虑“出口”。 您只需导入只有默认导出的 commonjs 版本。

啊我明白了, package.json似乎包括这个:

  "main": "index.cjs",
  "module": "index.js",
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "require": "./index.cjs",
      "import": "./index.js",
      "browser": "./index.browser.js"
    },
    ...
  }
  ...

所以可能 Node.js 正在寻找模块版本,而 Jest 使用的是没有命名导出的 CommonJS 版本,对吧?

我会等到Package Exports被检查,然后测试它,再次感谢所有的工作! 在此之前将这 2 条评论标记为已解决。 我所指的测试是这个

我正在重新审视它以了解它的工作原理 - 升级到 Jest 26.0.1 和节点 14.4。 将 package.json 设置为模块类型,将转换设置为{} ,将 env 设置为jest-environment-node并使用node --experimental-vm-modules 。 现在我收到这个新错误:

ES Modules are only supported if your test environment has the `getVmContext` function

我一直无法找到这方面的信息,除了 Jest 的更新日志说getVmContext已经添加了一段时间。

有任何想法吗?

你能分享一下你的package.json的相关部分吗@cyberwombat ? 包括您用于 Jest 的启动脚本。

作为参考,这是我在一个工作项目中的样子:

{
  ...
  "type": "module",
  "scripts": {
    ...
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
  },
  "jest": {
    "transform": {},
    "testEnvironment": "jest-environment-node"
  },
  ...

然后用npm test启动它

@franciscop我的基本上是一样的。 节点 14.4.0。 我可以很好地运行你的。 我将深入研究以查看差异。
包.json

{
  "type": "module",
  "devDependencies": {
    "jest": "^26.0.1",
  },
}

开玩笑的配置文件

export default {
  testEnvironment: 'jest-environment-node',
  setupFilesAfterEnv: ['./test/bootstrap.js'],
  testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/config/', '/<rootDir>/src/'],
  testRegex: '(\\.|/)(test|spec)\\.[jt]sx?$',
  transform: {
//    '^.+\\.jsx?$': 'babel-jest' // esm someday
  },
  transformIgnorePatterns: [],
  modulePaths: [
    '<rootDir>/test',
    '<rootDir>/src',
    '<rootDir>'
  ]
}

脚本:
node --experimental-vm-modules node_modules/jest/bin/jest.js

不确定,但我会尝试以相反的方式工作。 删除除transform: {}testEnvironment: 'jest-environment-node' ,然后开始添加每个选项,直到您看到哪个选项触发了上一个错误。 我特别怀疑transformIgnorePatterns _might_ 与transform冲突,但我对开玩笑的选项不太熟悉。

大家好! 我在使用 Jest 测试 Express 应用程序时遇到了一些问题。 更多细节在这里。 不确定这是否对您在此处执行/跟踪的操作有用:roll_eyes:

@x80486昨天遇到了在 StackOverflow 中回复了更长的解释。

编辑:我取消隐藏我之前的评论,因为它似乎可能相关,这个"exports"似乎很受欢迎,很可能来自这篇关于混合包的文章

exports在 #9771 中被跟踪

@franciscop ok 问题解决了 - 事实证明包中存在冲突 - 我安装了serverless-bundle导致ES Modules are only supported if your test environment has the getVmContext function错误。 我不知道为什么 - 我认为安装它不会导致与 Jest 的运行冲突,但显然它会。

@franciscop我认为pkg.exports相关问题现在开始浮出水面的原因是因为该功能在 Node.js 中未标记14.x并且一些包维护者(如我的uuid )开始添加pkg.exports字段。 因此,虽然您需要一个命令行标志来激活 Node.js 12.x该功能,但您现在默认情况下会获得该行为。

整个生态系统需要一段时间才能适应,因此感谢您报告有关该主题的问题!

对于那些发布关于exports ,如果它在这个问题的长线程中丢失了,我关于它的已关闭问题(https://github.com/facebook/jest/issues/9565)有一个例子其中的moduleNameMapper解决方法。

5 月份报告的globalSetup问题可能仍然存在(Jest 26.1.0)? 获得与示例 repo @aledalgrande 中相同的错误提供:

$ git clone [email protected]:aledalgrande/jest-example.git
$ cd jest-example
$ npm test

> @ test /Users/asko/Temp/jest-example
> node --experimental-vm-modules node_modules/jest/bin/jest.js --config=./jest.config.js

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/asko/Temp/jest-example/tests/setup.js
require() of ES modules is not supported.
require() of /Users/asko/Temp/jest-example/tests/setup.js from /Users/asko/Temp/jest-example/node_modules/@jest/transform/build/ScriptTransformer.js 

不着急。 检查CHANGELOG并且它没有提到使用 ES6 修复 globalSetup/globalTeardown。

Node.js 14.4.0,Jest 26.1.0


更新(20 年 8 月 13 日):

仍然不可能,Node.js 14.7.0,Jest 26.4.0

侧面意见,但这个问题应该是一个固定的问题,因为它是目前开玩笑的焦点吗?

关于使用 ES 模块编写的测试报告器需要做什么的任何想法?...
使用最新的 jest 版本,我收到错误,基本上说 testScheduler 需要 commonjs 格式的自定义报告器。


查看错误

~/projects/esw-ts/lib/dist/test/testReporter.js:1
从'os'导入操作系统;
^^^^^^

语法错误:不能在模块外使用导入语句
在 wrapSafe (internal/modules/cjs/loader.js:1116:16)
在 Module._compile (internal/modules/cjs/loader.js:1164:27)
在 Object.Module._extensions..js (internal/modules/cjs/loader.js:1220:10)
在 Module.load (internal/modules/cjs/loader.js:1049:32)
在 Function.Module._load (internal/modules/cjs/loader.js:937:14)
在 Module.require (internal/modules/cjs/loader.js:1089:19)
在要求 (internal/modules/cjs/helpers.js:73:18)
在 /Users/manish.gowardipe/Desktop/projects/esw-ts/lib/node_modules/@jest/core/build/TestScheduler.js:418:65
在 Array.forEach ()
在 TestScheduler._addCustomReporters (/Users/manish.gowardipe/Desktop/projects/esw-ts/lib/node_modules/@jest/core/build/TestScheduler.js:411:15)

嗨,我想在我的小项目中测试对 ES 模块的原生支持,但我是 NodeJS 的新手,我在这个问题中迷路了,我希望得到一些指导。

  • node --version : v14.5.0
  • yarn jest --version : 26.1.0
  • 我正在尝试测试这个小项目,非常简单。
  • 我有这样的文件:

包.json

{
"jest": {
    "transform": {},
    "testEnvironment": "jest-environment-node"
  }
}

markov.test.js

const fs = require("fs");
const Markov = require("./markov.mjs");
// import fs from "fs";
// import Markov from "./markov.mjs";

const file = fs.readFileSync("text.txt", "utf8");
const markov = new Markov(file.toString());

test("Generates sentence with especified words", () => {
  expect(markov.makeSentence(8).length).toBe(8);
});
  • 我运行yarn jest . ,它给了我这个错误:
    imagen

  • 我试过node node_modules/jest/bin/jest.js . ,它给了我同样的错误。

@pepetorres1998该线程是关于使用本机 esm 模块运行 Jest,其中涉及运行具有某些标志/选项的东西 - 请参阅上面的评论以了解要做什么(并在 package.json 中设置“type”:“module”)。 老实说,虽然在这一点上它还没有准备好迎接黄金时间,所以如果你需要你的项目工作,我可能会坚持使用 Babel。 有许多未经检查的问题是真正的表演障碍。 几周前我兴高采烈地尝试切换,然后哭着回到 Babel。

其他人在尝试使用此设置在测试文件中执行jest.setTimeout(...)类的操作时是否会收到ReferenceError: jest is not defined ? 试图弄清楚这是否与 es 模块环境、节点版本、jest 版本或这些东西的某种组合有关。 (目前使用 node v14.5.0, jest 26.1.0, environment jest-environment-node)

编辑
我现在在 jest 'global' 属性的问题描述中看到未选中的复选框。 🙃

@bdentino我认为您可以尝试显式导入它import {jest} from '@jest/globals';

25.4.0 已发布,并带有第一批支持。 除了上面提到的#9772,我还包括了#9842。 在 _theory_ 混合 CJS 和 ESM 现在应该可以正常工作(🤞)。

缺少的一项主要功能是支持jest对象。 我还没有决定是否应该坚持使用import.meta或要求人们通过import {jest} from '@jest/globals'导入它。 反馈赞赏!

我还没有为此编写文档,但是要激活它,您需要做 3 件事

  1. 确保您没有运行转换掉import语句(在配置中设置transform: {}或以其他方式确保babel不会将文件转换为 CJS,例如避免modules选项预设环境)
  2. 使用--experimental-vm-modules标志运行node@^12.16.0 || >=13.2.0
  3. 使用jest-environment-nodejest-environment-jsdom-sixteen运行您的测试

请尝试并提供反馈! 如果报告错误,如果您还可以包含如何在 Node.js 中运行相同的代码(减去任何特定于测试的代码),那就太好了。 在过去的几周里,我阅读了https://nodejs.org/api/esm.html _很多_,但我可能错过了一些东西。

@西门子
这个线程变得非常庞大,我认为那些想要开始玩笑/使用 ES 模块的人将很难找到和理解开始这样做的基本指南。
文档中是否有关于将笑话添加到 ES 模块项目(或某些“快速入门”)的正式解释?

@aldeed关于您在同一项目中

(顺便说一句,我们也使用reactioncommerce,所以欢呼吧哈哈)

@guilhermetelles不,现在在https://github.com/facebook/jest/issues/10025 中对其进行了跟踪。

我正在使用 Jest 26.1.0, node版本 14.6.0 和--experimental-vm-modules ,但是在 CommonJS 中使用import()时我仍然看到ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING . 我应该尝试提出一个最小的重现并打开一个新问题吗?

顺便说一句,既然 Jest 使用了 yarn berry,是否有一种简单的方法可以将yarn link的副本jest放入项目中? 我想尝试最新的master以防万一这是尚未发布的。 我试图做类似path/to/facebook/jest/.yarn/releases/yarn-sources.cjs link --all path/to/jest事情,但它会失败。 手动运行cd node_modules; for p in jest*; do if [[ -d path/to/jest/packages/$p ]]; then rm -rf $p; ln -s path/to/jest/packages/$p; fi; done也不起作用,我不知道为什么。

@vvanpo import() in CJS 在 Node 中被还原了,可以关注https://github.com/nodejs/node/issues/31860

至于在本地运行,我通常只是从我想测试的项目中卸载jest并执行../jest/jest 。 可能是nose ../jest/packages/jest/bin/jest.js 。 只要确保先运行yarnyarn build:js 。 如果这些说明不起作用(我在飞机上的手机上从内存中写入)请打开一个问题(或 PR),以便我们可以正确地将其写入CONTRIBUTING.md文件

您是否计划支持循环导入?

如果我有一个仅导入两个仅相互导入的文件之一的虚拟测试文件,我会得到RangeError: Maximum call stack size exceeded 。 如果我删除其中一个导入,则测试通过。 重现问题的回购

嘿! 我在一个空的节点项目中设置了它并且它运行得非常好,但是在我们的生产设置中,当我尝试运行测试时,我收到以下错误消息:

ES Modules are only supported if your test environment has the 'getVmContext' function

我在之前的回复( @cyberwombat )中看到其他人遇到了一些问题,但他们发现的罪魁祸首包不在我们的package.json文件中。 如何推断导致问题的包(或设置)? 我已经尝试系统地删除所有不需要进行这项工作的笑话设置,但我没有成功。

更新:我已经通过对jest-runtime稍作改动而取得了进展。 我在尝试访问 VM 上下文的行停止了调试器,虽然该函数确实不存在,但this.context (它应该返回)确实存在,所以我更改了该行以直接访问该属性。 我知道这可能并不理想,但也许@SimenB这可以让您了解出了什么问题?

预先感谢您的任何帮助

您是否计划支持循环导入?

确实! 你能开一个单独的问题吗?


@zsombro似乎您正在运行一些旧版本的测试环境。 如果您运行jest --show-configtestEnvironment什么?

似乎您正在运行一些旧版本的测试环境。 如果您运行jest --show-configtestEnvironment什么?

@SimenB它说如下:

"testEnvironment": "/Users/zberki/git/project-name/node_modules/jest-environment-node/build/index.js",
"testEnvironmentOptions": {},

我只是根据您的指示将其设置为jest-environment-node

在我开始这个过程之前,我使用yarn add jest@latest升级了 jest。 需要单独升级环境吗?

更新:事实证明我不得不这样做。 我删除了node_modulesyarn.lock以进行全新安装,但仍然无法正常工作。 但是,如果我使用yarn add -D jest-environment-node手动添加它,它似乎可以工作。 有没有更好的方法来管理这个? 在对我们的代码库执行此操作之前,我做了一个简约的测试项目,我不必这样做

yarn list jest-environemnt-node (或npm list jest-environemnt-node )可能会列出多个,是我的猜测

├─ [email protected]
│  └─ [email protected]
└─ [email protected]

26.2.0版本可能是我手动安装的(至少基于package.json ,这意味着jest-config安装了一个显然已经过时的版本?

您还有其他东西引入了旧版本的jest-config (可能是react-scriptscreate-react-app )?)。 这个问题不是讨论它的地方,不过🙂

无法在我的globalSetup使用 ES 模块开始受到伤害。

两点:

  • 是否应该在此问题的开头将其作为复选框提及(以便对其进行跟踪)?
  • 如果有 alpha/beta 我可以尝试,愿意这样做

一世:

  • 确保我运行的是最新的 Jest 版本 (26.4.0)
  • 将 jest-environment-node 添加到我的项目中
  • 通过检查锁定文件确保它没有重复
  • 在 jest.config.json 中添加了"testEnvironment": "jest-environment-node",
  • 在使用 jest 的地方添加了import { jest } from '@jest/globals';
  • 通过使用NODE_OPTIONS='--experimental-vm-modules' yarn jest运行测试命令设置--experimental-vm-modules NODE_OPTIONS='--experimental-vm-modules' yarn jest

它在以下代码上崩溃:

jest.mock('../../some/other/path', () => ({
  someOtherMethod: jest.fn().mockImplementation(…),
}));

出现以下错误(缩短 - 请注意“允许的对象”!):

ReferenceError: src/foo/bar.spec.js: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
Invalid variable access: jest
Allowed objects: Array, …, jest, …, unescape.
Note: This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with `mock` (case insensitive) are permitted.

我不能使用 Babel,因为它错误地解析了我在没有 Babel 的情况下在 Node 14 上运行的导入:

-import { map } from 'lodash';
+import lodash from 'lodash';
+const { map } = lodash;

不幸的是,它被@babel/preset-env解析不当,导致TypeError: Cannot destructure property 'map' of '_lodash.default' as it is undefined.

有人可以帮我解决这个问题吗?

编辑:看起来你_可以_通过使用 CommonJS 导入在原生 ES 模块兼容代码上使用 Jest+Babel 通过执行这个绝对令人厌恶的修复:

jest.mock('common-js-module', () => ({
  __esModule: false,
  ...jest.requireActual('common-js-module'),
}));

这条路,

import lodash from 'lodash';
const { map } = lodash;

被 Node 14 完美消耗,运行 Jest+Babel 产生的代码,

var _lodash = _interopRequireDefault(require("lodash"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const {
  map
} = _lodash.default;

也运行。

我们已经成功地将所有的 jest 测试转换为使用和导入我们的 ES6 代码,但是我们卡在了几个包上:即puppeteeruuid

该应用程序仅在我们将它们导入对象(如import uuid from 'uuid' )时才有效,但测试不会以这种方式运行。 但是,如果我们用解构语法(例如import { v4 } from 'uuid'替换此导入,则相反:测试有效,但应用程序抛出异常。

最初,我们按照指南关闭了所有转换,但我们也尝试创建一个纱线工作区,在其中安装了具有最小节点配置的 babel,但这并没有解决(或恶化)这个特定问题

但是,如果我们用解构语法替换此导入(例如 import { v4 } from 'uuid',则相反:测试有效,但应用程序抛出异常。

这听起来像是您的应用程序已编译为 CommonJS,并且在实践中并未使用模块。 从“真正的”ESM import uuid from 'uuid'不应该工作,因为uuid没有默认导出公开了 node 的 ESM 构建

@SimenB ,你认为一些关于此的初步文件是个好主意吗?

@grantcarthew绝对! 我曾希望我能花更多的时间在这上面并为 Jest 27 稳定它,但我怀疑我能做到这一点。 但是写一个关于现在有什么(并且它是实验性的)的文档页面听起来是个好主意

@SimenB我不知道问题的当前状态是什么,也不知道 Jest 是否应该已经适用于我的案例,但也许它可以以某种方式帮助您。

我正在尝试加载一个 esm-only 库(他们的扩展名是 cjs 但类型是模块和节点似乎没问题)但是 Jest 无法正确加载它并出现错误:

    C:\dev\codemirror-next-repro-cra\test-in-jest-esm\node_modules\style-mod\dist\style-mod.cjs:15
    export var StyleModule = function StyleModule(spec, options) {

这里是我最初打开的问题https://github.com/codemirror/codemirror.next/issues/310。 以及节点 14.13.1 https://github.com/dubzzz/codemirror-next-repro-cra/tree/main/test-in-jest-esm失败的 Jest + ESM 的再现

@dubzzz你不能在cjs文件中使用 ESM。 节点也失败

$ node node_modules/style-mod/dist/style-mod.cjs
(node:48829) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/simen/repos/codemirror-next-repro-cra/test-in-jest-esm/node_modules/style-mod/dist/style-mod.cjs:15
export var StyleModule = function StyleModule(spec, options) {
^^^^^^

SyntaxError: Unexpected token 'export'
    at wrapSafe (internal/modules/cjs/loader.js:1172:16)
    at Module._compile (internal/modules/cjs/loader.js:1220:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1277:10)
    at Module.load (internal/modules/cjs/loader.js:1105:32)
    at Function.Module._load (internal/modules/cjs/loader.js:967:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

哎呀很抱歉,我在节点方面尝试得太快了。 @nicolo-ribaudo 已经将这个问题通知了 lib 的作者。
真的非常感谢您的快速答复。

我在这里为一些(相当多的存根)文档打开了一个 PR:#10611。 我没有费心列举缺失的功能/错误,因为我认为这很难与现实保持同步,查看 github 问题是一种更好的方法,因为它们(希望......)是最新的。

@Pomax作为一个新问题,请🙂

我刚刚打开了#10620,它增加了对 CJS 的import()支持。 请求了几次就像https://github.com/facebook/jest/issues/9430#issuecomment -626054595

你好。 我很难在 node/jest 中快速了解 ESM 背后的整个故事,因此,可能我在问一些明显的或已经回答的问题。 我是否正确理解尚不支持以下情况? 或者,我希望,我正在做一些不正确的事情? 我觉得import x from 'x'可以工作,但import { sub } from 'x'解构不行。

包.json:

{
  "name": "jest-uuid",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "test": "node --experimental-vm-modules node_modules/.bin/jest"
  },
  "devDependencies": {
    "jest": "26.5.2"
  },
  "dependencies": {
    "uuid": "8.3.1"
  }
}

f.spec.js

import { v4 } from 'uuid';
test('', () => {});

npm 测试

> npm test

> [email protected] test /Users/igoro/p/tmp/jest-uuid
> node --experimental-vm-modules node_modules/.bin/jest

 FAIL  ./f.spec.js
  ● Test suite failed to run

    SyntaxError: The requested module 'uuid' does not provide an export named 'v4'

      at jasmine2 (node_modules/jest-jasmine2/build/index.js:228:5)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.879 s
Ran all test suites.
(node:94492) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
npm ERR! Test failed.  See above for more details.

你在等#9771。 在此之前,Jest 不知道将uuid加载为 ESM 是安全的(或者更确切地说,在知道它是 ESM 时加载哪个文件)

这会遵循 Node 自己的约定,其中 CJS 只能作为命名空间加载,还是会通过允许在 Node 本身中实际上不起作用的语法来“改进”它? (例如,Node 不允许import { readdirSync } from "fs-extra"因为这是一个 CJS 包,但它允许import fs from "fs-extra";然后可以使用const { readdirSync } = fs解包)。

(例如,Node 不允许 import { spawn } from "child_process" 因为这是一个 CJS 包,但它允许从 "child_process" 导入 child_process; 然后可以使用 const { spawn } = child_process; 解包)。

这是一个不幸的例子,因为 node 将“child_process”视为“内置”(而不是 CJS)模块,因此命名导出确实有效。 最新的 nodejs 还使用启发式方法使许多命名导出适用于 CJS 模块。 这可能是最难效仿的部分。

示例更新为使用fs-extra代替。 但是,如果命名导出在 Node 的路线图上以登陆这个或下一个专业,那么 Jest 抢占是有道理的。

这应该已经实现了 - Node 核心模块公开命名导出,“普通” CJS 没有。

最新的 nodejs 还使用启发式方法使许多命名导出适用于 CJS 模块。 这可能是最难效仿的部分。

你有执行它的 PR 的链接吗? 我们至少应该尝试效仿它🙂

公关在这里: https :

其背后的启发式发布为cjs-module-lexer (https://github.com/guybedford/cjs-module-lexer),但@guybedford可能对任何潜在偏差了解更多。

看看这个,看起来fs-extra正在使用类似的导出模式:

module.exports = {
  // Export promiseified graceful-fs:
  ...require('./fs'),
  // Export extra methods:
  ...require('./copy-sync'),
  ...require('./copy'),
  ...require('./empty'),
  ...require('./ensure'),
  ...require('./json'),
  ...require('./mkdirs'),
  ...require('./move-sync'),
  ...require('./move'),
  ...require('./output'),
  ...require('./path-exists'),
  ...require('./remove')
}

这目前不是我们检测到的再导出分析案例,但如果这是处理命名导出检测的有用案例,则可能会添加到 cjs-module-lexer。

感谢@jkrems和@guybedford! 我现在使用该模块打开了一个 PR:#10673

https://github.com/facebook/jest/issues/9430#issuecomment -713204282 中描述的确切 fs-extra 支持现在在[email protected]实现,在https://github上进行上游跟踪

_更新:测试这个构建,它正确地检测到所有 fs-extra 函数,但不幸的是它没有检测到 Node.js 本机函数,因为它们由于被 for 循环填充而无法静态分析。_

壮举:支持从 CJS 命名导出作为命名 ESM 导入 #10673

我认为原生 ESM 只支持将 CommonJS 模块的exports导入为default

@trusktr不再: https :

你好。 我很难在 node/jest 中快速了解 ESM 背后的整个故事,因此,可能我在问一些明显的或已经回答的问题。 我是否正确理解尚不支持以下情况? 或者,我希望,我正在做一些不正确的事情? 我觉得import x from 'x'可以工作,但import { sub } from 'x'解构不行。

...
从'uuid'导入{v4};

ESM 模块不支持解构导入,即使语法看起来像它。 为此,需要“export v4”。 'export default' 将不匹配。

https://kentcdodds.com/blog/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution

@sdwlig uuid提供命名导出并且没有默认导出。 它应该可以工作,但是 jest 尚不支持从带有“exports”字段的包中加载 esm。 而是加载 Commonjs,它只能通过默认导出来使用。
https://github.com/uuidjs/uuid/blob/master/src/index.js

我们可以为此添加包自引用支持 (#10883) 吗?

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

相关问题

Secretmapper picture Secretmapper  ·  3评论

jardakotesovec picture jardakotesovec  ·  3评论

samzhang111 picture samzhang111  ·  3评论

paularmstrong picture paularmstrong  ·  3评论

withinboredom picture withinboredom  ·  3评论