@solidjs/start 1.0.9 → 1.0.11

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.
@@ -58,7 +58,7 @@ export class SolidStartClientFileRouter extends BaseFileSystemRouter {
58
58
  }
59
59
  }
60
60
 
61
- const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
61
+ const HTTP_METHODS = ["HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"];
62
62
  function createHTTPHandlers(src, exports) {
63
63
  const handlers = {};
64
64
  for (const exp of exports) {
@@ -67,8 +67,15 @@ function createHTTPHandlers(src, exports) {
67
67
  src: src,
68
68
  pick: [exp.n]
69
69
  };
70
+ if (exp.n === "GET" && !exports.find(exp => exp.n === "HEAD")) {
71
+ handlers.$HEAD = {
72
+ src: src,
73
+ pick: [exp.n]
74
+ };
75
+ }
70
76
  }
71
77
  }
78
+
72
79
  return handlers;
73
80
  }
74
81
 
@@ -12,7 +12,7 @@ export declare function createMiddleware({ onRequest, onBeforeResponse }: {
12
12
  onRequest?: RequestMiddleware | RequestMiddleware[] | undefined;
13
13
  onBeforeResponse?: ResponseMiddleware | ResponseMiddleware[] | undefined;
14
14
  }): {
15
- onRequest?: import("h3")._RequestMiddleware<import("h3").EventHandlerRequest> | import("h3")._RequestMiddleware<import("h3").EventHandlerRequest>[] | undefined;
16
- onBeforeResponse?: import("h3")._ResponseMiddleware<import("h3").EventHandlerRequest, any> | import("h3")._ResponseMiddleware<import("h3").EventHandlerRequest, any>[] | undefined;
15
+ onRequest?: import("h3")._RequestMiddleware | import("h3")._RequestMiddleware[] | undefined;
16
+ onBeforeResponse?: import("h3")._ResponseMiddleware | import("h3")._ResponseMiddleware[] | undefined;
17
17
  };
18
18
  export {};
