env-runner 0.3.0 → 0.3.1

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.
@@ -1,15 +1,15 @@
1
- import { encodeVirtualModules, expandVirtualInvalidation, stripVirtualTypeScript, unsupportedVirtualJSXError, virtualModuleCode, virtualModuleCodeSource, virtualModuleFormat } from "./virtual-loader.mjs";
1
+ import { encodeVirtualModules, expandVirtualInvalidation, jsonModuleCode, queryOf, restoreInternalQuery, stripInternalQuery, stripQuery, stripVirtualTypeScript, transformedFormat, unsupportedVirtualJSXError, virtualModuleCode, virtualModuleCodeSource, virtualModuleFormat } from "./plugin-hooks.mjs";
2
2
  import { BaseEnvRunner } from "./common-base-runner.mjs";
3
+ import { init, parse } from "./libs/es-module-lexer.mjs";
3
4
  import { resolveRuntimeDep } from "./common-runtime-deps.mjs";
4
- import { init, parse } from "./libs/cjs-module-lexer.mjs";
5
- import { init as init$1, parse as parse$1 } from "./libs/es-module-lexer.mjs";
5
+ import { init as init$1, parse as parse$1 } from "./libs/cjs-module-lexer.mjs";
6
6
  import { createRequire } from "node:module";
7
7
  import { proxyUpgrade } from "httpxy";
8
8
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
9
  import { fileURLToPath, pathToFileURL } from "node:url";
