<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: ecmascript</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/ecmascript.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-02-18T21:53:56+00:00</updated><author><name>Simon Willison</name></author><entry><title>tc39/proposal-regex-escaping</title><link href="https://simonwillison.net/2025/Feb/18/tc39proposal-regex-escaping/#atom-tag" rel="alternate"/><published>2025-02-18T21:53:56+00:00</published><updated>2025-02-18T21:53:56+00:00</updated><id>https://simonwillison.net/2025/Feb/18/tc39proposal-regex-escaping/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/tc39/proposal-regex-escaping"&gt;tc39/proposal-regex-escaping&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I just heard &lt;a href="https://social.coop/@kriskowal/114026510846190089"&gt;from Kris Kowal&lt;/a&gt; that this proposal for ECMAScript has been approved for ECMA TC-39:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Almost 20 years later, @simon’s RegExp.escape idea comes to fruition. This reached “Stage 4” at ECMA TC-39 just now, which formalizes that multiple browsers have shipped the feature and it’s in the next revision of the JavaScript specification.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I'll be honest, I had completely forgotten about my 2006 blog entry &lt;a href="https://simonwillison.net/2006/Jan/20/escape/"&gt;Escaping regular expression characters in JavaScript&lt;/a&gt; where I proposed that JavaScript should have an equivalent of the Python &lt;a href="https://docs.python.org/3/library/re.html#re.escape"&gt;re.escape()&lt;/a&gt; function.&lt;/p&gt;
&lt;p&gt;It turns out my post was referenced in &lt;a href="https://esdiscuss.org/topic/regexp-escape"&gt;this 15 year old thread&lt;/a&gt; on the esdiscuss mailing list, which evolved over time into a proposal which turned into &lt;a href="https://caniuse.com/mdn-javascript_builtins_regexp_escape"&gt;implementations&lt;/a&gt; in Safari, Firefox and soon Chrome - here's &lt;a href="https://github.com/v8/v8/commit/b5c08badc7b3d4b85b2645b1a4d9973ee6efaa91"&gt;the commit landing it in v8&lt;/a&gt; on February 12th 2025.&lt;/p&gt;
&lt;p&gt;One of the best things about having a long-running blog is that sometimes posts you forgot about over a decade ago turn out to have a life of their own.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/blogging"&gt;blogging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ecmascript"&gt;ecmascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/regular-expressions"&gt;regular-expressions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/standards"&gt;standards&lt;/a&gt;&lt;/p&gt;



</summary><category term="blogging"/><category term="ecmascript"/><category term="javascript"/><category term="regular-expressions"/><category term="standards"/></entry><entry><title>download-esm: a tool for downloading ECMAScript modules</title><link href="https://simonwillison.net/2023/May/2/download-esm/#atom-tag" rel="alternate"/><published>2023-05-02T04:47:29+00:00</published><updated>2023-05-02T04:47:29+00:00</updated><id>https://simonwillison.net/2023/May/2/download-esm/#atom-tag</id><summary type="html">
    &lt;p&gt;I've built a new CLI tool, &lt;a href="https://github.com/simonw/download-esm"&gt;download-esm&lt;/a&gt;, which takes the name of an &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt; package and will attempt to download the ECMAScript module version of that package, plus all of its dependencies, directly from the &lt;a href="https://www.jsdelivr.com/"&gt;jsDelivr&lt;/a&gt; CDN - and then rewrite all of the import statements to point to those local copies.&lt;/p&gt;
