@solidjs/vite-plugin 3.0.0-next.36 → 3.0.0-next.37

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.
@@ -87,7 +87,16 @@ function webRequestFromNode(req, urlPath, res) {
87
87
  signal = controller.signal;
88
88
  }
89
89
  const method = req.method || 'GET';
90
- const body = method === 'GET' || method === 'HEAD' ? undefined : node_stream.Readable.toWeb(req);
90
+ // Only attach a body when the request actually carries one. A web Request
91
+ // built by the browser for a bodyless POST has `body === null`, and the
92
+ // runtime keys off that (a present body that decodes to nothing is a 400
93
+ // since @solidjs/web 2.0.0-rc.5) — so an unconditionally attached (empty)
94
+ // stream misparses bodyless calls. HTTP/1 signals a body via
95
+ // Content-Length/Transfer-Encoding (RFC 9112 §6); the h2 compat API sets
96
+ // `stream.endAfterHeaders` when END_STREAM rode the headers frame.
97
+ const h2Stream = req.stream;
98
+ const hasBody = method !== 'GET' && method !== 'HEAD' && (h2Stream ? !h2Stream.endAfterHeaders : req.headers['transfer-encoding'] !== undefined || req.headers['content-length'] !== undefined && req.headers['content-length'] !== '0');
99
+ const body = hasBody ? node_stream.Readable.toWeb(req) : undefined;
91
100
  return new Request(url, {
92
101
  method,
93
102
  headers,
@@ -1191,9 +1200,11 @@ function serverFunctions(options = {}, internal = {}) {
1191
1200
  `export function handleServerFunctionRequest(request, options) {`, ` const { event: eventInit, ...rest } = options || {};`, ` return handle(request, {`, ` provideEvent: provideRequestEvent,`, ` ...(eventInit ? { createEvent: (req) => ({ request: req, locals: {}, ...eventInit }) } : {}),`, ` ...rest,`, ` });`, `}`].join('\n');
1192
1201
  }
1193
1202
 
1194
- // Function IDs are `xxHash32(root-relative path)-<count>` (see compile.ts),
1195
- // so the hash segment maps an incoming ID back to its module. Rebuilt
1196
- // whenever a transform has grown the manifest.
1203
+ // Function IDs are `<name>-<xxHash32(root-relative path)>[-<ordinal>]`
1204
+ // (identity-keyed, solidjs/solid#3109). The name is a JS identifier and
1205
+ // never contains `-`, so the hash is always the second segment and maps
1206
+ // an incoming ID back to its module. Rebuilt whenever a transform has
1207
+ // grown the manifest.
1197
1208
  const hashIndex = new Map();
1198
1209
  let hashIndexSize = -1;
1199
1210
  function moduleForFunctionId(functionId) {
@@ -1205,7 +1216,7 @@ function serverFunctions(options = {}, internal = {}) {
1205
1216
  }
1206
1217
  hashIndexSize = manifest.server.size;
1207
1218
  }
1208
- return hashIndex.get(functionId.split('-', 1)[0]);
1219
+ return hashIndex.get(functionId.split('-')[1]);
1209
1220
  }
