env-runner 0.2.2 → 0.3.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/README.md +203 -26
- package/dist/_chunks/common-base-runner.d.mts +58 -51
- package/dist/_chunks/common-base-runner.mjs +127 -33
- package/dist/_chunks/common-process-data.mjs +13 -0
- package/dist/_chunks/common-runtime-deps.d.mts +7 -30
- package/dist/_chunks/common-worker-utils.mjs +464 -73
- package/dist/_chunks/deno-process-runner.mjs +11 -37
- package/dist/_chunks/lexer.mjs +2 -0
- package/dist/_chunks/libs/cjs-module-lexer.mjs +6 -1
- package/dist/_chunks/miniflare-runner.d.mts +56 -107
- package/dist/_chunks/miniflare-runner.mjs +834 -154
- package/dist/_chunks/netlify-runner.d.mts +3 -21
- package/dist/_chunks/node-worker-runner.mjs +1 -2
- package/dist/_chunks/server.mjs +20 -5
- package/dist/_chunks/types.d.mts +36 -9
- package/dist/_chunks/vercel-runner.mjs +1 -0
- package/dist/_chunks/virtual-loader.mjs +303 -29
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +31 -69
- package/dist/runners/bun-process/runner.mjs +4 -8
- package/dist/runners/bun-process/worker.mjs +8 -8
- package/dist/runners/deno-process/worker.mjs +17 -39
- package/dist/runners/node-process/runner.mjs +2 -6
- package/dist/runners/node-process/worker.mjs +8 -8
- package/dist/runners/node-worker/worker.mjs +6 -7
- package/dist/runners/self/runner.d.mts +2 -1
- package/dist/runners/self/runner.mjs +8 -0
- package/dist/runners/vercel/queue-dev.d.mts +9 -45
- package/dist/vite.d.mts +2 -12
- package/package.json +13 -13
- package/dist/_chunks/common-host-env.mjs +0 -11
|
@@ -7,27 +7,9 @@ export interface NetlifyEnvRunnerOptions {
|
|
|
7
7
|
hooks?: WorkerHooks;
|
|
8
8
|
data?: EnvRunnerData;
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* Unlike the other runtime-dependency options, this one takes a **specifier
|
|
15
|
-
* only**: the runtime has to be instantiated inside the worker thread, and a
|
|
16
|
-
* live module instance cannot cross that boundary.
|
|
17
|
-
*
|
|
18
|
-
* ```ts
|
|
19
|
-
* new NetlifyEnvRunner({
|
|
20
|
-
* name: "app",
|
|
21
|
-
* netlifyRuntime: import.meta.resolve("@netlify/runtime"),
|
|
22
|
-
* data: { entry },
|
|
23
|
-
* });
|
|
24
|
-
* ```
|
|
25
|
-
*
|
|
26
|
-
* A bare specifier (e.g. `"@netlify/runtime"`) is resolved from the current
|
|
27
|
-
* working directory. When omitted, the worker tries
|
|
28
|
-
* `import("@netlify/runtime")` and falls back to a lightweight
|
|
29
|
-
* `globalThis.Netlify` shim (env access only) if it isn't installed. Pass
|
|
30
|
-
* `false` to always use the shim.
|
|
10
|
+
* `@netlify/runtime` specifier (resolved from cwd), imported inside the worker
|
|
11
|
+
* since a module instance can't cross into it. Omitted: imported optionally,
|
|
12
|
+
* else an env-only `globalThis.Netlify` shim. `false` forces the shim.
|
|
31
13
|
*/
|
|
32
14
|
netlifyRuntime?: string | URL | false;
|
|
33
15
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { BaseEnvRunner } from "./common-base-runner.mjs";
|
|
2
|
-
import { hostEnv } from "./common-host-env.mjs";
|
|
1
|
+
import { BaseEnvRunner, hostEnv } from "./common-base-runner.mjs";
|
|
3
2
|
import { fileURLToPath } from "node:url";
|
|
4
3
|
import { existsSync } from "node:fs";
|
|
5
4
|
import { Worker } from "node:worker_threads";
|
package/dist/_chunks/server.mjs
CHANGED
|
@@ -168,9 +168,16 @@ var RunnerManager = class {
|
|
|
168
168
|
await this._runner.reloadModule(timeout);
|
|
169
169
|
this._moduleInvalidated = false;
|
|
170
170
|
}
|
|
171
|
+
async updateVirtualModules(changes, timeout) {
|
|
172
|
+
const runner = await this._waitForRunner();
|
|
173
|
+
if (!runner?.updateVirtualModules) throw new Error("Active runner does not support updateVirtualModules()");
|
|
174
|
+
await runner.updateVirtualModules(changes, timeout);
|
|
175
|
+
this._moduleInvalidated = true;
|
|
176
|
+
}
|
|
171
177
|
async invalidateModule(specifier, timeout) {
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
const runner = await this._waitForRunner();
|
|
179
|
+
if (!runner?.invalidateModule) throw new Error("Active runner does not support invalidateModule()");
|
|
180
|
+
await runner.invalidateModule(specifier, timeout);
|
|
174
181
|
this._moduleInvalidated = true;
|
|
175
182
|
}
|
|
176
183
|
async close() {
|
|
@@ -208,11 +215,11 @@ var RunnerManager = class {
|
|
|
208
215
|
runner.onMessage(this._internalListener);
|
|
209
216
|
for (const listener of this._messageListeners) runner.onMessage(listener);
|
|
210
217
|
const originalClose = runner.close.bind(runner);
|
|
211
|
-
runner.close = async () => {
|
|
212
|
-
await originalClose();
|
|
218
|
+
runner.close = async (cause) => {
|
|
219
|
+
await originalClose(cause);
|
|
213
220
|
if (this._runner === runner) {
|
|
214
221
|
this._runner = void 0;
|
|
215
|
-
for (const fn of this._closeListeners) fn(this);
|
|
222
|
+
for (const fn of this._closeListeners) fn(this, cause);
|
|
216
223
|
}
|
|
217
224
|
};
|
|
218
225
|
if (runner.ready) this._flushQueue();
|
|
@@ -263,6 +270,7 @@ var EnvServer = class extends RunnerManager {
|
|
|
263
270
|
_reloadTimeout;
|
|
264
271
|
_reloadListeners = /* @__PURE__ */ new Set();
|
|
265
272
|
_startPromise;
|
|
273
|
+
_virtual;
|
|
266
274
|
runner = null;
|
|
267
275
|
onReload(listener) {
|
|
268
276
|
this._reloadListeners.add(listener);
|
|
@@ -285,6 +293,12 @@ var EnvServer = class extends RunnerManager {
|
|
|
285
293
|
this.runner = runner ?? await this._createRunner();
|
|
286
294
|
await super.reload(this.runner);
|
|
287
295
|
}
|
|
296
|
+
async updateVirtualModules(changes, timeout) {
|
|
297
|
+
const virtual = this._virtual ??= { ...this._opts.data?.virtual };
|
|
298
|
+
for (const [key, source] of Object.entries(changes)) if (source === null) delete virtual[key];
|
|
299
|
+
else if (source !== void 0) virtual[key] = source;
|
|
300
|
+
if (await this._waitForRunner()) await super.updateVirtualModules(changes, timeout);
|
|
301
|
+
}
|
|
288
302
|
async close() {
|
|
289
303
|
this._stopWatching();
|
|
290
304
|
await super.close();
|
|
@@ -305,6 +319,7 @@ var EnvServer = class extends RunnerManager {
|
|
|
305
319
|
hooks: this._opts.hooks,
|
|
306
320
|
data: {
|
|
307
321
|
...this._opts.data,
|
|
322
|
+
...this._virtual && { virtual: { ...this._virtual } },
|
|
308
323
|
entry: this._opts.entry
|
|
309
324
|
},
|
|
310
325
|
execArgv: this._opts.execArgv
|
package/dist/_chunks/types.d.mts
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import { IncomingMessage } from "node:http";
|
|
2
2
|
import { Socket } from "node:net";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
4
|
+
* How a virtual module is served. Code formats are Node's load formats, plus
|
|
5
|
+
* `jsx`/`tsx` (Bun only). Raw formats follow import attribute types: `text`
|
|
6
|
+
* default-exports a string, `bytes` a `Uint8Array`, and `wasm` a compiled
|
|
7
|
+
* `WebAssembly.Module` (like workerd's Wasm modules).
|
|
8
8
|
*/
|
|
9
|
+
export type VirtualModuleFormat = "module" | "commonjs" | "module-typescript" | "commonjs-typescript" | "jsx" | "tsx" | "json" | "text" | "bytes" | "wasm";
|
|
10
|
+
/** A source with an explicit format. */
|
|
11
|
+
export interface VirtualModule {
|
|
12
|
+
/** Code or text; a `Uint8Array` for the `bytes` and `wasm` formats. */
|
|
13
|
+
source: string | Uint8Array;
|
|
14
|
+
/**
|
|
15
|
+
* Default: by the key's extension (`.cjs`, `.json`, `.ts`, `.wasm`, ...),
|
|
16
|
+
* else `module` for a string and `bytes` for a `Uint8Array`.
|
|
17
|
+
*/
|
|
18
|
+
format?: VirtualModuleFormat;
|
|
19
|
+
}
|
|
20
|
+
/** A source whose format follows its key (string or bytes), or a source with its format. */
|
|
21
|
+
export type VirtualModuleContent = string | Uint8Array | VirtualModule;
|
|
22
|
+
/** Factories run on the host: before the worker spawns, and on updates. */
|
|
23
|
+
export type VirtualModuleSource = VirtualModuleContent | (() => VirtualModuleContent | Promise<VirtualModuleContent>);
|
|
24
|
+
/** Virtual modules as a `specifier => source` map. */
|
|
25
|
+
export type VirtualModules = Record<string, VirtualModuleSource>;
|
|
26
|
+
/** Changes to apply to a running map: a source sets (adds or replaces) a key, `null` removes it. */
|
|
27
|
+
export type VirtualModuleUpdates = Record<string, VirtualModuleSource | null>;
|
|
28
|
+
/**
|
|
29
|
+
* A validated module, as workers receive it: a string follows its key's
|
|
30
|
+
* extension, any other source carries its format.
|
|
31
|
+
*/
|
|
32
|
+
export type ResolvedVirtualModule = string | Required<VirtualModule>;
|
|
33
|
+
/** Proxy a request to the worker; relative inputs resolve against `http://localhost`. */
|
|
9
34
|
export type FetchHandler = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
10
35
|
/** Callback for receiving messages from the worker. */
|
|
11
36
|
export type RunnerMessageListener = (data: unknown) => void;
|
|
@@ -30,11 +55,7 @@ export interface RunnerRPCHooks {
|
|
|
30
55
|
/** Remove a previously registered message listener. */
|
|
31
56
|
offMessage: (listener: RunnerMessageListener) => void;
|
|
32
57
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Address reported by the worker once it is ready.
|
|
35
|
-
*
|
|
36
|
-
* Either a TCP `host`/`port` pair or a Unix `socketPath`.
|
|
37
|
-
*/
|
|
58
|
+
/** Address reported by the worker once it is ready. */
|
|
38
59
|
export type WorkerAddress = {
|
|
39
60
|
host?: string;
|
|
40
61
|
port: number;
|
|
@@ -74,6 +95,12 @@ export interface EnvRunner extends RunnerRPCHooks, AsyncDisposable {
|
|
|
74
95
|
rpc<T = unknown>(name: string, data?: unknown, opts?: RPCOptions): Promise<T>;
|
|
75
96
|
/** Re-import the entry module without restarting the worker/process. */
|
|
76
97
|
reloadModule?(timeout?: number): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Set (add or replace) and remove (`null`) virtual modules in one round trip.
|
|
100
|
+
* Changed and removed modules, and their importers, evaluate fresh on the
|
|
101
|
+
* next `reloadModule()`; a removed key falls through to normal resolution.
|
|
102
|
+
*/
|
|
103
|
+
updateVirtualModules?(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
|
|
77
104
|
/**
|
|
78
105
|
* Invalidate a virtual module so the next `reloadModule()` re-evaluates it.
|
|
79
106
|
* A factory-valued source is re-run for fresh contents.
|
|
@@ -61,6 +61,7 @@ var VercelEnvRunner = class extends NodeWorkerEnvRunner {
|
|
|
61
61
|
input = this._resolveFetchInput(input);
|
|
62
62
|
const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
|
|
63
63
|
const requestId = generateVercelId();
|
|
64
|
+
await this._waitForAddress();
|
|
64
65
|
if (this._address && this._address.port != null && !headers.has("x-vercel-deployment-url")) {
|
|
65
66
|
const host = this._address.host || "127.0.0.1";
|
|
66
67
|
headers.set("x-vercel-deployment-url", `http://${host}:${this._address.port}`);
|
|
@@ -1,71 +1,345 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { isAbsolute } from "node:path";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
+
const VIRTUAL_MODULE_FORMATS = /* @__PURE__ */ new Set([
|
|
5
|
+
"module",
|
|
6
|
+
"commonjs",
|
|
7
|
+
"module-typescript",
|
|
8
|
+
"commonjs-typescript",
|
|
9
|
+
"jsx",
|
|
10
|
+
"tsx",
|
|
11
|
+
"json",
|
|
12
|
+
"text",
|
|
13
|
+
"bytes",
|
|
14
|
+
"wasm"
|
|
15
|
+
]);
|
|
16
|
+
const EXTENSION_FORMATS = {
|
|
17
|
+
".cjs": "commonjs",
|
|
18
|
+
".json": "json",
|
|
19
|
+
".ts": "module-typescript",
|
|
20
|
+
".mts": "module-typescript",
|
|
21
|
+
".cts": "commonjs-typescript",
|
|
22
|
+
".jsx": "jsx",
|
|
23
|
+
".tsx": "tsx",
|
|
24
|
+
".wasm": "wasm"
|
|
25
|
+
};
|
|
26
|
+
function virtualModuleFormat(key, module) {
|
|
27
|
+
return typeof module === "object" ? module.format : _extensionFormat(key) ?? "module";
|
|
28
|
+
}
|
|
29
|
+
function virtualModuleCodeSource(key, module) {
|
|
30
|
+
if (module === void 0) return;
|
|
31
|
+
const format = virtualModuleFormat(key, module);
|
|
32
|
+
if (format === "json" || format === "text" || _isBinaryFormat(format)) return;
|
|
33
|
+
return typeof module === "string" ? module : module.source;
|
|
34
|
+
}
|
|
35
|
+
function normalizeVirtualModule(key, content) {
|
|
36
|
+
const module = typeof content === "string" || content instanceof Uint8Array ? { source: content } : content;
|
|
37
|
+
const source = module?.source;
|
|
38
|
+
if (typeof source !== "string" && !(source instanceof Uint8Array)) throw new TypeError(`[env-runner] virtual module "${key}" must be a string, a Uint8Array or { source, format }.`);
|
|
39
|
+
const binary = typeof source !== "string";
|
|
40
|
+
const format = module.format ?? _extensionFormat(key) ?? (binary ? "bytes" : "module");
|
|
41
|
+
if (!VIRTUAL_MODULE_FORMATS.has(format)) throw new TypeError(`[env-runner] virtual module "${key}" has an unknown format "${format}" (expected one of: ${[...VIRTUAL_MODULE_FORMATS].join(", ")}).`);
|
|
42
|
+
if (binary !== _isBinaryFormat(format)) throw new TypeError(`[env-runner] virtual module "${key}" has format "${format}", which needs ${binary ? "a string" : "a Uint8Array"} source.`);
|
|
43
|
+
if (format === "wasm" && !WebAssembly.validate(source)) throw new TypeError(`[env-runner] virtual module "${key}" is not a valid WebAssembly binary.`);
|
|
44
|
+
if (typeof content === "string") return content;
|
|
45
|
+
return {
|
|
46
|
+
source: binary ? new Uint8Array(source) : source,
|
|
47
|
+
format
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function normalizeVirtualModules(virtual) {
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const [key, content] of Object.entries(virtual)) out[key] = normalizeVirtualModule(key, content);
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
2
55
|
async function resolveVirtualModules(virtual) {
|
|
3
|
-
const entries = await Promise.all(Object.entries(virtual).map(async ([key, value]) =>
|
|
56
|
+
const entries = await Promise.all(Object.entries(virtual).map(async ([key, value]) => {
|
|
57
|
+
return [key, normalizeVirtualModule(key, typeof value === "function" ? await value() : value)];
|
|
58
|
+
}));
|
|
4
59
|
return Object.fromEntries(entries);
|
|
5
60
|
}
|
|
61
|
+
function encodeVirtualModules(virtual) {
|
|
62
|
+
const out = {};
|
|
63
|
+
for (const [key, module] of Object.entries(virtual)) out[key] = module !== null && typeof module === "object" && typeof module.source !== "string" ? {
|
|
64
|
+
source: _toBase64(module.source),
|
|
65
|
+
format: module.format,
|
|
66
|
+
encoding: "base64"
|
|
67
|
+
} : module;
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function decodeVirtualModules(virtual) {
|
|
71
|
+
const out = {};
|
|
72
|
+
for (const [key, module] of Object.entries(virtual)) out[key] = module !== null && typeof module === "object" && "encoding" in module ? {
|
|
73
|
+
source: new Uint8Array(Buffer.from(module.source, "base64")),
|
|
74
|
+
format: module.format
|
|
75
|
+
} : module;
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
function virtualModuleCode(format, source) {
|
|
79
|
+
if (format === "text") return `export default ${JSON.stringify(source)};\n`;
|
|
80
|
+
const bytes = [
|
|
81
|
+
`const base64 = ${JSON.stringify(_toBase64(source))};`,
|
|
82
|
+
`let bytes;`,
|
|
83
|
+
`if (typeof Uint8Array.fromBase64 === "function") {`,
|
|
84
|
+
` bytes = Uint8Array.fromBase64(base64);`,
|
|
85
|
+
`} else {`,
|
|
86
|
+
` const binary = atob(base64);`,
|
|
87
|
+
` bytes = new Uint8Array(binary.length);`,
|
|
88
|
+
` for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);`,
|
|
89
|
+
`}`
|
|
90
|
+
];
|
|
91
|
+
const value = format === "wasm" ? "new WebAssembly.Module(bytes)" : "bytes";
|
|
92
|
+
return `${bytes.join("\n")}\nexport default ${value};\n`;
|
|
93
|
+
}
|
|
94
|
+
function unsupportedVirtualJSXError(key, format, runtime) {
|
|
95
|
+
return /* @__PURE__ */ new TypeError(`[env-runner] virtual module "${key}" has format "${format}", which ${runtime} can't load from memory; pre-transpile it to JavaScript (and pass { source, format: "module" } to keep the key).`);
|
|
96
|
+
}
|
|
6
97
|
const VIRTUAL_SCHEME = "virtual:";
|
|
7
|
-
function createVirtualHooks(virtual,
|
|
98
|
+
function createVirtualHooks(virtual, opts = {}) {
|
|
99
|
+
const { versions, importers, forcePlainModule } = opts;
|
|
100
|
+
const baseURL = opts.baseURL ?? _defaultBaseURL();
|
|
101
|
+
const keyURLs = /* @__PURE__ */ new Map();
|
|
102
|
+
const urlKeys = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const key of Object.keys(virtual)) {
|
|
104
|
+
const url = _virtualKeyURL(key);
|
|
105
|
+
if (url) {
|
|
106
|
+
keyURLs.set(key, url);
|
|
107
|
+
urlKeys.set(url, key);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const keyOf = (url) => {
|
|
111
|
+
if (url.startsWith(VIRTUAL_SCHEME)) {
|
|
112
|
+
const key = _virtualURLKey(url);
|
|
113
|
+
return key !== void 0 && Object.hasOwn(virtual, key) ? key : void 0;
|
|
114
|
+
}
|
|
115
|
+
return urlKeys.size > 0 && url.startsWith("file:") ? urlKeys.get(_bareURL(url)) : void 0;
|
|
116
|
+
};
|
|
117
|
+
const moduleFiles = importers && /* @__PURE__ */ new Set();
|
|
118
|
+
const track = (node, context) => {
|
|
119
|
+
const { parentURL } = context;
|
|
120
|
+
if (!importers || !parentURL) return;
|
|
121
|
+
let importer = keyOf(parentURL);
|
|
122
|
+
if (importer === void 0 && !context.conditions?.includes("require")) {
|
|
123
|
+
const file = _bareURL(parentURL);
|
|
124
|
+
importer = moduleFiles.has(file) ? file : void 0;
|
|
125
|
+
}
|
|
126
|
+
if (importer !== void 0 && importer !== node) {
|
|
127
|
+
let set = importers.get(node);
|
|
128
|
+
if (!set) importers.set(node, set = /* @__PURE__ */ new Set());
|
|
129
|
+
set.add(importer);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
const serve = (key, url, context) => {
|
|
133
|
+
track(key, context);
|
|
134
|
+
if (opts.opaquePathURL && keyURLs.has(key) && opts.opaquePathURL(keyURLs.get(key))) url = _virtualURL(key) + url.slice(_bareURL(url).length);
|
|
135
|
+
const version = versions?.get(key);
|
|
136
|
+
return {
|
|
137
|
+
url: version ? _appendQuery(url, `v=${version}`) : url,
|
|
138
|
+
shortCircuit: true
|
|
139
|
+
};
|
|
140
|
+
};
|
|
8
141
|
const resolve = (specifier, context, nextResolve) => {
|
|
9
142
|
const key = _stripQuery(specifier);
|
|
10
143
|
if (Object.hasOwn(virtual, key)) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
144
|
+
const keyURL = keyURLs.get(key);
|
|
145
|
+
const url = keyURL ? keyURL + specifier.slice(key.length) : _virtualURL(specifier);
|
|
146
|
+
return serve(key, url, context);
|
|
147
|
+
}
|
|
148
|
+
let parentURL = context.parentURL;
|
|
149
|
+
if (parentURL?.startsWith(VIRTUAL_SCHEME)) {
|
|
150
|
+
const parentKey = keyOf(parentURL);
|
|
151
|
+
parentURL = parentKey !== void 0 && keyURLs.get(parentKey) || baseURL;
|
|
16
152
|
}
|
|
17
|
-
if (
|
|
153
|
+
if (urlKeys.size > 0) {
|
|
154
|
+
const url = _resolvePathSpecifier(specifier, parentURL);
|
|
155
|
+
const pathKey = url && urlKeys.get(_bareURL(url));
|
|
156
|
+
if (pathKey) return serve(pathKey, url, context);
|
|
157
|
+
}
|
|
158
|
+
const result = nextResolve(specifier, parentURL === context.parentURL ? context : {
|
|
18
159
|
...context,
|
|
19
160
|
parentURL
|
|
20
161
|
});
|
|
21
|
-
|
|
162
|
+
const resolvedKey = urlKeys.size > 0 ? urlKeys.get(_bareURL(result.url)) : void 0;
|
|
163
|
+
if (resolvedKey) return serve(resolvedKey, result.url, context);
|
|
164
|
+
if (result.url.startsWith("file:")) {
|
|
165
|
+
const file = _bareURL(result.url);
|
|
166
|
+
track(file, context);
|
|
167
|
+
const version = versions?.get(file);
|
|
168
|
+
if (version) return {
|
|
169
|
+
...result,
|
|
170
|
+
url: _appendQuery(result.url, `v=${version}`)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
22
174
|
};
|
|
23
175
|
const load = (url, context, nextLoad) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
176
|
+
const key = keyOf(url);
|
|
177
|
+
if (key !== void 0) {
|
|
178
|
+
const module = virtual[key];
|
|
179
|
+
if (forcePlainModule) return {
|
|
180
|
+
format: "module",
|
|
181
|
+
source: module,
|
|
182
|
+
shortCircuit: true
|
|
183
|
+
};
|
|
184
|
+
return {
|
|
185
|
+
..._nodeLoadResult(key, module),
|
|
29
186
|
shortCircuit: true
|
|
30
187
|
};
|
|
31
188
|
}
|
|
32
|
-
|
|
189
|
+
const result = nextLoad(url, context);
|
|
190
|
+
if (moduleFiles && url.startsWith("file:") && _isModuleFormat(result.format)) moduleFiles.add(_bareURL(url));
|
|
191
|
+
return result;
|
|
192
|
+
};
|
|
193
|
+
const updateKeys = (keys) => {
|
|
194
|
+
for (const key of keys) {
|
|
195
|
+
const url = keyURLs.get(key) ?? _virtualKeyURL(key);
|
|
196
|
+
if (!url) continue;
|
|
197
|
+
if (Object.hasOwn(virtual, key)) {
|
|
198
|
+
keyURLs.set(key, url);
|
|
199
|
+
urlKeys.set(url, key);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
keyURLs.delete(key);
|
|
203
|
+
if (urlKeys.get(url) === key) {
|
|
204
|
+
urlKeys.delete(url);
|
|
205
|
+
for (const [other, otherURL] of keyURLs) if (otherURL === url) urlKeys.set(url, other);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
33
208
|
};
|
|
34
209
|
return {
|
|
35
210
|
resolve,
|
|
36
|
-
load
|
|
211
|
+
load,
|
|
212
|
+
updateKeys
|
|
37
213
|
};
|
|
38
214
|
}
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
215
|
+
function _nodeLoadResult(key, module) {
|
|
216
|
+
const format = virtualModuleFormat(key, module);
|
|
217
|
+
const source = typeof module === "string" ? module : module.source;
|
|
218
|
+
if (format === "text" || format === "bytes" || format === "wasm") return {
|
|
219
|
+
format: "module",
|
|
220
|
+
source: virtualModuleCode(format, source)
|
|
221
|
+
};
|
|
222
|
+
if (format === "jsx" || format === "tsx") throw unsupportedVirtualJSXError(key, format, "Node.js");
|
|
223
|
+
return {
|
|
224
|
+
format,
|
|
225
|
+
source
|
|
226
|
+
};
|
|
43
227
|
}
|
|
44
228
|
function stripVirtualTypeScript(specifier, source, stripTypeScriptTypes, hints) {
|
|
45
229
|
if (typeof stripTypeScriptTypes !== "function") throw new TypeError(`[env-runner] virtual TypeScript module "${specifier}" requires \`module.stripTypeScriptTypes\` ${hints.requirement}; ${hints.remedy} or provide a pre-transpiled JavaScript source instead.`);
|
|
46
|
-
|
|
230
|
+
try {
|
|
231
|
+
return stripTypeScriptTypes(source);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
throw new SyntaxError(`[env-runner] failed to strip types from virtual module "${specifier}": ${error?.message || error}`, { cause: error });
|
|
234
|
+
}
|
|
47
235
|
}
|
|
48
|
-
function expandVirtualInvalidation(virtual, specifier) {
|
|
49
|
-
const invalidated = [specifier];
|
|
236
|
+
function expandVirtualInvalidation(virtual, specifier, importers) {
|
|
237
|
+
const invalidated = [...new Set(typeof specifier === "string" ? [specifier] : specifier)];
|
|
50
238
|
const seen = new Set(invalidated);
|
|
239
|
+
const add = (key) => {
|
|
240
|
+
if (!seen.has(key)) {
|
|
241
|
+
seen.add(key);
|
|
242
|
+
invalidated.push(key);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
51
245
|
for (const target of invalidated) {
|
|
246
|
+
for (const key of importers?.get(target) ?? []) add(key);
|
|
247
|
+
if (!Object.hasOwn(virtual, target)) continue;
|
|
52
248
|
const refs = [
|
|
53
249
|
`"${target}"`,
|
|
54
250
|
`'${target}'`,
|
|
55
251
|
"`" + target + "`"
|
|
56
252
|
];
|
|
57
|
-
for (const [key,
|
|
58
|
-
|
|
59
|
-
|
|
253
|
+
for (const [key, module] of Object.entries(virtual)) {
|
|
254
|
+
const code = virtualModuleCodeSource(key, module);
|
|
255
|
+
if (code !== void 0 && refs.some((ref) => code.includes(ref))) add(key);
|
|
60
256
|
}
|
|
61
257
|
}
|
|
62
258
|
return invalidated;
|
|
63
259
|
}
|
|
260
|
+
function findVirtualPathKey(virtual, specifier) {
|
|
261
|
+
const url = _virtualKeyURL(_stripQuery(specifier));
|
|
262
|
+
if (!url) return;
|
|
263
|
+
let match;
|
|
264
|
+
for (const key of Object.keys(virtual)) if (_virtualKeyURL(key) === url) match = key;
|
|
265
|
+
return match;
|
|
266
|
+
}
|
|
267
|
+
const _warnedPathCollisions = /* @__PURE__ */ new Set();
|
|
268
|
+
function warnVirtualPathCollisions(keys) {
|
|
269
|
+
try {
|
|
270
|
+
const urlKeys = /* @__PURE__ */ new Map();
|
|
271
|
+
for (const key of keys) {
|
|
272
|
+
const url = _virtualKeyURL(key);
|
|
273
|
+
if (!url) continue;
|
|
274
|
+
const other = urlKeys.get(url);
|
|
275
|
+
if (other !== void 0 && !_warnedPathCollisions.has(`${other}\0${key}`)) {
|
|
276
|
+
_warnedPathCollisions.add(`${other}\0${key}`);
|
|
277
|
+
console.warn(`[env-runner] virtual modules "${other}" and "${key}" name the same file; keep only one of them (which one is served depends on the runtime).`);
|
|
278
|
+
}
|
|
279
|
+
urlKeys.set(url, key);
|
|
280
|
+
}
|
|
281
|
+
} catch {}
|
|
282
|
+
}
|
|
283
|
+
const virtualKeyURL = _virtualKeyURL;
|
|
284
|
+
function virtualModuleCacheId(key) {
|
|
285
|
+
const url = _virtualKeyURL(key);
|
|
286
|
+
return url ? fileURLToPath(url) : _virtualURL(key);
|
|
287
|
+
}
|
|
288
|
+
function _virtualKeyURL(key) {
|
|
289
|
+
if (key.startsWith("file:")) try {
|
|
290
|
+
return _bareURL(new URL(key).href);
|
|
291
|
+
} catch {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
return isAbsolute(key) ? pathToFileURL(key).href : void 0;
|
|
295
|
+
}
|
|
296
|
+
function _virtualURL(specifier) {
|
|
297
|
+
return VIRTUAL_SCHEME + specifier.replace(/[\0-\x20"%<>`\x7F-\u{10FFFF}]/gu, encodeURIComponent);
|
|
298
|
+
}
|
|
299
|
+
function _virtualURLKey(url) {
|
|
300
|
+
try {
|
|
301
|
+
return decodeURIComponent(_stripQuery(url.slice(8)));
|
|
302
|
+
} catch {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function _extensionFormat(key) {
|
|
307
|
+
const extension = /\.[^./\\]+$/.exec(key)?.[0];
|
|
308
|
+
return extension === void 0 ? void 0 : EXTENSION_FORMATS[extension];
|
|
309
|
+
}
|
|
310
|
+
function _isBinaryFormat(format) {
|
|
311
|
+
return format === "bytes" || format === "wasm";
|
|
312
|
+
}
|
|
313
|
+
function _toBase64(bytes) {
|
|
314
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
|
|
315
|
+
}
|
|
316
|
+
function _isModuleFormat(format) {
|
|
317
|
+
return format == null || format === "module" || format === "module-typescript";
|
|
318
|
+
}
|
|
64
319
|
function _stripQuery(specifier) {
|
|
65
320
|
const qIndex = specifier.indexOf("?");
|
|
66
321
|
return qIndex === -1 ? specifier : specifier.slice(0, qIndex);
|
|
67
322
|
}
|
|
68
|
-
function
|
|
323
|
+
function _bareURL(url) {
|
|
324
|
+
const index = url.search(/[?#]/);
|
|
325
|
+
return index === -1 ? url : url.slice(0, index);
|
|
326
|
+
}
|
|
327
|
+
function _appendQuery(url, param) {
|
|
328
|
+
const hashIndex = url.startsWith(VIRTUAL_SCHEME) ? -1 : url.indexOf("#");
|
|
329
|
+
const base = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
330
|
+
const hash = hashIndex === -1 ? "" : url.slice(hashIndex);
|
|
331
|
+
return base + (base.includes("?") ? "&" : "?") + param + hash;
|
|
332
|
+
}
|
|
333
|
+
function _resolvePathSpecifier(specifier, parentURL) {
|
|
334
|
+
try {
|
|
335
|
+
if (/^\.{0,2}\//.test(specifier) || specifier.startsWith("file:")) return new URL(specifier, parentURL).href;
|
|
336
|
+
if (isAbsolute(specifier)) {
|
|
337
|
+
const path = _stripQuery(specifier);
|
|
338
|
+
return pathToFileURL(path).href + specifier.slice(path.length);
|
|
339
|
+
}
|
|
340
|
+
} catch {}
|
|
341
|
+
}
|
|
342
|
+
function _defaultBaseURL() {
|
|
69
343
|
return pathToFileURL(process.cwd() + "/").href;
|
|
70
344
|
}
|
|
71
|
-
export { createVirtualHooks, expandVirtualInvalidation, resolveVirtualModules, stripVirtualTypeScript, virtualModuleFormat };
|
|
345
|
+
export { createVirtualHooks, decodeVirtualModules, encodeVirtualModules, expandVirtualInvalidation, findVirtualPathKey, normalizeVirtualModules, resolveVirtualModules, stripVirtualTypeScript, unsupportedVirtualJSXError, virtualKeyURL, virtualModuleCacheId, virtualModuleCode, virtualModuleCodeSource, virtualModuleFormat, warnVirtualPathCollisions };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EnvServer } from "./_chunks/server.mjs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import { serve } from "srvx";
|
|
4
3
|
import { parseArgs } from "node:util";
|
|
4
|
+
import { serve } from "srvx";
|
|
5
5
|
const { values, positionals } = parseArgs({
|
|
6
6
|
allowPositionals: true,
|
|
7
7
|
options: {
|