&lt;h4&gt;Why I built this&lt;/h4&gt;
&lt;p&gt;I have somewhat unconventional tastes when it comes to JavaScript.&lt;/p&gt;
&lt;p&gt;I really, really dislike having to use a local build script when I'm working with JavaScript code. I've tried plenty, and inevitably I find that six months later I return to the project and stuff doesn't work any more - dependencies need updating, or my Node.js is out of date, or the build tool I'm using has gone out of fashion.&lt;/p&gt;
&lt;p&gt;Julia Evans captured how I feel about this really clearly in &lt;a href="https://jvns.ca/blog/2023/02/16/writing-javascript-without-a-build-system/"&gt;Writing Javascript without a build system&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I just want to drop some &lt;code&gt;.js&lt;/code&gt; files into a directory, load them into an HTML file and start writing code.&lt;/p&gt;
&lt;p&gt;Working the way I want to work is becoming increasingly difficult over time. Many modern JavaScript packages assume you'll be using &lt;code&gt;npm&lt;/code&gt; and a set of build tools, and their documentation gets as far as &lt;code&gt;npm install package&lt;/code&gt; and then moves on to more exciting things.&lt;/p&gt;
&lt;p&gt;Some tools do offer a second option: a CDN link. This is great, and &lt;em&gt;almost&lt;/em&gt; what I want... but when I'm building software for other people (&lt;a href="https://datasette.io/plugins"&gt;Datasette plugins&lt;/a&gt; for example) I like to include the JavaScript dependencies in my installable package, rather than depending on a CDN staying available at that URL forever more.&lt;/p&gt;
&lt;p&gt;This is a key point: &lt;em&gt;I don't want to depend on a fixed CDN&lt;/em&gt;. If you're happy using a CDN then &lt;code&gt;download-esm&lt;/code&gt; is not a useful tool for you.&lt;/p&gt;
&lt;p&gt;Usually, that CDN link is enough: I can download the &lt;code&gt;.js&lt;/code&gt; file from the CDN, stash it in my own directory and get on with my project.&lt;/p&gt;
&lt;p&gt;This is getting increasingly difficult now, thanks to the growing popularity of ECMAScript modules.&lt;/p&gt;
&lt;h4&gt;ECMAScript modules&lt;/h4&gt;
&lt;p&gt;I &lt;em&gt;love&lt;/em&gt; the general idea of ECMAScript modules, which have been supported by all of the major browsers for a few years now.&lt;/p&gt;
&lt;p&gt;If you're not familiar with them, they let you do things like this (example from the Observable Plot &lt;a href="https://observablehq.com/plot/getting-started"&gt;getting started guide&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;div&lt;/span&gt; &lt;span class="pl-c1"&gt;id&lt;/span&gt;="&lt;span class="pl-s"&gt;myplot&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;module&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-v"&gt;Plot&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/+esm"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;plot&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Plot&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;rectY&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
    &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;length&lt;/span&gt;: &lt;span class="pl-c1"&gt;10000&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-v"&gt;Plot&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;binX&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
        &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;y&lt;/span&gt;: &lt;span class="pl-s"&gt;"count"&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
        &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt;: &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;random&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
    &lt;span class="pl-kos"&gt;)&lt;/span&gt;
