我已经构建了一个新的 CLI 工具download-esm ,它采用npm包的名称并将尝试直接从jsDelivr CDN 下载该包的 ECMAScript 模块版本及其所有依赖项 – 然后重写所有导入语句指向那些本地副本。
为什么我建造这个
在谈到 JavaScript 时,我有一些不同寻常的品味。
我真的非常不喜欢在使用 JavaScript 代码时不得不使用本地构建脚本。我已经尝试了很多,不可避免地我发现六个月后我返回项目并且东西不再工作 – 依赖项需要更新,或者我的 Node.js 已过时,或者我正在使用的构建工具已经过时了。
Julia Evans 在Writing Javascript without a build system中非常清楚地捕捉到了我对此的感受。
我只想将一些.js
文件放到一个目录中,将它们加载到一个 HTML 文件中并开始编写代码。
随着时间的推移,按照我想要的方式工作变得越来越困难。许多现代 JavaScript 包假设您将使用npm
和一组构建工具,并且它们的文档一直到npm install package
,然后继续进行更令人兴奋的事情。
有些工具确实提供了第二种选择:CDN 链接。这很棒,几乎是我想要的……但是当我为其他人构建软件时(例如Datasette 插件)我喜欢在我的可安装包中包含 JavaScript 依赖项,而不是依赖于 CDN 保持可用网址永远更多。
通常,该 CDN 链接就足够了:我可以从 CDN 下载.js
文件,将其存放在我自己的目录中,然后继续我的项目。
由于 ECMAScript 模块的日益流行,现在这变得越来越困难。
ECMAScript 模块
我喜欢ECMAScript 模块的总体思路,几年来所有主要浏览器都支持它。
如果你不熟悉它们,它们会让你做这样的事情(来自 Observable Plot入门指南的例子):
< div id ="我的情节" > </div> <脚本类型="模块" > 从“https://cdn.jsdelivr.net/npm/@observablehq/[email protected]/+esm”导入*作为情节; const plot =情节。正确的( {长度: 10000 } , 情节。 binX ( { y : "计数" } , { x :数学。随机} ) ) 。情节( ) ; const div =文件。查询选择器( “#myplot” ) ; 分区。追加(情节) ; </脚本>
这很漂亮。您可以按需导入代码,这使得延迟加载更容易。模块本身可以导入其他模块,浏览器将通过 HTTP/2 并行下载它们并缓存它们以备将来使用。
这里有一个大问题:从 CDN 下载这些文件并将它们存储在本地非常繁琐。
Observable Plot 例如有 40 个嵌套的依赖模块。下载全部 40 个是不够的,因为这些模块中的大多数都包含它们自己的参考,如下所示:
从“/npm/[email protected]/+esm”导出* ; 从“/npm/[email protected]/+esm”导出* ;
这些引用都需要重写以指向模块的本地副本。
来自 Observable Plot 的灵感
我在 Observable Plot 存储库上开了一个问题: Getting started documentation request: Vanilla JS with no CDN 。
一小时后,Mike Bostock提交了一个链接到d3.js
和plot3.js
的 UMB 包的修复程序——这是一个很好的解决方案,但不允许我将它们作为模块导入。但他也发表了这个有趣的评论:
我认为这里的答案可能是有人应该编写一个“下载器”工具,从 jsDelivr(或其他 CDN)下载编译后的 ES 模块,并重写导入语句以使用相对路径。然后你就可以下载这个网址
https://cdn.jsdelivr.net/npm/@observablehq/plot/+esm
你会得到直接的依赖
https://cdn.jsdelivr.net/npm/[email protected]/+esm https://cdn.jsdelivr.net/npm/[email protected]/+esm https://cdn.jsdelivr.net/ npm/[email protected]/+esm
和传递依赖性等作为单独的文件。
所以我建造了它!
下载esm
我构建的新工具称为download-esm 。您可以使用pip install download-esm
或pipx install download-esm
来安装它,如果这是您选择的新安装工具,甚至可以rye install download-esm
安装它。
安装后,您可以尝试下载任何npm
包的 ECMAScript 模块版本 – 及其依赖项 – 如下所示:
下载-esm @observablehq/plot plot/
这将下载每个文件的模块版本,重写它们的导入并将它们保存在plot/
目录中。
当我运行上面的命令时,我从ls plot/
得到以下信息:
binary-search-bounds-2-0-5.js d3-7-8-4.js d3-数组-3-2-0.js d3-数组-3-2-1.js d3-array-3-2-3.js d3-axis-3-0-0.js d3-brush-3-0-0.js d3-chord-3-0-1.js d3-color-3-1-0.js d3-轮廓-4-0-2.js d3-delaunay-6-0-4.js d3-dispatch-3-0-1.js d3-拖动-3-0-0.js d3-dsv-3-0-1.js d3-轻松-3-0-1.js d3-fetch-3-0-1.js d3-force-3-0-0.js d3-格式-3-1-0.js d3-geo-3-1-0.js d3-hierarchy-3-1-2.js d3-interpolate-3-0-1.js d3-路径-3-1-0.js d3-多边形-3-0-1.js d3-quadtree-3-0-1.js d3-随机-3-0-1.js d3-scale-4-0-2.js d3-scale-chromatic-3-0-0.js d3-selection-3-0-0.js d3-shape-3-2-0.js d3-time-3-1-0.js d3-time-format-4-1-0.js d3-timer-3-0-1.js d3-transition-3-0-1.js d3-zoom-3-0-0.js delaunator-5-0-0.js 实习地图-2-0-3.js 间隔树-1d-1-0-4.js 等格式-0-2-1.js 可观察的hq-plot-0-6-6.js robust-predicates-3-0-1.js
然后要使用 Observable Plot,您可以将其放在同一目录中的index.html
文件中:
< div id ="我的情节" > </div> <脚本类型="模块" > 从“./observablehq-plot-0-6-6.js”导入*作为情节; const plot =情节。正确的( {长度: 10000 } ,情节。 binX ( { y : "count" } , { x : Math . random } ) ) 。情节( ) ; const div =文件。查询选择器( “#myplot” ) ; 分区。追加(情节) ; </脚本>
然后运行python3 -m http.server
在端口 8000 上启动服务器(ECMAScript 模块不能直接通过打开文件工作),并在浏览器中打开http://localhost:8000/
。
怎么运行的
老实说,这没什么。这个文件中有 100 行 Python – 大部分工作是由一些正则表达式完成的,这些正则表达式本身主要由 ChatGPT 编写。
我发布了第一个 alpha 版本,因为它可以让 Observable Plot 工作,因为这是我创建这个项目的最初原因。
我有一个未解决的问题,邀请人们帮助使用其他软件包对其进行测试。该问题包括我自己对到目前为止尝试过的东西的评论。
到目前为止,我已经成功地将它用于preact和htm ,用于codemirror并部分用于monaco-editor – 尽管当您尝试启用语法突出显示时 Monaco 会中断,因为它会尝试从错误的位置动态加载其他模块。
需要你的帮助
在我看来,没有人解决这个问题的可能性很小——如果我能退出download-esm
以支持其他解决方案,我会很高兴。
如果这个工具确实填补了一个新的利基市场,我很乐意让它变得更加健壮。我不是一个经常使用 JavaScript 的开发人员,所以我敢肯定有各种各样的边缘情况和功能是我没有想到的。
欢迎投稿!
原文: http://simonwillison.net/2023/May/2/download-esm/#atom-everything