@@ -4,6 +4,7 @@ interface Route {
4
4
  children?: Route[];
5
5
  page?: boolean;
6
6
  $component?: any;
7
+ $HEAD?: any;
7
8
  $GET?: any;
8
9
  $POST?: any;
9
10
  $PUT?: any;
@@ -22,7 +22,7 @@ function defineRoutes(fileRoutes) {
22
22
  export function matchAPIRoute(path, method) {
23
23
  const match = router.lookup(path);
24
24
  if (match && match.route) {
25
- const handler = match.route[`$${method}`];
25
+ const handler = method === "HEAD" ? match.route["$HEAD"] || match.route["$GET"] : match.route[`$${method}`];
26
26
  if (handler === undefined)
27
27
  return;
28
28
  return {
@@ -32,7 +32,7 @@ export function matchAPIRoute(path, method) {
32
32
  }
33
33
  }
34
34
  function containsHTTP(route) {
35
- return route["$GET"] || route["$POST"] || route["$PUT"] || route["$PATCH"] || route["$DELETE"];
35
+ return route["$HEAD"] || route["$GET"] || route["$POST"] || route["$PUT"] || route["$PATCH"] || route["$DELETE"];
36
36
  }
37
37
  const router = createRouter({
38
38
  routes: fileRoutes.reduce((memo, route) => {
@@ -5,7 +5,7 @@ import { CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, Hea
5
5
  import { sharedConfig } from "solid-js";
6
6
  import { renderToString } from "solid-js/web";
7
7
  import { provideRequestEvent } from "solid-js/web/storage";
8
- import { eventHandler, setHeader, setResponseStatus } from "vinxi/http";
8
+ import { eventHandler, parseCookies, setHeader, setResponseStatus } from "vinxi/http";
9
9
  import invariant from "vinxi/lib/invariant";
10
10
  import { cloneEvent, getFetchEvent, mergeResponseHeaders } from "../server/fetchEvent";
11
11
  import { getExpectedRedirectStatus } from "../server/handler";
@@ -100,12 +100,13 @@ async function handleServerFunction(h3Event) {
100
100
  // This should never be the case in "proper" Nitro presets since node.req has to be IncomingMessage,
101
101
  // But the new azure-functions preset for some reason uses a ReadableStream in node.req (#1521)
102
102
  const isReadableStream = h3Request instanceof ReadableStream;
103
- const isH3EventBodyStreamLocked = isReadableStream && h3Request.locked;
103
+ const hasReadableStream = h3Request.body instanceof ReadableStream;
104
+ const isH3EventBodyStreamLocked = (isReadableStream && h3Request.locked) || (hasReadableStream && h3Request.body.locked);
104
105
  const requestBody = isReadableStream ? h3Request : h3Request.body;
105
106
  if (contentType?.startsWith("multipart/form-data") ||
106
107
  contentType?.startsWith("application/x-www-form-urlencoded")) {
107
108
  // workaround for https://github.com/unjs/nitro/issues/1721
108
- // (issue only in edge runtimes)
109
+ // (issue only in edge runtimes and netlify preset)
109
110
  parsed.push(await (isH3EventBodyStreamLocked
110
111
  ? request
111
112
  : new Request(request, { ...request, body: requestBody })).formData());
@@ -114,7 +115,7 @@ async function handleServerFunction(h3Event) {
114
115
  }
115
116
  else if (contentType?.startsWith("application/json")) {
116
117
  // workaround for https://github.com/unjs/nitro/issues/1721
117
- // (issue only in edge runtimes)
118
+ // (issue only in edge runtimes and netlify preset)
118
119
  const tmpReq = isH3EventBodyStreamLocked
119
120
  ? request
120
121
  : new Request(request, { ...request, body: requestBody });
@@ -207,31 +208,58 @@ async function handleServerFunction(h3Event) {
207
208
  function handleNoJS(result, request, parsed, thrown) {
208
209
  const url = new URL(request.url);
209
210
  const isError = result instanceof Error;
210
- let redirectUrl = new URL(request.headers.get("referer")).toString();
211
211
  let statusCode = 302;
212
- if (result instanceof Response && result.headers.has("Location")) {
213
- redirectUrl = new URL(result.headers.get("Location"), url.origin + import.meta.env.SERVER_BASE_URL).toString();
214
- statusCode = getExpectedRedirectStatus(result);
212
+ let headers;
213
+ if (result instanceof Response) {
214
+ headers = new Headers(result.headers);
215
+ if (result.headers.has("Location")) {
216
+ headers.set(`Location`, new URL(result.headers.get("Location"), url.origin + import.meta.env.SERVER_BASE_URL).toString());
217
+ statusCode = getExpectedRedirectStatus(result);
218
+ }
219
+ }
220
+ else
221
+ headers = new Headers({ Location: new URL(request.headers.get("referer")).toString() });
222
+ if (result) {
223
+ headers.append("Set-Cookie", `flash=${encodeURIComponent(JSON.stringify({
224
+ url: url.pathname + url.search,
225
+ result: isError ? result.message : result,
226
+ thrown: thrown,
227
+ error: isError,
228
+ input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]]
229
+ }))}; Secure; HttpOnly;`);
215
230
  }
216
231
  return new Response(null, {
217
232
  status: statusCode,
218
- headers: {
219
- Location: redirectUrl,
220
- ...(result
221
- ? {
222
- "Set-Cookie": `flash=${JSON.stringify({
223
- url: url.pathname + encodeURIComponent(url.search),
224
- result: isError ? result.message : result,
225
- thrown: thrown,
226
- error: isError,
227
- input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]]
228
- })}; Secure; HttpOnly;`
229
- }
230
- : {})
231
- }
233
+ headers
232
234
  });
233
235
  }
234
236
  let App;
237
+ function createSingleFlightHeaders(sourceEvent) {
238
+ // cookie handling logic is pretty simplistic so this might be imperfect
239
+ // unclear if h3 internals are available on all platforms but we need a way to
240
+ // update request headers on the underlying H3 event.
241
+ const headers = new Headers(sourceEvent.request.headers);
242
+ const cookies = parseCookies(sourceEvent.nativeEvent);
243
+ const SetCookies = sourceEvent.response.headers.getSetCookie();
244
+ headers.delete("cookie");
245
+ let useH3Internals = false;
246
+ if (sourceEvent.nativeEvent.node?.req) {
247
+ useH3Internals = true;
248
+ sourceEvent.nativeEvent.node.req.headers.cookie = "";
249
+ }
250
+ SetCookies.forEach((cookie) => {
251
+ if (!cookie)
252
+ return;
253
+ const keyValue = cookie.split(";")[0];
254
+ const [key, value] = keyValue.split("=");
255
+ key && value && (cookies[key] = value);
256
+ });
257
+ Object.entries(cookies).forEach(([key, value]) => {
258
+ headers.append("cookie", `${key}=${value}`);
259
+ useH3Internals && (sourceEvent.nativeEvent.node.req.headers.cookie += `${key}=${value};`);
260
+ });
261
+ return headers;
262
+ }
235
263
  async function handleSingleFlight(sourceEvent, result) {
236
264
  let revalidate;
237
265
  let url = new URL(sourceEvent.request.headers.get("referer")).toString();
@@ -242,7 +270,7 @@ async function handleSingleFlight(sourceEvent, result) {
242
270
  url = new URL(result.headers.get("Location"), new URL(sourceEvent.request.url).origin + import.meta.env.SERVER_BASE_URL).toString();
243
271
  }
244
272
  const event = cloneEvent(sourceEvent);
245
- event.request = new Request(url);
273
+ event.request = new Request(url, { headers: createSingleFlightHeaders(sourceEvent) });
246
274
  return await provideRequestEvent(event, async () => {
247
275
  await createPageEvent(event);
248
276
  /* @ts-ignore */
@@ -26,7 +26,7 @@ export function createBaseHandler(fn, createPageEvent, options = {}) {
26
26
  const match = matchAPIRoute(new URL(event.request.url).pathname, event.request.method);
27
27
  if (match) {
28
28
  const mod = await match.handler.import();
29
- const fn = mod[event.request.method];
29
+ const fn = event.request.method === "HEAD" ? mod["HEAD"] || mod["GET"] : mod[event.request.method];
30
30
  event.params = match.params || {};
31
31
  // @ts-ignore
32
32
  sharedConfig.context = { event };
@@ -4,19 +4,26 @@ function initFromFlash(ctx) {
4
4
  const flash = getCookie(ctx.nativeEvent, "flash");
5
5
  if (!flash)
6
6
  return;
7
- let param = JSON.parse(flash);
8
- if (!param || !param.result)
9
- return;
10
- const input = [...param.input.slice(0, -1), new Map(param.input[param.input.length - 1])];
11
- setCookie(ctx.nativeEvent, "flash", "", { maxAge: 0 });
12
- const result = param.error ? new Error(param.result) : param.result;
13
- return {
14
- input,
15
- url: param.url,
16
- pending: false,
17
- result: param.thrown ? undefined : result,
18
- error: param.thrown ? result : undefined,
19
- };
7
+ try {
8
+ let param = JSON.parse(flash);
9
+ if (!param || !param.result)
10
+ return;
11
+ const input = [...param.input.slice(0, -1), new Map(param.input[param.input.length - 1])];
12
+ const result = param.error ? new Error(param.result) : param.result;
13
+ return {
14
+ input,
15
+ url: param.url,
16
+ pending: false,
17
+ result: param.thrown ? undefined : result,
18
+ error: param.thrown ? result : undefined
19
+ };
20
+ }
21
+ catch (e) {
22
+ console.error(e);
23
+ }
24
+ finally {
25
+ setCookie(ctx.nativeEvent, "flash", "", { maxAge: 0 });
26
+ }
20
27
  }
21
28
  export async function createPageEvent(ctx) {
22
29
  const clientManifest = import.meta.env.MANIFEST["client"];
@@ -28,7 +35,9 @@ export async function createPageEvent(ctx) {
28
35
  manifest: await clientManifest.json(),
29
36
  assets: [
30
37
  ...(await clientManifest.inputs[clientManifest.handler].assets()),
31
- ...(import.meta.env.DEV ? await clientManifest.inputs[import.meta.env.START_APP].assets() : []),
38
+ ...(import.meta.env.DEV
39
+ ? await clientManifest.inputs[import.meta.env.START_APP].assets()
40
+ : []),
32
41
  ...(import.meta.env.START_ISLANDS
33
42
  ? (await serverManifest.inputs[serverManifest.handler].assets()).filter(s => s.attrs.rel !== "modulepreload")
34
43
  : [])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidjs/start",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "type": "module",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
@@ -73,7 +73,7 @@
73
73
  "source-map-js": "^1.0.2",
74
74
  "terracotta": "^1.0.4",
75
75
  "tinyglobby": "^0.2.2",
76
- "vite-plugin-solid": "^2.10.2"
76
+ "vite-plugin-solid": "^2.11.0"
77
77
  },
78
78
  "scripts": {
79
79
  "typecheck": "tsc --noEmit",