本文将指导你发布双模式、跨运行时的 JavaScript 包。了解如何创建与 ESM 和 CommonJS 以及 Node.js、Deno 和浏览器等不同运行时兼容的库。

随着 JavaScript 开发的不断发展,人们越来越需要能在多种环境中运行的强大依赖包。在本文中,我们将探讨如何发布跨运行时、双模式的 JavaScript 包。这些包弥补了 ESM 和 CommonJS 之间的差距,让开发人员可以在任何环境下使用相同的包和文档。

在深入了解之前,让我们先熟悉一些关键概念:

双模式包

双模式包旨在与多个 JavaScript 模块系统(尤其是 ES Modules (ESM) 和 CommonJS (CJS))配合使用。这确保了代码在各种环境中的可重用性和灵活性。创建双模式包有几个好处:

  • 更广泛的兼容性:并非所有项目都已过渡到使用 ESM。双模式确保你的包可以在仍然依赖于 CommonJS 的项目中使用。
  • 无缝过渡:随着 JavaScript 生态系统逐步转向 ESM,双模式包可确保用户实现无缝过渡,而无需更换包或重构代码库。
  • 减少维护:双模式包允许用户管理单一代码库,而无需分别维护 ESM 和 CJS 包。

不过,双模式并不能保证软件包在不同的运行环境下都能正常工作,这就带来了以下问题:

跨运行时包

跨运行时包可在 Deno、浏览器和 Node.js 等多种环境中运行。它们旨在为不同运行时提供一致的 API。一个全面的跨运行时包应同时支持 ESM 和 CJS,尤其是因为 Node.js 在很大程度上仍在使用 CommonJS。

如果我们忽略 Node.js 的传统限制(Node.js 严重依赖 CommonJS),我们可以只使用 ES 模块构建跨运行时包。这将简化包,但会限制其与旧版 Node.js 项目的兼容性。

Node还是Deno优先

你有两种主要方法:从 Deno 或 Node.js 开始。Deno 优先方法使用 Deno 的内置工具和 Deno 到Node工具(DNT)。另一方面,Node优先方法使用传统的构建工具来完成测试、检查和打包等任务。这种方法是转换现有 NPM 库的首选。

Deno优先方法

Deno优先方法依赖于DNT,你可以在GitHub上找到。

该工具通过版本库中的自定义构建脚本使用。

第一步是建立一个基本的 Deno 库,准备发布到 deno.land/x。之后,你就可以使用 DNT 了。

添加脚本

Deno优先方法的核心是构建流程。下面这个名为 scripts/build_npm.ts 的脚本使用 DNT 创建一个 /npm 文件夹,其中包含一个完整的 NPM 包,可以随时发布。

该脚本将处理清除 NPM 目录、复制测试数据和构建软件包等任务。它还会创建一个完整的 package.json 文件。

让我们一起来看看吧,请务必阅读注释。

import { build, copy, emptyDir } from "./deps.ts";

// Clear NPM directory
await emptyDir("./npm"); // (optional) Copy test data, if you have some
// await copy("tests/data", "npm/esm/tests/data", { overwrite: true }); // This assumes that the entrypoint of your module is ./mod.ts
await build({
entryPoints: ["./mod.ts"],
outDir: "./npm",
shims: {
deno: "dev",
},
/*
mappings: {
"<https://deno.land/x/zipjs@v2.7.17/index.js>": {
name: "@zip.js/zip.js",
version: "^2.7.17"
},
},
*/
package: {
// package.json template
name: "my-library-name",
version: Deno.args[0],
description: "My library description.",
license: "MIT",
repository: {/* ... */},
author: "You <your@mail>",
/* Additional information */
},
}); // (optional) post build steps, you might want to copy some files?
// ---------------------------------------------------------------
// Deno.copyFileSync("LICENSE", "npm/LICENSE");
// Deno.copyFileSync("README.md", "npm/README.md"); // (optional) Add .npmignore
// ---------------------------------------------------------------
// ensure the test data is ignored in the `.npmignore` file
// so it doesn't get published with your npm package, if relevant
/*
await Deno.writeTextFile(
"npm/.npmignore",
"esm/tests/data\nscript/tests/data\n",
{ append: true },
);
*/

现在,你只需运行 deno run -A scripts/build_npm.ts 0.0.1 来构建 0.0.1 版本的 npm 软件包。所有相关文件都将在 ./npm 中生成。

最后一步是导航到 ./npm 目录,然后运行 npm publish,就可以了!

使用 deno.json 更新构建管道流

要记录这一构建步骤,可以修改 deno.jsontask部分,将新的 NPM 构建步骤包括在内。下面是一个设置测试和 NPM 构建的配置示例:

{
/* ... existing configuration ... */
"tasks": {
"test": "deno test tests --allow-read",
"build": "deno run -A scripts/build_npm.ts"
}
}

现在,运行 deno task build 0.0.1 时将生成 npm 包。

Node优先方法

或者,你也可以选择Node优先的方法来创建跨运行时包。

