@solidjs/start 0.4.2 → 0.4.3

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.
package/config/index.d.ts CHANGED
@@ -3,6 +3,11 @@ import { InlineConfig } from "vite";
3
3
 
4
4
  type SolidStartInlineConfig = Omit<InlineConfig, "router"> & {
5
5
  start?: {
6
+ /**
7
+ * true: streaming mode
8
+ * false: csr only
9
+ * async: ssr is in async mode
10
+ */
6
11
  ssr?: boolean | "async",
7
12
  extensions?: string[],
8
13
  server?: AppOptions['server'],
package/config/index.js CHANGED
@@ -38,29 +38,27 @@ function solidStartServerFsRouter(config) {
38
38
  }
39
39
 
40
40
  export function defineConfig(baseConfig = {}) {
41
- let { plugins = [], start = {}, base, ...userConfig } = baseConfig;
41
+ let { plugins = [], start = {}, ...userConfig } = baseConfig;
42
42
  const extensions = [...DEFAULT_EXTENSIONS, ...(start.extensions || [])];
43
43
  start = defu(start, {
44
44
  appRoot: "./src",
45
45
  ssr: true,
46
46
  islands: false,
47
- server: { base },
48
47
  solid: {}
49
48
  });
50
49
  let server = start.server;
51
50
  if (!start.ssr) {
52
51
  server = { ...server, prerender: { routes: ["/"] } };
53
52
  }
54
- console.log("start", start)
55
- let entryExtension = ".tsx"
53
+ let entryExtension = ".tsx";
56
54
  if (existsSync(join(process.cwd(), start.appRoot, "app.jsx"))) {
57
- entryExtension = ".jsx"
55
+ entryExtension = ".jsx";
58
56
  }
59
57
 
60
58
  return createApp({
61
59
  server: {
62
60
  compressPublicAssets: {
63
- brotli: true
61
+ brotli: process.versions.bun ? false : true
64
62
  },
65
63
  ...server
66
64
  },
@@ -80,9 +78,10 @@ export function defineConfig(baseConfig = {}) {
80
78
  routes: solidStartServerFsRouter({ dir: `${start.appRoot}/routes`, extensions }),
81
79
  extensions,
82
80
  target: "server",
83
- plugins: () => [
81
+ plugins: async () => [
84
82
  config("user", userConfig),
85
- ...plugins,
83
+ ...(typeof plugins === "function" ? [...(await plugins())] : plugins),
84
+
86
85
  serverTransform({
87
86
  runtime: normalize(fileURLToPath(new URL("./server-fns-runtime.jsx", import.meta.url)))
88
87
  }),
@@ -121,9 +120,9 @@ export function defineConfig(baseConfig = {}) {
121
120
  }),
122
121
  extensions,
123
122
  target: "browser",
124
- plugins: () => [
123
+ plugins: async () => [
125
124
  config("user", userConfig),
126
- ...plugins,
125
+ ...(typeof plugins === "function" ? [...(await plugins())] : plugins),
127
126
  serverFunctions.client({
128
127
  runtime: normalize(fileURLToPath(new URL("./server-runtime.jsx", import.meta.url)))
129
128
  }),
@@ -161,9 +160,10 @@ export function defineConfig(baseConfig = {}) {
161
160
  handler: normalize(fileURLToPath(new URL("./server-handler.js", import.meta.url))),
162
161
  runtime: normalize(fileURLToPath(new URL("./server-fns-runtime.jsx", import.meta.url))),
163
162
  // routes: solidStartServerFsRouter({ dir: `${start.appRoot}/routes`, extensions }),
164
- plugins: () => [
163
+ plugins: async () => [
165
164
  config("user", userConfig),
166
- ...plugins,
165
+ ...(typeof plugins === "function" ? [...(await plugins())] : plugins),
166
+
167
167
  solid({ ...start.solid, ssr: true, extensions: extensions.map(ext => `.${ext}`) }),
168
168
  config("app-server", {
169
169
  resolve: {
@@ -1,6 +1,6 @@
1
1
  import { getRequestEvent } from "solid-js/web";
2
2
  import { provideRequestEvent } from "solid-js/web/storage";
3
- import { cloneEvent } from "../server";
3
+ import { cloneEvent } from "../server/middleware";
4
4
 
5
5
  export function createServerReference(fn, id, name) {
6
6
  return new Proxy(fn, {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="vinxi/types/server" />
2
- import { crossSerializeStream, fromJSON } from "seroval";
2
+ import { crossSerializeStream, fromJSON, getCrossReferenceHeader } from "seroval";
3
3
  import {
4
4
  CustomEventPlugin,
5
5
  DOMExceptionPlugin,
@@ -10,8 +10,10 @@ import {
10
10
  RequestPlugin,
11
11
  ResponsePlugin,
12
12
  URLPlugin,
13
- URLSearchParamsPlugin,
14
- } from 'seroval-plugins/web';
13
+ URLSearchParamsPlugin
14
+ } from "seroval-plugins/web";
15
+ import { sharedConfig } from "solid-js";
16
+ /* @ts-ignore */
15
17
  import { provideRequestEvent } from "solid-js/web/storage";
16
18
  import invariant from "vinxi/lib/invariant";
17
19
  import {
@@ -19,7 +21,7 @@ import {
19
21
  getHeader,
20
22
  getRequestURL,
21
23
  readBody,
22
- readFormData,
24
+ readRawBody,
23
25
  setHeader
24
26
  } from "vinxi/server";
25
27
  import { getFetchEvent } from "../server/middleware";
@@ -39,10 +41,10 @@ function serializeToStream(id, value) {
39
41
  RequestPlugin,
40
42
  ResponsePlugin,
41
43
  URLSearchParamsPlugin,
42
- URLPlugin,
44
+ URLPlugin
43
45
  ],
44
46
  onSerialize(data, initial) {
45
- const result = initial ? `($R["${id}"]=[],${data})` : data;
47
+ const result = initial ? `(${getCrossReferenceHeader(id)},${data})` : data;
46
48
  controller.enqueue(new TextEncoder().encode(`${result};\n`));
47
49
  },
48
50
  onDone() {
@@ -89,7 +91,15 @@ async function handleServerFunction(event) {
89
91
  contentType.startsWith("multipart/form-data") ||
90
92
  contentType.startsWith("application/x-www-form-urlencoded")
91
93
  ) {
92
- parsed.push(await readFormData(event));
94
+ // Temporary workaround until https://github.com/unjs/nitro/issues/1721 is resolved
95
+ // parsed.push(await readFormData(event));
96
+
97
+ const request = new Request(getRequestURL(event), {
98
+ method: event.method,
99
+ headers: event.headers,
100
+ body: await readRawBody(event)
101
+ });
102
+ parsed.push(await request.formData());
93
103
  } else {
94
104
  parsed = fromJSON(await readBody(event), {
95
105
  plugins: [
@@ -102,12 +112,17 @@ async function handleServerFunction(event) {
102
112
  RequestPlugin,
103
113
  ResponsePlugin,
104
114
  URLSearchParamsPlugin,
105
- URLPlugin,
106
- ],
115
+ URLPlugin
116
+ ]
107
117
  });
108
118
  }
109
119
  try {
110
- const result = await provideRequestEvent(getFetchEvent(event), () => action(...parsed));
120
+ const evt = getFetchEvent(event);
121
+ const result = await provideRequestEvent(evt, () => {
122
+ /* @ts-ignore */
123
+ sharedConfig.context = { event: evt };
124
+ return action(...parsed);
125
+ });
111
126
 
112
127
  // handle no JS success case
113
128
  if (!instance) {
@@ -117,16 +132,20 @@ async function handleServerFunction(event) {
117
132
  status: 302,
118
133
  headers: {
119
134
  Location: refererUrl.toString(),
120
- ...(result ? { "Set-Cookie": `flash=${JSON.stringify({
121
- url: url.pathname + encodeURIComponent(url.search),
122
- result: isError ? result.message : result,
123
- error: isError,
124
- input: [...parsed.slice(0, -1), [...parsed[parsed.length-1].entries()]]
125
- })}; Secure; HttpOnly;`} : {})
135
+ ...(result
136
+ ? {
137
+ "Set-Cookie": `flash=${JSON.stringify({
138
+ url: url.pathname + encodeURIComponent(url.search),
139
+ result: isError ? result.message : result,
140
+ error: isError,
141
+ input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]]
142
+ })}; Secure; HttpOnly;`
143
+ }
144
+ : {})
126
145
  }
127
146
  });
128
147
  }
129
-
148
+ if (typeof result === "string") return new Response(result);
130
149
  setHeader(event, "content-type", "text/javascript");
131
150
  return serializeToStream(instance, result);
132
151
  } catch (x) {
@@ -138,12 +157,7 @@ async function handleServerFunction(event) {
138
157
  }
139
158
  });
140
159
  }
141
- return new Response(serializeToStream(instance, x), {
142
- status: 500,
143
- headers: {
144
- "Content-Type": "text/javascript"
145
- }
146
- });
160
+ return x;
147
161
  }
148
162
  }
149
163
 
@@ -104,7 +104,15 @@ async function fetchServerFunction(base, id, args) {
104
104
  }
105
105
  throw response;
106
106
  }
107
- const result = deserializeStream(instance, response);
107
+ const contentType = response.headers.get("Content-Type");
108
+ let result;
109
+ if (contentType && contentType.startsWith("text/plain")) {
110
+ result = await response.text();
111
+ } else if(contentType && contentType.startsWith("application/json")) {
112
+ result = await response.json();
113
+ } else {
114
+ result = deserializeStream(instance, response);
115
+ }
108
116
  if (response.ok) {
109
117
  return result;
110
118
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidjs/start",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "author": "Ryan Carniato",
6
6
  "publishConfig": {
@@ -38,16 +38,21 @@
38
38
  }
39
39
  },
40
40
  "devDependencies": {
41
- "solid-js": "^1.8.7",
42
- "vinxi": "0.0.54"
41
+ "solid-js": "^1.8.8",
42
+ "vinxi": "0.1.1"
43
43
  },
44
44
  "dependencies": {
45
- "@vinxi/plugin-directives": "0.0.50",
46
- "@vinxi/server-components": "0.0.50",
47
- "@vinxi/server-functions": "0.0.51",
45
+ "@vinxi/plugin-directives": "^0.1.0",
46
+ "@vinxi/server-components": "^0.1.0",
47
+ "@vinxi/server-functions": "^0.1.0",
48
48
  "defu": "^6.1.2",
49
- "seroval": "^1.0.0",
50
- "seroval-plugins": "^1.0.1",
49
+ "error-stack-parser": "^2.1.4",
50
+ "html-to-image": "^1.11.11",
51
+ "seroval": "^1.0.2",
52
+ "seroval-plugins": "^1.0.2",
53
+ "shikiji": "^0.9.12",
54
+ "source-map-js": "^1.0.2",
55
+ "terracotta": "^1.0.4",
51
56
  "vite-plugin-inspect": "^0.7.33",
52
57
  "vite-plugin-solid": "^2.8.0"
53
58
  },
@@ -1,3 +1,4 @@
1
+ import { sharedConfig } from "solid-js";
1
2
  import { renderToStream } from "solid-js/web";
2
3
  /* @ts-ignore */
3
4
  import { provideRequestEvent } from "solid-js/web/storage";
@@ -42,6 +43,7 @@ export function createHandler(
42
43
  const mod = await match.handler.import();
43
44
  const fn = mod[event.request.method];
44
45
  (event as APIEvent).params = match.params;
46
+ sharedConfig.context = { event } as any;
45
47
  return await fn(event);
46
48
  }
47
49
 
@@ -63,7 +65,10 @@ export function createHandler(
63
65
  og(options);
64
66
  };
65
67
  } else cloned.onCompleteShell = handleShellCompleteRedirect(context, e);
66
- const stream = renderToStream(() => fn(context), cloned);
68
+ const stream = renderToStream(() => {
69
+ (sharedConfig.context as any).event = context;
70
+ return fn(context)
71
+ }, cloned);
67
72
  if (context.response && context.response.headers.get("Location")) {
68
73
  return sendRedirect(event, context.response.headers.get("Location"));
69
74
  }
@@ -12,7 +12,6 @@ export function StartServer(props) {
12
12
  <props.document
13
13
  assets={
14
14
  <>
15
- <script>$R = [];</script>
16
15
  {context.assets.map(m => renderAsset(m))}
17
16
  </>
18
17
  }
@@ -1,48 +1,25 @@
1
1
  import {
2
- createEffect,
3
- ErrorBoundary as ErrorBoundaryBase,
2
+ ErrorBoundary as DefaultErrorBoundary,
4
3
  ParentProps,
5
- resetErrorBoundaries
6
4
  } from "solid-js";
5
+ import { HttpStatusCode } from "./HttpStatusCode";
6
+ import { DevOverlay } from "./dev-overlay";
7
7
 
8
8
  export function ErrorBoundary(props: ParentProps) {
9
+ if (import.meta.env.DEV) {
10
+ return (
11
+ <DevOverlay>
12
+ {props.children}
13
+ </DevOverlay>
14
+ );
15
+ }
9
16
  return (
10
- <ErrorBoundaryBase fallback={e => <ErrorMessage error={e} />}>
17
+ <DefaultErrorBoundary
18
+ fallback={(
19
+ <HttpStatusCode code={500} />
20
+ )}
21
+ >
11
22
  {props.children}
12
- </ErrorBoundaryBase>
13
- );
14
- }
15
-
16
- function ErrorMessage(props: { error: any }) {
17
- createEffect(() => console.error(props.error));
18
-
19
- return (
20
- <div style="padding: 16px">
21
- <div
22
- style={`
23
- background-color: rgba(252, 165, 165);
24
- color: rgb(153, 27, 27);
25
- border-radius": 5px;
26
- overflow: scroll;
27
- padding: 16px;
28
- margin-bottom: 8px;
29
- `}
30
- >
31
- <p style="font-weight: bold" id="error-message">
32
- {props.error.message}
33
- </p>
34
- <button
35
- id="reset-errors"
36
- onClick={resetErrorBoundaries}
37
- style={`color: rgba(252, 165, 165);
38
- background-color: rgb(153, 27, 27);
39
- border-radius: 5px;
40
- padding: 4px 8px`}
41
- >
42
- Clear errors and retry
43
- </button>
44
- <pre style={"margin-top: 8px;width:100%"}>{props.error.stack}</pre>
45
- </div>
46
- </div>
23
+ </DefaultErrorBoundary>
47
24
  );
48
25
  }
@@ -0,0 +1,74 @@
1
+ import * as shikiji from 'shikiji';
2
+ import { createEffect, createResource, type JSX } from 'solid-js';
3
+
4
+ let HIGHLIGHTER: shikiji.Highlighter;
5
+
6
+ async function loadHighlighter() {
7
+ if (!HIGHLIGHTER) {
8
+ HIGHLIGHTER = await shikiji.getHighlighter({
9
+ themes: ['dark-plus'],
10
+ langs: ['js', 'ts', 'jsx', 'tsx'],
11
+ });
12
+ }
13
+ return HIGHLIGHTER;
14
+ }
15
+
16
+ export interface CodeViewProps {
17
+ fileName: string;
18
+ content: string;
19
+ line: number;
20
+ }
21
+
22
+ const RANGE = 8;
23
+
24
+ export function CodeView(props: CodeViewProps): JSX.Element | null {
25
+ const lines = () =>
26
+ props.content.split('\n').map((item, index) => ({
27
+ index: index + 1,
28
+ line: item,
29
+ }));
30
+
31
+ const minLine = () => Math.max(props.line - (1 + RANGE), 0);
32
+ const maxLine = () => Math.min(props.line + RANGE, lines().length - 1);
33
+
34
+ let ref: HTMLDivElement | undefined;
35
+
36
+ const [data] = createResource(() => (
37
+ lines()
38
+ .slice(minLine(), maxLine())
39
+ .map(item => item.line)
40
+ .join('\n')
41
+ ) ,async (value) => {
42
+ const highlighter = await loadHighlighter();
43
+ const lang = props.fileName
44
+ .split(/[#?]/)[0]
45
+ .split('.')
46
+ .pop()
47
+ ?.trim() as shikiji.BuiltinLanguage;
48
+ await highlighter.loadLanguage(lang);
49
+ return highlighter.codeToHtml(value, {
50
+ theme: 'dark-plus',
51
+ lang,
52
+ });
53
+ });
54
+
55
+ createEffect(() => {
56
+ const result = data();
57
+ if (ref && result) {
58
+ ref.innerHTML = result;
59
+
60
+ const lines = ref.querySelectorAll('span[class="line"]');
61
+
62
+ for (let i = 0, len = lines.length; i < len; i++) {
63
+ const el = lines[i];
64
+ if ((props.line - minLine() - 1) === i) {
65
+ el.innerHTML = `<mark style="background-color:#aaaaaa80">${el.innerHTML}</mark>`;
66
+ }
67
+ }
68
+ }
69
+ });
70
+
71
+ return <div ref={ref} class="dev-overlay-code-view" style={{
72
+ '--dev-overlay-code-view-start': minLine() + 1,
73
+ }} />;
74
+ }