&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;plot&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;div&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;document&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;querySelector&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"#myplot"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-s1"&gt;div&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;append&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;plot&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is beautiful. You can import code on-demand, which makes lazy loading easier. Modules can themselves import other modules, and the browser will download them in parallel over HTTP/2 and cache them for future use.&lt;/p&gt;
&lt;p&gt;There's one big catch here: downloading these files from the CDN and storing them locally is surprisingly fiddly.&lt;/p&gt;
&lt;p&gt;Observable Plot for example has 40 nested dependency modules. And downloading all 40 isn't enough, because most of those modules include their own references that look like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-js"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;export&lt;/span&gt;&lt;span class="pl-c1"&gt;*&lt;/span&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt;&lt;span class="pl-s"&gt;"/npm/d3-array@3.2.3/+esm"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;export&lt;/span&gt;&lt;span class="pl-c1"&gt;*&lt;/span&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt;&lt;span class="pl-s"&gt;"/npm/d3-axis@3.0.0/+esm"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These references all need to be rewritten to point to the local copies of the modules.&lt;/p&gt;
&lt;h4&gt;Inspiration from Observable Plot&lt;/h4&gt;
&lt;p&gt;I opened an issue on the Observable Plot repository: &lt;a href="https://github.com/observablehq/plot/issues/1496"&gt;Getting started documentation request: Vanilla JS with no CDN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An hour later Mike Bostock &lt;a href="https://github.com/observablehq/plot/commit/90a3876c037dc40e436ff4ad9c403f0681e4c203"&gt;committed a fix&lt;/a&gt; linking to UMB bundles for &lt;code&gt;d3.js&lt;/code&gt; and &lt;code&gt;plot3.js&lt;/code&gt; - which is a good solution, but doesn't let me import them as modules. But he also posted &lt;a href="https://github.com/observablehq/plot/issues/1496#issuecomment-1526116800"&gt;this intriguing comment&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I think maybe the answer here is that someone should write a “downloader” tool that downloads the compiled ES modules from jsDelivr (or other CDN) and rewrites the import statements to use relative paths. Then you could just download this URL&lt;/p&gt;
&lt;p&gt;&lt;a href="https://cdn.jsdelivr.net/npm/@observablehq/plot/+esm"&gt;https://cdn.jsdelivr.net/npm/@observablehq/plot/+esm&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;and you’d get the direct dependencies&lt;/p&gt;
&lt;p&gt;&lt;a href="https://cdn.jsdelivr.net/npm/d3@7.8.4/+esm"&gt;https://cdn.jsdelivr.net/npm/d3@7.8.4/+esm&lt;/a&gt; &lt;a href="https://cdn.jsdelivr.net/npm/isoformat@0.2.1/+esm"&gt;https://cdn.jsdelivr.net/npm/isoformat@0.2.1/+esm&lt;/a&gt; &lt;a href="https://cdn.jsdelivr.net/npm/interval-tree-1d@1.0.4/+esm"&gt;https://cdn.jsdelivr.net/npm/interval-tree-1d@1.0.4/+esm&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;and the transitive dependencies and so on as separate files.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So I built that!&lt;/p&gt;
&lt;h4&gt;download-esm&lt;/h4&gt;
&lt;p&gt;The new tool I've built is called &lt;a href="https://pypi.org/project/download-esm/"&gt;download-esm&lt;/a&gt;. You can install it using &lt;code&gt;pip install download-esm&lt;/code&gt;, or &lt;code&gt;pipx install download-esm&lt;/code&gt;, or even &lt;code&gt;rye install download-esm&lt;/code&gt; if that's your &lt;a href="https://til.simonwillison.net/python/rye"&gt;new installation tool of choice&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once installed, you can attempt to download the ECMAScript module version of any &lt;code&gt;npm&lt;/code&gt; package - plus its dependencies - like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;download-esm @observablehq/plot plot/&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will download the module versions of every file, rewrite their imports and save them in the &lt;code&gt;plot/&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;When I run the above I get the following from &lt;code&gt;ls plot/&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;binary-search-bounds-2-0-5.js
d3-7-8-4.js
d3-array-3-2-0.js
d3-array-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-contour-4-0-2.js
d3-delaunay-6-0-4.js
d3-dispatch-3-0-1.js
d3-drag-3-0-0.js
d3-dsv-3-0-1.js
d3-ease-3-0-1.js
d3-fetch-3-0-1.js
d3-force-3-0-0.js
d3-format-3-1-0.js
d3-geo-3-1-0.js
d3-hierarchy-3-1-2.js
d3-interpolate-3-0-1.js
d3-path-3-1-0.js
d3-polygon-3-0-1.js
d3-quadtree-3-0-1.js
d3-random-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
internmap-2-0-3.js
interval-tree-1d-1-0-4.js
isoformat-0-2-1.js
observablehq-plot-0-6-6.js
robust-predicates-3-0-1.js&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then to use Observable Plot you can put this in an &lt;code&gt;index.html&lt;/code&gt; file in the same directory:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;div&lt;/span&gt; &lt;span class="pl-c1"&gt;id&lt;/span&gt;="&lt;span class="pl-s"&gt;myplot&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;module&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-v"&gt;Plot&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"./observablehq-plot-0-6-6.js"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;plot&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Plot&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;rectY&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
    &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;length&lt;/span&gt;: &lt;span class="pl-c1"&gt;10000&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;Plot&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;binX&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;y&lt;/span&gt;: &lt;span class="pl-s"&gt;"count"&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt;: &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;random&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;plot&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;div&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;document&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;querySelector&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"#myplot"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-s1"&gt;div&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;append&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;plot&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then run &lt;code&gt;python3 -m http.server&lt;/code&gt; to start a server on port 8000 (ECMAScript modules don't work directly from opening files), and open &lt;code&gt;http://localhost:8000/&lt;/code&gt; in your browser.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/observable-plot-download-esm.jpg" alt="localhost:8000 displaying a random bar chart generated using Observable Plot" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;h4&gt;How it works&lt;/h4&gt;