第一步是确保你的项目同时支持 ESM 和 CommonJS。这既可以手动完成,也可以使用构建工具来处理。代码库最好是非转译的 javascript 或 typescript,以便 Rollup 或类似工具处理。

让我们以 @hexagon/base64 库为例进行分析。该库使用 Rollup 生成 ESM 和 CommonJS 版本的代码,配置如下:

// rollup.config.js
export default [
{
input: "./src/base64.single.js",
output: {
file: "dist/base64.cjs",
format: "umd",
name: "base64",
exports: "default"
}
},
{
input: "./src/base64.js",
output: {
file: "dist/base64.mjs",
format: "es"
}
}
];

该库的源代码(/src/base64.js)是以各种方式导出 base64 对象的普通 ES JavaScript。

// src/base64.js
/* ...
Library code making up the base64 object
... */ // Default export
export default base64; // Named export
export { base64 };

Rollup 无法处理多重导出,因此我还创建了一个 /src/base64.single.js,默认情况下它只负责重新导出 base64 对象。这是 Rollup 配置的 UMD 目标所使用的。

// /src/base64.single.js
import base64 from "./base64.js";
export default base64;

package.json

package.json 文件是设置双模式、跨运行时 JavaScript 包的关键。它决定了包在不同环境中的结构和行为方式。让我们来仔细看看其中的关键部分及其重要性:

{
/* ... your metadata ... */ "scripts": {
/* ... your existing build steps ... */
"build:dist": "rollup -c ./rollup.config.js",
}, "type": "module", "main": "./dist/base64.cjs",
"browser": "./dist/base64.min.js",
"module": "./src/base64.js",
"types": "types/base64.single.d.ts", "exports": {
".": {
"import": "./src/base64.js",
"require": "./dist/base64.cjs",
"browser": "./dist/base64.min.js"
}
}
}
  • "scripts" :该部分包含构建和管理包所需的脚本。在提供的示例中,"build:dist"用于触发 Rollup 打包过程。根据包的具体要求,你可能还需要其他脚本来进行测试、检查或执行其他任务。
  • "type" :该字段设置为"module",表示你的包是为使用 ESM(ES 模块)导入而设计的。
  • "main" :该字段指定了 CommonJS 环境(如 Node.js)的入口点。它指向包的 CommonJS 版本,通常位于 dist 目录中。
  • "browser" :该字段用于指定浏览器环境的替代入口点。它指向包的最小化版本,以增强与浏览器的兼容性。
  • "module" :与 "main"字段类似,该字段用于指定 ESM 环境的入口点。它指向软件包的 ESM 版本。
  • "types" :此字段指明软件包的 TypeScript 声明文件(.d.ts)的位置。这些文件为 TypeScript 用户提供了类型信息,改善了开发人员的体验。
  • "exports" :该字段是一项最新功能,允许你定义如何导入包。它为 ESM、CommonJS 和浏览器环境指定了不同的导入路径,确保了跨运行时的流畅兼容性。

根据包的具体需求和配置,你可能需要对 package.json 进行或多或少的修改。仔细调整和测试该文件以确保其在发布时正常运行至关重要。

跨运行时部分

前面提到的步骤主要是在 Node.js 中设置双模式兼容性。虽然 Deno 可以使用开箱即用的 npm 软件包,但要创建一个完整的跨运行时包,你还应该将其适配到 Deno。

这包括阅读 Deno 库的工作原理将软件包发布到 deno.land/x

还有就是,让你的软件包成为双模式软件也能帮助其他项目。

总结

创建双模式、跨运行时的 JavaScript 包是一种有益的体验。它能使你的代码具有可移植性和可重用性,让你在不同的 JavaScript 环境中接触到更多的用户。虽然会有一些障碍和注意事项,如管理兼容性以及与不同模块系统和运行时的配合,但利大于弊。