10
10
  import { existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
11
- import { resolveModulePath } from "exsolve";
12
11
  import { tmpdir } from "node:os";
12
+ import { resolveModulePath } from "exsolve";
13
13
  import { parseEnv } from "node:util";
14
14
  const IPC_PATH$1 = "/__env_runner_ipc";
15
15
  const IPC_BINDING = "__ENV_RUNNER_IPC";
@@ -170,7 +170,7 @@ async function __handleWsMessage(env, data) {
170
170
  if (msg.type === "reload" && env.__ENV_RUNNER_UNSAFE_EVAL__) {
171
171
  const version = msg.version || 0;
172
172
  try {
173
- const newEntry = await __loadEntry(env, __entryPath + "?t=" + version);
173
+ const newEntry = await __loadEntry(env, __entryPath + "?__envRunnerReload=" + version);
174
174
  const newServer = __createServer(newEntry);
175
175
  if (__userEntry?.ipc?.onClose) {
176
176
  await __userEntry.ipc.onClose();
@@ -1021,7 +1021,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1021
1021
  if (!virtual) throw new Error("Miniflare env runner should be initialized before updating modules.");
1022
1022
  const prepared = {};
1023
1023
  for (const [key, module] of Object.entries(changes)) if (module !== null) prepared[key] = await this.#prepareVirtualModule(key, module);
1024
- await init$1;
1024
+ await init;
1025
1025
  const merged = {
1026
1026
  ...virtual.sources,
1027
1027
  ...prepared
@@ -1045,6 +1045,9 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1045
1045
  _runtimeType() {
1046
1046
  return "miniflare";
1047
1047
  }
1048
+ _resolveConditions() {
1049
+ return this.#exportConditions;
1050
+ }
1048
1051
  async _closeRuntime() {
1049
1052
  if (!this.#miniflare) return;
1050
1053
  if (this.#ws) {
@@ -1208,19 +1211,22 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1208
1211
  return new Response(null, { status: 204 });
1209
1212
  }
1210
1213
  };
1211
- if (this.#transformRequest && !options.modulesRules && !miniflare.convertV4MiniflareOptions && !skipLocator) options.modulesRules = [{
1214
+ if ((this.#transformRequest || this._plugins) && !options.modulesRules && !miniflare.convertV4MiniflareOptions && !skipLocator) options.modulesRules = [{
1212
1215
  type: "ESModule",
1213
1216
  include: [
1214
- "**/*.ts",
1215
- "**/*.tsx",
1216
- "**/*.jsx",
1217
- "**/*.mts"
1218
- ]
1217
+ ".ts",
1218
+ ".tsx",
1219
+ ".jsx",
1220
+ ".mts",
1221
+ ...this._plugins ? [".cts"] : []
1222
+ ].map((ext) => `**/*${ext}`)
1219
1223
  }];
1220
1224
  if (!options.unsafeModuleFallbackService) {
1221
1225
  const _require = createRequire(entryBase);
1222
1226
  const _virtual = virtual;
1223
1227
  const _transformRequest = this.#transformRequest;
1228
+ const _livePlugins = () => ipc.runner._plugins;
1229
+ const _cjsOutput = /* @__PURE__ */ new Map();
1224
1230
  const _exportConditions = this.#exportConditions;
1225
1231
  const _applyVirtualVersions = (code, modulePath) => applyVirtualVersions(code, _virtual.versions, (specifier) => _virtual.versionedKeyOf(specifier, modulePath));
1226
1232
  const _serveVirtual = (module, modulePath) => {
@@ -1237,9 +1243,33 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1237
1243
  case "wasm": return { wasm: Array.from(source) };
1238
1244
  }
1239
1245
  };
1246
+ const _pluginCode = async (id, result) => {
1247
+ if (result.moduleType !== "ts") return result.code;
1248
+ const strip = await _getStripTypeScriptTypes();
1249
+ if (typeof strip !== "function") throw new TypeError(`[env-runner] "${id}" is still TypeScript after its plugins, which needs \`module.stripTypeScriptTypes\` on the host: upgrade Node.js or compile it in a plugin.`);
1250
+ return strip(result.code);
1251
+ };
1252
+ const _servePluginModule = async (id, result) => {
1253
+ if (result.moduleType === "json") return { esModule: jsonModuleCode(id, result.code) };
1254
+ const code = await _pluginCode(id, result);
1255
+ if (transformedFormat(id, code) === "module") return { esModule: _applyVirtualVersions(code, void 0) };
1256
+ return {
1257
+ commonJsModule: code,
1258
+ namedExports: commonJSExports(code).filter((e) => e !== "default")
1259
+ };
1260
+ };
1261
+ const _pluginModuleIds = /* @__PURE__ */ new Map();
1262
+ const _pluginErrorModule = (name, error) => {
1263
+ const message = error?.message || String(error);
1264
+ console.error(message);
1265
+ return Response.json({
1266
+ name,
1267
+ esModule: `throw new Error(${JSON.stringify(message)});`
1268
+ });
1269
+ };
1240
1270
  options.unsafeUseModuleFallbackService = true;
1241
1271
  const modulePathMap = /* @__PURE__ */ new Map();
1242
- const _lexersReady = Promise.all([ensureCjsLexer(), init$1]);
1272
+ const _lexersReady = Promise.all([ensureCjsLexer(), init]);
1243
1273
  options.unsafeModuleFallbackService = async (request) => {
1244
1274
  await _lexersReady;
1245
1275
  const url = new URL(request.url);
@@ -1274,9 +1304,54 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1274
1304
  ..._serveVirtual(_virtual.sources[virtualKey], keyPath)
1275
1305
  });
1276
1306
  }
1307
+ const rawQuery = specifier.includes("?") ? specifier.slice(specifier.indexOf("?")) : "";
1308
+ const name = (cleanSpecifier.startsWith("/") ? cleanSpecifier.slice(1) : cleanSpecifier) + rawQuery;
1309
+ const versionedKey = _virtual.versionedKeyOf(cleanRaw || cleanSpecifier, referrerPath);
1310
+ const version = versionedKey === void 0 ? void 0 : _virtual.versions.get(versionedKey);
1311
+ const query = queryOf(stripInternalQuery(specifier, version));
1277
1312
  let resolvedPath;
1313
+ let resolvedId;
1314
+ let pluginResolved = false;
1315
+ const _resolvePlugins = _livePlugins();
1316
+ const rawSource = rawSpecifier ? stripInternalQuery(rawSpecifier, version) : bareSpecifier + query;
1317
+ const _pluginResolve = async (fallback) => {
1318
+ if (!_resolvePlugins) return;
1319
+ try {
1320
+ const source = rawSource.startsWith("file:") ? fileURLToPath(stripQuery(rawSource)) + queryOf(rawSource) : rawSource;
1321
+ if (!_resolvePlugins.resolveFilter(source, fallback)) return;
1322
+ const resolved = await _resolvePlugins.resolveId(source, _pluginModuleIds.get(referrerKey) ?? referrerPath, { fallback });
1323
+ if (!resolved || resolved.external) return;
1324
+ const id = resolved.id;
1325
+ const idPath = stripQuery(id);
1326
+ const pathKey = isAbsolute(idPath) ? _virtual.keyOf(idPath) : void 0;
1327
+ const keyPath = pathKey === void 0 ? void 0 : virtualKeyPath(pathKey);
1328
+ if (keyPath) {
1329
+ const location = restoreInternalQuery(toWorkerdPath(keyPath) + queryOf(id), specifier);
1330
+ return new Response(null, {
1331
+ status: 301,
1332
+ headers: { location: Buffer.from(location, "utf8").toString("latin1") }
1333
+ });
1334
+ }
1335
+ if (isAbsolute(idPath)) {
1336
+ resolvedId = id;
1337
+ pluginResolved = true;
1338
+ return idPath;
1339
+ }
1340
+ const result = await _resolvePlugins.load(id);
1341
+ _pluginModuleIds.set(name, id);
1342
+ return Response.json({
1343
+ name,
1344
+ ...await _servePluginModule(id, result)
1345
+ });
1346
+ } catch (error) {
1347
+ return _pluginErrorModule(name, error);
1348
+ }
1349
+ };
1350
+ const pluginPath = await _pluginResolve(false);
1351
+ if (pluginPath instanceof Response) return pluginPath;
1352
+ resolvedPath = pluginPath;
1278
1353
  const fileUrlRaw = cleanRaw || cleanSpecifier;
1279
- if (fileUrlRaw.startsWith("file://")) try {
1354
+ if (resolvedPath !== void 0) {} else if (fileUrlRaw.startsWith("file://")) try {
1280
1355
  resolvedPath = fileURLToPath(fileUrlRaw);
1281
1356
  } catch {
1282
1357
  return new Response(null, { status: 404 });
@@ -1299,11 +1374,16 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1299
1374
  try: true
1300
1375
  }) || contextRequire.resolve(cleanRaw);
1301
1376
  } catch {
1302
- const name = cleanSpecifier.startsWith("/") ? cleanSpecifier.slice(1) : cleanSpecifier;
1303
- return Response.json({
1304
- name,
1305
- esModule: "export default undefined;"
1306
- });
1377
+ const failed = await _pluginResolve(true);
1378
+ if (failed instanceof Response) return failed;
1379
+ if (failed === void 0) {
1380
+ const name = cleanSpecifier.startsWith("/") ? cleanSpecifier.slice(1) : cleanSpecifier;
1381
+ return Response.json({
1382
+ name,
1383
+ esModule: "export default undefined;"
1384
+ });
1385
+ }
1386
+ resolvedPath = failed;
1307
1387
  }
1308
1388
  } else {
1309
1389
  const referrerReal = referrerPath || (referrer.startsWith("/") ? referrer : "/" + referrer);
@@ -1315,11 +1395,17 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1315
1395
  else try {
1316
1396
  resolvedPath = _require.resolve(raw);
1317
1397
  } catch {
1318
- return new Response(null, { status: 404 });
1398
+ const failed = await _pluginResolve(true);
1399
+ if (failed === void 0) return new Response(null, { status: 404 });
1400
+ if (failed instanceof Response) return failed;
1401
+ resolvedPath = failed;
1319
1402
  }
1320
1403
  }
1321
- const rawQuery = specifier.includes("?") ? specifier.slice(specifier.indexOf("?")) : "";
1322
- const name = (cleanSpecifier.startsWith("/") ? cleanSpecifier.slice(1) : cleanSpecifier) + rawQuery;
1404
+ if (!pluginResolved && _resolvePlugins && !existsSync(resolvedPath)) {
1405
+ const failed = await _pluginResolve(true);
1406
+ if (failed instanceof Response) return failed;
1407
+ resolvedPath = failed ?? resolvedPath;
1408
+ }
1323
1409
  if (_transformRequest) try {
1324
1410
  const result = await _transformRequest(resolvedPath);
1325
1411
  if (result?.code) {
@@ -1330,26 +1416,65 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1330
1416
  });
1331
1417
  }
1332
1418
  } catch {}
1419
+ let contents;
1333
1420
  try {
1334
- const contents = readFileSync(resolvedPath, "utf8");
1335
- modulePathMap.set(name, resolvedPath);
1336
- if (resolvedPath.endsWith(".mjs") || !resolvedPath.endsWith(".cjs") && /\b(import\s|import\(|export\s|export\{|import\.meta\b)/.test(contents)) return Response.json({
1337
- name,
1338
- esModule: _applyVirtualVersions(contents, resolvedPath)
1339
- });
1340
- const cjsSuffix = "?__cjs";
1341
- if (specifier.endsWith(cjsSuffix)) return Response.json({
1342
- name,
1343
- commonJsModule: contents
1344
- });
1345
- const esModule = createCjsEsmShim("./" + basename(resolvedPath) + cjsSuffix, contents);
1346
- return Response.json({
1347
- name,
1348
- esModule
1349
- });
1350
- } catch {
1351
- return new Response(null, { status: 404 });
1421
+ contents = readFileSync(resolvedPath, "utf8");
1422
+ } catch {}
1423
+ const _plugins = _livePlugins();
1424
+ const cjsRequest = /[?&]__cjs$/.test(specifier);
1425
+ const id = resolvedId ?? resolvedPath + query;
1426
+ const cachedCJS = cjsRequest && _cjsOutput.has(id);
1427
+ const pluginFile = !cachedCJS && _plugins?.filter(id, void 0, pluginResolved);
1428
+ if (contents === void 0 && !cachedCJS && !pluginFile) return new Response(null, { status: 404 });
1429
+ modulePathMap.set(name, resolvedPath);
1430
+ if (id !== resolvedPath) _pluginModuleIds.set(name, id);
1431
+ let transformed;
1432
+ if (cachedCJS) {
1433
+ transformed = _cjsOutput.get(id);
1434
+ _cjsOutput.delete(id);
1435
+ } else if (pluginFile) {
1436
+ try {
1437
+ const file = resolvedPath;
1438
+ const result = await _plugins.load(id, () => {
1439
+ if (contents === void 0) throw new Error(`Cannot find module "${file}"`);
1440
+ return contents;
1441
+ }, { resolved: pluginResolved });
1442
+ if (result?.moduleType === "json") return Response.json({
1443
+ name,
1444
+ esModule: jsonModuleCode(file, result.code)
1445
+ });
1446
+ transformed = result && await _pluginCode(id, result);
1447
+ } catch (error) {
1448
+ return _pluginErrorModule(name, error);
1449
+ }
1450
+ if (transformed === void 0 && contents === void 0) return new Response(null, { status: 404 });
1451
+ }
1452
+ if (transformed === void 0 && resolvedPath.endsWith(".wasm")) return Response.json({
1453
+ name,
1454
+ wasm: Array.from(readFileSync(resolvedPath))
1455
+ });
1456
+ let isESM;
1457
+ if (transformed === void 0) {
1458
+ contents = contents;
1459
+ isESM = resolvedPath.endsWith(".mjs") || !resolvedPath.endsWith(".cjs") && /\b(import\s|import\(|export\s|export\{|import\.meta\b)/.test(contents);
1460
+ } else {
1461
+ contents = transformed;
1462
+ isESM = transformedFormat(resolvedPath, contents) === "module";
1352
1463
  }
1464
+ if (isESM) return Response.json({
1465
+ name,
1466
+ esModule: _applyVirtualVersions(contents, resolvedPath)
1467
+ });
1468
+ if (cjsRequest) return Response.json({
1469
+ name,
1470
+ commonJsModule: contents
1471
+ });
1472
+ if (transformed !== void 0) _cjsOutput.set(id, transformed);
1473
+ const esModule = createCjsEsmShim("./" + basename(resolvedPath) + (query ? `${query}&` : "?") + "__cjs", contents);
1474
+ return Response.json({
1475
+ name,
1476
+ esModule
1477
+ });
1353
1478
  };
1354
1479
  }
1355
1480
  }
@@ -1358,6 +1483,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
1358
1483
  ...this.#miniflareOptions,
1359
1484
  _exportConditions: this.#exportConditions,
1360
1485
  _exports: this.#exports,
1486
+ _plugins: Boolean(this._plugins),
1361
1487
  _virtual: encodeVirtualModules(initialVirtual)
1362
1488
  });
