@solidjs/start 2.0.0-beta.9 → 2.0.0-rc.2

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.
Files changed (102) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/config/env.js +11 -8
  3. package/dist/config/fs-routes/tree-shake.js +175 -32
  4. package/dist/config/index.js +7 -2
  5. package/dist/fns/client.js +13 -3
  6. package/dist/fns/handler.js +20 -7
  7. package/dist/fns/serialization.d.ts +15 -0
  8. package/dist/fns/serialization.js +1 -1
  9. package/dist/middleware/index.js +4 -1
  10. package/dist/server/StartServer.jsx +1 -1
  11. package/dist/server/fetchEvent.d.ts +10 -0
  12. package/dist/server/fetchEvent.js +10 -0
  13. package/dist/server/handler.d.ts +2 -2
  14. package/dist/server/handler.js +12 -14
  15. package/dist/server/index.d.ts +1 -0
  16. package/dist/server/index.jsx +1 -0
  17. package/dist/server/spa/StartServer.jsx +1 -1
  18. package/dist/server/spa/handler.d.ts +2 -2
  19. package/dist/server/spa/handler.js +2 -2
  20. package/dist/server/strip-path-base.d.ts +1 -0
  21. package/dist/server/strip-path-base.js +10 -0
  22. package/dist/server/web-stream.d.ts +9 -0
  23. package/dist/server/web-stream.js +26 -0
  24. package/dist/shared/ErrorBoundary.jsx +3 -3
  25. package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/CodeView.jsx +3 -3
  26. package/dist/shared/dev-toolbar/error-viewer/create-stack-frame.js +90 -0
  27. package/dist/shared/{dev-overlay/DevOverlayDialog.d.ts → dev-toolbar/error-viewer/index.d.ts} +3 -2
  28. package/dist/shared/{dev-overlay/DevOverlayDialog.jsx → dev-toolbar/error-viewer/index.jsx} +66 -77
  29. package/dist/shared/dev-toolbar/error-viewer/styles.css +224 -0
  30. package/dist/shared/dev-toolbar/functions/BlobViewer.css +4 -0
  31. package/dist/shared/dev-toolbar/functions/BlobViewer.d.ts +6 -0
  32. package/dist/shared/dev-toolbar/functions/BlobViewer.jsx +41 -0
  33. package/dist/shared/dev-toolbar/functions/FormDataViewer.d.ts +5 -0
  34. package/dist/shared/dev-toolbar/functions/FormDataViewer.jsx +27 -0
  35. package/dist/shared/dev-toolbar/functions/HeadersViewer.css +8 -0
  36. package/dist/shared/dev-toolbar/functions/HeadersViewer.d.ts +6 -0
  37. package/dist/shared/dev-toolbar/functions/HeadersViewer.jsx +15 -0
  38. package/dist/shared/dev-toolbar/functions/HexViewer.css +34 -0
  39. package/dist/shared/dev-toolbar/functions/HexViewer.d.ts +11 -0
  40. package/dist/shared/dev-toolbar/functions/HexViewer.jsx +78 -0
  41. package/dist/shared/dev-toolbar/functions/SerovalValue.css +7 -0
  42. package/dist/shared/dev-toolbar/functions/SerovalValue.d.ts +7 -0
  43. package/dist/shared/dev-toolbar/functions/SerovalValue.jsx +10 -0
  44. package/dist/shared/dev-toolbar/functions/SerovalViewer.css +71 -0
  45. package/dist/shared/dev-toolbar/functions/SerovalViewer.d.ts +6 -0
  46. package/dist/shared/dev-toolbar/functions/SerovalViewer.jsx +796 -0
  47. package/dist/shared/dev-toolbar/functions/URLSearchParamsViewer.d.ts +5 -0
  48. package/dist/shared/dev-toolbar/functions/URLSearchParamsViewer.jsx +24 -0
  49. package/dist/shared/dev-toolbar/functions/index.d.ts +13 -0
  50. package/dist/shared/dev-toolbar/functions/index.jsx +216 -0
  51. package/dist/shared/dev-toolbar/functions/styles.css +110 -0
  52. package/dist/shared/dev-toolbar/functions/tracker.d.ts +19 -0
  53. package/dist/shared/dev-toolbar/functions/tracker.js +29 -0
  54. package/dist/shared/{dev-overlay → dev-toolbar}/icons.d.ts +9 -0
  55. package/dist/shared/{dev-overlay → dev-toolbar}/icons.jsx +19 -1
  56. package/dist/shared/dev-toolbar/index.css +88 -0
  57. package/dist/shared/dev-toolbar/index.d.ts +6 -0
  58. package/dist/shared/dev-toolbar/index.jsx +175 -0
  59. package/dist/shared/ui/Badge.css +34 -0
  60. package/dist/shared/ui/Badge.d.ts +7 -0
  61. package/dist/shared/ui/Badge.jsx +7 -0
  62. package/dist/shared/ui/Button.css +32 -0
  63. package/dist/shared/ui/Button.d.ts +4 -0
  64. package/dist/shared/ui/Button.jsx +4 -0
  65. package/dist/shared/ui/Cascade.css +29 -0
  66. package/dist/shared/ui/Cascade.d.ts +4 -0
  67. package/dist/shared/ui/Cascade.jsx +4 -0
  68. package/dist/shared/ui/Dialog.css +26 -0
  69. package/dist/shared/ui/Dialog.d.ts +5 -0
  70. package/dist/shared/ui/Dialog.jsx +5 -0
  71. package/dist/shared/ui/IconButton.css +39 -0
  72. package/dist/shared/ui/IconButton.d.ts +4 -0
  73. package/dist/shared/ui/IconButton.jsx +4 -0
  74. package/dist/shared/ui/Placeholder.css +12 -0
  75. package/dist/shared/ui/Placeholder.d.ts +6 -0
  76. package/dist/shared/ui/Placeholder.jsx +6 -0
  77. package/dist/shared/ui/Section.css +19 -0
  78. package/dist/shared/ui/Section.d.ts +9 -0
  79. package/dist/shared/ui/Section.jsx +12 -0
  80. package/dist/shared/ui/Select.css +37 -0
  81. package/dist/shared/ui/Select.d.ts +4 -0
  82. package/dist/shared/ui/Select.jsx +4 -0
  83. package/dist/shared/ui/Table.css +22 -0
  84. package/dist/shared/ui/Table.d.ts +11 -0
  85. package/dist/shared/ui/Table.jsx +13 -0
  86. package/dist/shared/ui/Tabs.css +51 -0
  87. package/dist/shared/ui/Tabs.d.ts +6 -0
  88. package/dist/shared/ui/Tabs.jsx +6 -0
  89. package/dist/shared/ui/Text.css +69 -0
  90. package/dist/shared/ui/Text.d.ts +12 -0
  91. package/dist/shared/ui/Text.jsx +17 -0
  92. package/package.json +3 -3
  93. package/dist/shared/dev-overlay/createStackFrame.js +0 -56
  94. package/dist/shared/dev-overlay/index.d.ts +0 -5
  95. package/dist/shared/dev-overlay/index.jsx +0 -39
  96. package/dist/shared/dev-overlay/styles.css +0 -292
  97. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/CodeView.d.ts +0 -0
  98. /package/dist/shared/{dev-overlay/createStackFrame.d.ts → dev-toolbar/error-viewer/create-stack-frame.d.ts} +0 -0
  99. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/download.d.ts +0 -0
  100. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/download.js +0 -0
  101. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/get-source-map.d.ts +0 -0
  102. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/get-source-map.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @solidjs/start
