diff --git a/scripts/update-npm-polyfills.sh b/scripts/update-npm-polyfills.sh deleted file mode 100755 index e898604fb..000000000 --- a/scripts/update-npm-polyfills.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -tag="0.0.1" -dlUrl="https://codeload.github.com/esm-dev/esm-npm-polyfills/tar.gz/refs/tags/${tag}" - -cd $(dirname $0) -curl -o "esm-npm-polyfills-${tag}.tar.gz" $dlUrl -mv "esm-npm-polyfills-${tag}.tar.gz" ../server/embed/npm-polyfills.tar.gz diff --git a/server/build.go b/server/build.go index 5a9511531..f9f0f3c81 100644 --- a/server/build.go +++ b/server/build.go @@ -448,10 +448,11 @@ func (ctx *BuildContext) buildModule() (result BuildResult, err error) { } } - // use polyfilled 'fsevents' module for browser + // use the polyfilled 'fsevents' module for browser if specifier == "fsevents" && ctx.isBrowserTarget() { + data := npmPolyfills[specifier] return api.OnResolveResult{ - Path: "npm_fsevents.js", + Path: fmt.Sprintf("data:text/javascript;base64,%s", base64.StdEncoding.EncodeToString(data)), External: true, }, nil } diff --git a/server/build_resolver.go b/server/build_resolver.go index 2bfe1aff6..f62ec22b3 100644 --- a/server/build_resolver.go +++ b/server/build_resolver.go @@ -780,7 +780,7 @@ func (ctx *BuildContext) resolveExternalModule(specifier string, kind api.Resolv return } if specifier == "node-fetch" && ctx.target != "node" { - resolvedPath = "/npm_node-fetch.js" + resolvedPath = "/node/fetch.js" return } diff --git a/server/embed.go b/server/embed.go index 40f728f92..3fd201b79 100644 --- a/server/embed.go +++ b/server/embed.go @@ -18,6 +18,7 @@ var ( ) type EmbedFS interface { + ReadDir(name string) ([]os.DirEntry, error) ReadFile(name string) ([]byte, error) } @@ -25,6 +26,10 @@ type MockEmbedFS struct { cwd string } +func (fs MockEmbedFS) ReadDir(name string) ([]os.DirEntry, error) { + return os.ReadDir(path.Join(fs.cwd, name)) +} + func (fs MockEmbedFS) ReadFile(name string) ([]byte, error) { return os.ReadFile(path.Join(fs.cwd, name)) } @@ -57,46 +62,38 @@ func loadNodeLibs(fs EmbedFS) (err error) { } } // override some libs - node_async_hooks_js, err := fs.ReadFile("server/embed/polyfills/node_async_hooks.js") + entries, err := fs.ReadDir("server/embed/polyfills") if err != nil { return } - nodeLibs["node/async_hooks.js"] = string(node_async_hooks_js) - // extra libs - node_filename_resolver_js, err := fs.ReadFile("server/embed/polyfills/node_filename_resolver.js") - if err != nil { - return + for _, entry := range entries { + name := entry.Name() + if !entry.IsDir() && strings.HasPrefix(name, "node_") && strings.HasSuffix(name, ".js") { + data, err := fs.ReadFile("server/embed/polyfills/" + name) + if err != nil { + return err + } + nodeLibs["node/"+name[5:]] = string(data) + } } - nodeLibs["node/filename_resolver.js"] = string(node_filename_resolver_js) return nil } func loadNpmPolyfills(fs EmbedFS) (err error) { - data, err := fs.ReadFile("server/embed/npm-polyfills.tar.gz") + entries, err := fs.ReadDir("server/embed/polyfills/npm") if err != nil { return } - gr, err := gzip.NewReader(bytes.NewReader(data)) - if err != nil { - return - } - tr := tar.NewReader(gr) - for { - h, err := tr.Next() - if err != nil { - break - } - if h.Typeflag == tar.TypeReg { - data, err := io.ReadAll(tr) + for _, entry := range entries { + name := entry.Name() + if !entry.IsDir() && strings.HasSuffix(name, ".mjs") { + data, err := fs.ReadFile("server/embed/polyfills/npm/" + name) if err != nil { return err } - if strings.HasSuffix(h.Name, ".mjs") { - name := strings.TrimSuffix(path.Base(h.Name), ".mjs") - data = bytes.ReplaceAll(data, []byte{';', '\n'}, []byte{';'}) - data = bytes.TrimSuffix(data, []byte{';'}) - npmPolyfills[name] = data - } + data = bytes.ReplaceAll(data, []byte{';', '\n'}, []byte{';'}) + data = bytes.TrimSuffix(data, []byte{';'}) + npmPolyfills[strings.TrimSuffix(name, ".mjs")] = data } } return nil diff --git a/server/embed/npm-polyfills.tar.gz b/server/embed/npm-polyfills.tar.gz deleted file mode 100644 index 247813b86..000000000 Binary files a/server/embed/npm-polyfills.tar.gz and /dev/null differ diff --git a/server/embed/polyfills/npm_node-fetch.js b/server/embed/polyfills/node_fetch.js similarity index 100% rename from server/embed/polyfills/npm_node-fetch.js rename to server/embed/polyfills/node_fetch.js diff --git a/server/embed/polyfills/npm/README.md b/server/embed/polyfills/npm/README.md new file mode 100644 index 000000000..1b5136342 --- /dev/null +++ b/server/embed/polyfills/npm/README.md @@ -0,0 +1,37 @@ +# esm-npm-polyfills + +Using _native_ APIs instead of importing from NPM. + +- [abort-controller](https://www.npmjs.com/package/abort-controller) → [abort-controller.mjs](./abort-controller.mjs) +- [array-every](https://www.npmjs.com/package/array-every) → [array-every.mjs](./array-every.mjs) +- [array-flatten](https://www.npmjs.com/package/array-flatten) → [array-flatten.mjs](./array-flatten.mjs) +- [array-includes](https://www.npmjs.com/package/array-includes) → [array-includes.mjs](./array-includes.mjs) +- [array.prototype.find](https://www.npmjs.com/package/array.prototype.find) → [array.prototype.find.mjs](./array.prototype.find.mjs) +- [define-properties](https://www.npmjs.com/package/define-properties) → [define-properties.mjs](./define-properties.mjs) +- [define-property](https://www.npmjs.com/package/define-property) → [define-property.mjs](./define-property.mjs) +- [es-define-property](https://www.npmjs.com/package/es-define-property) → [es-define-property.mjs](./es-define-property.mjs) +- [filter-array](https://www.npmjs.com/package/filter-array) → [filter-array.mjs](./filter-array.mjs) +- [for-each](https://www.npmjs.com/package/for-each) → [for-each.mjs](./for-each.mjs) +- [function-bind](https://www.npmjs.com/package/function-bind) → [function-bind.mjs](./function-bind.mjs) +- [has-own-prop](https://www.npmjs.com/package/has-own-prop) → [has-own-prop.mjs](./has-own-prop.mjs) +- [has-own](https://www.npmjs.com/package/has-own) → [has-own.mjs](./has-own.mjs) +- [has-property-descriptors](https://www.npmjs.com/package/has-property-descriptors) → [has-property-descriptors.mjs](./has-property-descriptors.mjs) +- [has-proto](https://www.npmjs.com/package/has-proto) → [has-proto.mjs](./has-proto.mjs) +- [has-symbols](https://www.npmjs.com/package/has-symbols) → [has-symbols.mjs](./has-symbols.mjs) +- [has-tostringtag](https://www.npmjs.com/package/has-tostringtag) → [has-tostringtag.mjs](./has-tostringtag.mjs) +- [hasown](https://www.npmjs.com/package/hasown) → [hasown.mjs](./hasown.mjs) +- [index-of](https://www.npmjs.com/package/index-of) → [index-of.mjs](./index-of.mjs) +- [is-even](https://www.npmjs.com/package/is-even) → [is-even.mjs](./is-even.mjs) +- [is-nan](https://www.npmjs.com/package/is-nan) → [is-nan.mjs](./is-nan.mjs) +- [is-number](https://www.npmjs.com/package/is-number) → [is-number.mjs](./is-number.mjs) +- [is-odd](https://www.npmjs.com/package/is-odd) → [is-odd.mjs](./is-odd.mjs) +- [is-regexp](https://www.npmjs.com/package/is-regexp) → [is-regexp.mjs](./is-regexp.mjs) +- [is-string](https://www.npmjs.com/package/is-string) → [is-string.mjs](./is-string.mjs) +- [left-pad](https://www.npmjs.com/package/left-pad) → [left-pad.mjs](./left-pad.mjs) +- [pad-left](https://www.npmjs.com/package/pad-left) → [pad-left.mjs](./pad-left.mjs) +- [object-assign](https://www.npmjs.com/package/object-assign) → [object-assign.mjs](./object-assign.mjs) +- [object-is](https://www.npmjs.com/package/object-is) → [object-is.mjs](./object-is.mjs) +- [object-keys](https://www.npmjs.com/package/object-keys) → [object-keys.mjs](./object-keys.mjs) +- [object.entries](https://www.npmjs.com/package/object.entries) → [object.entries.mjs](./object.entries.mjs) +- [object.values](https://www.npmjs.com/package/object.values) → [object.values.mjs](./object.values.mjs) +- [regexp.prototype.flags](https://www.npmjs.com/package/regexp.prototype.flags) → [regexp.prototype.flags.mjs](./regexp.prototype.flags.mjs) diff --git a/server/embed/polyfills/npm/abort-controller.mjs b/server/embed/polyfills/npm/abort-controller.mjs new file mode 100644 index 000000000..22c86be75 --- /dev/null +++ b/server/embed/polyfills/npm/abort-controller.mjs @@ -0,0 +1,3 @@ +export const AbortSignal=globalThis.AbortSignal; +export const AbortController=globalThis.AbortController; +export default AbortController; diff --git a/server/embed/polyfills/npm/array-every.mjs b/server/embed/polyfills/npm/array-every.mjs new file mode 100644 index 000000000..51db38072 --- /dev/null +++ b/server/embed/polyfills/npm/array-every.mjs @@ -0,0 +1,2 @@ +export const every=(a,p,t)=>a.every(p,t); +export default every; diff --git a/server/embed/polyfills/npm/array-flatten.mjs b/server/embed/polyfills/npm/array-flatten.mjs new file mode 100644 index 000000000..2d1e5cd2f --- /dev/null +++ b/server/embed/polyfills/npm/array-flatten.mjs @@ -0,0 +1,2 @@ +export const flatten=(a,d)=>a.flat(typeof d<"u"?d:Infinity); +export default flatten; diff --git a/server/embed/polyfills/npm/array-includes.mjs b/server/embed/polyfills/npm/array-includes.mjs new file mode 100644 index 000000000..ea807cc90 --- /dev/null +++ b/server/embed/polyfills/npm/array-includes.mjs @@ -0,0 +1 @@ +export default (a,p,i)=>a.includes(p,i); diff --git a/server/embed/polyfills/npm/array.prototype.find.mjs b/server/embed/polyfills/npm/array.prototype.find.mjs new file mode 100644 index 000000000..c5936f7a6 --- /dev/null +++ b/server/embed/polyfills/npm/array.prototype.find.mjs @@ -0,0 +1 @@ +export default (a,p,t)=>a.find(p,t); diff --git a/server/embed/polyfills/npm/define-properties.mjs b/server/embed/polyfills/npm/define-properties.mjs new file mode 100644 index 000000000..545bcfe99 --- /dev/null +++ b/server/embed/polyfills/npm/define-properties.mjs @@ -0,0 +1 @@ +export default Object.defineProperties; diff --git a/server/embed/polyfills/npm/define-property.mjs b/server/embed/polyfills/npm/define-property.mjs new file mode 100644 index 000000000..d07878600 --- /dev/null +++ b/server/embed/polyfills/npm/define-property.mjs @@ -0,0 +1 @@ +export default Object.defineProperty; diff --git a/server/embed/polyfills/npm/es-define-property.mjs b/server/embed/polyfills/npm/es-define-property.mjs new file mode 100644 index 000000000..d07878600 --- /dev/null +++ b/server/embed/polyfills/npm/es-define-property.mjs @@ -0,0 +1 @@ +export default Object.defineProperty; diff --git a/server/embed/polyfills/npm/filter-array.mjs b/server/embed/polyfills/npm/filter-array.mjs new file mode 100644 index 000000000..be41d20b0 --- /dev/null +++ b/server/embed/polyfills/npm/filter-array.mjs @@ -0,0 +1 @@ +export default (a,p,t)=>a.filter(p,t); diff --git a/server/embed/polyfills/npm/for-each.mjs b/server/embed/polyfills/npm/for-each.mjs new file mode 100644 index 000000000..40bd1032c --- /dev/null +++ b/server/embed/polyfills/npm/for-each.mjs @@ -0,0 +1,9 @@ +export default (v, cb) => { + if (Array.isArray(v)) { + v.forEach(cb); + } else if (v && typeof v === "object") { + for (const k in v) { + cb(v[k], k, v); + } + } +}; diff --git a/server/embed/polyfills/npm_fsevents.js b/server/embed/polyfills/npm/fsevents.mjs similarity index 100% rename from server/embed/polyfills/npm_fsevents.js rename to server/embed/polyfills/npm/fsevents.mjs diff --git a/server/embed/polyfills/npm/function-bind.mjs b/server/embed/polyfills/npm/function-bind.mjs new file mode 100644 index 000000000..1fe118b95 --- /dev/null +++ b/server/embed/polyfills/npm/function-bind.mjs @@ -0,0 +1 @@ +export default Function.prototype.bind; diff --git a/server/embed/polyfills/npm/has-own-prop.mjs b/server/embed/polyfills/npm/has-own-prop.mjs new file mode 100644 index 000000000..33642a2dc --- /dev/null +++ b/server/embed/polyfills/npm/has-own-prop.mjs @@ -0,0 +1 @@ +export default (obj,prop)=>Object.prototype.hasOwnProperty.call(obj,prop); diff --git a/server/embed/polyfills/npm/has-own.mjs b/server/embed/polyfills/npm/has-own.mjs new file mode 100644 index 000000000..c25fc7dc8 --- /dev/null +++ b/server/embed/polyfills/npm/has-own.mjs @@ -0,0 +1 @@ +export default Object.hasOwn??((o,p)=>Object.prototype.hasOwnProperty.call(o,p)); diff --git a/server/embed/polyfills/npm/has-property-descriptors.mjs b/server/embed/polyfills/npm/has-property-descriptors.mjs new file mode 100644 index 000000000..3c72b2d38 --- /dev/null +++ b/server/embed/polyfills/npm/has-property-descriptors.mjs @@ -0,0 +1 @@ +export default () => true; diff --git a/server/embed/polyfills/npm/has-proto.mjs b/server/embed/polyfills/npm/has-proto.mjs new file mode 100644 index 000000000..c63c6109c --- /dev/null +++ b/server/embed/polyfills/npm/has-proto.mjs @@ -0,0 +1,3 @@ +const foo={bar:{}}; +const O=Object; +export default ()=>({__proto__:foo}).bar===foo.bar&&!({__proto__:null}instanceof O); diff --git a/server/embed/polyfills/npm/has-symbols.mjs b/server/embed/polyfills/npm/has-symbols.mjs new file mode 100644 index 000000000..da93f455d --- /dev/null +++ b/server/embed/polyfills/npm/has-symbols.mjs @@ -0,0 +1 @@ +export default ()=>true; diff --git a/server/embed/polyfills/npm/has-tostringtag.mjs b/server/embed/polyfills/npm/has-tostringtag.mjs new file mode 100644 index 000000000..f99885733 --- /dev/null +++ b/server/embed/polyfills/npm/has-tostringtag.mjs @@ -0,0 +1 @@ +export default ()=>typeof Symbol.toStringTag==="symbol"; diff --git a/server/embed/polyfills/npm/hasown.mjs b/server/embed/polyfills/npm/hasown.mjs new file mode 100644 index 000000000..c25fc7dc8 --- /dev/null +++ b/server/embed/polyfills/npm/hasown.mjs @@ -0,0 +1 @@ +export default Object.hasOwn??((o,p)=>Object.prototype.hasOwnProperty.call(o,p)); diff --git a/server/embed/polyfills/npm/index-of.mjs b/server/embed/polyfills/npm/index-of.mjs new file mode 100644 index 000000000..c8943d687 --- /dev/null +++ b/server/embed/polyfills/npm/index-of.mjs @@ -0,0 +1 @@ +export default (a,el,i)=>a.indexOf(el,i); diff --git a/server/embed/polyfills/npm/is-even.mjs b/server/embed/polyfills/npm/is-even.mjs new file mode 100644 index 000000000..24d983f7a --- /dev/null +++ b/server/embed/polyfills/npm/is-even.mjs @@ -0,0 +1 @@ +export default n=>(n%2)===0; diff --git a/server/embed/polyfills/npm/is-nan.mjs b/server/embed/polyfills/npm/is-nan.mjs new file mode 100644 index 000000000..3cceafd24 --- /dev/null +++ b/server/embed/polyfills/npm/is-nan.mjs @@ -0,0 +1 @@ +export default Number.isNaN; diff --git a/server/embed/polyfills/npm/is-number.mjs b/server/embed/polyfills/npm/is-number.mjs new file mode 100644 index 000000000..c4fdd9a17 --- /dev/null +++ b/server/embed/polyfills/npm/is-number.mjs @@ -0,0 +1 @@ +export default v=>typeof v==="number"; diff --git a/server/embed/polyfills/npm/is-odd.mjs b/server/embed/polyfills/npm/is-odd.mjs new file mode 100644 index 000000000..2b3c0b239 --- /dev/null +++ b/server/embed/polyfills/npm/is-odd.mjs @@ -0,0 +1 @@ +export default n=>(n%2)===1; diff --git a/server/embed/polyfills/npm/is-regexp.mjs b/server/embed/polyfills/npm/is-regexp.mjs new file mode 100644 index 000000000..696cbbecd --- /dev/null +++ b/server/embed/polyfills/npm/is-regexp.mjs @@ -0,0 +1 @@ +export default v=>v instanceof RegExp; diff --git a/server/embed/polyfills/npm/is-string.mjs b/server/embed/polyfills/npm/is-string.mjs new file mode 100644 index 000000000..078d8d694 --- /dev/null +++ b/server/embed/polyfills/npm/is-string.mjs @@ -0,0 +1 @@ +export default v=>typeof v==="string"; diff --git a/server/embed/polyfills/npm/left-pad.mjs b/server/embed/polyfills/npm/left-pad.mjs new file mode 100644 index 000000000..8af6c0337 --- /dev/null +++ b/server/embed/polyfills/npm/left-pad.mjs @@ -0,0 +1 @@ +export default (s,l,c)=>s.padStart(l,c); diff --git a/server/embed/polyfills/npm/object-assign.mjs b/server/embed/polyfills/npm/object-assign.mjs new file mode 100644 index 000000000..9379ae6f1 --- /dev/null +++ b/server/embed/polyfills/npm/object-assign.mjs @@ -0,0 +1 @@ +export default Object.assign; diff --git a/server/embed/polyfills/npm/object-is.mjs b/server/embed/polyfills/npm/object-is.mjs new file mode 100644 index 000000000..5721c8185 --- /dev/null +++ b/server/embed/polyfills/npm/object-is.mjs @@ -0,0 +1 @@ +export default Object.is; diff --git a/server/embed/polyfills/npm/object-keys.mjs b/server/embed/polyfills/npm/object-keys.mjs new file mode 100644 index 000000000..4583a8faa --- /dev/null +++ b/server/embed/polyfills/npm/object-keys.mjs @@ -0,0 +1 @@ +export default Object.keys; diff --git a/server/embed/polyfills/npm/object.entries.mjs b/server/embed/polyfills/npm/object.entries.mjs new file mode 100644 index 000000000..2ecd723c3 --- /dev/null +++ b/server/embed/polyfills/npm/object.entries.mjs @@ -0,0 +1 @@ +export default Object.entries; diff --git a/server/embed/polyfills/npm/object.values.mjs b/server/embed/polyfills/npm/object.values.mjs new file mode 100644 index 000000000..fcb5d39ec --- /dev/null +++ b/server/embed/polyfills/npm/object.values.mjs @@ -0,0 +1 @@ +export default Object.values; diff --git a/server/embed/polyfills/npm/pad-left.mjs b/server/embed/polyfills/npm/pad-left.mjs new file mode 100644 index 000000000..8af6c0337 --- /dev/null +++ b/server/embed/polyfills/npm/pad-left.mjs @@ -0,0 +1 @@ +export default (s,l,c)=>s.padStart(l,c); diff --git a/server/embed/polyfills/npm/regexp.prototype.flags.mjs b/server/embed/polyfills/npm/regexp.prototype.flags.mjs new file mode 100644 index 000000000..23462090f --- /dev/null +++ b/server/embed/polyfills/npm/regexp.prototype.flags.mjs @@ -0,0 +1 @@ +export default r=>r.flags; diff --git a/server/router.go b/server/router.go index 97f839c93..66bd319c5 100644 --- a/server/router.go +++ b/server/router.go @@ -472,16 +472,9 @@ func router() rex.Handle { return rex.Content(pathname, startTime, bytes.NewReader(code)) } - // use embed polyfills/types - if endsWith(pathname, ".js", ".d.ts") && strings.Count(pathname, "/") == 1 { - var data []byte - var err error - isDts := strings.HasSuffix(pathname, ".d.ts") - if isDts { - data, err = embedFS.ReadFile("server/embed/types" + pathname) - } else { - data, err = embedFS.ReadFile("server/embed/polyfills" + pathname) - } + // use embed types + if strings.HasSuffix(pathname, ".d.ts") && strings.Count(pathname, "/") == 1 { + data, err := embedFS.ReadFile("server/embed/types" + pathname) if err == nil { ifNoneMatch := ctx.R.Header.Get("If-None-Match") if ifNoneMatch != "" && ifNoneMatch == globalETag { @@ -495,18 +488,7 @@ func router() rex.Handle { header.Set("ETag", globalETag) } } - if isDts { - header.Set("Content-Type", ctTypeScript) - } else { - target := getBuildTargetByUA(userAgent) - code, err := minify(string(data), targets[target], api.LoaderJS) - if err != nil { - return throwErrorJS(ctx, fmt.Sprintf("Transform error: %v", err), false) - } - data = []byte(code) - header.Set("Content-Type", ctJavaScript) - appendVaryHeader(header, "User-Agent") - } + header.Set("Content-Type", ctTypeScript) return rex.Content(pathname, startTime, bytes.NewReader(data)) } } diff --git a/test/esm-worker/test.ts b/test/esm-worker/test.ts index 55abc5dc7..1a9ef0975 100644 --- a/test/esm-worker/test.ts +++ b/test/esm-worker/test.ts @@ -241,14 +241,6 @@ Deno.test("esm-worker", { sanitizeOps: false, sanitizeResources: false }, async assertEquals(res6.headers.get("Content-Type"), "application/javascript; charset=utf-8"); assertEquals(res6.headers.get("Cache-Control"), "public, max-age=31536000, immutable"); - const res7 = await fetch(`${workerOrigin}/npm_node-fetch.js`); - assertEquals(res7.status, 200); - assertEquals(res7.headers.get("Content-Type"), "application/javascript; charset=utf-8"); - assertEquals(res7.headers.get("Etag"), `W/"${version}"`); - assertEquals(res7.headers.get("Cache-Control"), "public, max-age=86400"); - assertStringIncludes(res7.headers.get("Vary")!, "User-Agent"); - assertStringIncludes(await res7.text(), "fetch"); - const fs = await import(`${workerOrigin}/node/fs.js`); fs.writeFileSync("foo.txt", "bar", "utf8"); assertEquals(fs.readFileSync("foo.txt", "utf8"), "bar"); diff --git a/worker/src/index.ts b/worker/src/index.ts index b872606d6..d85b2d0da 100644 --- a/worker/src/index.ts +++ b/worker/src/index.ts @@ -296,7 +296,7 @@ function withESMWorker(middleware?: Middleware, cache: Cache = (caches as any).d pathname === "/run.d.ts" || pathname === "/sw" || pathname === "/tsx" || - ((pathname.startsWith("/node/") || pathname.startsWith("/npm_")) && pathname.endsWith(".js")) + (pathname.startsWith("/node/") && pathname.endsWith(".js")) ) { const varyUA = !pathname.endsWith(".ts"); const isChunkjs = pathname.startsWith("/node/chunk-");