&lt;p&gt;There's honestly not a lot to this. It's 100 lines of Python &lt;a href="https://github.com/simonw/download-esm/blob/0.1a0/download_esm/cli.py"&gt;in this file&lt;/a&gt; - most of the work is done by some regular expressions, which were themselves mostly written by ChatGPT.&lt;/p&gt;
&lt;p&gt;I shipped the first alpha release as soon as it could get Observable Plot working, because that was my initial reason for creating the project.&lt;/p&gt;
&lt;p&gt;I have an &lt;a href="https://github.com/simonw/download-esm/issues/2"&gt;open issue&lt;/a&gt; inviting people to help test it with other packages. That issue includes my own comments of stuff I've tried with it so far.&lt;/p&gt;
&lt;p&gt;So far I've successfully used it for &lt;a href="https://www.npmjs.com/package/preact"&gt;preact&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/htm"&gt;htm&lt;/a&gt;, for &lt;a href="https://www.npmjs.com/package/codemirror"&gt;codemirror&lt;/a&gt; and partially for &lt;a href="https://www.npmjs.com/package/monaco-editor"&gt;monaco-editor&lt;/a&gt; - though Monaco breaks when you attempt to enable syntax highlighting, as it attempts to dynamically load additional modules from the wrong place.&lt;/p&gt;
&lt;h4&gt;Your help needed&lt;/h4&gt;
&lt;p&gt;It seems very unlikely to me that no-one has solved this problem - I would be delighted if I could retire &lt;code&gt;download-esm&lt;/code&gt; in favour of some other solution.&lt;/p&gt;
&lt;p&gt;If this tool does turn out to fill a new niche, I'd love to make it more robust. I'm not a frequent JavaScript developer so I'm certain there are all sorts of edge-cases and capabilities I haven't thought of.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/download-esm"&gt;Contributions welcome&lt;/a&gt;!&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cli"&gt;cli&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ecmascript"&gt;ecmascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/npm"&gt;npm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="cli"/><category term="ecmascript"/><category term="javascript"/><category term="projects"/><category term="npm"/><category term="ai-assisted-programming"/></entry><entry><title>ECMAScript Harmony</title><link href="https://simonwillison.net/2008/Aug/14/harmony/#atom-tag" rel="alternate"/><published>2008-08-14T09:37:18+00:00</published><updated>2008-08-14T09:37:18+00:00</updated><id>https://simonwillison.net/2008/Aug/14/harmony/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://ejohn.org/blog/ecmascript-harmony/"&gt;ECMAScript Harmony&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
John Resig explains the outcome of the recent “Oslo meeting” where proponents of ECMAScript 3.1 (incremental improvements to JS as it exists today) and 4 (massive, sweeping changes including many new programming constructs) harmonised their differences. The combined effort is closer to 3.1 than it is to 4, which I think is the right decision.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ecmascript"&gt;ecmascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/harmony"&gt;harmony&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/john-resig"&gt;john-resig&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/oslomeeting"&gt;oslomeeting&lt;/a&gt;&lt;/p&gt;



</summary><category term="ecmascript"/><category term="harmony"/><category term="javascript"/><category term="john-resig"/><category term="oslomeeting"/></entry><entry><title>The Story Behind ES4</title><link href="https://simonwillison.net/2007/Nov/2/neil/#atom-tag" rel="alternate"/><published>2007-11-02T06:15:57+00:00</published><updated>2007-11-02T06:15:57+00:00</updated><id>https://simonwillison.net/2007/Nov/2/neil/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.neilmix.com/2007/11/01/the-story-behind-es4/"&gt;The Story Behind ES4&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
If you’re scratching your head at the recent eruption of acrimony surrounding ECMAScript 4 (the next standardised version of JavaScript) Neil Mix has a relatively easy to follow catch-up post.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ecmascript"&gt;ecmascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/es4"&gt;es4&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/neil-mix"&gt;neil-mix&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/standards"&gt;standards&lt;/a&gt;&lt;/p&gt;



</summary><category term="ecmascript"/><category term="es4"/><category term="javascript"/><category term="neil-mix"/><category term="standards"/></entry><entry><title>J4P5: Javascript For PHP 5</title><link href="https://simonwillison.net/2007/Jul/12/j4p5/#atom-tag" rel="alternate"/><published>2007-07-12T22:24:58+00:00</published><updated>2007-07-12T22:24:58+00:00</updated><id>https://simonwillison.net/2007/Jul/12/j4p5/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://j4p5.sourceforge.net/"&gt;J4P5: Javascript For PHP 5&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
“J4P5 is a JavaScript interpreter written in PHP 5, that allows to run untrusted scripts in a sandbox on your server. It aims to implement most of Ecma-262 3rd edition.”

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://twitter.com/tommorris/statuses/147119642"&gt;Tom Morris&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ecmascript"&gt;ecmascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/j4p5"&gt;j4p5&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php5"&gt;php5&lt;/a&gt;&lt;/p&gt;



</summary><category term="ecmascript"/><category term="j4p5"/><category term="javascript"/><category term="php"/><category term="php5"/></entry><entry><title>ECMAScript 4 Reference Implementation</title><link href="https://simonwillison.net/2007/Jun/10/ecmascript/#atom-tag" rel="alternate"/><published>2007-06-10T12:49:01+00:00</published><updated>2007-06-10T12:49:01+00:00</updated><id>https://simonwillison.net/2007/Jun/10/ecmascript/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://lambda-the-ultimate.org/node/2289"&gt;ECMAScript 4 Reference Implementation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Including discussion of the benefits of writing it in Standard ML.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ecmascript"&gt;ecmascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/standardml"&gt;standardml&lt;/a&gt;&lt;/p&gt;



</summary><category term="ecmascript"/><category term="standardml"/></entry></feed>