2
2
 
3
+ ## 2.0.0-rc.2
4
+
5
+ ### Minor Changes
6
+
7
+ - eeff49a: add new dev toolbar
8
+
9
+ ### Patch Changes
10
+
11
+ - bd8cb01: Update srvx to version 0.12.0.
12
+ - 4e98ac0: Restore the optional `routerLoad` third argument to `createHandler`, which primes custom routers (e.g. TanStack Router) on the server before SSR rendering. It was accidentally dropped in the v2 rewrite.
13
+
14
+ ## 2.0.0-rc.1
15
+
16
+ ### Minor Changes
17
+
18
+ - 02e561e: Expose experimental `decorateHandler` and `decorateMiddleware` helpers from
19
+ `@solidjs/start/server` for providing Solid's request context to custom h3 handlers and
20
+ middleware.
21
+
22
+ ### Patch Changes
23
+
24
+ - f66b5a9: fix: keep non-picked route exports that picked exports reference instead of deleting their bindings, so API handlers can call helpers exported from the same file (#2100); also fixes picked exports declared via `export { ... }` specifiers being dropped (#1659)
25
+ - 1763296: fix: run `onBeforeResponse` middleware in declared order (#2131)
26
+
27
+ Applications that reversed their `onBeforeResponse` arrays as a workaround should restore the intended declaration order.
28
+
29
+ - cd98e7d: Fix API route matching when `server.baseURL` is configured.
30
+
31
+ ## 2.0.0-beta.10
32
+
33
+ ### Patch Changes
34
+
35
+ - 8af6304: Support for Vite 8.1 Experimental Bundled Dev Mode
36
+ - 9d91484: fix: don't crash when a server function throws an error whose message contains non-latin1 characters.
37
+ - 068b64c: Return a cancellation-safe web `ReadableStream` for streaming SSR in development. Returning Solid's
38
+ raw stream only rendered on Node; Bun and Deno coerced it to `[object Object]`.
39
+
3
40
  ## 2.0.0-beta.9
4
41
 
5
42
  ### Minor Changes
@@ -1,4 +1,5 @@
1
1
  import { loadEnv } from "vite";
2
+ import { parseIdQuery } from "./utils.js";
2
3
  const LOADERS = {
3
4
  node: `export default key => process.env[key];`,
4
5
  "cloudflare-workers": `import { env } from 'cloudflare:workers';export default key => env[key];`,
@@ -39,17 +40,19 @@ export function envPlugin(options) {
39
40
  env = config.mode !== "production" ? "development" : "production";
40
41
  },
41
42
  resolveId(id) {
42
- if (id === SERVER_ENV ||
43
- id === CLIENT_ENV ||
44
- id === SERVER_RUNTIME_ENV ||
45
- id === SERVER_RUNTIME_LOADER) {
43
+ const { filename } = parseIdQuery(id);
44
+ if (filename === SERVER_ENV ||
45
+ filename === CLIENT_ENV ||
46
+ filename === SERVER_RUNTIME_ENV ||
47
+ filename === SERVER_RUNTIME_LOADER) {
46
48
  return id;
47
49
  }
48
50
  return undefined;
49
51
  },
50
52
  load(id) {
53
+ const { filename } = parseIdQuery(id);
51
54
  const isServer = this.environment.config.consumer === "server";
52
- if (id === SERVER_ENV) {
55
+ if (filename === SERVER_ENV) {
53
56
  if (!isServer) {
54
57
  return SERVER_ONLY_MODULE;
55
58
  }
@@ -58,19 +61,19 @@ export function envPlugin(options) {
58
61
  : loadEnv(env, '.', serverPrefix);
59
62
  return convertObjectToModule(vars);
60
63
  }
61
- if (id === CLIENT_ENV) {
64
+ if (filename === CLIENT_ENV) {
62
65
  const vars = currentOptions.client?.load
63
66
  ? currentOptions.client.load(env)
64
67
  : loadEnv(env, '.', clientPrefix);
65
68
  return convertObjectToModule(vars);
66
69
  }
67
- if (id === SERVER_RUNTIME_LOADER) {
70
+ if (filename === SERVER_RUNTIME_LOADER) {
68
71
  if (!isServer) {
69
72
  return SERVER_ONLY_MODULE;
70
73
  }
71
74
  return runtimeCode;
72
75
  }
73
- if (id === SERVER_RUNTIME_ENV) {
76
+ if (filename === SERVER_RUNTIME_ENV) {
74
77
  if (!isServer) {
75
78
  return SERVER_ONLY_MODULE;
76
79
  }
@@ -20,13 +20,28 @@ function treeShakeTransform({ types: t }) {
20
20
  }
21
21
  return path.node.id && path.node.id.type === "Identifier" ? path.get("id") : null;
22
22
  }
23
- function isIdentifierReferenced(ident) {
23
+ function isTypeOnlyReference(path) {
24
+ return path.getAncestry().some(parent => {
25
+ if (parent.isTSType())
26
+ return true;
27
+ if (parent.isExportNamedDeclaration())
28
+ return parent.node.exportKind === "type";
29
+ if (parent.isExportSpecifier())
30
+ return parent.node.exportKind === "type";
31
+ return false;
32
+ });
33
+ }
34
+ function runtimeReferences(binding) {
35
+ return binding.referencePaths.filter(ref => !isTypeOnlyReference(ref));
36
+ }
37
+ function isIdentifierReferenced(ident, includeTypes = false) {
24
38
  const b = ident.scope.getBinding(ident.node.name);
25
- if (b?.referenced) {
39
+ if (b) {
40
+ const references = b.constantViolations.concat(includeTypes ? b.referencePaths : runtimeReferences(b));
41
+ if (!references.length)
42
+ return false;
26
43
  if (b.path.type === "FunctionDeclaration") {
27
- return !b.constantViolations
28
- .concat(b.referencePaths)
29
- .every(ref => ref.findParent(p => p === b.path));
44
+ return !references.every(ref => ref.findParent(p => p === b.path));
30
45
  }
31
46
  return true;
32
47
  }
@@ -34,13 +49,13 @@ function treeShakeTransform({ types: t }) {
34
49
  }
35
50
  function markFunction(path, state) {
36
51
  const ident = getIdentifier(path);
37
- if (ident && ident.node && isIdentifierReferenced(ident)) {
52
+ if (ident && ident.node && isIdentifierReferenced(ident, true)) {
38
53
  state.refs.add(ident);
39
54
  }
40
55
  }
41
56
  function markImport(path, state) {
42
57
  const local = path.get("local");
43
- if (isIdentifierReferenced(local)) {
58
+ if (isIdentifierReferenced(local, true)) {
44
59
  state.refs.add(local);
45
60
  }
46
61
  }
@@ -49,12 +64,13 @@ function treeShakeTransform({ types: t }) {
49
64
  Program: {
50
65
  enter(path, state) {
51
66
  state.refs = new Set();
67
+ state.candidates = new Set();
52
68
  state.done = false;
53
69
  path.traverse({
54
70
  VariableDeclarator(variablePath, variableState) {
55
71
  if (variablePath.node.id.type === "Identifier") {
56
72
  const local = variablePath.get("id");
57
- if (isIdentifierReferenced(local)) {
73
+ if (isIdentifierReferenced(local, true)) {
58
74
  variableState.refs.add(local);
59
75
  }
60
76
  }
@@ -69,7 +85,7 @@ function treeShakeTransform({ types: t }) {
69
85
  : (() => {
70
86
  throw new Error("invariant");
71
87
  })());
72
- if (isIdentifierReferenced(local)) {
88
+ if (isIdentifierReferenced(local, true)) {
73
89
  variableState.refs.add(local);
74
90
  }
75
91
  });
@@ -88,29 +104,41 @@ function treeShakeTransform({ types: t }) {
88
104
  else {
89
105
  return;
90
106
  }
91
- if (isIdentifierReferenced(local)) {
107
+ if (isIdentifierReferenced(local, true)) {
92
108
  variableState.refs.add(local);
93
109
  }
94
110
  });
95
111
  }
96
112
  },
97
113
  ExportDefaultDeclaration(exportNamedPath) {
98
- // if opts.keep is true, we don't remove the routeData export
99
114
  if (state.opts.pick && !state.opts.pick.includes("default")) {
100
- exportNamedPath.remove();
115
+ const decl = exportNamedPath.node.declaration;
116
+ // A named function/class declaration creates a module-scope
117
+ // binding that picked exports may reference, so only strip
118
+ // the `export default`; the sweep below removes the
119
+ // declaration again if nothing references it.
120
+ if ((t.isFunctionDeclaration(decl) || t.isClassDeclaration(decl)) && decl.id) {
121
+ const [newPath] = exportNamedPath.replaceWith(decl);
122
+ const id = newPath.get("id");
123
+ state.refs.add(id);
124
+ state.candidates.add(id);
125
+ }
126
+ else {
127
+ exportNamedPath.remove();
128
+ }
101
129
  }
102
130
  },
103
131
  ExportNamedDeclaration(exportNamedPath) {
104
- // if opts.keep is false, we don't remove the routeData export
132
+ // No pick list means nothing gets pruned.
105
133
  if (!state.opts.pick) {
106
134
  return;
107
135
  }
108
136
  const specifiers = exportNamedPath.get("specifiers");
109
137
  if (specifiers.length) {
110
138
  specifiers.forEach(s => {
111
- if (t.isIdentifier(s.node.exported)
112
- ? s.node.exported.name
113
- : state.opts.pick.includes(s.node.exported.value)) {
139
+ const exported = s.node.exported;
140
+ const exportedName = t.isIdentifier(exported) ? exported.name : exported.value;
141
+ if (!state.opts.pick.includes(exportedName)) {
114
142
  s.remove();
115
143
  }
116
144
  });
@@ -124,24 +152,51 @@ function treeShakeTransform({ types: t }) {
124
152
  return;
125
153
  }
126
154
  switch (decl.node.type) {
127
- case "FunctionDeclaration": {
155
+ case "FunctionDeclaration":
156
+ case "ClassDeclaration": {
128
157
  const name = decl.node.id?.name;
129
158
  if (name && state.opts.pick && !state.opts.pick.includes(name)) {
130
- exportNamedPath.remove();
159
+ // Keep the declaration in module scope since picked
160
+ // exports may reference it; the sweep below removes it
161
+ // again if unreferenced.
162
+ const [newPath] = exportNamedPath.replaceWith(decl.node);
163
+ const id = newPath.get("id");
164
+ state.refs.add(id);
165
+ state.candidates.add(id);
131
166
  }
132
167
  break;
133
168
  }
134
169
  case "VariableDeclaration": {
135
- const inner = decl.get("declarations");
136
- inner.forEach(d => {
137
- if (d.node.id.type !== "Identifier") {
138
- return;
139
- }
140
- const name = d.node.id.name;
141
- if (state.opts.pick && !state.opts.pick.includes(name)) {
142
- d.remove();
143
- }
170
+ const declNode = decl.node;
171
+ // Destructuring declarators are conservatively treated as
172
+ // picked (matching the previous behavior of leaving them
173
+ // untouched).
174
+ const isPicked = (d) => d.id.type !== "Identifier" || state.opts.pick.includes(d.id.name);
175
+ if (declNode.declarations.every(isPicked)) {
176
+ break;
177
+ }
178
+ // Split into one declaration per declarator (preserving
179
+ // evaluation order) and export only the picked ones.
180
+ // Unpicked bindings are kept since picked exports may
181
+ // reference them; the sweep below removes them again if
182
+ // unreferenced.
183
+ const statements = declNode.declarations.map(d => {
184
+ const single = t.variableDeclaration(declNode.kind, [d]);
185
+ single.declare = declNode.declare;
186
+ return isPicked(d) ? t.exportNamedDeclaration(single, []) : single;
144
187
  });
188
+ const newPaths = exportNamedPath.replaceWithMultiple(statements);
189
+ for (const p of newPaths) {
190
+ if (!p.isVariableDeclaration())
191
+ continue;
192
+ for (const d of p.get("declarations")) {
193
+ if (d.node.id.type === "Identifier") {
194
+ const id = d.get("id");
195
+ state.refs.add(id);
196
+ state.candidates.add(id);
197
+ }
198
+ }
199
+ }
145
200
  break;
146
201
  }
147
202
  default: {
@@ -152,6 +207,8 @@ function treeShakeTransform({ types: t }) {
152
207
  FunctionDeclaration: markFunction,
153
208
  FunctionExpression: markFunction,
154
209
  ArrowFunctionExpression: markFunction,
210
+ ClassDeclaration: markFunction,
211
+ ClassExpression: markFunction,
155
212
  ImportSpecifier: markImport,
156
213
  ImportDefaultSpecifier: markImport,
157
214
  ImportNamespaceSpecifier: markImport,
@@ -163,11 +220,95 @@ function treeShakeTransform({ types: t }) {
163
220
  }
164
221
  },
165
222
  }, state);
223
+ // References alone cannot distinguish live bindings from dead cycles.
224
+ // Build a graph of localized exports and retain only those reachable
225
+ // from runtime code outside that graph.
226
+ path.scope.crawl();
227
+ const tracked = new Map();
228
+ const candidateOwners = new Map();
229
+ const addOwner = (owners, binding, owner) => {
230
+ const paths = owners.get(binding) ?? new Set();
231
+ paths.add(owner);
232
+ owners.set(binding, paths);
233
+ };
234
+ for (const identifier of state.refs) {
235
+ if (identifier.node?.type !== "Identifier" || !identifier.parentPath)
236
+ continue;
237
+ const binding = identifier.scope.getBinding(identifier.node.name);
238
+ if (binding)
239
+ addOwner(tracked, binding, identifier.parentPath);
240
+ }
241
+ for (const identifier of state.candidates) {
242
+ if (!identifier.parentPath)
243
+ continue;
244
+ const binding = identifier.scope.getBinding(identifier.node.name);
245
+ if (!binding)
246
+ continue;
247
+ addOwner(tracked, binding, identifier.parentPath);
248
+ addOwner(candidateOwners, binding, identifier.parentPath);
249
+ }
250
+ const dependencies = new Map();
251
+ const live = new Set();
252
+ for (const [binding, owners] of tracked) {
253
+ dependencies.set(binding, new Set());
254
+ if ([...owners].some(owner => owner.findParent(parent => parent.isExportNamedDeclaration() || parent.isExportDefaultDeclaration()))) {
255
+ live.add(binding);
256
+ }
257
+ }
258
+ const ownerBindings = new Map();
259
+ for (const [binding, owners] of tracked) {
260
+ for (const owner of owners) {
261
+ ownerBindings.set(owner, binding);
262
+ }
263
+ }
264
+ const owningBinding = (reference) => {
265
+ const owner = reference.find(parent => ownerBindings.has(parent));
266
+ return owner && ownerBindings.get(owner);
267
+ };
268
+ for (const target of tracked.keys()) {
269
+ const references = target.constantViolations.concat(runtimeReferences(target));
270
+ for (const reference of references) {
271
+ const source = owningBinding(reference);
272
+ if (source)
273
+ dependencies.get(source)?.add(target);
274
+ else
275
+ live.add(target);
276
+ }
277
+ }
278
+ const pending = [...live];
279
+ while (pending.length) {
280
+ const source = pending.pop();
281
+ for (const dependency of dependencies.get(source) ?? []) {
282
+ if (live.has(dependency))
283
+ continue;
284
+ live.add(dependency);
285
+ pending.push(dependency);
286
+ }
287
+ }
288
+ const deadCandidates = new Set([...candidateOwners.keys()].filter(candidate => !live.has(candidate)));
289
+ path.traverse({
290
+ VariableDeclarator(variablePath) {
291
+ if (variablePath.node.id.type !== "Identifier")
292
+ return;
293
+ const binding = variablePath.scope.getBinding(variablePath.node.id.name);
294
+ if (binding && deadCandidates.has(binding))
295
+ variablePath.remove();
296
+ },
297
+ });
298
+ for (const [candidate, owners] of candidateOwners) {
299
+ if (!deadCandidates.has(candidate))
300
+ continue;
301
+ for (const owner of owners) {
302
+ if (!owner.removed && !owner.isVariableDeclarator())
303
+ owner.remove();
304
+ }
305
+ }
166
306
  const refs = state.refs;
307
+ const refNodes = new Set([...refs].map(ref => ref.node));
167
308
  let count = 0;
168
309
  const sweepFunction = (sweepPath) => {
169
310
  const ident = getIdentifier(sweepPath);
170
- if (ident && ident.node && refs.has(ident) && !isIdentifierReferenced(ident)) {
311
+ if (ident && ident.node && refNodes.has(ident.node) && !isIdentifierReferenced(ident)) {
171
312
  ++count;
172
313
  if (t.isAssignmentExpression(sweepPath.parentPath) ||
173
314
  t.isVariableDeclarator(sweepPath.parentPath)) {
@@ -180,7 +321,7 @@ function treeShakeTransform({ types: t }) {
180
321
  };
181
322
  function sweepImport(sweepPath) {
182
323
  const local = sweepPath.get("local");
183
- if (refs.has(local) && !isIdentifierReferenced(local)) {
324
+ if (refNodes.has(local.node) && !isIdentifierReferenced(local)) {
184
325
  ++count;
185
326
  sweepPath.remove();
186
327
  if (sweepPath.parent.specifiers.length === 0) {
@@ -195,7 +336,7 @@ function treeShakeTransform({ types: t }) {
195
336
  VariableDeclarator(variablePath) {
196
337
  if (variablePath.node.id.type === "Identifier") {
197
338
  const local = variablePath.get("id");
198
- if (refs.has(local) && !isIdentifierReferenced(local)) {
339
+ if (refNodes.has(local.node) && !isIdentifierReferenced(local)) {
199
340
  ++count;
200
341
  variablePath.remove();
201
342
  }
@@ -212,7 +353,7 @@ function treeShakeTransform({ types: t }) {
212
353
  : (() => {
213
354
  throw new Error("invariant");
214
355
  })());
215
- if (refs.has(local) && !isIdentifierReferenced(local)) {
356
+ if (refNodes.has(local.node) && !isIdentifierReferenced(local)) {
216
357
  ++count;
217
358
  p.remove();
218
359
  }
@@ -236,7 +377,7 @@ function treeShakeTransform({ types: t }) {
236
377
  else {
237
378
  return;
238
379
  }
239
- if (refs.has(local) && !isIdentifierReferenced(local)) {
380
+ if (refNodes.has(local.node) && !isIdentifierReferenced(local)) {
240
381
  ++count;
241
382
  e.remove();
242
383
  }
@@ -249,6 +390,8 @@ function treeShakeTransform({ types: t }) {
249
390
  FunctionDeclaration: sweepFunction,
250
391
  FunctionExpression: sweepFunction,
251
392
  ArrowFunctionExpression: sweepFunction,
393
+ ClassDeclaration: sweepFunction,
394
+ ClassExpression: sweepFunction,
252
395
  ImportSpecifier: sweepImport,
253
396
  ImportDefaultSpecifier: sweepImport,
254
397
  ImportNamespaceSpecifier: sweepImport,
@@ -1,6 +1,6 @@
1
1
  import { defu } from "defu";
2
2
  import { globSync } from "node:fs";
3
- import { extname, isAbsolute, join } from "node:path";
3
+ import { basename, extname, isAbsolute, join } from "node:path";
4
4
  import solid from "vite-plugin-solid";
5
5
  import { serverFunctionsPlugin } from "../directives/index.js";
6
6
  import { boundaryModules } from "./boundary-modules.js";
@@ -61,8 +61,11 @@ export function solidStart(options) {
61
61
  },
62
62
  };
63
63
  },
64
- async config(_, env) {
64
+ async config(config, env) {
65
65
  const clientInput = [handlers.client];
66
+ const clientEntryUrl = env.command === "serve" && config.experimental?.bundledDev
67
+ ? `assets/${basename(handlers.client, entryExtension)}.js`
68
+ : handlers.client;
66
69
  if (env.command === "build") {
67
70
  const clientRouter = globalThis.ROUTERS.client;
68
71
  for (const route of await clientRouter.getRoutes()) {
@@ -149,7 +152,9 @@ export function solidStart(options) {
149
152
  // esbuild receives a valid JS string literal for the define value
150
153
  "import.meta.env.START_APP_ENTRY": JSON.stringify(appEntryPath),
151
154
  "import.meta.env.START_CLIENT_ENTRY": JSON.stringify(handlers.client),
155
+ "import.meta.env.START_CLIENT_ENTRY_URL": JSON.stringify(clientEntryUrl),
152
156
  "import.meta.env.START_DEV_OVERLAY": JSON.stringify(start.devOverlay),
157
+ "import.meta.env.SERVER_BASE_URL": JSON.stringify(config.server?.baseURL ?? ""),
153
158
  "import.meta.env.SEROVAL_MODE": JSON.stringify(start.serialization?.mode || "json"),
154
159
  },
155
160
  builder: {
@@ -1,10 +1,11 @@
1
+ import { pushRequest, pushResponse } from "../shared/dev-toolbar/functions/tracker.js";
1
2
  import {
2
3
  // serializeToJSONStream,
3
4
  serializeToJSONString, } from "./serialization.js";
4
- import { BODY_FORMAT_KEY, BodyFormat, extractBody, getHeadersAndBody, } from "./shared.js";
5
+ import { BODY_FORMAT_KEY, BodyFormat, extractBody, getHeadersAndBody } from "./shared.js";
5
6
  let INSTANCE = 0;
6
- function createRequest(base, id, instance, options) {
7
- return fetch(base, {
7
+ async function createRequest(base, id, instance, options) {
8
+ const request = new Request(base, {
8
9
  method: "POST",
9
10
  ...options,
10
11
  headers: {
@@ -13,6 +14,15 @@ function createRequest(base, id, instance, options) {
13
14
  "X-Server-Instance": instance,
14
15
  },
15
16
  });
17
+ if (import.meta.env.DEV) {
18
+ console.log(pushRequest);
19
+ pushRequest(id, instance, request.clone());
20
+ }
21
+ const response = await fetch(request);
22
+ if (import.meta.env.DEV) {
23
+ pushResponse(id, instance, response.clone());
24
+ }
25
+ return response;
16
26
  }
17
27
  async function initializeResponse(base, id, instance, options, args) {
18
28
  // No args, skip serialization
@@ -121,7 +121,6 @@ export async function handleServerFunction(h3Event) {
121
121
  h3Event.res.headers.set("content-type", "text/javascript");
122
122
  return serializeToJSStream(instance, result);
123
123
  }
124
- h3Event.res.headers.set("content-type", "text/plain");
125
124
  return serializeToJSONStream(result);
126
125
  }
127
126
  catch (x) {
@@ -133,7 +132,8 @@ export async function handleServerFunction(h3Event) {
133
132
  if (x.headers)
134
133
  mergeResponseHeaders(h3Event, x.headers);
135
134
  // forward non-redirect statuses
136
- if (x.status && (!instance || x.status < 300 || x.status >= 400))
135
+ if (x.status &&
136
+ (!instance || x.status < 300 || x.status >= 400))
137
137
  h3Event.res.status = x.status;
138
138
  if (x.customBody) {
139
139
  x = await x.customBody();
@@ -144,7 +144,7 @@ export async function handleServerFunction(h3Event) {
144
144
  }
145
145
  else if (instance) {
146
146
  const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
147
- h3Event.res.headers.set("X-Error", error.replace(/[\r\n]+/g, ""));
147
+ h3Event.res.headers.set("X-Error", toHeaderValue(error));
148
148
  }
149
149
  else {
150
150
  x = handleNoJS(x, request, parsed, true);
@@ -158,7 +158,7 @@ export async function handleServerFunction(h3Event) {
158
158
  headers.set("X-Error", errorHeader);
159
159
  }
160
160
  return new Response(body.body, {
161
- headers: body.headers,
161
+ headers,
162
162
  });
163
163
  }
164
164
  h3Event.res.headers.set(BODY_FORMAT_KEY, BodyFormat.Seroval);
@@ -166,12 +166,22 @@ export async function handleServerFunction(h3Event) {
166
166
  h3Event.res.headers.set("content-type", "text/javascript");
167
167
  return serializeToJSStream(instance, x);
168
168
  }
169
- h3Event.res.headers.set("content-type", "text/plain");
170
169
  return serializeToJSONStream(x);
171
170
  }
172
171
  return x;
173
172
  }
174
173
  }
174
+ // header values must be ByteString-safe (no chars > 0xFF), otherwise Headers.set throws
175
+ function toHeaderValue(value) {
176
+ const stripped = value.replace(/[\r\n]+/g, "");
177
+ try {
178
+ return /[^\x00-\xFF]/.test(stripped) ? encodeURIComponent(stripped) : stripped;
179
+ }
180
+ catch {
181
+ // encodeURIComponent throws on lone surrogates
182
+ return "true";
183
+ }
184
+ }
175
185
  function handleNoJS(result, request, parsed, thrown) {
176
186
  const url = new URL(request.url);
177
187
  const isError = result instanceof Error;
@@ -194,7 +204,10 @@ function handleNoJS(result, request, parsed, thrown) {
194
204
  result: isError ? result.message : result,
195
205
  thrown: thrown,
196
206
  error: isError,
197
- input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]],
207
+ input: [
208
+ ...parsed.slice(0, -1),
209
+ [...parsed[parsed.length - 1].entries()],
210
+ ],
198
211
  }))}; Secure; HttpOnly;`);
199
212
  }
200
213
  return new Response(null, {
@@ -216,7 +229,7 @@ export function createSingleFlightHeaders(sourceEvent) {
216
229
  // useH3Internals = true;
217
230
  // sourceEvent.nativeEvent.node.req.headers.cookie = "";
218
231
  // }
219
- SetCookies.forEach(cookie => {
232
+ SetCookies.forEach((cookie) => {
220
233
  if (!cookie)
221
234
  return;
222
235
  const { maxAge, expires, name, value } = parseSetCookie(cookie);
@@ -1,5 +1,20 @@
1
1
  export declare function serializeToJSStream(id: string, value: any): ReadableStream<any>;
2
2
  export declare function serializeToJSONStream(value: any): ReadableStream<any>;
3
+ export declare class SerovalChunkReader {
4
+ reader: ReadableStreamDefaultReader<Uint8Array>;
5
+ buffer: Uint8Array;
6
+ done: boolean;
7
+ constructor(stream: ReadableStream<Uint8Array>);
8
+ readChunk(): Promise<void>;
9
+ next(): Promise<{
10
+ done: true;
11
+ value: undefined;
12
+ } | {
13
+ done: false;
14
+ value: string;
15
+ }>;
16
+ drain(interpret: (chunk: string) => void): Promise<void>;
17
+ }
3
18
  export declare function serializeToJSONString(value: any): Promise<string>;
4
19
  export declare function deserializeFromJSONString(json: string): Promise<unknown>;
5
20
  export declare function deserializeJSONStream(response: Response | Request): Promise<unknown>;
@@ -81,7 +81,7 @@ export function serializeToJSONStream(value) {
81
81
  },
82
82
  });
83
83
  }
84
- class SerovalChunkReader {
84
+ export class SerovalChunkReader {
85
85
  reader;
86
86
  buffer;
87
87
  done;
@@ -54,7 +54,10 @@ export function createMiddleware(args) {
54
54
  mw.push(wrapResponseMiddleware(args.onBeforeResponse));
55
55
  }
56
56
  else if (Array.isArray(args.onBeforeResponse)) {
57
- mw.push(...args.onBeforeResponse.map(wrapResponseMiddleware));
57
+ // h3 middleware compose onion-style: logic after `await next()` unwinds
58
+ // innermost-first (reverse registration order). Register the wrappers
59
+ // reversed so onBeforeResponse functions run in their declared order.
60
+ mw.push(...args.onBeforeResponse.map(wrapResponseMiddleware).reverse());
58
61
  }
59
62
  return mw;
60
63
  }
@@ -19,7 +19,7 @@ export function StartServer(props) {
19
19
  <TopErrorBoundary>
20
20
  <props.document assets={<HydrationScript />} scripts={<>
21
21
  <PatchVirtualDevStyles nonce={nonce}/>
22
- <script type="module" nonce={nonce} async src={getSsrManifest("client").path(import.meta.env.START_CLIENT_ENTRY)}/>
22
+ <script type="module" nonce={nonce} async src={getSsrManifest("client").path(import.meta.env.START_CLIENT_ENTRY_URL)}/>
23
23
  </>}>
24
24
  {!import.meta.env.START_ISLANDS ? (<Hydration>
25
25
  <ErrorBoundary>
@@ -3,5 +3,15 @@ import type { FetchEvent } from "./types.ts";
3
3
  export declare function createFetchEvent(event: H3Event): FetchEvent;
4
4
  export declare function getFetchEvent(h3Event: H3Event): FetchEvent;
5
5
  export declare function mergeResponseHeaders(h3Event: H3Event, headers: Headers): void;
6
+ /**
7
+ * Wrap an h3 event handler so Solid's request context is available while it runs.
8
+ *
9
+ * @experimental
10
+ */
6
11
  export declare const decorateHandler: <T extends EventHandler>(fn: T) => T;
12
+ /**
13
+ * Wrap h3 middleware so Solid's request context is available while it runs.
14
+ *
15
+ * @experimental
16
+ */
7
17
  export declare const decorateMiddleware: <T extends Middleware>(fn: T) => T;