@statewalker/webrun-modules 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +356 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +809 -0
- package/dist/resolution/node-builtins.d.ts +24 -0
- package/dist/resolution/node-builtins.d.ts.map +1 -0
- package/dist/resolution/resolve-entry.d.ts +9 -0
- package/dist/resolution/resolve-entry.d.ts.map +1 -0
- package/dist/server/new-module-server.d.ts +4 -0
- package/dist/server/new-module-server.d.ts.map +1 -0
- package/dist/server/specifiers.d.ts +11 -0
- package/dist/server/specifiers.d.ts.map +1 -0
- package/dist/sources/npm-registry-source.d.ts +29 -0
- package/dist/sources/npm-registry-source.d.ts.map +1 -0
- package/dist/sources/untar.d.ts +9 -0
- package/dist/sources/untar.d.ts.map +1 -0
- package/dist/transform/index.d.ts +15 -0
- package/dist/transform/index.d.ts.map +1 -0
- package/dist/transform/scan.d.ts +11 -0
- package/dist/transform/scan.d.ts.map +1 -0
- package/dist/transform/to-js.d.ts +10 -0
- package/dist/transform/to-js.d.ts.map +1 -0
- package/dist/transform/transform-cjs.d.ts +13 -0
- package/dist/transform/transform-cjs.d.ts.map +1 -0
- package/dist/transform/transform-esm.d.ts +9 -0
- package/dist/transform/transform-esm.d.ts.map +1 -0
- package/dist/types.d.ts +127 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +66 -0
- package/src/index.ts +26 -0
- package/src/resolution/node-builtins.ts +79 -0
- package/src/resolution/resolve-entry.ts +65 -0
- package/src/server/new-module-server.ts +416 -0
- package/src/server/specifiers.ts +26 -0
- package/src/sources/npm-registry-source.ts +86 -0
- package/src/sources/untar.ts +37 -0
- package/src/transform/index.ts +52 -0
- package/src/transform/scan.ts +32 -0
- package/src/transform/to-js.ts +18 -0
- package/src/transform/transform-cjs.ts +85 -0
- package/src/transform/transform-esm.ts +35 -0
- package/src/types.ts +143 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2026 statewalker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# @statewalker/webrun-modules
|
|
2
|
+
|
|
3
|
+
Run authored TypeScript/JavaScript apps **and the arbitrary npm modules they
|
|
4
|
+
import** in the browser (or Node) with **no runtime CDN dependency** — and with
|
|
5
|
+
**no install step**: packages are downloaded, resolved, and transformed **on
|
|
6
|
+
request**.
|
|
7
|
+
|
|
8
|
+
Given a TS/JS entry, `webrun-modules` resolves, downloads, and transforms the
|
|
9
|
+
dependency graph — from npm (or a CDN) **at resolve time only** — and serves the
|
|
10
|
+
transformed, browser-runnable ESM from a local
|
|
11
|
+
[`FilesApi`](https://www.npmjs.com/package/@statewalker/webrun-files) cache. Every
|
|
12
|
+
internal import is rewritten to a same-origin local URL, so nothing is fetched
|
|
13
|
+
from a third party at run time. The result is always a URL any module-compatible
|
|
14
|
+
runtime can `import` directly — no bespoke client loader.
|
|
15
|
+
|
|
16
|
+
It is **isomorphic**: the same code runs in a browser ServiceWorker and in Node —
|
|
17
|
+
the only difference is which `FilesApi` backend you inject.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm add @statewalker/webrun-modules
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { newModuleServer, npmRegistrySource } from "@statewalker/webrun-modules";
|
|
29
|
+
import { NodeFilesApi } from "@statewalker/webrun-files-node";
|
|
30
|
+
|
|
31
|
+
const server = newModuleServer({
|
|
32
|
+
cache: new NodeFilesApi({ rootDir: "./.modules-cache" }),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Resolve an npm package (with optional subpath) to an importable URL:
|
|
36
|
+
const zod = await server.resolve({ pkg: "zod" });
|
|
37
|
+
// → { url: "/zod@3.23.8/lib/index.mjs", target: "browser" }
|
|
38
|
+
|
|
39
|
+
// Serve it — `server.fetch` is a standard Web handler, mountable anywhere:
|
|
40
|
+
Deno.serve(server.fetch); // Deno
|
|
41
|
+
export default { fetch: server.fetch }; // Bun / Cloudflare
|
|
42
|
+
self.addEventListener("fetch", (e) => e.respondWith(server.fetch(e.request))); // ServiceWorker
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
There is **no separate "install" call**. The first time a module URL is requested
|
|
46
|
+
— via `resolve`, `prime`, or a direct `fetch` — its package is downloaded and
|
|
47
|
+
transformed on demand, then cached. Requesting a URL for a package that isn't in
|
|
48
|
+
the cache yet just works:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// Nothing primed, nothing resolved — this single fetch downloads + transforms + serves:
|
|
52
|
+
const res = await server.fetch(new Request("http://host/ms@2.1.3/index.js"));
|
|
53
|
+
// → 200, content-type: text/javascript
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Eager priming (optional)
|
|
57
|
+
|
|
58
|
+
To download and transform a whole dependency graph up front (e.g. before going
|
|
59
|
+
offline, or to warm a cache), use `prime`. It walks the entry's transitive graph,
|
|
60
|
+
transforms every module, and writes the lockfile:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
await server.prime({ pkg: "react-dom", version: "^18" });
|
|
64
|
+
// entry + every transitive dep are now cached and importable with the network off.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Listing what an entry needs, or what a package contains
|
|
68
|
+
|
|
69
|
+
Two questions, two methods:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
// (1) Every module URL required to RUN an entry — the reachable graph (primes it).
|
|
73
|
+
const urls = await server.listResources({ pkg: "react" });
|
|
74
|
+
// → [ "/react@19.2.8/index.js",
|
|
75
|
+
// "/react@19.2.8/cjs/react.production.js",
|
|
76
|
+
// "/react@19.2.8/cjs/react.development.js" ] ← the exact set of scripts to serve
|
|
77
|
+
|
|
78
|
+
// (2) Every FILE in a package — the full tarball contents (loads it if needed).
|
|
79
|
+
const files = await server.listPackageFiles({ pkg: "react" });
|
|
80
|
+
// → [ "LICENSE", "index.js", "jsx-runtime.js", "cjs/react.development.js", … ] (27 files)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`listResources` is the minimal set (what the entry actually imports);
|
|
84
|
+
`listPackageFiles` is everything the package ships (including alternative entry
|
|
85
|
+
points like `react/jsx-runtime` the main entry never imports). To capture an app's
|
|
86
|
+
full needs, call `listResources` for each entry point you import and take the union.
|
|
87
|
+
|
|
88
|
+
To **download** everything for later offline/static serving, point the cache at a
|
|
89
|
+
real directory and prime — the transformed files land under `{rootDir}/t/{target}/`:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const server = newModuleServer({ cache: new NodeFilesApi({ rootDir: "./react-bundle" }) });
|
|
93
|
+
await server.prime({ pkg: "react" });
|
|
94
|
+
await server.prime({ pkg: "react", subpath: "jsx-runtime" }); // if you use JSX
|
|
95
|
+
// ./react-bundle/t/browser/react@19.2.8/… now holds the importable scripts.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Serving your own source too
|
|
99
|
+
|
|
100
|
+
Point the server at a project `FilesApi` and it resolves local scripts the same
|
|
101
|
+
way — bare imports rewritten to `/{name}@{version}/…`, relative imports kept
|
|
102
|
+
relative, TS/JSX transpiled:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const server = newModuleServer({ cache, project: myProjectFiles });
|
|
106
|
+
const app = await server.resolve({ url: "/src/app.ts" }); // → importable URL
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Examples
|
|
110
|
+
|
|
111
|
+
Three runnable examples (each has a package script; all hit the live npm
|
|
112
|
+
registry, so they need network on first run):
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
pnpm --filter @statewalker/webrun-modules example # full-cycle (alias)
|
|
116
|
+
pnpm --filter @statewalker/webrun-modules example:full-cycle # examples/full-cycle.ts
|
|
117
|
+
pnpm --filter @statewalker/webrun-modules example:server # examples/http-server.ts (unpkg-like)
|
|
118
|
+
pnpm --filter @statewalker/webrun-modules example:site # examples/site-pipeline.ts
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
(From inside the package directory you can drop the `--filter …` prefix:
|
|
122
|
+
`pnpm example:server`.)
|
|
123
|
+
|
|
124
|
+
[`examples/full-cycle.ts`](./examples/full-cycle.ts) demonstrates the entire
|
|
125
|
+
cycle against the live npm registry — lazy download-on-request, `resolve`,
|
|
126
|
+
`prime`, executing a served module, `?raw`, and the lockfile.
|
|
127
|
+
|
|
128
|
+
### An unpkg-like HTTP service
|
|
129
|
+
|
|
130
|
+
Because `server.fetch` is a standard Web handler, exposing an unpkg-style endpoint
|
|
131
|
+
is a thin wrapper — mount it on any host and add the one convenience of
|
|
132
|
+
redirecting a bare/ranged spec to its pinned, versioned URL.
|
|
133
|
+
[`examples/http-server.ts`](./examples/http-server.ts) is a complete, dependency-
|
|
134
|
+
free Node server that does exactly this:
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
pnpm --filter @statewalker/webrun-modules example:server
|
|
138
|
+
# then:
|
|
139
|
+
curl -L localhost:8787/lodash-es@4/merge # 302 → /lodash-es@4.18.1/merge.js → importable ESM
|
|
140
|
+
curl -L localhost:8787/debug # 302 → /debug@4.4.3/src/browser.js
|
|
141
|
+
curl 'localhost:8787/react?meta' # JSON: react's full file list
|
|
142
|
+
curl 'localhost:8787/react?graph' # JSON: every module URL needed to run react
|
|
143
|
+
curl 'localhost:8787/react@19.2.8/package.json' # non-JS files served raw (application/json)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
For a given package it returns an importable JS module with every dependency
|
|
147
|
+
already resolved to a same-origin URL — e.g. requesting `lodash-es@4/merge` serves
|
|
148
|
+
`merge.js` whose `import "./_baseMerge.js"` / `import "./_createAssigner.js"` all
|
|
149
|
+
point back at the same server. Use it straight from a browser:
|
|
150
|
+
|
|
151
|
+
```html
|
|
152
|
+
<script type="module">
|
|
153
|
+
import merge from "http://localhost:8787/lodash-es@4/merge";
|
|
154
|
+
console.log(merge({ a: 1 }, { b: 2 }));
|
|
155
|
+
</script>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### In-browser site pipeline (replacing a jspm-based resolver)
|
|
159
|
+
|
|
160
|
+
Because the server transpiles first-party TS/TSX **and** resolves the npm deps,
|
|
161
|
+
one `newModuleServer` replaces an entire `@jspm/generator`-based pipeline
|
|
162
|
+
(resolver + CDN providers + `es-module-lexer` rewrite + recursive prefetch +
|
|
163
|
+
`/external` mount). Put your source in a `project` `FilesApi`, mount `server.fetch`
|
|
164
|
+
under a site, and run server modules through the existing server-runner:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
const server = newModuleServer({ cache, project: myAppFiles, target: "browser" });
|
|
168
|
+
|
|
169
|
+
new SiteBuilder()
|
|
170
|
+
.setEndpoint("/", server.fetch) // html + transpiled TSX + deps
|
|
171
|
+
.setEndpoint("/api", newServerRunner(serverEntryUrl, () => baseUrl)) // run server modules
|
|
172
|
+
.build();
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
[`examples/site-pipeline.ts`](./examples/site-pipeline.ts) runs the whole thing
|
|
176
|
+
(JSX/TSX transpiled, `import "react"` rewritten to a same-origin URL, `react` +
|
|
177
|
+
`react/jsx-runtime` resolved, `listResources` = the exact scripts to serve).
|
|
178
|
+
**Note:** the server resolves a bare `import "react"` to *latest* unless a version
|
|
179
|
+
is pinned — seed `lock` (e.g. `{ react: "18.3.1" }`) to honor a project's
|
|
180
|
+
`package.json` versions reproducibly.
|
|
181
|
+
|
|
182
|
+
## Options
|
|
183
|
+
|
|
184
|
+
| Option | Default | Purpose |
|
|
185
|
+
|-------------|--------------------------|---------|
|
|
186
|
+
| `cache` | — (required) | Injected `FilesApi` for the module cache. |
|
|
187
|
+
| `project` | — | `FilesApi` of local project files to serve. |
|
|
188
|
+
| `sources` | `[npmRegistrySource()]` | Acquisition sources (npm tarball by default). |
|
|
189
|
+
| `transform` | `newDefaultTransform()` | Per-file transform (ESM + CJS-interop). |
|
|
190
|
+
| `target` | `"browser"` | Selects `exports` conditions + cache key; `"node"` supported. |
|
|
191
|
+
| `lock` | — | A `Lockfile` (pins versions); `prime` also writes one back. |
|
|
192
|
+
| `basePath` | `"/"` | Mount prefix, e.g. `"/deps/v1/"`. |
|
|
193
|
+
|
|
194
|
+
### `ModuleServer`
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
interface ModuleServer {
|
|
198
|
+
resolve(ref: ModuleRef, importer?: string): Promise<ResolvedModule>; // single ref → URL
|
|
199
|
+
prime(entry: ModuleRef): Promise<ResolvedModule>; // warm the whole graph
|
|
200
|
+
listResources(entry: ModuleRef): Promise<string[]>; // every URL the entry needs
|
|
201
|
+
listPackageFiles(ref: ModuleRef): Promise<string[]>; // a package's full file list
|
|
202
|
+
fetch(request: Request): Promise<Response>; // standard Web handler
|
|
203
|
+
readonly lock: Lockfile; // resolution map
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
type ModuleRef =
|
|
207
|
+
| { pkg: string; version?: string; subpath?: string } // e.g. { pkg: "lodash-es", subpath: "merge" }
|
|
208
|
+
| { url: string }; // a local project script
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Targets: browser vs node
|
|
212
|
+
|
|
213
|
+
`target` selects which `package.json` `exports` conditions win and how Node
|
|
214
|
+
builtins are handled, and is part of the cache key (a browser build and a node
|
|
215
|
+
build of the same package never collide):
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
// Browser (default): node:* builtins → self-hosted @jspm/core polyfill URLs.
|
|
219
|
+
newModuleServer({ cache, target: "browser" });
|
|
220
|
+
|
|
221
|
+
// Node: node:* builtins stay external (real Node builtins).
|
|
222
|
+
newModuleServer({ cache, target: "node" });
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Reproducible resolution (the lockfile)
|
|
226
|
+
|
|
227
|
+
The resolution map is a `Lockfile` (`{ [name]: version }`). `prime` writes it to
|
|
228
|
+
the cache and returns it via `server.lock`. Supply it back as `lock` to pin
|
|
229
|
+
versions reproducibly — a partial lockfile pins only the names it lists:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
const first = newModuleServer({ cache });
|
|
233
|
+
await first.prime({ pkg: "app" });
|
|
234
|
+
const lock = first.lock; // e.g. { app: "1.0.0", react: "18.3.1", … }
|
|
235
|
+
|
|
236
|
+
// Elsewhere / later: identical resolution, no re-solve.
|
|
237
|
+
const pinned = newModuleServer({ cache, lock });
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Custom `Source` (npm / JSR / URL / your own registry)
|
|
241
|
+
|
|
242
|
+
A `Source` turns a reference into a package's files + manifest. The default is
|
|
243
|
+
`npmRegistrySource()`; provide your own (or several — the first whose `matches`
|
|
244
|
+
returns true wins):
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import type { Source } from "@statewalker/webrun-modules";
|
|
248
|
+
import { MemFilesApi } from "@statewalker/webrun-files-mem";
|
|
249
|
+
|
|
250
|
+
const myRegistry: Source = {
|
|
251
|
+
matches: (ref) => "pkg" in ref,
|
|
252
|
+
async load(ref) {
|
|
253
|
+
// fetch + unpack however you like; return the package tree + manifest
|
|
254
|
+
const files = new MemFilesApi();
|
|
255
|
+
// … write files …
|
|
256
|
+
return { name: ref.pkg, version: "1.0.0", files, manifest: { name: ref.pkg, version: "1.0.0" } };
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
newModuleServer({ cache, sources: [myRegistry, npmRegistrySource()] });
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`npmRegistrySource(options?)` accepts `{ registryUrl, fetch, createFiles }` — pass
|
|
264
|
+
a custom `fetch` (e.g. to add auth or point at a private registry) or a private
|
|
265
|
+
registry URL.
|
|
266
|
+
|
|
267
|
+
## Custom `Transform`
|
|
268
|
+
|
|
269
|
+
The default transform (`newDefaultTransform()`) dispatches per file: ESM/TS/JSX go
|
|
270
|
+
through `newEsmTransform()`, CommonJS through `newCjsTransform()`. Swap in your own
|
|
271
|
+
`Transform` — it receives one file and a `rewrite(specifier) => url` callback and
|
|
272
|
+
returns browser-runnable ESM:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { newDefaultTransform, detectFormat } from "@statewalker/webrun-modules";
|
|
276
|
+
import type { Transform } from "@statewalker/webrun-modules";
|
|
277
|
+
|
|
278
|
+
const myTransform: Transform = {
|
|
279
|
+
async transform(file, rewrite) {
|
|
280
|
+
// file = { path, source, format: "esm" | "cjs" | "ts" | "tsx" }
|
|
281
|
+
// call rewrite(spec) for each import specifier to get its local URL
|
|
282
|
+
return /* transformed ESM */ file.source;
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
newModuleServer({ cache, transform: myTransform });
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`detectFormat(path, source, manifest?)` returns the `SourceFormat` the default
|
|
290
|
+
transform would infer.
|
|
291
|
+
|
|
292
|
+
## Serving surface
|
|
293
|
+
|
|
294
|
+
`server.fetch(request)` is a plain `(Request) => Promise<Response>`:
|
|
295
|
+
|
|
296
|
+
- JS/TS module files are transformed and served as `text/javascript`;
|
|
297
|
+
- non-module files (`package.json`, `README.md`, `.css`, …) are served **raw**,
|
|
298
|
+
untransformed, with a content-type guessed from the extension
|
|
299
|
+
(`application/json`, `text/markdown`, …);
|
|
300
|
+
- append `?raw` to get the raw bytes of *any* file as `application/octet-stream`;
|
|
301
|
+
- an unresolvable path returns a `404` `Response` (never throws).
|
|
302
|
+
|
|
303
|
+
Mount it under any `basePath` (returned URLs carry the prefix; the cached bytes
|
|
304
|
+
stay portable, because internal imports are rewritten as **relative** URLs):
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
const server = newModuleServer({ cache, basePath: "/deps/v1/" });
|
|
308
|
+
const r = await server.resolve({ pkg: "zod" }); // → { url: "/deps/v1/zod@3.23.8/lib/index.mjs" }
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Errors
|
|
312
|
+
|
|
313
|
+
- `ModuleResolveError { ref, reason }` — a package / version / subpath can't be
|
|
314
|
+
resolved (surfaced as a `404` from `fetch`).
|
|
315
|
+
- `ModuleTransformError { path, reason }` — a file can't be transformed to runnable
|
|
316
|
+
ESM.
|
|
317
|
+
|
|
318
|
+
## Utilities
|
|
319
|
+
|
|
320
|
+
Also exported: `untarTgz(bytes)` (isomorphic npm-tarball unpacker),
|
|
321
|
+
`parseSpecifier(spec)` (bare specifier → `{ pkg, subpath? }`, scope-aware), and
|
|
322
|
+
`relativeUrl(fromId, toId)`.
|
|
323
|
+
|
|
324
|
+
## How it works
|
|
325
|
+
|
|
326
|
+
- **Acquire** — the default `Source` fetches the npm registry tarball, untars it
|
|
327
|
+
in memory (pure-JS, isomorphic), and caches every file.
|
|
328
|
+
- **Resolve** — versions resolve against the registry with whole-name dedupe (one
|
|
329
|
+
version per package where semver allows; incompatible ranges are kept side by
|
|
330
|
+
side). `package.json` `exports`/`imports` conditions are honored for the target;
|
|
331
|
+
Node builtins map to `@jspm/core` polyfills (browser) or stay external (node).
|
|
332
|
+
The resolution map is persisted as a lockfile.
|
|
333
|
+
- **Transform** — each file becomes browser-runnable ESM one-to-one. ESM/TS/JSX is
|
|
334
|
+
transpiled and its specifiers rewritten in place; CommonJS is wrapped so the ESM
|
|
335
|
+
module graph itself provides `require` (synchronously, backed by the eagerly
|
|
336
|
+
primed graph). Internal imports are rewritten as **relative** URLs, so cached
|
|
337
|
+
bytes are portable across mount prefixes.
|
|
338
|
+
|
|
339
|
+
## Limitations
|
|
340
|
+
|
|
341
|
+
- **Computed `require(expr)`** across package boundaries can't be pre-resolved and
|
|
342
|
+
throws at execution time — the boundary where an `esbuild-wasm` bundle fallback
|
|
343
|
+
would take over.
|
|
344
|
+
- Dedupe is greedy (first-resolved version wins per name), not a full constraint
|
|
345
|
+
hoist.
|
|
346
|
+
- **Free Node globals under `target: "browser"`.** `require("process")` is
|
|
347
|
+
polyfilled, but packages that reference `process`, `Buffer`, or `global` as bare
|
|
348
|
+
*free variables* (e.g. React's `process.env.NODE_ENV`) need the page to define
|
|
349
|
+
them — set `globalThis.process = { env: { NODE_ENV: "development" } }` (as
|
|
350
|
+
esbuild/Vite do via a define). Under `target: "node"` they resolve natively.
|
|
351
|
+
- Bundling/copying the resolved graph into a distributable tree, `.d.ts` type
|
|
352
|
+
serving, package lifecycle scripts, and HMR are out of scope.
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { newModuleServer } from "./server/new-module-server.js";
|
|
2
|
+
export { parseSpecifier, relativeUrl } from "./server/specifiers.js";
|
|
3
|
+
export type { NpmRegistrySourceOptions } from "./sources/npm-registry-source.js";
|
|
4
|
+
export { npmRegistrySource } from "./sources/npm-registry-source.js";
|
|
5
|
+
export { untarTgz } from "./sources/untar.js";
|
|
6
|
+
export { detectFormat, newCjsTransform, newDefaultTransform, newEsmTransform, } from "./transform/index.js";
|
|
7
|
+
export type { LoadedPackage, Lockfile, ModuleRef, ModuleServer, ModuleServerOptions, ModuleTarget, PackageManifest, ResolvedModule, Source, SourceFile, SourceFormat, Transform, } from "./types.js";
|
|
8
|
+
export { ModuleResolveError, ModuleTransformError } from "./types.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrE,YAAY,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,aAAa,EACb,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,MAAM,EACN,UAAU,EACV,YAAY,EACZ,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC"}
|