Jest: 请考虑添加对 ES 模块的原生支持

创建于 2017-11-05  ·  81评论  ·  资料来源: facebook/jest

您要请求功能还是报告错误
我想申请一个功能。
目前的行为是什么?
现在 Jest 不支持带有import语句的测试套件。 它们导致以下错误:

SyntaxError: Unexpected token import

      at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:305:17)
          at Generator.next (<anonymous>)
          at new Promise (<anonymous>)

什么是预期行为?
如果 Jest 原生支持 ES 模块,那就太好了。

请提供您确切的 Jest 配置并提及您的 Jest、节点、yarn/npm 版本和操作系统。
笑话: 21.2.1
节点: 8.9.0
npm: 5.5.1

之前,ES 模块的原生支持是不可能的,因为 node.js 不支持它们。 从几个版本之前开始,node.js 添加了对带有标志的 ES 模块的支持(https://nodejs.org/api/esm.html)。 如果 Jest 也将其与添加对 ES 模块的支持相匹配,可能带有标志或什至没有标志,那绝对是很棒的。

Node.js 要求 ES 模块具有.mjs扩展名。 为了支持 ES 模块,Jest 需要添加对这些扩展的识别。 Jest 还需要将--experimental-modules标志传递给 node.js,直到 node 实现对没有标志的模块的支持。 我不确定在 Jest 中是否需要任何其他必需的更改来支持这一点。 我只能希望这不会太难。

理想情况下,即使 Jest 也能识别没有.mjs扩展名的文件中的模块会很酷,因为针对浏览器的代码不使用它们,但我不知道这是否可能。 Node.js 为此提供了加载器钩子 (https://nodejs.org/api/esm.html),但这仍然不能通过可靠地确定文件是什么类型的模块来解决问题。

我相信 ES 模块是一个很棒的特性,远远优于所有现有的 JS 模块解决方案。 在 node.js 中实现它为 Jest 也打开了一扇门。 这将使开发人员不仅在整个开发过程中坚持使用第一个真正标准化的 JS 模块格式,而且还通过测试。

Discussion ES Modules

最有用的评论

可能只需要对 Jest 如何进入 CJS 进行一些调整。 例如,当模拟module对象时,它可以使用require("module")代替普通对象,然后包装/覆盖module.require以拦截请求并根据需要进行处理。

更新:

我现在正致力于使用esm开箱即用地启用 Jest (希望 Jest 不必做任何不同的事情) 。 我将在周末继续试验,但早期迹象看起来不错。

所有81条评论

Jest 有自己的require实现(https://github.com/facebook/jest/blob/master/packages/jest-runtime/src/index.js),所以它会比仅仅涉及更多支持语法并默认查看.mjs 。 我也反对激活实验标志。

自动转译import / export是可能的,但是实现语义是一项艰巨的任务,并且由于支持节点 6 可能会被阻止一年。

我同意 SimenB。 我们需要来自节点团队的许多钩子才能与 vm 模块一起工作。 但是,我认为我们应该同时支持这一点,但不是通过镜像完整的本机实现,而是通过使用 babel 并将其编译为 require 内部babel-jest 。 我认为出于测试目的,这将正常工作,我们不必提供节点运行时需要提供的相同保证。

所以只需将babel-plugin-transform-es2015-modules-commonjsbabel-plugin-dynamic-import-nodebabel-jest

是的,我就是这么想的。

伙计们,集成https://github.com/standard-things/esm怎么样? 它速度很快并且维护了很多边缘情况。

@TrySound具体会是什么样子? 你能做一个原型吗?

我们仍然有自己的需求实现(需要模拟),所以我认为这没有多大帮助。

我们需要同时处理 Node 的规则和浏览器的规则。

我很高兴得到纠正并让它对我们完美地工作:D

@std/esm显然应该只是开玩笑: https :

任何人都可以试一试并返回文档的 PR 吗? 🙂

我猜用户希望到处都有支持,但我发现它仅适用于测试文件的依赖项。

// test.js
require = require('@std/esm')(module, { esm: 'js', cjs: true });
const utils = require('./utils');
// utils.js
export { default as update } from './update';

它更好,但并不理想。

那么只需将 babel-plugin-transform-es2015-modules-commonjs & babel-plugin-dynamic-import-node 添加到 babel-jest 中?

我不认为这是一个很好的解决方案,因为它不会执行任何对 ES 模块非常有价值的“丢失导出”检查。 例如,在 React repo 中,我开始更频繁地运行 build 只是因为 Rollup 发现了这些错误,但 Jest with es2015-modules-commonjs没有。

@std/esm 显然应该只用玩笑

将一些时间投入到他们的互操作中会非常棒。 很确定这个 hacky 解决方案最终会崩溃: https :

@SimenB ,在我看来,直接的步骤不会太复杂。 紧迫的是允许人们使用 .mjs 模块,即使 babel 正在帮助测试场景。 否则,如果人们想使用 .mjs,他们可能不得不寻找不同的测试解决方案。

  1. 修复 testMatch 属性以允许找到 .mjs 文件
    (目前,即使正则表达式似乎已经正确,它也不起作用,也许某处有一个硬代码拒绝 .mjs 扩展名)
  2. 在运行 node.js 时传递 --experimental-modules 标志,以便它可以在本地运行
  3. Jest 现在应该像对待 .js 一样对待 .mjs(例如在 jest require 中)——今天在 .js 中已经允许导入语句,所以只需要允许 .mjs 扩展名。

最终的解决方案可能很复杂并且需要时间,但这是不可避免的,不是吗?

你好,有人能修复这个错误吗?

我们正在使用带有“node --experimental-modules”选项的 .mjs。 任何解决方法?

我们正在使用带有“node --experimental-modules”选项的 .mjs。 任何解决方法?

这是实验性的,并没有完全充实。 基本的东西仍然有很多流失,比如如何导入内置模块,仍然悬而未决。 像 AVA 这样的项目已经开始允许使用@std/esm作为他们的加载器管道(如果使用的话)(绕过 Babel)。 也许 jest 可以采取类似的方法。

支持@std/esm是我们想做的事情,非常欢迎帮助实现它!

@SimenB你能在环聊的某个时候聊天吗?

@SimenB 👋

一个esm用户贡献了esm + Jest 演示,我认为这可能是创建更正式的牛路径的一个很好的起点。

更新:

esm + Jest 演示已更新,支持基本模块名称映射

这很酷! 感谢分享。

我们必须弄清楚我们希望集成在哪里。 它如何处理 CSS(或其他非 js 资产)文件? 它应该只是一个转换吗? 内置的 babel 转换怎么样? 如果 Jest 确实会影响任何东西,那么在传入加载器时应该如何表现?

似乎对社区贡献esm启用的备用玩笑跑步者(或非官方/实验性标志)可能有好处,因此我们可以在类似的事情上取得进展。 Jest 团队会对此感兴趣吗?

require没有在 runner 中实现,它在运行时本身中。 任何使其可插拔的贡献都非常受欢迎(参考#848)。

我敢肯定,如果您可以将示例代码@jdalton链接到工作而没有问题(或接近它),它应该足够简单,可以在开玩笑本身的标志后面加载 esm 加载器。 我认为有问题的一件事是它想要真正的module全局,而不是我们创建的假的。 不确定这是否意味着模块会在测试之间泄漏? 我不知道 esm 在幕后做了什么。 它也不处理模拟,所以用import模拟仍然会中断

可能只需要对 Jest 如何进入 CJS 进行一些调整。 例如,当模拟module对象时,它可以使用require("module")代替普通对象,然后包装/覆盖module.require以拦截请求并根据需要进行处理。

更新:

我现在正致力于使用esm开箱即用地启用 Jest (希望 Jest 不必做任何不同的事情) 。 我将在周末继续试验,但早期迹象看起来不错。

@jdalton任何与esm兼容的更新?

@JasonCust ,哇,我的评论引起了一些注意!

我在确定在esm启用 Jest 支持所需的工作方面取得了进展。 在我的实验中,我使用 ESM 编写了 Jest 加载和评估测试。 esm加载器端所需的工作是使我们处理vm.Script使用更通用。 目前我们主要是为了 REPL 使用它,它假设一个模块。 我们必须让 Jest 支持更通用一些。 看起来 Jest 不需要改变任何东西。 我们的vm.Script支持的重构仍然在我的 TODO 上,并且在我发布实验性 WASM 支持之类的东西之前仍然会得到解决。 目前,我一直在解决围绕改进的 APM 和模拟支持出现的错误。

感谢@jdalton的更新,因为我很高兴能够在 Jest 中使用esm 。 为了在您处理这些事情时不打扰您,是否有esm的任务可以让我们跟随进度?

您可以继续订阅此线程以关注 repo。 Jest 支持将在 v3.1.0 版本中提供,因此您可以留意该版本。

@jdalton 有关于在

嗨@deepj!

它仍然在我的清单上,在完成这项工作之前我可以处理的项目正在减少。 我一直在改进 Jest 中测试esm模块的补充 Jest 支持 _(虽然 CJS 在 Jest 测试中)_。 在 Jest 方面仍然没有任何严重阻碍实现_(所以他们没有工作要做)_。 我的雇主 Microsoft 也是 Jest 的重度用户,因此改善这一点也是我的日常工作目标之一。 我希望尽快解决它。

@jdalton

真的很期待这个功能:speak_no_evil:

过去两个小时一直在尝试使此功能正常工作。 有这么多部分解决方案,半文档化的答案,彼此都不同......过去很容易测试node.js包。

所以只需将babel-plugin-transform-es2015-modules-commonjsbabel-plugin-dynamic-import-nodebabel-jest

这个想法是怎么回事? 实施时有什么问题吗?

嗨@jdalton。 我想知道您是否可以让我们了解此问题的最新状态。 对不起,如果我打扰你,但我等了一段时间,如果我们至少能在过去/未来六个月收到更新会更好。 谢谢 :)

嗨@SurenAt93!

感谢您的耐心等待。 我希望能在本月底在 Jest 的支持下发布一个版本。 有了它,您将指定esm作为 Jest变换

凉爽的。 这种转换与 Babel 有何不同?

@kenotron

使用 Jest 的transform选项是我侧载esm加载器的一种方式。 因此,虽然它在技术上也在转换源代码,但它也在连接esm加载器。

如果问题更多,Babel 和esm之间有什么区别。 Babel 是转换源的包的集合,其中一个目标可能是 ESM 语法。 esm加载器是一个模拟运行时环境的零依赖加载器。 所以esm支持动态import() ,通过相关的test262规范(时间死区、实时绑定、前期错误等),并支持将 CJS/ESM/WASM 的混合加载到按配置品味。

@jdalton感谢您的工作和支持!

@tomheller ;)

过去两个小时一直在尝试使此功能正常工作。 有这么多部分解决方案,半文档化的答案,彼此都不同......过去很容易测试node.js包。

我同意。

我创建了一个简单的Vue项目,也体现了这个问题。
https://github.com/igasparetto/vue-jest-test
我无法让它工作。

我按照以下页面的说明进行操作:

我的机器(不确定它是否重要):

  • Windows 10 专业版 64 位
  • 节点 v8.9.4
  • Vue:3.2.3
  • npm:6.5.0

@kenotron

使用 Jest 的transform选项是我侧载esm加载器的一种方式。 因此,虽然它在技术上也在转换源代码,但它也在连接esm加载器。

如果问题更多,Babel 和esm之间有什么区别。 Babel 是转换源的包的集合,其中一个目标可能是 ESM 语法。 esm加载器是一个模拟运行时环境的零依赖加载器。 所以esm支持动态import() ,通过相关的test262规范(时间死区、实时绑定、前期错误等),并支持将 CJS/ESM/WASM 的混合加载到按配置品味。

@kenotron你能给我们提供更新吗?

@igasparetto@jdalton的工作应该能够实现这一点。 我还没有尝试过那个解决方案。

我认为那里的最新状态是: https :

是的! 抱歉,它花费的时间比我想要的要长。 在本地,我现在已经通过了所有相关的 test262 测试。 在获取这些内容的过程中,与模拟相关的场景测试失败了,因此我必须在发布之前将它们重新拾起。 这不是不可逾越的,只是需要一点时间。 我将于 1 月 16 日在Covalence Conf上发表演讲,希望届时能发布。 在其他新闻中, npm tink早期采用了esm作为其 ESM 语法支持🤝。

1 月 16 日,希望届时能发布

@jdalton我希望演讲进展顺利。

请问你有这个版本的更新吗?

我在这里留下一个小的“数据点”来帮助您进行规划。 我意识到你们都在贡献自己的时间和技能来解决这个问题。 作为开发人员,我感谢您的贡献。

我昨天花了大部分时间,至少一个星期六,来加快客户端和服务器端的 javascript 模块的速度。 ESM、CommonJS、AMD,真是一团乱麻。 我从来没有得到一个笑话测试来使用 ESM 加载一个使用 .mjs 扩展名的(节点)模块。 我可以在客户端成功加载相同的模块。 我可以创建一个节点“客户端”,该节点使用带有导入语句的模块。 我无法正确配置 jest 以使用相同的 import 语句,无论有没有 babel,有或没有 esm。 我最终切换到 ava,并按照他们网站上的食谱让它工作。 是的,我遵循了一个食谱,我不完全了解使所有零件正常工作的机器。 但至少我现在可以编写 ESM 加载 javascript 模块和相关的单元测试。 我认为。 我是在一次成功的基础上进行推断的。 他们还有一个将 ava 连接到 webstorm 的方法。 但至少他们正在向像我这样的凡人展示食谱。

我也意识到这条消息会读起来就像我在抱怨(部分是我)。 我看到所有的工作都变成了玩笑。 但是这个 ESM 支持对我来说是一个杀手和一个交易破坏者。 以为您会欣赏一些反馈,但如果没有,请忽略此内容或要求我将其删除,我会的。

@dandv我一直在调查 Jest 是否可以支持节点 12 中的原生 ES 模块。这将涉及 Jest 使用vm.SourceTextModule API,这需要将一些 CLI 标志传递给node :

--experimental-modules --es-module-specifier-resolution=node --experimental-vm-modules

API 也是超低级的。

待定。

https://github.com/nodejs/node/issues/27387 中的讨论

更新:被告知要等到 Node 的模块加载器的设计被锁定。 同时,standard-things/esm#706 可能是最好的选择。

jest 是一个非常好的测试库。 支持esm真的一切都需要完成!

更新:被告知要等到 Node 的模块加载器的设计被锁定。 同时, standard-things/esm#706可能是最好的选择。

可悲的是,这不适用于开玩笑。

@jdalton我们正在使用lodash-es ,它是lodash-es可以通过 Webpack v4 进行摇树! ( ◔ ౪◔)⊃━☆゚.*・.

不幸的是,鉴于 Jest 尚不支持 ES 模块,这给我们带来了问题。 我们希望这个功能很快就会成为 Jest 的一部分。 有人会碰巧知道如何让lodash-es与 Jest 一起工作吗?

Jest 失败并显示错误消息:

node_modules\lodash-es\lodash.js:10
    export { default as add } from './add.js';
    ^^^^^^

    SyntaxError: Unexpected token export

我们的 jest.config.js

我们也在使用jest-preset-angular npm 包。

module.exports = {
  testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
  transform: {
    '^.+\\.(ts|js|html)$': 'ts-jest'
  },
  resolver: '@nrwl/builders/plugins/jest/resolver',
  moduleFileExtensions: ['ts', 'js', 'html'],
  collectCoverage: true,
  coverageReporters: ['html']
};

不幸的是,鉴于 Jest 尚不支持 ES 模块,这给我们带来了问题。 我们希望这个功能很快就会成为 Jest 的一部分。 有人会碰巧知道如何让lodash-es与 Jest 一起工作吗?

告诉 Jest 在转换时不要忽略 lodash-es:

  "transformIgnorePatterns": [
    "[/\\\\]node_modules[/\\\\](?!lodash-es/).+\\.js$"
  ],

@azz你知道 Node 的模块加载器的设计什么时候会被锁定吗?
因为:

npx -n '--experimental-modules' jest func.spec.js

会非常酷和轻松的生活。

@haraldrudell根据说明不清楚如何使用该包,它说:在 package.json 旁边创建名为 jest.config.js content module.exports = require('jest-esnext') ,但是如果我已经有了配置怎么办? 如何整合?

这是使用的文件

https://github.com/haraldrudell/ECMAScript2049/blob/master/packages/jest-esnext/config/jest.config.js

您可以将_default的内容替换为jest.config.js

嗨,大家好,
node 12.13.0 LTS 终于发布了……有关于这件事的消息吗?

@mtsmachado8恐怕 v8 团队仍然需要一些时间来解决这个问题,因为我看到 ESM 模块仍然被标记为实验性......他们可能在路线图上失败了。

https://nodejs.org/api/esm.html

对于未标记的 ESM,此 PR 已到位
https://github.com/nodejs/node/pull/29866

@azder @haraldrudell所以基本上在您的解决方案中,您对所有 JS 文件(包括node_modules文件)进行 Babel 转换?

就我而言,我不得不直接使用您的预设,因为我还没有找到如何像这样配置变压器:

const babelPreset7Esnext = require('babel-preset-7-esnext');
const babelJest = require('babel-jest');

module.exports = babelJest.createTransformer(
    babelPreset7Esnext(undefined, {decorators: {legacy: true}})
);

节点现在默认切换模块支持

https://github.com/nodejs/node/pull/29866

在加载器可用之前,我们不会对此进行处理。 没有它们,Node 就没有 Jest 提供适当支持所需的钩子。 请参阅https://medium.com/@nodejs/annoucing -core-node-js-support-for-ecmascript-modules-c5d6dc29b663(我无法直接链接到该部分,但它是“正在进行的工作”部分在底部)

对于那些使用本机模块并想要使用jest 的人
在您处理此问题时,我建议对 node v. 13.2.0 进行快速修复:
babel-plugin-transform-default-import
用法(在 _package.json_ 中):

{
  "babel": {
    "presets": [
      [
        "@babel/preset-env",
        {
          "targets": {
            "node": "current"
          }
        }
      ]
    ],
    "plugins": ["transform-default-import"]
  },
}

libs需要安装:
npm i --save-dev @babel/core @babel/preset-env babel-plugin-transform-default-import

注意:如果你没有名为 export 的 babel 库(或者你不使用它),你可能不需要使用 babel-plugin-transform-default-import

@infodusha很棒:)。 这次真是万分感谢。

在我的项目中,它无需插件即可工作:

npm i --save-dev @babel/preset-env

babel.config.js (必须如此命名,而不是.babelrc ):

module.exports = {
    presets: [
        [
            '@babel/preset-env',
            {
                targets: {
                    node: '13.2',
                },
                modules: 'commonjs',
            },
        ],
    ],

    plugins: [],
};

诀窍是将文件分成目录并在其中使用适当的package.json

test目录中,我使用了

{
  "type": "commonjs"
}

而在src目录中:

{
  "type": "module"
}

@azder但是,如果您导入在本机模块中提供 commonjs 命名导入的包,则需要使用默认导入来导入它,而在 babel 中,您需要使用命名导入。 可能你没有这样的包,或者没有提供 es6 导出的包,但并不是所有的包都准备好明天做

@infodusha可能我现在没有什么? 您是否尝试过我写的内容并发现它有问题,还是只是假设我在使用它时遇到了问题?

请提供一个实际示例,因为我不清楚您所说的“在本机模块中提供名为 import 的 commonjs 的导入包”是什么意思。

到目前为止,我在编写所有带有.js文件扩展名的文件时没有遇到任何问题,其中./src目录是"type":"module"./test目录"type":"commonjs"与此:

const imported = require('../src/module.js').default;
const {anotherOne} = require('../src/module.js');

那是因为Jest默默地将ES 模块转换CommonJS代码。

这是我认为应该在本地测试ES 模块的内容

(async () => {
    const [
        { default: imported },
        { anotherOne },
    ] = await Promise.all([
        import('../src/some-module.js'),
        import('../src/another-module.js'),
    ]);

    // Rest of your test goes here.
})();

@azder这些是解决方法。

使用 package.son type=module和其中的两个文件创建一个目录mymodulefirst.jssecond.js
然后尝试import { first } from "mymodule";

您需要在 json 中设置exports字段才能使用 Node ESM,并且现在没有任何包具有它(即:lodash)。

您的示例可能似乎有效,但是只要some-module.jsanother-module.js尝试import命名模块,它就会中断:它们将在级联上中断。

@damianobarbati您_ _必要性"type": "module"package.json ,没有它,一切.js您的模块文件被加载,为CommonJS的

exports仅用于限制暴露给外部消费者的内容,对于条件导出,它对.js文件是否会被解析为CommonJSESM完全没有影响。

@damianobarbati你错了,正如@Exe-Boss 所说,顺便说一句,

这是因为 Jest 默默地将 ES 模块转换为 CommonJS 代码。

是的,我依赖 Jest 怪癖,这就是我使用babel.config.js ,甚至没有在devDependencies添加 babel

@damianobarbati

请注意,我有一个工作项目,其中我使用 Jest 转译,而 src 目录具有模块类型,注意, ./src不是 babel 配置文件所在的根目录(那个是 CJS由于怪癖)。

此外,您是对的,因为我没有从 NPM 中导入任何 CJS 的内容,因为我认为 NPM(或包作者)还没有准备好进行混合搭配。

我的项目的目标是只有 ESM(减少工具脂肪),所以 Jest 是唯一可以自行编译的例外。

@damianobarbati

类似的东西,这里是项目https://github.com/azder/clip 。 请注意,根据博客文章摘录的最后一句,我的package.json没有“依赖项”,我决定不混合来自 NPM 的 ESM 和 CJS 模块。

这种方式为了满足 Jest 的需求,它确实转换了我的 ESM 模块的要求,但可能需要更多的 babel 配置来处理node_modules目录。

https://medium.com/@nodejs/annoucing -a-new-experimental-modules-1be8d2d6c2ff

目前,无法创建可通过 require('pkg') 和 import 'pkg' 使用的包。 正在努力解决这个问题,并且可能涉及对上述内容的更改。 特别是,Node.js 可能会选择“main”以外的字段来定义包的 ES 模块入口点。 虽然我们知道社区已经接受了“模块”领域,但 Node.js 不太可能采用该领域,因为许多使用“模块”发布的包包括 ES 模块 JavaScript,它们可能无法在 Node.js 中评估(因为扩展名不包含在文件名中,或者代码包含 require 语句等)。 在解决此问题之前,请不要发布任何供 Node.js 使用的 ES 模块包。

请参阅我刚刚打开的 #9430 以跟踪 Jest 中的支持。 我会保持这个问题开放讨论。

@SimenB这是一个好兆头! 这一点以及在 Jest 25 发行说明中提到的模块让人希望早日看到支持。

@SimenB如果我没记错的话,Jest 无法使用仅作为 ESM 发布的 NPM 包,这也被迫为某些node_modules包启用 Babel。 这有改变吗?

您需要转换导入/导出,直到支持登陆,用户和库代码

@西蒙B

您需要转换导入/导出,直到支持登陆,用户和库代码

对于我们的情况,到目前为止最好的方法是这样,因为我们所有的包都有/es/目录,但这很脆弱,可能不适合其他项目:

transformIgnorePatterns: ['node_modules/(?!.*?/es/.*\\.js)'],

就像你说的那样,尽管type: module ,Jest 并没有选择那些没有转换的包。

您需要转换导入/导出,直到支持登陆,用户和库代码

有没有粗略的时间表?
从节点 14 版本开始:

我们相信当前的实现为编写 ESM 模块提供了一个面向未来的模型,为通用 JavaScript 铺平了道路。 请在我们的文档中阅读更多内容。

Node.js 中的 ESM 实现仍处于试验阶段,但我们相信我们已经非常接近能够在 Node.js 中“稳定”调用 ESM。 消除警告是朝着这个方向迈出的一大步。

因此,我不愿意使用 commonJS 更长时间地导出 NPM 包(这些包很可能被实现 JEST 测试框架的测试所消耗)。

我们正在发布 Jest 25.4 的实验版本。 在 25.5 中修复了相当多的错误,但它仍然不是它应该的地方。 可以关注#9430的进度

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