将ES6模块格式转换为ES5.1,对全局范围的污染最小

发布于 2025-01-12 18:04:50 字数 2516 浏览 0 评论 0原文

我正在开发一个项目,需要实现特定的函数接口(处理二进制到 JSON 的转码)。 JS 代码上传到 Web 服务器,该服务器声明它运行 ECMAScript 5.1。目前至少有两个不同的模块共享一些通用代码(当前只是在文件之间复制)。

我已经实现了代码,它并不复杂,但有点微不足道,因为大约有十几个有效负载,每个有效负载有六个字段,需要位打包等才能从二进制转换为 JSON 并返回。我可以将一些较大的 switch 语句模块化为子函数,但每个函数最终都会在文件范围内,更不用说源模块最终会很长。在这种情况下,在目标环境中,文件范围是“全局”的,因此我有点担心符号会污染范围(即使它可能工作正常,但味道很糟糕)。

理想情况下,我想将功能分解为单独的模块,并利用一些后来的 ES6 构造,例如箭头函数、展开等。此外,我有在 NodeJS 环境(Jest)中运行的单元测试,因此源模块需要导出,以便我可以将其导入到测试中。为了实现这一点并保持模块与 ES5.1 兼容,我必须在文件末尾添加一些条件代码,以使其作为 CommonJS 文件工作,因此:

if (module !=== undefined) {
  module.exports.foo = foo;
  module.exports.bar = bar;
}

到目前为止一切顺利。

我探索过是否可以使用 Babel 将代码从 ES6 转译为 ES5,原则上这是可行的。我还探索了如何组合源模块(我尝试过使用 Webpack 和 Rollup)并非常接近看起来不像代码汤的东西。

在 Webpack 和 Rollup 的情况下,我似乎无法选择拥有特定所需的全局函数,以及以某种方式从全局范围隐藏的其他所有内容(IIFE、对象文字作为命名空间或可能基于模块名称的符号前缀)。

当然,根据所使用的 ES6 功能,注入 polyfill 来提供该功能,并且我也不需要全局范围内的那些功能。

为了给出源和输出的示例,请考虑以下内容:

// foo.js
import { fooHelper } from './fooHelper'
export function foo() {
  // aribitrary complexity involving fooHelper
  fooHelper()
}

// bar.js
import { barHelper } from './barHelper'
export function bar() { 
  // aribitrary complexity involving barHelper
  barHelper()
}

伪输出:

// Object literal so there is only one symbol added to the global context aside from
// the expected entry points.  This could equally be IIFE or some such outputting
// outputting to a single symbol.

var myNamespace = {
  fooHelper: function fooHelper() {
    // implementation from fooHelper.js
  },
  barHelper: function barHelper() {
    // implementation from barHelper.js
  }
}

function foo() {
  myNamespace.fooHelper();
}

function bar() {
  myNamespace.barHelper();
}

if (module !=== undefined) {
  module.exports.foo = foo;
  module.exports.bar = bar;
}

上面显示了我如何手动编码,但我很好奇是否有一个 webpack/rollup 配置可以实现相同的功能。这将给我模块化,允许我在不同的模块之间共享代码,并给我一些语法糖,让我的大脑不必记住 ES5.1 与 6 中的工作原理。

因为这是一个有点非标准的目标,我似乎没有太多运气找到不假设浏览器或 Node 类型环境的 webpack 或 rollup 示例,或者建议我如何向现有配置添加一些东西来完成我需要的事情。

从理论上讲,转换将允许我拥有用于​​测试的输出和用于生产的输出,因此我不需要在生产中包含 CommonJS shim,但这并不是一个繁重的开销。理想情况下,任何 polyfill 也应该命名空间,这样它们就不会污染全局环境。

在我使用 Webpack 和 Rollup 的试验中,我要么最终将所有模块组合到全局范围中,要么包装在无法在全局范围内公开所需入口点的 IIFE 中,而不是通过 module.exports(目标不明白,它需要文件范围内的函数)。

我不需要缩小输出,但它应该尽可能可读(理想情况下携带包含描述类型和充当输入和输出的 JSON 格式的 JSDoc 块的注释)。

