@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.
@@ -63,7 +63,16 @@ function webRequestFromNode(req, urlPath, res) {
63
63
  signal = controller.signal;
64
64
  }
65
65
  const method = req.method || 'GET';
66
- const body = method === 'GET' || method === 'HEAD' ? undefined : Readable.toWeb(req);
66
+ // Only attach a body when the request actually carries one. A web Request
67
+ // built by the browser for a bodyless POST has `body === null`, and the
68
+ // runtime keys off that (a present body that decodes to nothing is a 400
69
+ // since @solidjs/web 2.0.0-rc.5) — so an unconditionally attached (empty)
70
+ // stream misparses bodyless calls. HTTP/1 signals a body via
71
+ // Content-Length/Transfer-Encoding (RFC 9112 §6); the h2 compat API sets
72
+ // `stream.endAfterHeaders` when END_STREAM rode the headers frame.
73
+ const h2Stream = req.stream;
74
+ const hasBody = method !== 'GET' && method !== 'HEAD' && (h2Stream ? !h2Stream.endAfterHeaders : req.headers['transfer-encoding'] !== undefined || req.headers['content-length'] !== undefined && req.headers['content-length'] !== '0');
75
+ const body = hasBody ? Readable.toWeb(req) : undefined;
67
76
  return new Request(url, {
68
77
  method,
69
78
  headers,
@@ -1167,9 +1176,11 @@ function serverFunctions(options = {}, internal = {}) {
1167
1176
  `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');
1168
1177
  }
1169
1178
 
1170
- // Function IDs are `xxHash32(root-relative path)-<count>` (see compile.ts),
1171
- // so the hash segment maps an incoming ID back to its module. Rebuilt
1172
- // whenever a transform has grown the manifest.
1179
+ // Function IDs are `<name>-<xxHash32(root-relative path)>[-<ordinal>]`
1180
+ // (identity-keyed, solidjs/solid#3109). The name is a JS identifier and
1181
+ // never contains `-`, so the hash is always the second segment and maps
1182
+ // an incoming ID back to its module. Rebuilt whenever a transform has
1183
+ // grown the manifest.
1173
1184
  const hashIndex = new Map();
1174
1185
  let hashIndexSize = -1;
1175
1186
  function moduleForFunctionId(functionId) {
@@ -1181,7 +1192,7 @@ function serverFunctions(options = {}, internal = {}) {
1181
1192
  }
1182
1193
  hashIndexSize = manifest.server.size;
1183
1194
  }
1184
- return hashIndex.get(functionId.split('-', 1)[0]);
1195
+ return hashIndex.get(functionId.split('-')[1]);
1185
1196
  }
1186
1197
  function moduleDevUrl(entry) {
1187
1198
  const relative = path.relative(root, entry).split(path.sep).join('/');
@@ -1219,10 +1230,11 @@ function serverFunctions(options = {}, internal = {}) {
1219
1230
  if (internal.externalDevServer || !isRunnableEnvironment(ssrEnvironment)) {
1220
1231
  return;
1221
1232
  }
1222
- // A call's address is `<endpoint>/<id>` (solidjs/solid#3076)the
1223
- // mount plus exactly one path segment. Bare-mount requests still
1224
- // reach the runtime handler (it answers 404), so misdirected posts
1225
- // fail through the endpoint rather than falling through to SSR.
1233
+ // A call's address is `<endpoint>/<id>` plain HTTP or
1234
+ // `<endpoint>/data/<id>` the scripted transport's own path
1235
+ // (solidjs/solid#3076, #3094). Bare-mount requests still reach the
1236
+ // runtime handler (it answers 404), so misdirected posts fail
1237
+ // through the endpoint rather than falling through to SSR.
1226
1238
  const underMount = (pathname, mount) => pathname === mount || pathname.startsWith(mount + '/');
1227
1239
  server.middlewares.use((req, res, next) => {
1228
1240
  const url = new URL(req.url || '/', 'http://localhost');
@@ -1241,9 +1253,15 @@ function serverFunctions(options = {}, internal = {}) {
1241
1253
  // Make sure the referenced module has been evaluated in the SSR
1242
1254
  // environment so its registration exists — functions only client
1243
1255
  // code references are never loaded by the SSR render itself.
1244
- // The id lives in the path segment after the mount.
1256
+ // The id lives in the path segment after the mount — behind a
1257
+ // literal `data` segment on the scripted transport's address
1258
+ // (solidjs/solid#3094). Segment count keeps the two apart: an id
1259
+ // occupies exactly one segment, so `data/<id>` is only ever a
1260
+ // data address, and a function id spelled `data` still parses at
1261
+ // the bare one.
1245
1262
  const mount = basePrefixed ? resolvedEndpoint : endpoint;
1246
- const segment = url.pathname.slice(mount.length + 1);
1263
+ let segment = url.pathname.slice(mount.length + 1);
1264
+ if (segment.startsWith('data/')) segment = segment.slice(5);
1247
1265
  let functionId = null;
1248
1266
  if (segment && !segment.includes('/')) {
1249
1267
  try {
@@ -1252,14 +1270,6 @@ function serverFunctions(options = {}, internal = {}) {
1252
1270
  // not an address; the runtime handler answers the 404
1253
1271
  }
1254
1272
  }
1255
- if (!functionId) {
1256
- // TRANSITIONAL (remove before 3.0 stable): the retired header
1257
- // and `?id=` addressing, kept only for the RC window where this
1258
- // plugin meets a @solidjs/web older than the path-addressing
1259
- // change (solidjs/solid#3076).
1260
- const headerId = req.headers['x-server-function-id'];
1261
- functionId = (typeof headerId === 'string' ? headerId.split('#')[0] : undefined) || url.searchParams.get('id');
1262
- }
1263
1273
  if (functionId) {
1264
1274
  const entry = moduleForFunctionId(functionId);
1265
1275
  if (entry) await ssrEnvironment.runner.import(moduleDevUrl(entry));
@@ -1898,7 +1908,8 @@ function startServe(options, internal = {}) {
1898
1908
  lines.push(``, `async function dispatchRequest(request, event, options) {`);
1899
1909
  if (composeServerFunctions) {
1900
1910
  lines.push(
1901
- // A call's address is `<endpoint>/<id>` (solidjs/solid#3076); the
1911
+ // A call's address is `<endpoint>/<id>` or `<endpoint>/data/<id>`
1912
+ // (solidjs/solid#3076, #3094); the prefix gate covers both, and the
1902
1913
  // bare mount still routes so a misaddressed request 404s through the
1903
1914
  // runtime handler instead of rendering a page at it.
1904
1915
  ` const requestPath = new URL(request.url).pathname;`, ` if (requestPath === endpoint || requestPath.startsWith(endpoint + '/')) {`,
@@ -1971,6 +1982,7 @@ function startServe(options, internal = {}) {
1971
1982
  devtoolsResolutions = {};
1972
1983
  devtoolsIds = {};
1973
1984
  entries = resolveEntries(root, options, clientMode);
1985
+ internal.onDocumentResolved?.(entries.document);
1974
1986
  middlewarePath = options.middleware ? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware')) : null;
1975
1987
  // Server-mode only, like `entryServer`/`external` (a documented
1976
1988
  // no-op in client mode so configs survive the `ssr` boolean flip).
@@ -2964,6 +2976,11 @@ const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
2964
2976
  * solid-refresh#85 — is no longer used at all).
2965
2977
  */
2966
2978
  const REFRESH_RUNTIME_SOURCE = 'solid-js/refresh';
2979
+
2980
+ // Appended to the document shell's client compile instead of a refresh
2981
+ // boundary (see documentModuleId in solidPlugin): self-accept, then
2982
+ // invalidate — Vite's spelling for "this module cannot hot-update, reload".
2983
+ const DOCUMENT_HMR_DECLINE = '\nif (import.meta.hot) {\n import.meta.hot.accept(() => import.meta.hot.invalidate());\n}\n';
2967
2984
  const DEFAULT_STYLE_EXCLUDE = /node_modules/;
2968
2985
  const VIRTUAL_MANIFEST_ID = 'virtual:solid-manifest';
2969
2986
  const RESOLVED_VIRTUAL_MANIFEST_ID = '\0' + VIRTUAL_MANIFEST_ID;
@@ -3255,6 +3272,15 @@ function solidPlugin(options = {}) {
3255
3272
  const externalDevServer = !!options.ssr && !!startOptions?.external;
3256
3273
  let needHmr = false;
3257
3274
  let replaceDev = false;
3275
+ // Resolved absolute path of the start-mode document shell (normalized to
3276
+ // forward slashes, matching Vite ids), reported back by the start plugin's
3277
+ // config hook. The document is the one module whose client compile must
3278
+ // decline HMR instead of taking a refresh boundary: it hydrates the whole
3279
+ // `document`, and no component swap can re-claim `document.documentElement`
3280
+ // — an accepted update would be absorbed with nothing visibly changing
3281
+ // (solidjs/solid#3151). Declining makes a save invalidate the module, so
3282
+ // Vite falls back to a full page reload: the honest cost.
3283
+ let documentModuleId = null;
3258
3284
  // The live dev server, kept so the dev manifest module can bake the bridge
3259
3285
  // endpoint URL in when its code is generated (see devManifestBridgeUrl).
3260
3286
  let devServer = null;
@@ -3569,7 +3595,8 @@ function solidPlugin(options = {}) {
3569
3595
  };
3570
3596
  },
3571
3597
  hotUpdate({
3572
- modules
3598
+ modules,
3599
+ file
3573
3600
  }) {
3574
3601
  // solid-refresh only injects HMR boundaries into client modules, so
3575
3602
  // non-client environments have no accept handlers. Without this, Vite
@@ -3589,6 +3616,19 @@ function solidPlugin(options = {}) {
3589
3616
  this.environment.hot.send({
3590
3617
  type: 'full-reload'
3591
3618
  });
3619
+ // Server-only modules are the exception to the suppression: a file
3620
+ // with no modules in the client graph has no browser HMR path at
3621
+ // all — nothing client-side accepts it, so staying silent leaves
3622
+ // the browser rendering stale server output until a manual refresh
3623
+ // (e.g. the document shell, which only the server ever imports;
3624
+ // solidjs/solid#3151). Reload the page: the honest cost, and there
3625
+ // is no client update to race with by construction.
3626
+ const clientEnv = devServer?.environments.client;
3627
+ if (clientEnv && !clientEnv.moduleGraph.getModulesByFile(file)?.size) {
3628
+ clientEnv.hot.send({
3629
+ type: 'full-reload'
3630
+ });
3631
+ }
3592
3632
  }
3593
3633
  return [];
3594
3634
  }
@@ -3692,7 +3732,12 @@ function solidPlugin(options = {}) {
3692
3732
  if (shouldBeProcessedWithTypescript) {
3693
3733
  plugins.push('typescript');
3694
3734
  }
3695
- const needRefresh = needHmr && !isSsr && !inNodeModules;
3735
+
3736
+ // See the documentModuleId declaration: the document shell declines HMR
3737
+ // (no refresh boundary, explicit self-invalidation) so edits full-reload.
3738
+ const isDocumentShell = documentModuleId !== null && id === documentModuleId;
3739
+ const needRefresh = needHmr && !isSsr && !inNodeModules && !isDocumentShell;
3740
+ const declineHmr = isDocumentShell && needHmr && !isSsr;
3696
3741
  const babelUserOptions = await getBabelUserOptions(options, source, id, !!isSsr);
3697
3742
 
3698
3743
  // The native compiler picks its parser dialect from the file
@@ -3764,7 +3809,7 @@ function solidPlugin(options = {}) {
3764
3809
  maps.push(result.map);
3765
3810
  const finalCode = injectSsrModuleId(await resolveLazyModuleUrls(this, result.code || '', id), moduleId, !!isSsr);
3766
3811
  return {
3767
- code: finalCode,
3812
+ code: declineHmr ? finalCode + DOCUMENT_HMR_DECLINE : finalCode,
3768
3813
  map: combineSourcemaps(maps)
3769
3814
  };
3770
3815
  }
@@ -3785,7 +3830,7 @@ function solidPlugin(options = {}) {
3785
3830
  maps.push(result.map);
3786
3831
  const finalCode = injectSsrModuleId(await resolveLazyModuleUrls(this, result.code || '', id), moduleId, !!isSsr);
3787
3832
  return {
3788
- code: finalCode,
3833
+ code: declineHmr ? finalCode + DOCUMENT_HMR_DECLINE : finalCode,
3789
3834
  map: combineSourcemaps(maps)
3790
3835
  };
3791
3836
  }
@@ -3819,7 +3864,11 @@ function solidPlugin(options = {}) {
3819
3864
  serverComponents,
3820
3865
  ssr: !!options.ssr,
3821
3866
  styleFilter: filterDevStyles,
3822
- diagnostics: !!options.diagnostics
3867
+ diagnostics: !!options.diagnostics,
3868
+ onDocumentResolved(documentPath) {
3869
+ // Normalize to forward slashes to match Vite's transform ids.
3870
+ documentModuleId = documentPath ? documentPath.split(path.sep).join('/') : null;
3871
+ }
3823
3872
  }));
3824
3873
  }
3825
3874