Skip to content

Commit

Permalink
Add ?raw to support requests for raw package source files (#731)
Browse files Browse the repository at this point in the history
* Add ?raw option to request raw (untransformed) package source files

In rare cases, you may want to request JS source files from packages, as-is, without transformation into ES modules. This commit adds support for this via a new `?raw`querystring option.

* Also support &raw as an extra query (after package version)

* Add ?raw support to esm-worker; Fix content-type

Add support for ?raw query and &raw extra query in esm-worker.

Add explicit Content-Type header values for raw source files (to ensure consistent results).
  • Loading branch information
johnyanarella authored Sep 27, 2023
1 parent fc98290 commit e9e8438
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 0 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,35 @@ package version.
> esm.sh also provides a [CLI Script](#using-cli-script) in Deno to generate and
> update the import maps that resolves dependencies automatically.
### Escape Hatch: Raw Source Files
In rare cases, you may want to request JS source files from packages, as-is,
without transformation into ES modules. To do so, you need to add a `?raw`
query to the request URL.
For example, you might need to register a package's source script as a service worker
in a browser that [does not yet support](https://caniuse.com/mdn-api_serviceworker_ecmascript_modules)
the `type: "module"` option:

```js
await navigator.serviceWorker.register(
new URL(
"https://esm.sh/[email protected]/playground-service-worker.js?raw",
import.meta.url.href
),
{ scope: '/' }
);
```

You may alternatively specify an `&raw` extra query after the package version:

```html
<playground-project sandbox-base-url="https://esm.sh/[email protected]&raw/"
></playground-project>
```

so that transitive references in the raw assets will also be raw requests.

## Deno Compatibility

esm.sh is a **Deno-friendly** CDN that resolves Node's built-in modules (such as
Expand Down
1 change: 1 addition & 0 deletions packages/esm-worker/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ async function fetchESM(
const KV = Reflect.get(env, "KV") as KVNamespace | undefined ?? asKV(storage);
const noStore = req.headers.has("X-Real-Origin");
const isModule = !(
ctx.url.searchParams.has('raw') ||
pathname.endsWith(".d.ts") ||
pathname.endsWith(".d.mts") ||
pathname.endsWith(".map")
Expand Down
7 changes: 7 additions & 0 deletions server/server_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,8 @@ func esmHandler() rex.Handle {
return rex.Redirect(url, http.StatusMovedPermanently)
}
reqType = "types"
} else if ctx.R.URL.Query().Has("raw") {
reqType = "raw"
} else if hasBuildVerPrefix && hasTargetSegment(reqPkg.Subpath) {
reqType = "builds"
}
Expand Down Expand Up @@ -802,6 +804,11 @@ func esmHandler() rex.Handle {
return rex.Status(404, "File Not Found")
}
header.Set("Cache-Control", "public, max-age=31536000, immutable")
if endsWith(savePath, ".js", ".mjs", ".jsx") {
header.Set("Content-Type", "application/javascript; charset=utf-8")
} else if endsWith(savePath, ".ts", ".mts", ".tsx") {
header.Set("Content-Type", "application/typescript; charset=utf-8")
}
return rex.Content(savePath, fi.ModTime(), content) // auto closed
}

Expand Down
16 changes: 16 additions & 0 deletions test/esm-worker/esm-worker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,22 @@ Deno.test("esm-worker", {
assertEquals(pkgJson.name, "react");
});

await t.step("npm assets (raw)", async () => {
const res = await fetch(
`${workerOrigin}/[email protected]&raw/playground-service-worker.js`
);
assertEquals(res.status, 200);
assertEquals(
res.headers.get("Content-Type"),
"application/javascript; charset=utf-8",
);
assertEquals(
res.headers.get("Cache-Control"),
"public, max-age=31536000, immutable",
);
assertStringIncludes(await res.text(), "!function(){");
});

await t.step("gh modules", async () => {
const res = await fetch(`${workerOrigin}/gh/microsoft/tslib`, {
redirect: "manual",
Expand Down
23 changes: 23 additions & 0 deletions test/raw/raw.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
assertEquals,
assertStringIncludes,
} from "https://deno.land/[email protected]/testing/asserts.ts";

Deno.test("raw untransformed JS via ?raw query", async () => {
const res = await fetch(
"http://localhost:8080/[email protected]/playground-service-worker.js?raw",
);
assertEquals(res.status, 200);
assertEquals(res.headers.get("content-type"), "application/javascript; charset=utf-8");
assertStringIncludes(await res.text(), "!function(){");
});

Deno.test("raw untransformed JS via &raw extra query", async () => {
const res = await fetch(
"http://localhost:8080/[email protected]&raw/playground-service-worker.js",
);
assertEquals(res.status, 200);
assertEquals(res.headers.get("content-type"), "application/javascript; charset=utf-8");
assertStringIncludes(await res.text(), "!function(){");
});

0 comments on commit e9e8438

Please sign in to comment.