@solidjs/start 1.0.0-rc.1 → 1.0.1

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 (37) hide show
  1. package/config/fs-router.js +15 -10
  2. package/config/index.d.ts +10 -4
  3. package/config/index.js +13 -7
  4. package/dist/client/StartClient.d.ts +4 -0
  5. package/dist/client/StartClient.jsx +4 -0
  6. package/dist/client/islands.d.ts +4 -0
  7. package/dist/client/islands.jsx +4 -0
  8. package/dist/client/mount.d.ts +4 -0
  9. package/dist/client/mount.js +8 -2
  10. package/dist/router/FileRoutes.d.ts +4 -0
  11. package/dist/router/FileRoutes.js +8 -3
  12. package/dist/router/lazyRoute.js +7 -2
  13. package/dist/router/routes.d.ts +1 -0
  14. package/dist/router/routes.js +2 -2
  15. package/dist/runtime/server-handler.js +4 -1
  16. package/dist/server/StartServer.d.ts +4 -0
  17. package/dist/server/StartServer.jsx +30 -23
  18. package/dist/server/handler.d.ts +10 -1
  19. package/dist/server/handler.js +22 -3
  20. package/dist/server/serverFunction.d.ts +4 -0
  21. package/dist/server/serverFunction.jsx +4 -0
  22. package/dist/server/spa/StartServer.d.ts +4 -0
  23. package/dist/server/spa/StartServer.jsx +14 -7
  24. package/dist/server/spa/handler.d.ts +4 -0
  25. package/dist/server/spa/handler.js +4 -0
  26. package/dist/shared/ErrorBoundary.d.ts +1 -0
  27. package/dist/shared/ErrorBoundary.jsx +28 -8
  28. package/dist/shared/GET.d.ts +4 -0
  29. package/dist/shared/GET.js +4 -0
  30. package/dist/shared/HttpHeader.d.ts +4 -0
  31. package/dist/shared/HttpHeader.jsx +4 -0
  32. package/dist/shared/HttpStatusCode.d.ts +4 -0
  33. package/dist/shared/HttpStatusCode.jsx +4 -0
  34. package/dist/shared/clientOnly.d.ts +4 -0
  35. package/dist/shared/clientOnly.jsx +4 -0
  36. package/dist/shared/dev-overlay/get-source-map.d.ts +0 -1
  37. package/package.json +6 -2