创建一个双模式跨运行时的 JavaScript 包的更多相关文章

  1. [Selenium]Grid模式下运行时打印出当前Case在哪台node机器上运行

    当Case在本地运行成功,在Grid模式下运行失败时,我们需要在Grid模式下进行调试,同时登录远程的node去查看运行的情况. Hub是随机将case分配到某台node上运行的,怎样知道当前的cas ...

  2. 创建一个py文件并运行

    在 Linux 中,可以直接用vim 或者 vi 来编辑一个 python 文件 vim hello.py 进入编辑页面 #coding:utf-8 print("你好") (因为 ...

  3. 【MOOC操作系统】测试题大题-进程调度 先入先服务算法例题 【某多道程序系统供用户使用的主存为100K,磁带机2台,打印机1台,采用可变分区存储管理,静态方式分配外围设备(进程获得所需全部设备才能进入内容),忽略用户作业的I/O时间。采用动态分区、首次匹配法(从低地址区开始)分配主存,一个作业创建一个进程,且运行中不紧缩内存。作业调度采用FCFS算法,在主存中的进程采用剩余时间最短调度算法。】

    分析图: 答案: (1) 8 : 00作业1到达,占有资源并调入主存运行. 8: 20作业2和3同时到达,但作业2因分不到打印机,只能在后备队列等待.作业3资源满足,可进主存运行,并与作业1平分CPU ...

  4. (转载)如何创建一个以管理员权限运行exe 的快捷方式? How To Create a Shortcut That Lets a Standard User Run An Application as Administrator

    How To Create a Shortcut That Lets a Standard User Run An Application as Administrator by Chris Hoff ...

  5. Java 9 揭秘(7. 创建自定义运行时映像)

    Tips 做一个终身学习的人. 在第一章节中,主要介绍以下内容: 什么是自定义运行时映像和JIMAGE格式 如何使用jlink工具创建自定义的运行时映像 如何指定命令名称来运行存储在自定义映像中的应用 ...

  6. UE4编程之C++创建一个FPS工程(一)创建模式&角色&处理输入

    转自:http://blog.csdn.net/u011707076/article/details/44180951 从今天开始,我们一起来学习一下,如何使用C++将一个不带有任何初学者内容的空模板 ...

  7. 框架原理第三讲,RTTCreate,运行时类型创建.(以MFC框架讲解)

    框架原理第三讲,RTTCreate,运行时类型创建.(以MFC框架讲解) 通过昨天的讲解,我们已经理解了运行时类型识别是什么. 比如  CObject * pthis = (Cobject *)Cre ...

  8. 教你如何检查一个函数是否为JavaScript运行时环境内建函数

    在开发过程中,对于某些API在现有的JavaScript运行时环境不支持的时候,我们大都会采用加入polyfill来解决这个问题.但有些时候我们可能需要知道现在某个API到底是否为运行时环境所原生支持 ...

  9. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  10. JVM 内存区域 (运行时数据区域)

    JVM 内存区域 (运行时数据区域) 链接:https://www.jianshu.com/p/ec479baf4d06 运行时数据区域 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内 ...

随机推荐

  1. Mac SpringBoot项目 Gradle 7.3 转 Maven 手把手教学,包学会~

    导读 最近我手上有个使用Gradle构建的项目,国内使用Gradle的人相对较少.而且我也觉得Gradle的依赖管理方式有些复杂,让我感到有些困惑.因此,我想将项目转换为Maven构建方式.Maven ...

  2. 探索抽象同步队列 AQS

    by emanjusaka from https://www.emanjusaka.top/archives/8 彼岸花开可奈何 本文欢迎分享与聚合,全文转载请留下原文地址. 前言 AbstractQ ...

  3. 聊聊基于Alink库的推荐系统

    概述 Alink提供了一系列与推荐相关的组件,从组件使用得角度来看,需要重点关注如下三个方面: 算法选择 推荐领域有很多算法,常用的有基于物品/用户的协同过滤.ALS.FM算法等.对于不同的数据场景, ...

  4. 内网离线安装docker并配置使用nexus为docker私服

    背景 本文简单记录下最近在内网服务器离线安装docker及配置nexus作为docker私服,踩的一些坑.docker和k8s这块技术我跟得不是很紧,18年的时候用过一阵docker,后来发现它并不能 ...

  5. 2022 ICPC 杭州站

    gym 知乎 尝试先读题而不是写缺省源感觉不太好 E 一头雾水.F 是签到就先上去写了,结果读错题交了个样例都没过的代码,小改了一下就过了.G 不太会做.zsy 把 M 丢给我想了一下 然后 gjk ...

  6. Spring扩展接口(2):BeanDefinitionRegistryPostProcessor

    在此系列文章中,我总结了Spring几乎所有的扩展接口,以及各个扩展点的使用场景.并整理出一个bean在spring中从被加载到最终初始化的所有可扩展点的顺序调用图.这样,我们也可以看到bean是如何 ...

  7. JAVA图搜索算法之DFS-BFS

    图算法DFS与BFS BFS和DFS代表对图进行遍历,即搜索的算法,搜索算法中常用的只要有两种算法:深度优先遍历(Depth-First-Search : DFS)和广度优先遍历(Breadth-Fi ...

  8. [SUCTF 2019]CheckIn 1

    看到字符upload 就想到了文件上传漏洞 就先上传一个一句话木马试试 似乎直接上传不成功,可能是有什么过滤 再上传一个包含一句话木马的图片试试 发现提示不能是图片,这时候就不会了,在网上找了一下wp ...

  9. Django+celery+eventlet+flower+redis异步任务创建及查询实现

    1.环境版本:Django 3.2.12celery 5.3.4eventlet 0.33.3flower 2.0.1redis 3.5.3项目名称:new_project 2.celery配置(se ...

  10. 原来 Lambda 这么简单

    目录 前身 -- 匿名类 lambda 表达式 方法引用 类名::静态方法 类名::实例方法 对象::实例方法 总结 前身 -- 匿名类 何为匿名类? 没有类名,直接通过new关键字创建这个类的实例 ...