@solidjs/start 0.4.8 → 0.4.10

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.
@@ -22,6 +22,13 @@ import {
22
22
  } from "vinxi/server";
23
23
  import { getFetchEvent } from "../server/middleware";
24
24
 
25
+ function createChunk(data) {
26
+ const bytes = data.length;
27
+ const baseHex = bytes.toString(16);
28
+ const totalHex = '00000000'.substring(0, 8 - baseHex.length) + baseHex; // 32-bit
29
+ return new TextEncoder().encode(`;0x${totalHex};${data}`);
30
+ }
31
+
25
32
  function serializeToStream(id, value) {
26
33
  return new ReadableStream({
27
34
  start(controller) {
@@ -40,15 +47,12 @@ function serializeToStream(id, value) {
40
47
  URLPlugin
41
48
  ],
42
49
  onSerialize(data, initial) {
43
- const result = initial ? `(${getCrossReferenceHeader(id)},${data})` : data;
44
- controller.enqueue(new TextEncoder().encode(`${result};\n`));
50
+ controller.enqueue(createChunk(initial ? `(${getCrossReferenceHeader(id)},${data})` : data));
45
51
  },
46
52
  onDone() {
47
- // controller.enqueue(`delete $R["${id}"];\n`);
48
53
  controller.close();
49
54
  },
50
55
  onError(error) {
51
- // controller.enqueue(`delete $R["${id}"];\n`);
52
56
  controller.error(error);
53
57
  }
54
58
  });
@@ -13,54 +13,94 @@ import {
13
13
  } from 'seroval-plugins/web';
14
14
  import { createIslandReference } from "../server/islands";
15
15
 
16
- async function deserializeStream(id, response) {
17
- if (!response.body) {
18
- throw new Error("missing body");
16
+ class SerovalChunkReader {
17
+ constructor(stream) {
18
+ this.reader = stream.getReader();
19
+ this.buffer = '';
20
+ this.done = false;
19
21
  }
20
- const reader = response.body.getReader();
21
22
 
22
- async function pop() {
23
- const result = await reader.read();
24
- if (!result.done) {
25
- const serialized = new TextDecoder().decode(result.value);
26
- const splits = serialized.split("\n");
27
- for (const split of splits) {
28
- if (split !== "") {
29
- deserialize(split);
30
- }
31
- }
32
- await pop();
23
+ async readChunk() {
24
+ // if there's no chunk, read again
25
+ const chunk = await this.reader.read();
26
+ if (!chunk.done) {
27
+ // repopulate the buffer
28
+ this.buffer += new TextDecoder().decode(chunk.value);
29
+ } else {
30
+ this.done = true;
33
31
  }
34
32
  }
35
33
 
36
- const result = await reader.read();
37
- if (result.done) {
38
- throw new Error("Unexpected end of body");
39
- }
40
- const serialized = new TextDecoder().decode(result.value);
41
- let pending = true;
42
- let revived;
43
- const splits = serialized.split("\n");
44
- for (const split of splits) {
45
- if (split !== "") {
46
- const current = deserialize(split);
47
- if (pending) {
48
- revived = current;
49
- pending = false;
34
+ async next() {
35
+ // Check if the buffer is empty
36
+ if (this.buffer === '') {
37
+ // if we are already done...
38
+ if (this.done) {
39
+ return {
40
+ done: true,
41
+ value: undefined,
42
+ };
50
43
  }
44
+ // Otherwise, read a new chunk
45
+ await this.readChunk();
46
+ return await this.next();
51
47
  }
48
+ // Read the "byte header"
49
+ // The byte header tells us how big the expected data is
50
+ // so we know how much data we should wait before we
51
+ // deserialize the data
52
+ const bytes = Number.parseInt(this.buffer.substring(1, 11), 16); // ;0x00000000;
53
+ // Check if the buffer has enough bytes to be parsed
54
+ while (bytes > this.buffer.length - 12) {
55
+ // If it's not enough, and the reader is done
56
+ // then the chunk is invalid.
57
+ if (this.done) {
58
+ throw new Error('Malformed server function stream.')
59
+ }
60
+ // Otherwise, we read more chunks
61
+ await this.readChunk();
62
+ }
63
+ // Extract the exact chunk as defined by the byte header
64
+ const partial = this.buffer.substring(12, 12 + bytes);
65
+ // The rest goes to the buffer
66
+ this.buffer = this.buffer.substring(12 + bytes);
67
+ // Deserialize the chunk
68
+ return {
69
+ done: false,
70
+ value: deserialize(partial),
71
+ };
52
72
  }
53
73
 
54
- pop().then(
55
- () => {
56
- delete $R[id];
57
- },
58
- () => {
59
- // no-op
74
+ async drain() {
75
+ while (true) {
76
+ const result = await this.next();
77
+ if (result.done) {
78
+ break;
79
+ }
60
80
  }
61
- );
81
+ }
82
+ }
83
+
84
+ async function deserializeStream(id, response) {
85
+ if (!response.body) {
86
+ throw new Error("missing body");
87
+ }
88
+ const reader = new SerovalChunkReader(response.body);
89
+
90
+ const result = await reader.next();
91
+
92
+ if (!result.done) {
93
+ reader.drain().then(
94
+ () => {
95
+ delete $R[id];
96
+ },
97
+ () => {
98
+ // no-op
99
+ }
100
+ );
101
+ }
62
102
 
63
- return revived;
103
+ return result.value;
64
104
  }
65
105
 
66
106
  let INSTANCE = 0;
@@ -108,7 +148,7 @@ async function fetchServerFunction(base, id, args) {
108
148
  let result;
109
149
  if (contentType && contentType.startsWith("text/plain")) {
110
150
  result = await response.text();
111
- } else if(contentType && contentType.startsWith("application/json")) {
151
+ } else if (contentType && contentType.startsWith("application/json")) {
112
152
  result = await response.json();
113
153
  } else {
114
154
  result = deserializeStream(instance, response);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidjs/start",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "type": "module",
5
5
  "author": "Ryan Carniato",
6
6
  "publishConfig": {
@@ -43,13 +43,13 @@
43
43
  }
44
44
  },
45
45
  "devDependencies": {
46
- "solid-js": "^1.8.10",
47
- "vinxi": "0.1.1"
46
+ "solid-js": "^1.8.11",
47
+ "vinxi": "^0.1.4"
48
48
  },
49
49
  "dependencies": {
50
- "@vinxi/plugin-directives": "^0.1.0",
51
- "@vinxi/server-components": "^0.1.0",
52
- "@vinxi/server-functions": "^0.1.0",
50
+ "@vinxi/plugin-directives": "^0.1.2",
51
+ "@vinxi/server-components": "^0.1.2",
52
+ "@vinxi/server-functions": "^0.1.2",
53
53
  "defu": "^6.1.2",
54
54
  "error-stack-parser": "^2.1.4",
55
55
  "html-to-image": "^1.11.11",
@@ -1,8 +1,6 @@
1
1
  import {
2
2
  defineMiddleware,
3
- EventHandlerRequest,
4
3
  getRequestIP,
5
- getRequestURL,
6
4
  H3Event,
7
5
  sendWebResponse,
8
6
  toWebRequest
@@ -18,15 +16,11 @@ const eventTraps = {
18
16
  }
19
17
  };
20
18
 
21
- export function createFetchEvent(event: H3Event<EventHandlerRequest>): FetchEvent {
22
- event.web ||
23
- (event.web = {
24
- url: getRequestURL(event),
25
- request: toWebRequest(event)
26
- });
19
+ export function createFetchEvent(event: H3Event): FetchEvent {
20
+ const request = toWebRequest(event);
27
21
  return new Proxy(
28
22
  {
29
- request: event.web.request,
23
+ request: request,
30
24
  clientAddress: getRequestIP(event),
31
25
  locals: {},
32
26
  // @ts-ignore
@@ -1,8 +1,9 @@
1
1
  import type { BuiltinLanguage, Highlighter } from 'shikiji';
2
- import { getHighlighterCore } from 'shikiji/core';
3
- import { getWasmInlined } from 'shikiji/wasm';
2
+ import { getHighlighterCore, loadWasm } from 'shikiji/core';
4
3
  import { createEffect, createResource, type JSX } from 'solid-js';
5
4
 
5
+ import url from 'shikiji/onig.wasm?url';
6
+
6
7
  import langJS from 'shikiji/langs/javascript.mjs';
7
8
  import langJSX from 'shikiji/langs/jsx.mjs';
8
9
  import langTSX from 'shikiji/langs/tsx.mjs';
@@ -13,6 +14,7 @@ let HIGHLIGHTER: Highlighter;
13
14
 
14
15
  async function loadHighlighter() {
15
16
  if (!HIGHLIGHTER) {
17
+ await loadWasm(await fetch(url))
16
18
  HIGHLIGHTER = await getHighlighterCore({
17
19
  themes: [
18
20
  darkPlus,
@@ -23,7 +25,6 @@ async function loadHighlighter() {
23
25
  langTS,
24
26
  langTSX,
25
27
  ],
26
- loadWasm: getWasmInlined,
27
28
  });
28
29
  }
29
30
  return HIGHLIGHTER;
@@ -77,7 +78,7 @@ export function CodeView(props: CodeViewProps): JSX.Element | null {
77
78
  for (let i = 0, len = lines.length; i < len; i++) {
78
79
  const el = lines[i];
79
80
  if ((props.line - minLine() - 1) === i) {
80
- el.innerHTML = `<mark style="background-color:#aaaaaa80">${el.innerHTML}</mark>`;
81
+ el.classList.add('dev-overlay-error-line');
81
82
  }
82
83
  }
83
84
  }
@@ -281,4 +281,8 @@
281
281
  display: inline-block;
282
282
  text-align: right;
283
283
  color: rgba(115, 138, 148, .4)
284
+ }
285
+
286
+ .dev-overlay-error-line {
287
+ background-color: rgba(239, 68, 68, .4);
284
288
  }
@@ -1,60 +1,54 @@
1
1
  /// <reference types="vinxi/types/client" />
2
2
  import { createComponent, lazy, onCleanup } from "solid-js";
3
- import { appendStyles, cleanupStyles, updateStyles } from "vinxi/css";
3
+ import { appendStyles, cleanupStyles, updateStyles, preloadStyles } from "vinxi/css";
4
4
 
5
5
  import { renderAsset } from "../server/renderAsset";
6
6
 
7
- export default function lazyRoute(
8
- component,
9
- clientManifest,
10
- serverManifest,
11
- exported = "default",
12
- ) {
13
- return lazy(async () => {
14
- if (import.meta.env.DEV) {
15
- let manifest = import.meta.env.SSR ? serverManifest : clientManifest;
7
+ export default function lazyRoute(component, clientManifest, serverManifest, exported = "default") {
8
+ return lazy(async () => {
9
+ if (import.meta.env.DEV) {
10
+ let manifest = import.meta.env.SSR ? serverManifest : clientManifest;
16
11
 
17
- const mod = await manifest.inputs[component.src].import();
18
- if (!mod[exported]) console.error(`Module ${component.src} does not export ${exported}`)
19
- const Component = mod[exported];
20
- let assets = await clientManifest.inputs?.[component.src].assets();
21
- const styles = assets.filter((asset) => asset.tag === "style");
12
+ const mod = await manifest.inputs[component.src].import();
13
+ if (!mod[exported]) console.error(`Module ${component.src} does not export ${exported}`);
14
+ const Component = mod[exported];
15
+ let assets = await clientManifest.inputs?.[component.src].assets();
16
+ const styles = assets.filter(asset => asset.tag === "style");
22
17
 
23
- if (typeof window !== "undefined" && import.meta.hot) {
24
- import.meta.hot.on("css-update", (data) => {
25
- updateStyles(styles, data);
26
- });
27
- }
18
+ if (typeof window !== "undefined" && import.meta.hot) {
19
+ import.meta.hot.on("css-update", data => {
20
+ updateStyles(styles, data);
21
+ });
22
+ }
28
23
 
29
- const Comp = (props) => {
30
- if (typeof window !== "undefined") {
31
- appendStyles(styles);
32
- }
24
+ const Comp = props => {
25
+ if (typeof window !== "undefined") {
26
+ appendStyles(styles);
27
+ }
33
28
 
34
- onCleanup(() => {
35
- if (typeof window !== "undefined") {
36
- // remove style tags added by vite when a CSS file is imported
37
- cleanupStyles(styles);
38
- }
39
- });
40
- return [
41
- ...assets.map((asset) => renderAsset(asset)),
42
- createComponent(Component, props),
43
- ];
44
- };
45
- return { default: Comp };
46
- } else {
47
- const mod = await component.import();
48
- const Component = mod[exported];
49
- let assets = await clientManifest.inputs?.[component.src].assets();
50
- const styles = assets.filter((asset) => asset.tag === "style" || asset.attrs.rel === "stylesheet");
51
- const Comp = (props) => {
52
- return [
53
- ...styles.map((asset) => renderAsset(asset)),
54
- createComponent(Component, props),
55
- ];
56
- };
57
- return { default: Comp };
58
- }
59
- });
29
+ onCleanup(() => {
30
+ if (typeof window !== "undefined") {
31
+ // remove style tags added by vite when a CSS file is imported
32
+ cleanupStyles(styles);
33
+ }
34
+ });
35
+ return [...assets.map(asset => renderAsset(asset)), createComponent(Component, props)];
36
+ };
37
+ return { default: Comp };
38
+ } else {
39
+ const mod = await component.import();
40
+ const Component = mod[exported];
41
+ let assets = await clientManifest.inputs?.[component.src].assets();
42
+ const styles = assets.filter(
43
+ asset => asset.tag === "style" || asset.attrs.rel === "stylesheet"
44
+ );
45
+ if (typeof window !== "undefined") {
46
+ preloadStyles(styles);
47
+ }
48
+ const Comp = props => {
49
+ return [...styles.map(asset => renderAsset(asset)), createComponent(Component, props)];
50
+ };
51
+ return { default: Comp };
52
+ }
53
+ });
60
54
  }