@@ -24,6 +24,7 @@ export class SolidStartClientFileRouter extends BaseFileSystemRouter {
24
24
 
25
25
  if (src.endsWith(".md") || src.endsWith(".mdx")) {
26
26
  return {
27
+ page: true,
27
28
  $component: {
28
29
  src: src,
29
30
  pick: ["$css"]
@@ -35,10 +36,11 @@ export class SolidStartClientFileRouter extends BaseFileSystemRouter {
35
36
  }
36
37
 
37
38
  const [_, exports] = analyzeModule(src);
38
- const hasDefault = exports.find(e => e.n === "default");
39
- const hasRouteConfig = exports.find(e => e.n === "route");
39
+ const hasDefault = !!exports.find(e => e.n === "default");
40
+ const hasRouteConfig = !!exports.find(e => e.n === "route");
40
41
  if (hasDefault) {
41
42
  return {
43
+ page: true,
42
44
  $component: {
43
45
  src: src,
44
46
  pick: ["default", "$css"]
@@ -93,6 +95,7 @@ export class SolidStartServerFileRouter extends BaseFileSystemRouter {
93
95
  let path = this.toPath(src);
94
96
  if (src.endsWith(".md") || src.endsWith(".mdx")) {
95
97
  return {
98
+ page: true,
96
99
  $component: {
97
100
  src: src,
98
101
  pick: ["$css"]
@@ -105,16 +108,18 @@ export class SolidStartServerFileRouter extends BaseFileSystemRouter {
105
108
 
106
109
  const [_, exports] = analyzeModule(src);
107
110
  const hasRouteConfig = exports.find(e => e.n === "route");
108
- const hasDefault = exports.find(e => e.n === "default");
109
- const hasAPIRoutes = exports.find(exp => HTTP_METHODS.includes(exp.n));
111
+ const hasDefault = !!exports.find(e => e.n === "default");
112
+ const hasAPIRoutes = !!exports.find(exp => HTTP_METHODS.includes(exp.n));
110
113
  if (hasDefault || hasAPIRoutes) {
111
114
  return {
112
- $component: hasDefault
113
- ? {
114
- src: src,
115
- pick: ["default", "$css"]
116
- }
117
- : undefined,
115
+ page: hasDefault,
116
+ $component:
117
+ !this.config.dataOnly && hasDefault
118
+ ? {
119
+ src: src,
120
+ pick: ["default", "$css"]
121
+ }
122
+ : undefined,
118
123
  $$route: hasRouteConfig
119
124
  ? {
120
125
  src: src,
package/config/index.d.ts CHANGED
@@ -6,7 +6,7 @@ import type { Options } from "vite-plugin-solid";
6
6
  // atleast until we sort which server options are good to use
7
7
  type ViteCustomizableConfig = CustomizableConfig & {
8
8
  server?: InlineConfig["server"];
9
- }
9
+ };
10
10
 
11
11
  type SolidStartInlineConfig = {
12
12
  ssr?: boolean;
@@ -16,13 +16,19 @@ type SolidStartInlineConfig = {
16
16
  appRoot?: string;
17
17
  routeDir?: string;
18
18
  middleware?: string;
19
- devOverlay?: boolean;
19
+ devOverlay?: boolean;
20
20
  experimental?: {
21
21
  islands?: boolean;
22
- }
22
+ };
23
23
  vite?:
24
24
  | ViteCustomizableConfig
25
25
  | ((options: { router: "server" | "client" | "server-function" }) => ViteCustomizableConfig);
26
26
  };
27
27
 
28
- export declare function defineConfig(baseConfig?: SolidStartInlineConfig): ReturnType<typeof createApp>;
28
+ /**
29
+ *
30
+ * Read more: https://docs.solidjs.com/solid-start/reference/entrypoints/app-config
31
+ */
32
+ export declare function defineConfig(
33
+ baseConfig?: SolidStartInlineConfig
34
+ ): ReturnType<typeof createApp>;
package/config/index.js CHANGED
@@ -30,7 +30,8 @@ function solidStartServerFsRouter(config) {
30
30
  new SolidStartServerFileRouter(
31
31
  {
32
32
  dir: resolve.absolute(config.dir, router.root),
33
- extensions: config.extensions ?? DEFAULT_EXTENSIONS
33
+ extensions: config.extensions ?? DEFAULT_EXTENSIONS,
34
+ dataOnly: config.dataOnly
34
35
  },
35
36
  router,
36
37
  app
@@ -90,7 +91,7 @@ export function defineConfig(baseConfig = {}) {
90
91
  },
91
92
  handler: `${start.appRoot}/entry-server${entryExtension}`,
92
93
  middleware: start.middleware,
93
- routes: solidStartServerFsRouter({ dir: routeDir, extensions }),
94
+ routes: solidStartServerFsRouter({ dir: routeDir, extensions, dataOnly: !start.ssr }),
94
95
  extensions,
95
96
  target: "server",
96
97
  plugins: async () => {
@@ -133,7 +134,9 @@ export function defineConfig(baseConfig = {}) {
133
134
  },
134
135
  cacheDir: "node_modules/.vinxi/server",
135
136
  define: {
136
- "import.meta.env.START_APP": JSON.stringify(`${start.appRoot}/app${entryExtension}`),
137
+ "import.meta.env.START_APP": JSON.stringify(
138
+ `${start.appRoot}/app${entryExtension}`
139
+ ),
137
140
  "import.meta.env.START_ISLANDS": JSON.stringify(start.experimental.islands),
138
141
  "import.meta.env.SSR": JSON.stringify(true),
139
142
  "import.meta.env.START_SSR": JSON.stringify(start.ssr),
@@ -201,7 +204,9 @@ export function defineConfig(baseConfig = {}) {
201
204
  },
202
205
  cacheDir: "node_modules/.vinxi/client",
203
206
  define: {
204
- "import.meta.env.START_APP": JSON.stringify(`${start.appRoot}/app${entryExtension}`),
207
+ "import.meta.env.START_APP": JSON.stringify(
208
+ `${start.appRoot}/app${entryExtension}`
209
+ ),
205
210
  "import.meta.env.START_ISLANDS": JSON.stringify(start.experimental.islands),
206
211
  "import.meta.env.SSR": JSON.stringify(false),
207
212
  "import.meta.env.START_SSR": JSON.stringify(start.ssr),
@@ -213,7 +218,6 @@ export function defineConfig(baseConfig = {}) {
213
218
  ];
214
219
  }
215
220
  },
216
-
217
221
  {
218
222
  name: "server-fns",
219
223
  type: "http",
@@ -223,7 +227,7 @@ export function defineConfig(baseConfig = {}) {
223
227
  ),
224
228
  middleware: start.middleware,
225
229
  target: "server",
226
- routes: solidStartServerFsRouter({ dir: routeDir, extensions }),
230
+ routes: solidStartServerFsRouter({ dir: routeDir, extensions, dataOnly: !start.ssr }),
227
231
  plugins: async () => {
228
232
  const userConfig =
229
233
  typeof vite === "function" ? await vite({ router: "server-function" }) : { ...vite };
@@ -263,7 +267,9 @@ export function defineConfig(baseConfig = {}) {
263
267
  }
264
268
  },
265
269
  define: {
266
- "import.meta.env.START_APP": JSON.stringify(`${start.appRoot}/app${entryExtension}`),
270
+ "import.meta.env.START_APP": JSON.stringify(
271
+ `${start.appRoot}/app${entryExtension}`
272
+ ),
267
273
  "import.meta.env.START_ISLANDS": JSON.stringify(start.experimental.islands),
268
274
  "import.meta.env.SSR": JSON.stringify(true),
269
275
  "import.meta.env.START_SSR": JSON.stringify(start.ssr),
@@ -1,3 +1,7 @@
1
1
  import type { JSX } from "solid-js";
2
2
  import "./mount";
3
+ /**
4
+ *
5
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/start-client
6
+ */
3
7
  export declare function StartClient(): JSX.Element;
@@ -6,6 +6,10 @@ import "./mount";
6
6
  function Dummy(props) {
7
7
  return props.children;
8
8
  }
9
+ /**
10
+ *
11
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/start-client
12
+ */
9
13
  export function StartClient() {
10
14
  return (<Dummy>
11
15
  <Dummy>
@@ -1,3 +1,7 @@
1
1
  import "vinxi/client";
2
+ /**
3
+ *
4
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/start-client
5
+ */
2
6
  export declare function StartClient(): null;
3
7
  export { mount } from "./mount";
@@ -1,5 +1,9 @@
1
1
  // @refresh skip
2
2
  import "vinxi/client";
3
+ /**
4
+ *
5
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/start-client
6
+ */
3
7
  export function StartClient() {
4
8
  return null;
5
9
  }
@@ -1,3 +1,7 @@
1
1
  import type { JSX } from "solid-js";
2
2
  import { type MountableElement } from "solid-js/web";
3
+ /**
4
+ *
5
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/mount
6
+ */
3
7
  export declare function mount(fn: () => JSX.Element, el: MountableElement): (() => void) | undefined;
@@ -1,5 +1,9 @@
1
1
  import { createStore } from "solid-js/store";
2
2
  import { createComponent, getHydrationKey, getOwner, hydrate } from "solid-js/web";
3
+ /**
4
+ *
5
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/mount
6
+ */
3
7
  export function mount(fn, el) {
4
8
  if (import.meta.env.START_ISLANDS) {
5
9
  const map = new WeakMap();
@@ -17,7 +21,8 @@ export function mount(fn, el) {
17
21
  }
18
22
  let mod = await import(
19
23
  /* @vite-ignore */
20
- import.meta.env.MANIFEST["client"].chunks[el.dataset.id.split("#")[0]].output.path);
24
+ import.meta.env.MANIFEST["client"].chunks[el.dataset.id.split("#")[0]].output
25
+ .path);
21
26
  if (!mod || !el.dataset.hk)
22
27
  return;
23
28
  let Component = mod[el.dataset.id.split("#")[1]];
@@ -58,7 +63,8 @@ export function mount(fn, el) {
58
63
  const islands = document.querySelectorAll("solid-island[data-hk]");
59
64
  const assets = new Set();
60
65
  islands.forEach((el) => el.dataset.id && assets.add(el.dataset.id));
61
- Promise.all([...assets].map(asset => import(/* @vite-ignore */ import.meta.env.MANIFEST["client"].chunks[asset.split("#")[0]].output.path)))
66
+ Promise.all([...assets].map(asset => import(
67
+ /* @vite-ignore */ import.meta.env.MANIFEST["client"].chunks[asset.split("#")[0]].output.path)))
62
68
  .then(() => {
63
69
  islands.forEach((el) => {
64
70
  if (el.dataset.when === "idle" && "requestIdleCallback" in window) {
@@ -1,2 +1,6 @@
1
1
  export declare function createRoutes(): any[];
2
+ /**
3
+ *
4
+ * Read more: https://docs.solidjs.com/solid-start/reference/routing/file-routes
5
+ */
2
6
  export declare const FileRoutes: () => any[];
@@ -10,9 +10,10 @@ export function createRoutes() {
10
10
  ...(route.$$route ? route.$$route.require().route.info : {}),
11
11
  filesystem: true
12
12
  },
13
- component: lazyRoute(route.$component, import.meta.env.START_ISLANDS
14
- ? import.meta.env.MANIFEST["ssr"]
15
- : import.meta.env.MANIFEST["client"], import.meta.env.MANIFEST["ssr"]),
13
+ component: route.$component &&
14
+ lazyRoute(route.$component, import.meta.env.START_ISLANDS
15
+ ? import.meta.env.MANIFEST["ssr"]
16
+ : import.meta.env.MANIFEST["client"], import.meta.env.MANIFEST["ssr"]),
16
17
  children: route.children ? route.children.map(createRoute) : undefined
17
18
  };
18
19
  }
@@ -20,6 +21,10 @@ export function createRoutes() {
20
21
  return routes;
21
22
  }
22
23
  let routes;
24
+ /**
25
+ *
26
+ * Read more: https://docs.solidjs.com/solid-start/reference/routing/file-routes
27
+ */
23
28
  export const FileRoutes = isServer
24
29
  ? () => getRequestEvent().routes
25
30
  : () => routes || (routes = createRoutes());
@@ -6,11 +6,16 @@ export default function lazyRoute(component, clientManifest, serverManifest, exp
6
6
  return lazy(async () => {
7
7
  if (import.meta.env.DEV) {
8
8
  let manifest = import.meta.env.SSR ? serverManifest : clientManifest;
9
- const mod = await manifest.inputs[component.src].import();
9
+ // import() throws if a module doesn't exist, which includes any
10
+ // modules loaded by the route itself, so it's important we catch here
11
+ const mod = await manifest.inputs[component.src].import().catch(() => null);
12
+ // fallback to an empty component as any errors will surface in the vite overlay
13
+ if (!mod)
14
+ return { default: () => [] };
10
15
  if (!mod[exported])
11
16
  console.error(`Module ${component.src} does not export ${exported}`);
12
17
  const Component = mod[exported];
13
- let assets = await clientManifest.inputs?.[component.src].assets();
18
+ let assets = await clientManifest.inputs?.[component.src]?.assets();
14
19
  const styles = assets.filter((asset) => asset.tag === "style");
15
20
  if (typeof window !== "undefined" && import.meta.hot) {
16
21
  import.meta.hot.on("css-update", data => {
@@ -2,6 +2,7 @@ interface Route {
2
2
  path: string;
3
3
  id: string;
4
4
  children?: Route[];
5
+ page?: boolean;
5
6
  $component?: any;
6
7
  $GET?: any;
7
8
  $POST?: any;
@@ -1,6 +1,6 @@
1
1
  import { createRouter } from "radix3";
2
2
  import fileRoutes from "vinxi/routes";
3
- export const pageRoutes = defineRoutes(fileRoutes.filter(o => o.$component));
3
+ export const pageRoutes = defineRoutes(fileRoutes.filter(o => o.page));
4
4
  function defineRoutes(fileRoutes) {
5
5
  function processRoute(routes, route, id, full) {
6
6
  const parentRoute = Object.values(routes).find(o => {
@@ -21,7 +21,7 @@ function defineRoutes(fileRoutes) {
21
21
  }
22
22
  export function matchAPIRoute(path, method) {
23
23
  const match = router.lookup(path);
24
- if (match) {
24
+ if (match && match.route) {
25
25
  const handler = match.route[`$${method}`];
26
26
  if (handler === undefined)
27
27
  return;
@@ -9,6 +9,7 @@ import { eventHandler, 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 { createPageEvent } from "../server/pageEvent";
12
+ import { getExpectedRedirectStatus } from "../server/handler";
12
13
  function createChunk(data) {
13
14
  const encodeData = new TextEncoder().encode(data);
14
15
  const bytes = encodeData.length;
@@ -155,11 +156,13 @@ async function handleServerFunction(h3Event) {
155
156
  if (!instance) {
156
157
  const isError = result instanceof Error;
157
158
  let redirectUrl = new URL(request.headers.get("referer")).toString();
159
+ let statusCode = 302;
158
160
  if (result instanceof Response && result.headers.has("Location")) {
159
161
  redirectUrl = new URL(result.headers.get("Location"), new URL(request.url).origin + import.meta.env.SERVER_BASE_URL).toString();
162
+ statusCode = getExpectedRedirectStatus(result);
160
163
  }
161
164
  return new Response(null, {
162
- status: 302,
165
+ status: statusCode,
163
166
  headers: {
164
167
  Location: redirectUrl,
165
168
  ...(result
@@ -1,5 +1,9 @@
1
1
  import type { Component, JSX } from "solid-js";
2
2
  import type { DocumentComponentProps } from "./types";
3
+ /**
4
+ *
5
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/start-server
6
+ */
3
7
  export declare function StartServer(props: {
4
8
  document: Component<DocumentComponentProps>;
5
9
  }): JSX.Element;
@@ -2,7 +2,7 @@
2
2
  // @ts-ignore
3
3
  import App from "#start/app";
4
4
  import { Hydration, HydrationScript, NoHydration, getRequestEvent, ssr, useAssets } from "solid-js/web";
5
- import { ErrorBoundary } from "../shared/ErrorBoundary";
5
+ import { ErrorBoundary, TopErrorBoundary } from "../shared/ErrorBoundary";
6
6
  import { renderAsset } from "./renderAsset";
7
7
  const docType = ssr("<!DOCTYPE html>");
8
8
  function matchRoute(matches, routes, matched = []) {
@@ -22,12 +22,17 @@ function matchRoute(matches, routes, matched = []) {
22
22
  return next;
23
23
  }
24
24
  }
25
+ /**
26
+ *
27
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/start-server
28
+ */
25
29
  export function StartServer(props) {
26
30
  const context = getRequestEvent();
27
31
  // @ts-ignore
28
32
  const nonce = context.nonce;
29
33
  let assets = [];
30
34
  Promise.resolve().then(async () => {
35
+ let assetPromises = [];
31
36
  // @ts-ignore
32
37
  if (context.router && context.router.matches) {
33
38
  // @ts-ignore
@@ -40,39 +45,41 @@ export function StartServer(props) {
40
45
  const segment = matched[i];
41
46
  const part = import.meta.env.MANIFEST[import.meta.env.START_ISLANDS ? "ssr" : "client"]
42
47
  .inputs[segment["$component"].src];
43
- const asset = (await part.assets());
44
- assets.push.apply(assets, asset);
48
+ assetPromises.push(part.assets());
45
49
  }
46
50
  }
47
- else
51
+ else if (import.meta.env.DEV)
48
52
  console.warn("No route matched for preloading js assets");
49
53
  }
54
+ assets = await Promise.all(assetPromises).then(a =>
50
55
  // dedupe assets
51
- assets = [...new Map(assets.map(item => [item.attrs.key, item])).values()].filter(asset => import.meta.env.START_ISLANDS
56
+ [...new Map(a.flat().map(item => [item.attrs.key, item])).values()].filter(asset => import.meta.env.START_ISLANDS
52
57
  ? false
53
58
  : asset.attrs.rel === "modulepreload" &&
54
- !context.assets.find((a) => a.attrs.key === asset.attrs.key));
59
+ !context.assets.find((a) => a.attrs.key === asset.attrs.key)));
55
60
  });
56
61
  useAssets(() => (assets.length ? assets.map(m => renderAsset(m)) : undefined));
57
62
  return (<NoHydration>
58
63
  {docType}
59
- <props.document assets={<>
60
- <HydrationScript />
61
- {context.assets.map((m) => renderAsset(m, nonce))}
62
- </>} scripts={nonce ? (<>
63
- <script nonce={nonce} innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}/>
64
- <script type="module" nonce={nonce} async src={import.meta.env.MANIFEST["client"].inputs[import.meta.env.MANIFEST["client"].handler].output.path}/>
65
- </>) : (<>
66
- <script innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}/>
67
- <script type="module" async src={import.meta.env.MANIFEST["client"].inputs[import.meta.env.MANIFEST["client"].handler].output.path}/>
68
- </>)}>
69
- {!import.meta.env.START_ISLANDS ? (<Hydration>
70
- <ErrorBoundary>
64
+ <TopErrorBoundary>
65
+ <props.document assets={<>
66
+ <HydrationScript />
67
+ {context.assets.map((m) => renderAsset(m, nonce))}
68
+ </>} scripts={nonce ? (<>
69
+ <script nonce={nonce} innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}/>
70
+ <script type="module" nonce={nonce} async src={import.meta.env.MANIFEST["client"].inputs[import.meta.env.MANIFEST["client"].handler].output.path}/>
71
+ </>) : (<>
72
+ <script innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}/>
73
+ <script type="module" async src={import.meta.env.MANIFEST["client"].inputs[import.meta.env.MANIFEST["client"].handler].output.path}/>
74
+ </>)}>
75
+ {!import.meta.env.START_ISLANDS ? (<Hydration>
76
+ <ErrorBoundary>
77
+ <App />
78
+ </ErrorBoundary>
79
+ </Hydration>) : (<ErrorBoundary>
71
80
  <App />
72
- </ErrorBoundary>
73
- </Hydration>) : (<ErrorBoundary>
74
- <App />
75
- </ErrorBoundary>)}
76
- </props.document>
81
+ </ErrorBoundary>)}
82
+ </props.document>
83
+ </TopErrorBoundary>
77
84
  </NoHydration>);
78
85
  }
@@ -1,3 +1,12 @@
1
- import type { FetchEvent, HandlerOptions, PageEvent } from "./types";
1
+ import type { FetchEvent, HandlerOptions, PageEvent, ResponseStub } from "./types";
2
+ /**
3
+ * Checks if user has set a redirect status in the response.
4
+ * If not, falls back to the 302 (temporary redirect)
5
+ */
6
+ export declare function getExpectedRedirectStatus(response: ResponseStub): number;
2
7
  export declare function createBaseHandler(fn: (context: PageEvent) => unknown, createPageEvent: (event: FetchEvent) => Promise<PageEvent>, options?: HandlerOptions | ((context: PageEvent) => HandlerOptions | Promise<HandlerOptions>)): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
8
+ /**
9
+ *
10
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/create-handler
11
+ */
3
12
  export declare function createHandler(fn: (context: PageEvent) => unknown, options?: HandlerOptions | ((context: PageEvent) => HandlerOptions | Promise<HandlerOptions>)): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
@@ -5,6 +5,18 @@ import { eventHandler, sendRedirect, setHeader, setResponseStatus } from "vinxi/
5
5
  import { matchAPIRoute } from "../router/routes";
6
6
  import { getFetchEvent } from "./fetchEvent";
7
7
  import { createPageEvent } from "./pageEvent";
8
+ // according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages
9
+ const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);
10
+ /**
11
+ * Checks if user has set a redirect status in the response.
12
+ * If not, falls back to the 302 (temporary redirect)
13
+ */
14
+ export function getExpectedRedirectStatus(response) {
15
+ if (response.status && validRedirectStatuses.has(response.status)) {
16
+ return response.status;
17
+ }
18
+ return 302;
19
+ }
8
20
  export function createBaseHandler(fn, createPageEvent, options = {}) {
9
21
  return eventHandler({
10
22
  handler: (e) => {
@@ -39,7 +51,8 @@ export function createBaseHandler(fn, createPageEvent, options = {}) {
39
51
  }, resolvedOptions);
40
52
  context.complete = true;
41
53
  if (context.response && context.response.headers.get("Location")) {
42
- return sendRedirect(e, context.response.headers.get("Location"));
54
+ const status = getExpectedRedirectStatus(context.response);
55
+ return sendRedirect(e, context.response.headers.get("Location"), status);
43
56
  }
44
57
  return html;
45
58
  }
@@ -66,7 +79,8 @@ export function createBaseHandler(fn, createPageEvent, options = {}) {
66
79
  return fn(context);
67
80
  }, resolvedOptions);
68
81
  if (context.response && context.response.headers.get("Location")) {
69
- return sendRedirect(e, context.response.headers.get("Location"));
82
+ const status = getExpectedRedirectStatus(context.response);
83
+ return sendRedirect(e, context.response.headers.get("Location"), status);
70
84
  }
71
85
  if (mode === "async")
72
86
  return stream;
@@ -81,7 +95,8 @@ export function createBaseHandler(fn, createPageEvent, options = {}) {
81
95
  function handleShellCompleteRedirect(context, e) {
82
96
  return () => {
83
97
  if (context.response && context.response.headers.get("Location")) {
84
- setResponseStatus(e, 302);
98
+ const status = getExpectedRedirectStatus(context.response);
99
+ setResponseStatus(e, status);
85
100
  setHeader(e, "Location", context.response.headers.get("Location"));
86
101
  }
87
102
  };
@@ -93,6 +108,10 @@ function handleStreamCompleteRedirect(context) {
93
108
  to && write(`<script>window.location="${to}"</script>`);
94
109
  };
95
110
  }
111
+ /**
112
+ *
113
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/create-handler
114
+ */
96
115
  export function createHandler(fn, options) {
97
116
  return createBaseHandler(fn, createPageEvent, options);
98
117
  }
@@ -1,2 +1,6 @@
1
1
  import type { ServerFunctionMeta } from "./types";
2
+ /**
3
+ *
4
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/get-server-function-meta
5
+ */
2
6
  export declare function getServerFunctionMeta(): ServerFunctionMeta | undefined;
@@ -1,4 +1,8 @@
1
1
  import { getRequestEvent } from "solid-js/web";
2
+ /**
3
+ *
4
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/get-server-function-meta
5
+ */
2
6
  export function getServerFunctionMeta() {
3
7
  return getRequestEvent()?.locals.serverFunctionMeta;
4
8
  }
@@ -1,5 +1,9 @@
1
1
  import type { Component } from "solid-js";
2
2
  import type { DocumentComponentProps } from "../types";
3
+ /**
4
+ *
5
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/start-server
6
+ */
3
7
  export declare function StartServer(props: {
4
8
  document: Component<DocumentComponentProps>;
5
9
  }): import("solid-js").JSX.Element;
@@ -1,18 +1,25 @@
1
1
  import { NoHydration, getRequestEvent, ssr } from "solid-js/web";
2
+ import { TopErrorBoundary } from "../../shared/ErrorBoundary";
2
3
  import { renderAsset } from "../renderAsset";
3
4
  const docType = ssr("<!DOCTYPE html>");
5
+ /**
6
+ *
7
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/start-server
8
+ */
4
9
  export function StartServer(props) {
5
10
  const context = getRequestEvent();
6
11
  // @ts-ignore
7
12
  const nonce = context.nonce;
8
13
  return (<NoHydration>
9
14
  {docType}
10
- <props.document assets={<>{context.assets.map((m) => renderAsset(m))}</>} scripts={nonce ? (<>
11
- <script nonce={nonce} innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}/>
12
- <script type="module" src={import.meta.env.MANIFEST["client"].inputs[import.meta.env.MANIFEST["client"].handler].output.path}/>
13
- </>) : (<>
14
- <script innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}/>
15
- <script type="module" src={import.meta.env.MANIFEST["client"].inputs[import.meta.env.MANIFEST["client"].handler].output.path}/>
16
- </>)}/>
15
+ <TopErrorBoundary>
16
+ <props.document assets={<>{context.assets.map((m) => renderAsset(m))}</>} scripts={nonce ? (<>
17
+ <script nonce={nonce} innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}/>
18
+ <script type="module" src={import.meta.env.MANIFEST["client"].inputs[import.meta.env.MANIFEST["client"].handler].output.path}/>
19
+ </>) : (<>
20
+ <script innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}/>
21
+ <script type="module" src={import.meta.env.MANIFEST["client"].inputs[import.meta.env.MANIFEST["client"].handler].output.path}/>
22
+ </>)}/>
23
+ </TopErrorBoundary>
17
24
  </NoHydration>);
18
25
  }
@@ -1,3 +1,7 @@
1
1
  import { FetchEvent, HandlerOptions, PageEvent } from "../types";
2
+ /**
3
+ *
4
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/create-handler
5
+ */
2
6
  export declare function createHandler(fn: (context: PageEvent) => unknown, options?: HandlerOptions | ((context: PageEvent) => HandlerOptions)): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
3
7
  export declare function createPageEvent(ctx: FetchEvent): Promise<PageEvent>;
@@ -1,4 +1,8 @@
1
1
  import { createBaseHandler } from "../handler";
2
+ /**
3
+ *
4
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/create-handler
5
+ */
2
6
  export function createHandler(fn, options) {
3
7
  return createBaseHandler(fn, createPageEvent, options);
4
8
  }
@@ -1,2 +1,3 @@
1
1
  import { type ParentProps } from "solid-js";
2
2
  export declare const ErrorBoundary: (props: ParentProps) => import("solid-js").JSX.Element;
3
+ export declare const TopErrorBoundary: (props: ParentProps) => import("solid-js").JSX.Element;
@@ -1,16 +1,36 @@
1
1
  // @refresh skip
2
- import { ErrorBoundary as DefaultErrorBoundary } from "solid-js";
2
+ import { ErrorBoundary as DefaultErrorBoundary, catchError } from "solid-js";
3
3
  import { isServer } from "solid-js/web";
4
4
  import { HttpStatusCode } from "./HttpStatusCode";
5
5
  import { DevOverlay } from "./dev-overlay";
6
6
  export const ErrorBoundary = import.meta.env.DEV && import.meta.env.START_DEV_OVERLAY
7
7
  ? (props) => <DevOverlay>{props.children}</DevOverlay>
8
8
  : (props) => {
9
- const message = isServer ? "500 | Internal Server Error" : "Error | Uncaught Client Exception";
10
- return (<DefaultErrorBoundary fallback={<>
11
- <span style="font-size:1.5em;text-align:center;position:fixed;left:0px;bottom:55%;width:100%;">{message}</span>
12
- <HttpStatusCode code={500}/>
13
- </>}>
14
- {props.children}
15
- </DefaultErrorBoundary>);
9
+ const message = isServer
10
+ ? "500 | Internal Server Error"
11
+ : "Error | Uncaught Client Exception";
12
+ return (<DefaultErrorBoundary fallback={error => {
13
+ console.error(error);
14
+ return (<>
15
+ <span style="font-size:1.5em;text-align:center;position:fixed;left:0px;bottom:55%;width:100%;">
16
+ {message}
17
+ </span>
18
+ <HttpStatusCode code={500}/>
19
+ </>);
20
+ }}>
21
+ {props.children}
22
+ </DefaultErrorBoundary>);
16
23
  };
24
+ export const TopErrorBoundary = (props) => {
25
+ let isError = false;
26
+ const res = catchError(() => props.children, err => {
27
+ console.error(err);
28
+ isError = !!err;
29
+ });
30
+ return isError ? (<>
31
+ <span style="font-size:1.5em;text-align:center;position:fixed;left:0px;bottom:55%;width:100%;">
32
+ 500 | Internal Server Error
33
+ </span>
34
+ <HttpStatusCode code={500}/>
35
+ </>) : (res);
36
+ };
@@ -1 +1,5 @@
1
+ /**
2
+ *
3
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/get
4
+ */
1
5
  export declare function GET<T extends (...args: any[]) => any>(fn: T): (...args: Parameters<T>) => ReturnType<T>;
@@ -1,3 +1,7 @@
1
+ /**
2
+ *
3
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/get
4
+ */
1
5
  export function GET(fn) {
2
6
  return (...args) => {
3
7
  return fn.GET(...args);
@@ -3,4 +3,8 @@ export interface HttpHeaderProps {
3
3
  value: string;
4
4
  append?: boolean;
5
5
  }
6
+ /**
7
+ *
8
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/http-header
9
+ */
6
10
  export declare const HttpHeader: (props: HttpHeaderProps) => null;
@@ -1,6 +1,10 @@
1
1
  // @refresh skip
2
2
  import { onCleanup } from "solid-js";
3
3
  import { getRequestEvent, isServer } from "solid-js/web";
4
+ /**
5
+ *
6
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/http-header
7
+ */
4
8
  export const HttpHeader = isServer
5
9
  ? (props) => {
6
10
  const event = getRequestEvent();
@@ -2,4 +2,8 @@ export interface HttpStatusCodeProps {
2
2
  code: number;
3
3
  text?: string;
4
4
  }
5
+ /**
6
+ *
7
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/http-status-code
8
+ */
5
9
  export declare const HttpStatusCode: (props: HttpStatusCodeProps) => null;
@@ -1,6 +1,10 @@
1
1
  // @refresh skip
2
2
  import { onCleanup } from "solid-js";
3
3
  import { getRequestEvent, isServer } from "solid-js/web";
4
+ /**
5
+ *
6
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/http-status-code
7
+ */
4
8
  export const HttpStatusCode = isServer
5
9
  ? (props) => {
6
10
  const event = getRequestEvent();
@@ -1,4 +1,8 @@
1
1
  import type { Component, ComponentProps, JSX } from "solid-js";
2
+ /**
3
+ *
4
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/client-only
5
+ */
2
6
  export default function clientOnly<T extends Component<any>>(fn: () => Promise<{
3
7
  default: T;
4
8
  }>): (props: ComponentProps<T> & {
@@ -1,5 +1,9 @@
1
1
  import { createMemo, createSignal, onMount, sharedConfig, splitProps, untrack } from "solid-js";
2
2
  import { isServer } from "solid-js/web";
3
+ /**
4
+ *
5
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/client-only
6
+ */
3
7
  // not using Suspense
4
8
  export default function clientOnly(fn) {
5
9
  if (isServer)
@@ -1,3 +1,2 @@
1
- /// <reference types=".pnpm/source-map-js@1.2.0/node_modules/source-map-js" />
2
1
  import { SourceMapConsumer } from 'source-map-js';
3
2
  export default function getSourceMap(url: string, content: string): Promise<SourceMapConsumer | null>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidjs/start",
3
- "version": "1.0.0-rc.1",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
@@ -29,7 +29,11 @@
29
29
  "./server/spa": "./dist/server/spa/index.jsx",
30
30
  "./client": "./dist/client/index.jsx",
31
31
  "./client/islands": "./dist/client/islands.jsx",
32
- "./client/spa": "./dist/client/spa/index.jsx"
32
+ "./client/spa": "./dist/client/spa/index.jsx",
33
+ "./env": {
34
+ "types": "./env.d.ts",
35
+ "import": null
36
+ }
33
37
  },
34
38
  "typesVersions": {
35
39
  "*": {