vite-node 3.2.0-beta.1 → 3.2.0-beta.3

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.
@@ -14,6 +14,7 @@ function viteNodeHmrPlugin() {
14
14
  return {
15
15
  name: "vite-node:hmr",
16
16
  config() {
17
+ // chokidar fsevents is unstable on macos when emitting "ready" event
17
18
  if (process.platform === "darwin" && false);
18
19
  },
19
20
  configureServer(server) {
@@ -23,6 +24,8 @@ function viteNodeHmrPlugin() {
23
24
  _send(payload);
24
25
  emitter.emit("message", payload);
25
26
  };
27
+ // eslint-disable-next-line ts/ban-ts-comment
28
+ // @ts-ignore Vite 6 compat
26
29
  const environments = server.environments;
27
30
  if (environments) environments.ssr.hot.send = function(payload) {
28
31
  _send(payload);
@@ -55,6 +58,7 @@ function sendMessageBuffer(runner, emitter) {
55
58
  maps.messageBuffer.length = 0;
56
59
  }
57
60
  async function reload(runner, files) {
61
+ // invalidate module cache but not node_modules
58
62
  Array.from(runner.moduleCache.keys()).forEach((fsPath) => {
59
63
  if (!fsPath.includes("node_modules")) runner.moduleCache.delete(fsPath);
60
64
  });
@@ -82,9 +86,14 @@ async function fetchUpdate(runner, { path, acceptedPath }) {
82
86
  acceptedPath = utils.normalizeRequestId(acceptedPath);
83
87
  const maps = getCache(runner);
84
88
  const mod = maps.hotModulesMap.get(path);
85
- if (!mod) return;
89
+ if (!mod)
90
+ // In a code-splitting project,
91
+ // it is common that the hot-updating module is not loaded yet.
92
+ // https://github.com/vitejs/vite/issues/721
93
+ return;
86
94
  const isSelfUpdate = path === acceptedPath;
87
95
  let fetchedModule;
96
+ // determine the qualified callbacks before we re-import the modules
88
97
  const qualifiedCallbacks = mod.callbacks.filter(({ deps }) => deps.includes(acceptedPath));
89
98
  if (isSelfUpdate || qualifiedCallbacks.length > 0) {
90
99
  const disposer = maps.disposeMap.get(acceptedPath);
@@ -115,6 +124,7 @@ async function handleMessage(runner, emitter, files, payload) {
115
124
  await notifyListeners(runner, "vite:beforeUpdate", payload);
116
125
  await Promise.all(payload.updates.map((update) => {
117
126
  if (update.type === "js-update") return queueUpdate(runner, fetchUpdate(runner, update));
127
+ // css-update
118
128
  console.error(`${browser.s.cyan("[vite-node]")} no support css hmr.}`);
119
129
  return null;
120
130
  }));
@@ -147,6 +157,8 @@ function createHotContext(runner, emitter, files, ownerPath) {
147
157
  debugHmr("createHotContext", ownerPath);
148
158
  const maps = getCache(runner);
149
159
  if (!maps.dataMap.has(ownerPath)) maps.dataMap.set(ownerPath, {});
160
+ // when a file is hot updated, a new context is created
161
+ // clear its stale callbacks
150
162
  const mod = maps.hotModulesMap.get(ownerPath);
151
163
  if (mod) mod.callbacks = [];
152
164
  const newListeners = new Map();
@@ -170,8 +182,12 @@ function createHotContext(runner, emitter, files, ownerPath) {
170
182
  acceptDeps([ownerPath], callback && (([mod]) => callback(mod)));
171
183
  },
172
184
  accept(deps, callback) {
173
- if (typeof deps === "function" || !deps) acceptDeps([ownerPath], ([mod]) => deps && deps(mod));
174
- else if (typeof deps === "string") acceptDeps([deps], ([mod]) => callback && callback(mod));
185
+ if (typeof deps === "function" || !deps)
186
+ // self-accept: hot.accept(() => {})
187
+ acceptDeps([ownerPath], ([mod]) => deps && deps(mod));
188
+ else if (typeof deps === "string")
189
+ // explicit deps
190
+ acceptDeps([deps], ([mod]) => callback && callback(mod));
175
191
  else if (Array.isArray(deps)) acceptDeps(deps, callback);
176
192
  else throw new TypeError("invalid hot.accept() usage.");
177
193
  },
@@ -12,6 +12,7 @@ function viteNodeHmrPlugin() {
12
12
  return {
13
13
  name: "vite-node:hmr",
14
14
  config() {
15
+ // chokidar fsevents is unstable on macos when emitting "ready" event
15
16
  if (process.platform === "darwin" && false);
16
17
  },
17
18
  configureServer(server) {
@@ -21,6 +22,8 @@ function viteNodeHmrPlugin() {
21
22
  _send(payload);
22
23
  emitter.emit("message", payload);
23
24
  };
25
+ // eslint-disable-next-line ts/ban-ts-comment
26
+ // @ts-ignore Vite 6 compat
24
27
  const environments = server.environments;
25
28
  if (environments) environments.ssr.hot.send = function(payload) {
26
29
  _send(payload);
@@ -53,6 +56,7 @@ function sendMessageBuffer(runner, emitter) {
53
56
  maps.messageBuffer.length = 0;
54
57
  }
55
58
  async function reload(runner, files) {
59
+ // invalidate module cache but not node_modules
56
60
  Array.from(runner.moduleCache.keys()).forEach((fsPath) => {
57
61
  if (!fsPath.includes("node_modules")) runner.moduleCache.delete(fsPath);
58
62
  });
@@ -80,9 +84,14 @@ async function fetchUpdate(runner, { path, acceptedPath }) {
80
84
  acceptedPath = normalizeRequestId(acceptedPath);
81
85
  const maps = getCache(runner);
82
86
  const mod = maps.hotModulesMap.get(path);
83
- if (!mod) return;
87
+ if (!mod)
88
+ // In a code-splitting project,
89
+ // it is common that the hot-updating module is not loaded yet.
90
+ // https://github.com/vitejs/vite/issues/721
91
+ return;
84
92
  const isSelfUpdate = path === acceptedPath;
85
93
  let fetchedModule;
94
+ // determine the qualified callbacks before we re-import the modules
86
95
  const qualifiedCallbacks = mod.callbacks.filter(({ deps }) => deps.includes(acceptedPath));
87
96
  if (isSelfUpdate || qualifiedCallbacks.length > 0) {
88
97
  const disposer = maps.disposeMap.get(acceptedPath);
@@ -113,6 +122,7 @@ async function handleMessage(runner, emitter, files, payload) {
113
122
  await notifyListeners(runner, "vite:beforeUpdate", payload);
114
123
  await Promise.all(payload.updates.map((update) => {
115
124
  if (update.type === "js-update") return queueUpdate(runner, fetchUpdate(runner, update));
125
+ // css-update
116
126
  console.error(`${s.cyan("[vite-node]")} no support css hmr.}`);
117
127
  return null;
118
128
  }));
@@ -145,6 +155,8 @@ function createHotContext(runner, emitter, files, ownerPath) {
145
155
  debugHmr("createHotContext", ownerPath);
146
156
  const maps = getCache(runner);
147
157
  if (!maps.dataMap.has(ownerPath)) maps.dataMap.set(ownerPath, {});
158
+ // when a file is hot updated, a new context is created
159
+ // clear its stale callbacks
148
160
  const mod = maps.hotModulesMap.get(ownerPath);
149
161
  if (mod) mod.callbacks = [];
150
162
  const newListeners = new Map();
@@ -168,8 +180,12 @@ function createHotContext(runner, emitter, files, ownerPath) {
168
180
  acceptDeps([ownerPath], callback && (([mod]) => callback(mod)));
169
181
  },
170
182
  accept(deps, callback) {
171
- if (typeof deps === "function" || !deps) acceptDeps([ownerPath], ([mod]) => deps && deps(mod));
172
- else if (typeof deps === "string") acceptDeps([deps], ([mod]) => callback && callback(mod));
183
+ if (typeof deps === "function" || !deps)
184
+ // self-accept: hot.accept(() => {})
185
+ acceptDeps([ownerPath], ([mod]) => deps && deps(mod));
186
+ else if (typeof deps === "string")
187
+ // explicit deps
188
+ acceptDeps([deps], ([mod]) => callback && callback(mod));
173
189
  else if (Array.isArray(deps)) acceptDeps(deps, callback);
174
190
  else throw new TypeError("invalid hot.accept() usage.");
175
191
  },
package/dist/cli.cjs CHANGED
@@ -21,7 +21,7 @@ require('node:perf_hooks');
21
21
  require('es-module-lexer');
22
22
  require('./constants.cjs');
23
23
 
24
- var version = "3.2.0-beta.1";
24
+ var version = "3.2.0-beta.3";
25
25
 
26
26
  const cli = cac("vite-node");
27
27
  cli.option("-r, --root <path>", "Use specified root directory").option("-c, --config <path>", "Use specified config file").option("-m, --mode <mode>", "Set env mode").option("-w, --watch", "Restart on file changes, similar to \"nodemon\"").option("--script", "Use vite-node as a script runner").option("--options <options>", "Use specified Vite server options").option("-v, --version", "Output the version number").option("-h, --help", "Display help for command");
@@ -76,7 +76,9 @@ async function run(files, options = {}) {
76
76
  plugins: [options.watch && hmr.viteNodeHmrPlugin()]
77
77
  });
78
78
  if (Number(vite.version.split(".")[0]) < 6) await server$1.pluginContainer.buildStart({});
79
- else await server$1.environments.client.pluginContainer.buildStart({});
79
+ else
80
+ // directly access client plugin container until https://github.com/vitejs/vite/issues/19607
81
+ await server$1.environments.client.pluginContainer.buildStart({});
80
82
  const env = vite.loadEnv(server$1.config.mode, server$1.config.envDir, "");
81
83
  for (const key in env) {
82
84
  var _process$env;
@@ -97,6 +99,7 @@ async function run(files, options = {}) {
97
99
  return hmr.createHotContext(runner, server$1.emitter, files, url);
98
100
  }
99
101
  });
102
+ // provide the vite define variable in this context
100
103
  await runner.executeId("/@vite/env");
101
104
  for (const file of files) await runner.executeFile(file);
102
105
  if (!options.watch) await server$1.close();
package/dist/cli.mjs CHANGED
@@ -3,23 +3,23 @@ import cac from 'cac';
3
3
  import { s } from './chunk-browser.mjs';
4
4
  import { createServer, version as version$1, loadEnv } from 'vite';
5
5
  import { ViteNodeRunner } from './client.mjs';
6
+ import { v as viteNodeHmrPlugin, a as createHotContext, h as handleMessage } from './chunk-hmr.mjs';
6
7
  import { ViteNodeServer } from './server.mjs';
7
8
  import { installSourcemapsSupport } from './source-map.mjs';
8
9
  import { toArray } from './utils.mjs';
9
- import { v as viteNodeHmrPlugin, a as createHotContext, h as handleMessage } from './chunk-hmr.mjs';
10
10
  import 'node:module';
11
11
  import 'node:url';
12
12
  import 'node:vm';
13
13
  import 'debug';
14
14
  import 'pathe';
15
15
  import 'node:fs';
16
+ import 'node:events';
16
17
  import 'node:assert';
17
18
  import 'node:perf_hooks';
18
19
  import 'es-module-lexer';
19
20
  import './constants.mjs';
20
- import 'node:events';
21
21
 
22
- var version = "3.2.0-beta.1";
22
+ var version = "3.2.0-beta.3";
23
23
 
24
24
  const cli = cac("vite-node");
25
25
  cli.option("-r, --root <path>", "Use specified root directory").option("-c, --config <path>", "Use specified config file").option("-m, --mode <mode>", "Set env mode").option("-w, --watch", "Restart on file changes, similar to \"nodemon\"").option("--script", "Use vite-node as a script runner").option("--options <options>", "Use specified Vite server options").option("-v, --version", "Output the version number").option("-h, --help", "Display help for command");
@@ -74,7 +74,9 @@ async function run(files, options = {}) {
74
74
  plugins: [options.watch && viteNodeHmrPlugin()]
75
75
  });
76
76
  if (Number(version$1.split(".")[0]) < 6) await server.pluginContainer.buildStart({});
77
- else await server.environments.client.pluginContainer.buildStart({});
77
+ else
78
+ // directly access client plugin container until https://github.com/vitejs/vite/issues/19607
79
+ await server.environments.client.pluginContainer.buildStart({});
78
80
  const env = loadEnv(server.config.mode, server.config.envDir, "");
79
81
  for (const key in env) {
80
82
  var _process$env;
@@ -95,6 +97,7 @@ async function run(files, options = {}) {
95
97
  return createHotContext(runner, server.emitter, files, url);
96
98
  }
97
99
  });
100
+ // provide the vite define variable in this context
98
101
  await runner.executeId("/@vite/env");
99
102
  for (const file of files) await runner.executeFile(file);
100
103
  if (!options.watch) await server.close();
package/dist/client.cjs CHANGED
@@ -155,12 +155,14 @@ class ViteNodeRunner {
155
155
  const { imports, importers } = mod;
156
156
  if (importee) importers.add(importee);
157
157
  const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map((p) => ` - ${p}`).join("\n")}`;
158
+ // check circular dependency
158
159
  if (callstack.includes(fsPath) || Array.from(imports.values()).some((i) => importers.has(i))) {
159
160
  if (mod.exports) return mod.exports;
160
161
  }
161
162
  let debugTimer;
162
163
  if (this.debug) debugTimer = setTimeout(() => console.warn(`[vite-node] module ${fsPath} takes over 2s to load.\n${getStack()}`), 2e3);
163
164
  try {
165
+ // cached module
164
166
  if (mod.promise) return await mod.promise;
165
167
  const promise = this.directRequest(id, fsPath, callstack);
166
168
  Object.assign(mod, {
@@ -183,6 +185,7 @@ class ViteNodeRunner {
183
185
  const { path, exists } = utils.toFilePath(dep, this.root);
184
186
  if (!this.options.resolveId || exists) return [dep, path];
185
187
  const resolved = await this.options.resolveId(dep, importer);
188
+ // supported since Vite 5-beta.19
186
189
  if (resolved === null || resolved === void 0 || (_resolved$meta = resolved.meta) === null || _resolved$meta === void 0 || (_resolved$meta = _resolved$meta["vite:alias"]) === null || _resolved$meta === void 0 ? void 0 : _resolved$meta.noResolved) {
187
190
  const error = new Error(`Cannot find module '${id}'${importer ? ` imported from '${importer}'` : ""}.
188
191
 
@@ -206,6 +209,7 @@ class ViteNodeRunner {
206
209
  }
207
210
  async resolveUrl(id, importee) {
208
211
  const resolveKey = `resolve:${id}`;
212
+ // put info about new import as soon as possible, so we can start tracking it
209
213
  this.moduleCache.setByModuleId(resolveKey, { resolving: true });
210
214
  try {
211
215
  return await this._resolveUrl(id, importee);
@@ -221,6 +225,7 @@ class ViteNodeRunner {
221
225
  try {
222
226
  return await this.options.fetchModule(id);
223
227
  } catch (cause) {
228
+ // rethrow vite error if it cannot load the module because it's not resolved
224
229
  if (typeof cause === "object" && cause.code === "ERR_LOAD_URL" || typeof (cause === null || cause === void 0 ? void 0 : cause.message) === "string" && cause.message.includes("Failed to load url")) {
225
230
  const error = new Error(`Cannot find ${utils.isBareImport(id) ? "package" : "module"} '${id}'${importer ? ` imported from '${importer}'` : ""}`, { cause });
226
231
  error.code = "ERR_MODULE_NOT_FOUND";
@@ -253,6 +258,7 @@ class ViteNodeRunner {
253
258
  if (transformed == null) throw new Error(`[vite-node] Failed to load "${id}" imported from ${callstack[callstack.length - 2]}`);
254
259
  const { Object, Reflect, Symbol } = this.getContextPrimitives();
255
260
  const modulePath = utils.cleanUrl(moduleId);
261
+ // disambiguate the `<UNIT>:/` on windows: see nodejs/node#31710
256
262
  const href = node_url.pathToFileURL(modulePath).href;
257
263
  const __filename = node_url.fileURLToPath(href);
258
264
  const __dirname = path.dirname(__filename);
@@ -270,6 +276,8 @@ class ViteNodeRunner {
270
276
  });
271
277
  const SYMBOL_NOT_DEFINED = Symbol("not defined");
272
278
  let moduleExports = SYMBOL_NOT_DEFINED;
279
+ // this proxy is triggered only on exports.{name} and module.exports access
280
+ // inside the module itself. imported module is always "exports"
273
281
  const cjsExports = new Proxy(exports, {
274
282
  get: (target, p, receiver) => {
275
283
  if (Reflect.has(target, p)) return Reflect.get(target, p, receiver);
@@ -277,12 +285,16 @@ class ViteNodeRunner {
277
285
  },
278
286
  getPrototypeOf: () => Object.prototype,
279
287
  set: (_, p, value) => {
288
+ // treat "module.exports =" the same as "exports.default =" to not have nested "default.default",
289
+ // so "exports.default" becomes the actual module
280
290
  if (p === "default" && this.shouldInterop(modulePath, { default: value }) && cjsExports !== value) {
281
291
  exportAll(cjsExports, value);
282
292
  exports.default = value;
283
293
  return true;
284
294
  }
285
295
  if (!Reflect.has(exports, "default")) exports.default = {};
296
+ // returns undefined, when accessing named exports, if default is not an object
297
+ // but is still present inside hasOwnKeys, this is Node behaviour for CJS
286
298
  if (moduleExports !== SYMBOL_NOT_DEFINED && utils.isPrimitive(moduleExports)) {
287
299
  defineExport(exports, p, () => void 0);
288
300
  return true;
@@ -306,6 +318,7 @@ class ViteNodeRunner {
306
318
  return cjsExports;
307
319
  }
308
320
  };
321
+ // Vite hot context
309
322
  let hotContext;
310
323
  if (this.options.createHotContext) Object.defineProperty(meta, "hot", {
311
324
  enumerable: true,
@@ -318,11 +331,20 @@ class ViteNodeRunner {
318
331
  hotContext = value;
319
332
  }
320
333
  });
334
+ // Be careful when changing this
335
+ // changing context will change amount of code added on line :114 (vm.runInThisContext)
336
+ // this messes up sourcemaps for coverage
337
+ // adjust `WRAPPER_LENGTH` variable in packages/coverage-v8/src/provider.ts if you do change this
321
338
  const context = this.prepareContext({
322
339
  __vite_ssr_import__: request,
323
340
  __vite_ssr_dynamic_import__: request,
324
341
  __vite_ssr_exports__: exports,
325
342
  __vite_ssr_exportAll__: (obj) => exportAll(exports, obj),
343
+ __vite_ssr_exportName__: (name, getter) => Object.defineProperty(exports, name, {
344
+ enumerable: true,
345
+ configurable: true,
346
+ get: getter
347
+ }),
326
348
  __vite_ssr_import_meta__: meta,
327
349
  require: node_module.createRequire(href),
328
350
  exports: cjsExports,
@@ -331,6 +353,7 @@ class ViteNodeRunner {
331
353
  __dirname
332
354
  });
333
355
  debugExecute(__filename);
356
+ // remove shebang
334
357
  if (transformed[0] === "#") transformed = transformed.replace(/^#!.*/, (s) => " ".repeat(s.length));
335
358
  await this.runModule(context, transformed);
336
359
  return exports;
@@ -344,6 +367,7 @@ class ViteNodeRunner {
344
367
  }
345
368
  async runModule(context, transformed) {
346
369
  var _this$options$moduleE;
370
+ // add 'use strict' since ESM enables it by default
347
371
  const codeDefinition = `'use strict';async (${Object.keys(context).join(",")})=>{{`;
348
372
  const code = `${codeDefinition}${transformed}\n}}`;
349
373
  const options = {
@@ -364,6 +388,8 @@ class ViteNodeRunner {
364
388
  */
365
389
  shouldInterop(path, mod) {
366
390
  if (this.options.interopDefault === false) return false;
391
+ // never interop ESM modules
392
+ // TODO: should also skip for `.js` with `type="module"`
367
393
  return !path.endsWith(".mjs") && "default" in mod;
368
394
  }
369
395
  importExternalModule(path) {
@@ -415,6 +441,7 @@ function interopModule(mod) {
415
441
  defaultExport
416
442
  };
417
443
  }
444
+ // keep consistency with Vite on how exports are defined
418
445
  function defineExport(exports, key, value) {
419
446
  Object.defineProperty(exports, key, {
420
447
  enumerable: true,
@@ -423,6 +450,8 @@ function defineExport(exports, key, value) {
423
450
  });
424
451
  }
425
452
  function exportAll(exports, sourceModule) {
453
+ // #1120 when a module exports itself it causes
454
+ // call stack error
426
455
  if (exports === sourceModule) return;
427
456
  if (utils.isPrimitive(sourceModule) || Array.isArray(sourceModule) || sourceModule instanceof Promise) return;
428
457
  for (const key in sourceModule) if (key !== "default" && !(key in exports)) try {
package/dist/client.mjs CHANGED
@@ -153,12 +153,14 @@ class ViteNodeRunner {
153
153
  const { imports, importers } = mod;
154
154
  if (importee) importers.add(importee);
155
155
  const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map((p) => ` - ${p}`).join("\n")}`;
156
+ // check circular dependency
156
157
  if (callstack.includes(fsPath) || Array.from(imports.values()).some((i) => importers.has(i))) {
157
158
  if (mod.exports) return mod.exports;
158
159
  }
159
160
  let debugTimer;
160
161
  if (this.debug) debugTimer = setTimeout(() => console.warn(`[vite-node] module ${fsPath} takes over 2s to load.\n${getStack()}`), 2e3);
161
162
  try {
163
+ // cached module
162
164
  if (mod.promise) return await mod.promise;
163
165
  const promise = this.directRequest(id, fsPath, callstack);
164
166
  Object.assign(mod, {
@@ -181,6 +183,7 @@ class ViteNodeRunner {
181
183
  const { path, exists } = toFilePath(dep, this.root);
182
184
  if (!this.options.resolveId || exists) return [dep, path];
183
185
  const resolved = await this.options.resolveId(dep, importer);
186
+ // supported since Vite 5-beta.19
184
187
  if (resolved === null || resolved === void 0 || (_resolved$meta = resolved.meta) === null || _resolved$meta === void 0 || (_resolved$meta = _resolved$meta["vite:alias"]) === null || _resolved$meta === void 0 ? void 0 : _resolved$meta.noResolved) {
185
188
  const error = new Error(`Cannot find module '${id}'${importer ? ` imported from '${importer}'` : ""}.
186
189
 
@@ -204,6 +207,7 @@ class ViteNodeRunner {
204
207
  }
205
208
  async resolveUrl(id, importee) {
206
209
  const resolveKey = `resolve:${id}`;
210
+ // put info about new import as soon as possible, so we can start tracking it
207
211
  this.moduleCache.setByModuleId(resolveKey, { resolving: true });
208
212
  try {
209
213
  return await this._resolveUrl(id, importee);
@@ -219,6 +223,7 @@ class ViteNodeRunner {
219
223
  try {
220
224
  return await this.options.fetchModule(id);
221
225
  } catch (cause) {
226
+ // rethrow vite error if it cannot load the module because it's not resolved
222
227
  if (typeof cause === "object" && cause.code === "ERR_LOAD_URL" || typeof (cause === null || cause === void 0 ? void 0 : cause.message) === "string" && cause.message.includes("Failed to load url")) {
223
228
  const error = new Error(`Cannot find ${isBareImport(id) ? "package" : "module"} '${id}'${importer ? ` imported from '${importer}'` : ""}`, { cause });
224
229
  error.code = "ERR_MODULE_NOT_FOUND";
@@ -251,6 +256,7 @@ class ViteNodeRunner {
251
256
  if (transformed == null) throw new Error(`[vite-node] Failed to load "${id}" imported from ${callstack[callstack.length - 2]}`);
252
257
  const { Object, Reflect, Symbol } = this.getContextPrimitives();
253
258
  const modulePath = cleanUrl(moduleId);
259
+ // disambiguate the `<UNIT>:/` on windows: see nodejs/node#31710
254
260
  const href = pathToFileURL(modulePath).href;
255
261
  const __filename = fileURLToPath(href);
256
262
  const __dirname = dirname(__filename);
@@ -268,6 +274,8 @@ class ViteNodeRunner {
268
274
  });
269
275
  const SYMBOL_NOT_DEFINED = Symbol("not defined");
270
276
  let moduleExports = SYMBOL_NOT_DEFINED;
277
+ // this proxy is triggered only on exports.{name} and module.exports access
278
+ // inside the module itself. imported module is always "exports"
271
279
  const cjsExports = new Proxy(exports, {
272
280
  get: (target, p, receiver) => {
273
281
  if (Reflect.has(target, p)) return Reflect.get(target, p, receiver);
@@ -275,12 +283,16 @@ class ViteNodeRunner {
275
283
  },
276
284
  getPrototypeOf: () => Object.prototype,
277
285
  set: (_, p, value) => {
286
+ // treat "module.exports =" the same as "exports.default =" to not have nested "default.default",
287
+ // so "exports.default" becomes the actual module
278
288
  if (p === "default" && this.shouldInterop(modulePath, { default: value }) && cjsExports !== value) {
279
289
  exportAll(cjsExports, value);
280
290
  exports.default = value;
281
291
  return true;
282
292
  }
283
293
  if (!Reflect.has(exports, "default")) exports.default = {};
294
+ // returns undefined, when accessing named exports, if default is not an object
295
+ // but is still present inside hasOwnKeys, this is Node behaviour for CJS
284
296
  if (moduleExports !== SYMBOL_NOT_DEFINED && isPrimitive(moduleExports)) {
285
297
  defineExport(exports, p, () => void 0);
286
298
  return true;
@@ -304,6 +316,7 @@ class ViteNodeRunner {
304
316
  return cjsExports;
305
317
  }
306
318
  };
319
+ // Vite hot context
307
320
  let hotContext;
308
321
  if (this.options.createHotContext) Object.defineProperty(meta, "hot", {
309
322
  enumerable: true,
@@ -316,11 +329,20 @@ class ViteNodeRunner {
316
329
  hotContext = value;
317
330
  }
318
331
  });
332
+ // Be careful when changing this
333
+ // changing context will change amount of code added on line :114 (vm.runInThisContext)
334
+ // this messes up sourcemaps for coverage
335
+ // adjust `WRAPPER_LENGTH` variable in packages/coverage-v8/src/provider.ts if you do change this
319
336
  const context = this.prepareContext({
320
337
  __vite_ssr_import__: request,
321
338
  __vite_ssr_dynamic_import__: request,
322
339
  __vite_ssr_exports__: exports,
323
340
  __vite_ssr_exportAll__: (obj) => exportAll(exports, obj),
341
+ __vite_ssr_exportName__: (name, getter) => Object.defineProperty(exports, name, {
342
+ enumerable: true,
343
+ configurable: true,
344
+ get: getter
345
+ }),
324
346
  __vite_ssr_import_meta__: meta,
325
347
  require: createRequire(href),
326
348
  exports: cjsExports,
@@ -329,6 +351,7 @@ class ViteNodeRunner {
329
351
  __dirname
330
352
  });
331
353
  debugExecute(__filename);
354
+ // remove shebang
332
355
  if (transformed[0] === "#") transformed = transformed.replace(/^#!.*/, (s) => " ".repeat(s.length));
333
356
  await this.runModule(context, transformed);
334
357
  return exports;
@@ -342,6 +365,7 @@ class ViteNodeRunner {
342
365
  }
343
366
  async runModule(context, transformed) {
344
367
  var _this$options$moduleE;
368
+ // add 'use strict' since ESM enables it by default
345
369
  const codeDefinition = `'use strict';async (${Object.keys(context).join(",")})=>{{`;
346
370
  const code = `${codeDefinition}${transformed}\n}}`;
347
371
  const options = {
@@ -362,6 +386,8 @@ class ViteNodeRunner {
362
386
  */
363
387
  shouldInterop(path, mod) {
364
388
  if (this.options.interopDefault === false) return false;
389
+ // never interop ESM modules
390
+ // TODO: should also skip for `.js` with `type="module"`
365
391
  return !path.endsWith(".mjs") && "default" in mod;
366
392
  }
367
393
  importExternalModule(path) {
@@ -413,6 +439,7 @@ function interopModule(mod) {
413
439
  defaultExport
414
440
  };
415
441
  }
442
+ // keep consistency with Vite on how exports are defined
416
443
  function defineExport(exports, key, value) {
417
444
  Object.defineProperty(exports, key, {
418
445
  enumerable: true,
@@ -421,6 +448,8 @@ function defineExport(exports, key, value) {
421
448
  });
422
449
  }
423
450
  function exportAll(exports, sourceModule) {
451
+ // #1120 when a module exports itself it causes
452
+ // call stack error
424
453
  if (exports === sourceModule) return;
425
454
  if (isPrimitive(sourceModule) || Array.isArray(sourceModule) || sourceModule instanceof Promise) return;
426
455
  for (const key in sourceModule) if (key !== "default" && !(key in exports)) try {
package/dist/hmr.d.ts CHANGED
@@ -21,6 +21,8 @@ declare module "vite" {
21
21
  declare function createHmrEmitter(): HMREmitter;
22
22
  declare function viteNodeHmrPlugin(): Plugin;
23
23
 
24
+ /* eslint-disable no-console */
25
+
24
26
  type ModuleNamespace = Record<string, any> & {
25
27
  [Symbol.toStringTag]: "Module"
26
28
  };
@@ -30,6 +32,7 @@ interface HotModule {
30
32
  callbacks: HotCallback[];
31
33
  }
32
34
  interface HotCallback {
35
+ // the dependencies must be fetchable paths
33
36
  deps: string[];
34
37
  fn: (modules: (ModuleNamespace | undefined)[]) => void;
35
38
  }
package/dist/server.cjs CHANGED
@@ -34,6 +34,7 @@ function _interopNamespaceDefault(e) {
34
34
 
35
35
  var esModuleLexer__namespace = /*#__PURE__*/_interopNamespaceDefault(esModuleLexer);
36
36
 
37
+ /* eslint-disable no-console */
37
38
  function hashCode(s) {
38
39
  return s.split("").reduce((a, b) => {
39
40
  a = (a << 5) - a + b.charCodeAt(0);
@@ -128,6 +129,7 @@ function guessCJSversion(id) {
128
129
  ]) if (fs.existsSync(i)) return i;
129
130
  }
130
131
  }
132
+ // The code from https://github.com/unjs/mlly/blob/c5bcca0cda175921344fd6de1bc0c499e73e5dac/src/syntax.ts#L51-L98
131
133
  async function isValidNodeImport(id) {
132
134
  const extension = pathe.extname(id);
133
135
  if (BUILTIN_EXTENSIONS.has(extension)) return true;
@@ -152,12 +154,17 @@ async function shouldExternalize(id, options, cache = _defaultExternalizeCache)
152
154
  }
153
155
  async function _shouldExternalize(id, options) {
154
156
  if (utils.isNodeBuiltin(id)) return id;
157
+ // data: should be processed by native import,
158
+ // since it is a feature of ESM.
159
+ // also externalize network imports since nodejs allows it when --experimental-network-imports
155
160
  if (id.startsWith("data:") || /^(?:https?:)?\/\//.test(id)) return id;
156
161
  id = patchWindowsImportPath(id);
157
162
  const moduleDirectories = (options === null || options === void 0 ? void 0 : options.moduleDirectories) || ["/node_modules/"];
158
163
  if (matchExternalizePattern(id, moduleDirectories, options === null || options === void 0 ? void 0 : options.inline)) return false;
159
164
  if ((options === null || options === void 0 ? void 0 : options.inlineFiles) && (options === null || options === void 0 ? void 0 : options.inlineFiles.includes(id))) return false;
160
165
  if (matchExternalizePattern(id, moduleDirectories, options === null || options === void 0 ? void 0 : options.external)) return id;
166
+ // Unless the user explicitly opted to inline them, externalize Vite deps.
167
+ // They are too big to inline by default.
161
168
  if ((options === null || options === void 0 ? void 0 : options.cacheDir) && id.includes(options.cacheDir)) return id;
162
169
  const isLibraryModule = moduleDirectories.some((dir) => id.includes(dir));
163
170
  const guessCJS = isLibraryModule && (options === null || options === void 0 ? void 0 : options.fallbackCJS);
@@ -204,13 +211,18 @@ class ViteNodeServer {
204
211
  externalizeCache = new Map();
205
212
  debugger;
206
213
  constructor(server, options = {}) {
214
+ var _options$deps3;
207
215
  this.server = server;
208
216
  this.options = options;
209
- var _options$deps3;
210
217
  const ssrOptions = server.config.ssr;
211
218
  options.deps ?? (options.deps = {});
212
219
  options.deps.cacheDir = pathe.relative(server.config.root, options.deps.cacheDir || server.config.cacheDir);
213
220
  if (ssrOptions) {
221
+ // we don't externalize ssr, because it has different semantics in Vite
222
+ // if (ssrOptions.external) {
223
+ // options.deps.external ??= []
224
+ // options.deps.external.push(...ssrOptions.external)
225
+ // }
214
226
  if (ssrOptions.noExternal === true) {
215
227
  var _options$deps;
216
228
  (_options$deps = options.deps).inline ?? (_options$deps.inline = true);
@@ -240,6 +252,7 @@ class ViteNodeServer {
240
252
  if (!dir.endsWith("/")) dir += "/";
241
253
  return pathe.normalize(dir);
242
254
  });
255
+ // always add node_modules as a module directory
243
256
  if (!options.deps.moduleDirectories.includes("/node_modules/")) options.deps.moduleDirectories.push("/node_modules/");
244
257
  }
245
258
  shouldExternalize(id) {
@@ -293,6 +306,7 @@ class ViteNodeServer {
293
306
  const moduleId = utils.normalizeModuleId(id);
294
307
  this.assertMode(mode);
295
308
  const promiseMap = this.fetchPromiseMap[mode];
309
+ // reuse transform for concurrent requests
296
310
  if (!promiseMap.has(moduleId)) promiseMap.set(moduleId, this._fetchModule(moduleId, mode).finally(() => {
297
311
  promiseMap.delete(moduleId);
298
312
  }));
@@ -302,6 +316,7 @@ class ViteNodeServer {
302
316
  const mode = transformMode || this.getTransformMode(id);
303
317
  this.assertMode(mode);
304
318
  const promiseMap = this.transformPromiseMap[mode];
319
+ // reuse transform for concurrent requests
305
320
  if (!promiseMap.has(id)) promiseMap.set(id, this._transformRequest(id, filepath, mode).finally(() => {
306
321
  promiseMap.delete(id);
307
322
  }));
@@ -327,6 +342,7 @@ class ViteNodeServer {
327
342
  if (module) return module;
328
343
  const _modules = this.server.moduleGraph.getModulesByFile(file);
329
344
  if (!_modules || !_modules.size) return null;
345
+ // find the latest changed module
330
346
  const modules = [..._modules];
331
347
  let mod = modules[0];
332
348
  let latestMax = -1;
@@ -354,6 +370,9 @@ class ViteNodeServer {
354
370
  const { path: filePath } = utils.toFilePath(id, this.server.config.root);
355
371
  const moduleNode = this.getChangedModule(id, filePath);
356
372
  const cache = this.fetchCaches[transformMode].get(filePath);
373
+ // lastUpdateTimestamp is the timestamp that marks the last time the module was changed
374
+ // if lastUpdateTimestamp is 0, then the module was not changed since the server started
375
+ // we test "timestamp === 0" for expressiveness, but it's not necessary
357
376
  const timestamp = moduleNode ? Math.max(moduleNode.lastHMRTimestamp, moduleNode.lastInvalidationTimestamp) : 0;
358
377
  if (cache && (timestamp === 0 || cache.timestamp >= timestamp)) return cache.result;
359
378
  const time = Date.now();
@@ -401,6 +420,8 @@ class ViteNodeServer {
401
420
  if (result) return result;
402
421
  }
403
422
  if (transformMode === "web") {
423
+ // for components like Vue, we want to use the client side
424
+ // plugins but then convert the code to be consumed by the server
404
425
  result = await this.server.transformRequest(id);
405
426
  if (result) result = await this.server.ssrTransform(result.code, result.map, id);
406
427
  } else result = await this.server.transformRequest(id, { ssr: true });