1363
1489
  const cached = _miniflareCache.get(this.#cacheKey);
@@ -1539,7 +1665,7 @@ function virtualImporters(virtual, keyOf) {
1539
1665
  function versionableImports(code) {
1540
1666
  let imports;
1541
1667
  try {
1542
- [imports] = parse$1(code);
1668
+ [imports] = parse(code);
1543
1669
  } catch {
1544
1670
  return;
1545
1671
  }
@@ -1575,12 +1701,12 @@ function _getStripTypeScriptTypes() {
1575
1701
  }
1576
1702
  let _cjsLexerReady;
1577
1703
  function ensureCjsLexer() {
1578
- if (!_cjsLexerReady) _cjsLexerReady = init();
1704
+ if (!_cjsLexerReady) _cjsLexerReady = init$1();
1579
1705
  return _cjsLexerReady;
1580
1706
  }
1581
1707
  function commonJSExports(contents) {
1582
1708
  try {
1583
- return parse(contents).exports;
1709
+ return parse$1(contents).exports;
1584
1710
  } catch {
1585
1711
  return [];
1586
1712
  }
@@ -1,11 +1,13 @@
1
1
  import { WorkerHooks } from "./types.mjs";
2
- import { EnvRunnerData } from "./common-base-runner.mjs";
2
+ import { EnvRunnerData, EnvRunnerPluginOption } from "./common-base-runner.mjs";
3
3
  import { NodeWorkerEnvRunner } from "./node-worker-runner.mjs";
4
4
  export interface NetlifyEnvRunnerOptions {
5
5
  name: string;
6
6
  workerEntry?: string;
7
7
  hooks?: WorkerHooks;
8
8
  data?: EnvRunnerData;
9
+ /** Host-side transform plugins (see `EnvRunnerPluginOption`). */
10
+ plugins?: EnvRunnerPluginOption[];
9
11
  /**
10
12
  * `@netlify/runtime` specifier (resolved from cwd), imported inside the worker
11
13
  * since a module instance can't cross into it. Omitted: imported optionally,
@@ -1,5 +1,5 @@
1
1
  import { WorkerHooks } from "./types.mjs";
2
- import { BaseEnvRunner, EnvRunnerData } from "./common-base-runner.mjs";
2
+ import { BaseEnvRunner, EnvRunnerData, EnvRunnerPluginOption } from "./common-base-runner.mjs";
3
3
  export declare class NodeWorkerEnvRunner extends BaseEnvRunner {
4
4
  #private;
5
5
  constructor(opts: {
@@ -7,6 +7,8 @@ export declare class NodeWorkerEnvRunner extends BaseEnvRunner {
7
7
  workerEntry?: string;
8
8
  hooks?: WorkerHooks;
9
9
  data?: EnvRunnerData;
10
+ /** Host-side transform plugins (see `EnvRunnerPluginOption`). */
11
+ plugins?: EnvRunnerPluginOption[];
10
12
  });
11
13
  sendMessage(message: unknown): void;
12
14
  protected _hasRuntime(): boolean;
@@ -1,3 +1,4 @@
1
+ import { PLUGINS_DATA_KEY } from "./plugin-hooks.mjs";
1
2
  import { BaseEnvRunner, hostEnv } from "./common-base-runner.mjs";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { existsSync } from "node:fs";
@@ -36,12 +37,16 @@ var NodeWorkerEnvRunner = class extends BaseEnvRunner {
36
37
  this.close(`worker entry not found in "${this._workerEntry}".`);
37
38
  return;
38
39
  }
40
+ const plugins = this._pluginWorkerData("port");
41
+ const port = plugins[PLUGINS_DATA_KEY]?.port;
39
42
  const worker = new Worker(this._workerEntry, {
40
43
  env: hostEnv(),
41
44
  workerData: {
42
45
  name: this._name,
43
- ...this._data
44
- }
46
+ ...this._data,
47
+ ...plugins
48
+ },
49
+ transferList: port ? [port] : void 0
45
50
  });
46
51
  worker.once("exit", (code) => {
47
52
  worker._exitCode = code;