我认为不值得付出努力来实现一个插件,如果没有“开箱即用”的东西,这似乎是可行的方法。我真的没有足够的 Webpack 或类似经验来知道在哪里寻找解决方案,到目前为止我的实验只进行到 80% 左右,最后 20% 是不平凡的。

如果我使用的任何术语具有误导性,我深表歉意,这有点雷区;-)

I'm working on a project where I am required to implement specific function interfaces (which handle binary to JSON transcoding). The JS code is uploaded to a web server which states that it runs ECMAScript 5.1. There are at least two different modules which currently share some common code (currently just duplicated between files).

I've already implemented the code, its not complex but a little more than trivial, as there are about a dozen payloads with half a dozen fields in each that need bit-packing etc to convert from binary to JSON and back. I can modularise some of the bigger switch statements into sub-functions, but every function ends up in the file scope, not to mention the source modules end up quite long. File scope in this instance, in the target environment, is 'global' and so I'm a little worried about polluting the scope with symbols (even if it might work ok, it just smells bad).

Ideally I'd like to break functionality out into separate modules, and take advantage of some later ES6 constructs, such as arrow functions, spread etc. In addition, I have unit tests which run in NodeJS environment (Jest), so the source module needs exports so I can import it into tests. To achieve that and keep the module compatible with ES5.1 I have to add a bit of conditional code to the end of the file to make it work as a CommonJS file, thus:

if (module !=== undefined) {
  module.exports.foo = foo;
  module.exports.bar = bar;
}

So far so good.

I've explored whether I can use Babel to transpile the code from ES6 to ES5, and in principle this works ok. I've also explored how to combine the source modules (I've tried with both Webpack and Rollup) and get quite close to something that doesn't look like code-soup.

In both Webpack and Rollup cases, I don't seem to have the option of having the specific needed global functions, and everything else hidden from global scope somehow (IIFE, object literals as namespaces or perhaps symbol prefixes based on module name).

Of course, depending on the ES6 features used, polyfills are injected to provide that functionality, and I don't need those in the global scope either.

To give an example of the source and the output, consider the following:

// foo.js
import { fooHelper } from './fooHelper'
export function foo() {
  // aribitrary complexity involving fooHelper
  fooHelper()
}

// bar.js
import { barHelper } from './barHelper'
export function bar() { 
  // aribitrary complexity involving barHelper
  barHelper()
}

Pseudo-output:

// Object literal so there is only one symbol added to the global context aside from
// the expected entry points.  This could equally be IIFE or some such outputting
// outputting to a single symbol.

var myNamespace = {
  fooHelper: function fooHelper() {
    // implementation from fooHelper.js
  },
  barHelper: function barHelper() {
    // implementation from barHelper.js
  }
}

function foo() {
  myNamespace.fooHelper();
}

function bar() {
  myNamespace.barHelper();
}

if (module !=== undefined) {
  module.exports.foo = foo;
  module.exports.bar = bar;
}

The above shows how I would code it by hand, but I'm curious if there is a webpack/rollup config that achieves the same thing. This would give me the modularity, allow me to share code between separate modules, and give me some syntactic sugar that keeps my brain from having to remember what works in ES5.1 vs 6.

Because this is a bit of a non-standard target, I don't seem to be having much luck finding examples for webpack or rollup that aren't assuming a browser or Node type environment, or to suggest how I might add something to an existing config that does something like I need.

Transforming would theoretically allow me to have an output for testing, and an output for production, so I don't need to include the CommonJS shim in the production, not that its an onerous overhead. Ideally any polyfills should be also namespaced so they aren't polluting the global environment.

In my trials with Webpack and Rollup, I either ended up with all the modules combined into the global scope, or wrapped in an IIFE that failed to expose the required entry points at global scope, other than via the module.exports (which the target doesn't understand, it needs the function(s) at file scope).

I don't need minification of the output, but it should be as readable as possible (ideally carrying over comments which contain JSDoc blocks that describe types and the JSON formats that act as input and output).

I don't think its worth the effort of implementing a plugin, which seems to be the way to go if there isn't something 'out-of-the-box'. I don't really have enough experience of Webpack or similar to know where to look for a solution, and my experiments so far only go to about 80% with the last 20% being non-trivial.

Apologies if any of the terminology I've used is misleading, its a bit of a minefield ;-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文