1210
1221
  function moduleDevUrl(entry) {
1211
1222
  const relative = path.relative(root, entry).split(path.sep).join('/');
@@ -1243,10 +1254,11 @@ function serverFunctions(options = {}, internal = {}) {
1243
1254
  if (internal.externalDevServer || !isRunnableEnvironment(ssrEnvironment)) {
1244
1255
  return;
1245
1256
  }
1246
- // A call's address is `<endpoint>/<id>` (solidjs/solid#3076)the
1247
- // mount plus exactly one path segment. Bare-mount requests still
1248
- // reach the runtime handler (it answers 404), so misdirected posts
1249
- // fail through the endpoint rather than falling through to SSR.
1257
+ // A call's address is `<endpoint>/<id>` plain HTTP or
1258
+ // `<endpoint>/data/<id>` the scripted transport's own path
1259
+ // (solidjs/solid#3076, #3094). Bare-mount requests still reach the
1260
+ // runtime handler (it answers 404), so misdirected posts fail
1261
+ // through the endpoint rather than falling through to SSR.
1250
1262
  const underMount = (pathname, mount) => pathname === mount || pathname.startsWith(mount + '/');
1251
1263
  server.middlewares.use((req, res, next) => {
1252
1264
  const url = new URL(req.url || '/', 'http://localhost');
@@ -1265,9 +1277,15 @@ function serverFunctions(options = {}, internal = {}) {
1265
1277
  // Make sure the referenced module has been evaluated in the SSR
1266
1278
  // environment so its registration exists — functions only client
1267
1279
  // code references are never loaded by the SSR render itself.
1268
- // The id lives in the path segment after the mount.
1280
+ // The id lives in the path segment after the mount — behind a
1281
+ // literal `data` segment on the scripted transport's address
1282
+ // (solidjs/solid#3094). Segment count keeps the two apart: an id
1283
+ // occupies exactly one segment, so `data/<id>` is only ever a
1284
+ // data address, and a function id spelled `data` still parses at
1285
+ // the bare one.
1269
1286
  const mount = basePrefixed ? resolvedEndpoint : endpoint;
1270
- const segment = url.pathname.slice(mount.length + 1);
1287
+ let segment = url.pathname.slice(mount.length + 1);
1288
+ if (segment.startsWith('data/')) segment = segment.slice(5);
1271
1289
  let functionId = null;
1272
1290
  if (segment && !segment.includes('/')) {
1273
1291
  try {
@@ -1276,14 +1294,6 @@ function serverFunctions(options = {}, internal = {}) {
1276
1294
  // not an address; the runtime handler answers the 404
1277
1295
  }
1278
1296
  }
1279
- if (!functionId) {
1280
- // TRANSITIONAL (remove before 3.0 stable): the retired header
1281
- // and `?id=` addressing, kept only for the RC window where this
1282
- // plugin meets a @solidjs/web older than the path-addressing
1283
- // change (solidjs/solid#3076).
1284
- const headerId = req.headers['x-server-function-id'];
1285
- functionId = (typeof headerId === 'string' ? headerId.split('#')[0] : undefined) || url.searchParams.get('id');
1286
- }
1287
1297
  if (functionId) {
1288
1298
  const entry = moduleForFunctionId(functionId);
1289
1299
  if (entry) await ssrEnvironment.runner.import(moduleDevUrl(entry));
@@ -1922,7 +1932,8 @@ function startServe(options, internal = {}) {
1922
1932
  lines.push(``, `async function dispatchRequest(request, event, options) {`);
1923
1933
  if (composeServerFunctions) {
1924
1934
  lines.push(
1925
- // A call's address is `<endpoint>/<id>` (solidjs/solid#3076); the
1935
+ // A call's address is `<endpoint>/<id>` or `<endpoint>/data/<id>`
1936
+ // (solidjs/solid#3076, #3094); the prefix gate covers both, and the
1926
1937
  // bare mount still routes so a misaddressed request 404s through the
1927
1938
  // runtime handler instead of rendering a page at it.
1928
1939
  ` const requestPath = new URL(request.url).pathname;`, ` if (requestPath === endpoint || requestPath.startsWith(endpoint + '/')) {`,
@@ -1995,6 +2006,7 @@ function startServe(options, internal = {}) {
1995
2006
  devtoolsResolutions = {};
1996
2007
  devtoolsIds = {};
1997
2008
  entries = resolveEntries(root, options, clientMode);
2009
+ internal.onDocumentResolved?.(entries.document);
1998
2010
  middlewarePath = options.middleware ? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware')) : null;
1999
2011
  // Server-mode only, like `entryServer`/`external` (a documented
2000
2012
  // no-op in client mode so configs survive the `ssr` boolean flip).
@@ -2988,6 +3000,11 @@ const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
2988
3000
  * solid-refresh#85 — is no longer used at all).
2989
3001
  */
2990
3002
  const REFRESH_RUNTIME_SOURCE = 'solid-js/refresh';
3003
+
3004
+ // Appended to the document shell's client compile instead of a refresh
3005
+ // boundary (see documentModuleId in solidPlugin): self-accept, then
3006
+ // invalidate — Vite's spelling for "this module cannot hot-update, reload".
3007
+ const DOCUMENT_HMR_DECLINE = '\nif (import.meta.hot) {\n import.meta.hot.accept(() => import.meta.hot.invalidate());\n}\n';
2991
3008
  const DEFAULT_STYLE_EXCLUDE = /node_modules/;
2992
3009
  const VIRTUAL_MANIFEST_ID = 'virtual:solid-manifest';
2993
3010
  const RESOLVED_VIRTUAL_MANIFEST_ID = '\0' + VIRTUAL_MANIFEST_ID;
@@ -3279,6 +3296,15 @@ function solidPlugin(options = {}) {
3279
3296
  const externalDevServer = !!options.ssr && !!startOptions?.external;
3280
3297
  let needHmr = false;
3281
3298
  let replaceDev = false;
3299
+ // Resolved absolute path of the start-mode document shell (normalized to
3300
+ // forward slashes, matching Vite ids), reported back by the start plugin's
3301
+ // config hook. The document is the one module whose client compile must
3302
+ // decline HMR instead of taking a refresh boundary: it hydrates the whole
3303
+ // `document`, and no component swap can re-claim `document.documentElement`
3304
+ // — an accepted update would be absorbed with nothing visibly changing
3305
+ // (solidjs/solid#3151). Declining makes a save invalidate the module, so
3306
+ // Vite falls back to a full page reload: the honest cost.
3307
+ let documentModuleId = null;
3282
3308
  // The live dev server, kept so the dev manifest module can bake the bridge
3283
3309
  // endpoint URL in when its code is generated (see devManifestBridgeUrl).
3284
3310
  let devServer = null;
@@ -3593,7 +3619,8 @@ function solidPlugin(options = {}) {
3593
3619
  };
3594
3620
  },
3595
3621
  hotUpdate({
3596
- modules
3622
+ modules,
3623
+ file
3597
3624
  }) {
3598
3625
  // solid-refresh only injects HMR boundaries into client modules, so
3599
3626
  // non-client environments have no accept handlers. Without this, Vite
@@ -3613,6 +3640,19 @@ function solidPlugin(options = {}) {
3613
3640
  this.environment.hot.send({
3614
3641
  type: 'full-reload'
3615
3642
  });
3643
+ // Server-only modules are the exception to the suppression: a file
3644
+ // with no modules in the client graph has no browser HMR path at
3645
+ // all — nothing client-side accepts it, so staying silent leaves
3646
+ // the browser rendering stale server output until a manual refresh
3647
+ // (e.g. the document shell, which only the server ever imports;
3648
+ // solidjs/solid#3151). Reload the page: the honest cost, and there
3649
+ // is no client update to race with by construction.
3650
+ const clientEnv = devServer?.environments.client;
3651
+ if (clientEnv && !clientEnv.moduleGraph.getModulesByFile(file)?.size) {
3652
+ clientEnv.hot.send({
3653
+ type: 'full-reload'
3654
+ });
3655
+ }
3616
3656
  }
3617
3657
  return [];
3618
3658
  }
@@ -3716,7 +3756,12 @@ function solidPlugin(options = {}) {
3716
3756
  if (shouldBeProcessedWithTypescript) {
3717
3757
  plugins.push('typescript');
3718
3758
  }
3719
- const needRefresh = needHmr && !isSsr && !inNodeModules;
3759
+
3760
+ // See the documentModuleId declaration: the document shell declines HMR
3761
+ // (no refresh boundary, explicit self-invalidation) so edits full-reload.
3762
+ const isDocumentShell = documentModuleId !== null && id === documentModuleId;
3763
+ const needRefresh = needHmr && !isSsr && !inNodeModules && !isDocumentShell;
3764
+ const declineHmr = isDocumentShell && needHmr && !isSsr;
3720
3765
  const babelUserOptions = await getBabelUserOptions(options, source, id, !!isSsr);
3721
3766
 
3722
3767
  // The native compiler picks its parser dialect from the file
@@ -3788,7 +3833,7 @@ function solidPlugin(options = {}) {
3788
3833
  maps.push(result.map);
3789
3834
  const finalCode = injectSsrModuleId(await resolveLazyModuleUrls(this, result.code || '', id), moduleId, !!isSsr);
3790
3835
  return {
3791
- code: finalCode,
3836
+ code: declineHmr ? finalCode + DOCUMENT_HMR_DECLINE : finalCode,
3792
3837
  map: combineSourcemaps(maps)
3793
3838
  };
3794
3839
  }
@@ -3809,7 +3854,7 @@ function solidPlugin(options = {}) {
3809
3854
  maps.push(result.map);
3810
3855
  const finalCode = injectSsrModuleId(await resolveLazyModuleUrls(this, result.code || '', id), moduleId, !!isSsr);
3811
3856
  return {
3812
- code: finalCode,
3857
+ code: declineHmr ? finalCode + DOCUMENT_HMR_DECLINE : finalCode,
3813
3858
  map: combineSourcemaps(maps)
3814
3859
  };
3815
3860
  }
@@ -3843,7 +3888,11 @@ function solidPlugin(options = {}) {
3843
3888
  serverComponents,
3844
3889
  ssr: !!options.ssr,
3845
3890
  styleFilter: filterDevStyles,
3846
- diagnostics: !!options.diagnostics
3891
+ diagnostics: !!options.diagnostics,
3892
+ onDocumentResolved(documentPath) {
3893
+ // Normalize to forward slashes to match Vite's transform ids.
3894
+ documentModuleId = documentPath ? documentPath.split(path.sep).join('/') : null;
3895
+ }
3847
3896
  }));
3848
3897
  }
3849
3898