@solidjs/start 0.4.2 → 0.4.4
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 +8 -0
- package/config/index.js +12 -12
- package/config/server-fns-runtime.jsx +1 -1
- package/config/server-handler.js +38 -35
- package/config/server-runtime.jsx +9 -1
- package/package.json +13 -8
- package/server/handler.tsx +6 -1
- package/server/spa/StartServer.tsx +0 -1
- package/server/types.ts +2 -2
- package/shared/ErrorBoundary.tsx +16 -39
- package/shared/FileRoutes.tsx +5 -2
- package/shared/dev-overlay/CodeView.tsx +89 -0
- package/shared/dev-overlay/DevOverlayDialog.tsx +277 -0
- package/shared/dev-overlay/createStackFrame.ts +71 -0
- package/shared/dev-overlay/download.ts +6 -0
- package/shared/dev-overlay/env.d.ts +4 -0
- package/shared/dev-overlay/get-source-map.ts +35 -0
- package/shared/dev-overlay/icons.tsx +287 -0
- package/shared/dev-overlay/index.tsx +60 -0
- package/shared/dev-overlay/styles.css +287 -0
- package/shared/routes.ts +3 -2
package/config/index.d.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { AppOptions } from "vinxi";
|
|
2
2
|
import { InlineConfig } from "vite";
|
|
3
|
+
import type { Options } from "vite-plugin-solid";
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
type SolidStartInlineConfig = Omit<InlineConfig, "router"> & {
|
|
5
7
|
start?: {
|
|
8
|
+
/**
|
|
9
|
+
* true: streaming mode
|
|
10
|
+
* false: csr only
|
|
11
|
+
* async: ssr is in async mode
|
|
12
|
+
*/
|
|
6
13
|
ssr?: boolean | "async",
|
|
14
|
+
solid: Options,
|
|
7
15
|
extensions?: string[],
|
|
8
16
|
server?: AppOptions['server'],
|
|
9
17
|
appRoot?: string,
|
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 = {},
|
|
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
|
-
|
|
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, {
|
package/config/server-handler.js
CHANGED
|
@@ -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,16 +10,14 @@ import {
|
|
|
10
10
|
RequestPlugin,
|
|
11
11
|
ResponsePlugin,
|
|
12
12
|
URLPlugin,
|
|
13
|
-
URLSearchParamsPlugin
|
|
14
|
-
} from
|
|
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 {
|
|
18
20
|
eventHandler,
|
|
19
|
-
getHeader,
|
|
20
|
-
getRequestURL,
|
|
21
|
-
readBody,
|
|
22
|
-
readFormData,
|
|
23
21
|
setHeader
|
|
24
22
|
} from "vinxi/server";
|
|
25
23
|
import { getFetchEvent } from "../server/middleware";
|
|
@@ -39,10 +37,10 @@ function serializeToStream(id, value) {
|
|
|
39
37
|
RequestPlugin,
|
|
40
38
|
ResponsePlugin,
|
|
41
39
|
URLSearchParamsPlugin,
|
|
42
|
-
URLPlugin
|
|
40
|
+
URLPlugin
|
|
43
41
|
],
|
|
44
42
|
onSerialize(data, initial) {
|
|
45
|
-
const result = initial ? `($
|
|
43
|
+
const result = initial ? `(${getCrossReferenceHeader(id)},${data})` : data;
|
|
46
44
|
controller.enqueue(new TextEncoder().encode(`${result};\n`));
|
|
47
45
|
},
|
|
48
46
|
onDone() {
|
|
@@ -58,12 +56,14 @@ function serializeToStream(id, value) {
|
|
|
58
56
|
});
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
async function handleServerFunction(
|
|
62
|
-
invariant(
|
|
59
|
+
async function handleServerFunction(h3Event) {
|
|
60
|
+
invariant(h3Event.method === "POST", `Invalid method ${h3Event.method}. Expected POST.`);
|
|
61
|
+
const event = getFetchEvent(h3Event);
|
|
62
|
+
const request = event.request;
|
|
63
63
|
|
|
64
|
-
const serverReference =
|
|
65
|
-
const instance =
|
|
66
|
-
const url =
|
|
64
|
+
const serverReference = request.headers.get("x-server-id");
|
|
65
|
+
const instance = request.headers.get("x-server-instance");
|
|
66
|
+
const url = new URL(request.url);
|
|
67
67
|
let filepath, name;
|
|
68
68
|
if (serverReference) {
|
|
69
69
|
invariant(typeof serverReference === "string", "Invalid server function");
|
|
@@ -84,14 +84,14 @@ async function handleServerFunction(event) {
|
|
|
84
84
|
const args = url.searchParams.get("args");
|
|
85
85
|
if (args) JSON.parse(args).forEach(arg => parsed.push(arg));
|
|
86
86
|
}
|
|
87
|
-
const contentType =
|
|
87
|
+
const contentType = request.headers.get("content-type");
|
|
88
88
|
if (
|
|
89
89
|
contentType.startsWith("multipart/form-data") ||
|
|
90
90
|
contentType.startsWith("application/x-www-form-urlencoded")
|
|
91
91
|
) {
|
|
92
|
-
parsed.push(await
|
|
92
|
+
parsed.push(await request.formData());
|
|
93
93
|
} else {
|
|
94
|
-
parsed = fromJSON(await
|
|
94
|
+
parsed = fromJSON(await request.json(), {
|
|
95
95
|
plugins: [
|
|
96
96
|
CustomEventPlugin,
|
|
97
97
|
DOMExceptionPlugin,
|
|
@@ -102,32 +102,40 @@ async function handleServerFunction(event) {
|
|
|
102
102
|
RequestPlugin,
|
|
103
103
|
ResponsePlugin,
|
|
104
104
|
URLSearchParamsPlugin,
|
|
105
|
-
URLPlugin
|
|
106
|
-
]
|
|
105
|
+
URLPlugin
|
|
106
|
+
]
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
try {
|
|
110
|
-
const result = await provideRequestEvent(
|
|
110
|
+
const result = await provideRequestEvent(event, () => {
|
|
111
|
+
/* @ts-ignore */
|
|
112
|
+
sharedConfig.context = { event };
|
|
113
|
+
return action(...parsed);
|
|
114
|
+
});
|
|
111
115
|
|
|
112
116
|
// handle no JS success case
|
|
113
117
|
if (!instance) {
|
|
114
118
|
const isError = result instanceof Error;
|
|
115
|
-
const refererUrl = new URL(
|
|
119
|
+
const refererUrl = new URL(request.headers.get("referer"));
|
|
116
120
|
return new Response(null, {
|
|
117
121
|
status: 302,
|
|
118
122
|
headers: {
|
|
119
123
|
Location: refererUrl.toString(),
|
|
120
|
-
...(result
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
...(result
|
|
125
|
+
? {
|
|
126
|
+
"Set-Cookie": `flash=${JSON.stringify({
|
|
127
|
+
url: url.pathname + encodeURIComponent(url.search),
|
|
128
|
+
result: isError ? result.message : result,
|
|
129
|
+
error: isError,
|
|
130
|
+
input: [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]]
|
|
131
|
+
})}; Secure; HttpOnly;`
|
|
132
|
+
}
|
|
133
|
+
: {})
|
|
126
134
|
}
|
|
127
135
|
});
|
|
128
136
|
}
|
|
129
|
-
|
|
130
|
-
setHeader(
|
|
137
|
+
if (typeof result === "string") return new Response(result);
|
|
138
|
+
setHeader(h3Event, "content-type", "text/javascript");
|
|
131
139
|
return serializeToStream(instance, result);
|
|
132
140
|
} catch (x) {
|
|
133
141
|
if (x instanceof Response && x.status === 302) {
|
|
@@ -138,12 +146,7 @@ async function handleServerFunction(event) {
|
|
|
138
146
|
}
|
|
139
147
|
});
|
|
140
148
|
}
|
|
141
|
-
return
|
|
142
|
-
status: 500,
|
|
143
|
-
headers: {
|
|
144
|
-
"Content-Type": "text/javascript"
|
|
145
|
-
}
|
|
146
|
-
});
|
|
149
|
+
return x;
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
|
|
@@ -104,7 +104,15 @@ async function fetchServerFunction(base, id, args) {
|
|
|
104
104
|
}
|
|
105
105
|
throw response;
|
|
106
106
|
}
|
|
107
|
-
const
|
|
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.
|
|
3
|
+
"version": "0.4.4",
|
|
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.
|
|
42
|
-
"vinxi": "0.
|
|
41
|
+
"solid-js": "^1.8.8",
|
|
42
|
+
"vinxi": "0.1.1"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@vinxi/plugin-directives": "0.0
|
|
46
|
-
"@vinxi/server-components": "0.0
|
|
47
|
-
"@vinxi/server-functions": "0.0
|
|
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
|
-
"
|
|
50
|
-
"
|
|
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
|
},
|
package/server/handler.tsx
CHANGED
|
@@ -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(() =>
|
|
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
|
}
|
package/server/types.ts
CHANGED
package/shared/ErrorBoundary.tsx
CHANGED
|
@@ -1,48 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
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
|
-
<
|
|
17
|
+
<DefaultErrorBoundary
|
|
18
|
+
fallback={(
|
|
19
|
+
<HttpStatusCode code={500} />
|
|
20
|
+
)}
|
|
21
|
+
>
|
|
11
22
|
{props.children}
|
|
12
|
-
</
|
|
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
|
}
|
package/shared/FileRoutes.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { getRequestEvent, isServer } from "solid-js/web";
|
|
3
3
|
import lazyRoute from "./lazyRoute";
|
|
4
4
|
|
|
5
|
-
import { PageEvent } from "../server";
|
|
5
|
+
import type { PageEvent } from "../server/types";
|
|
6
6
|
import { pageRoutes as routeConfigs } from "./routes";
|
|
7
7
|
|
|
8
8
|
export function createRoutes() {
|
|
@@ -10,7 +10,10 @@ export function createRoutes() {
|
|
|
10
10
|
return {
|
|
11
11
|
...route,
|
|
12
12
|
...(route.$$route ? route.$$route.require().route : undefined),
|
|
13
|
-
metadata: {
|
|
13
|
+
metadata: {
|
|
14
|
+
...(route.$$route ? route.$$route.require().route.metadata : {}),
|
|
15
|
+
filesystem: true
|
|
16
|
+
},
|
|
14
17
|
component: lazyRoute(
|
|
15
18
|
route.$component,
|
|
16
19
|
import.meta.env.START_ISLANDS
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { BuiltinLanguage, Highlighter } from 'shikiji';
|
|
2
|
+
import { getHighlighterCore } from 'shikiji/core';
|
|
3
|
+
import { getWasmInlined } from 'shikiji/wasm';
|
|
4
|
+
import { createEffect, createResource, type JSX } from 'solid-js';
|
|
5
|
+
|
|
6
|
+
import langJS from 'shikiji/langs/javascript.mjs';
|
|
7
|
+
import langJSX from 'shikiji/langs/jsx.mjs';
|
|
8
|
+
import langTSX from 'shikiji/langs/tsx.mjs';
|
|
9
|
+
import langTS from 'shikiji/langs/typescript.mjs';
|
|
10
|
+
import darkPlus from 'shikiji/themes/dark-plus.mjs';
|
|
11
|
+
|
|
12
|
+
let HIGHLIGHTER: Highlighter;
|
|
13
|
+
|
|
14
|
+
async function loadHighlighter() {
|
|
15
|
+
if (!HIGHLIGHTER) {
|
|
16
|
+
HIGHLIGHTER = await getHighlighterCore({
|
|
17
|
+
themes: [
|
|
18
|
+
darkPlus,
|
|
19
|
+
],
|
|
20
|
+
langs: [
|
|
21
|
+
langJS,
|
|
22
|
+
langJSX,
|
|
23
|
+
langTS,
|
|
24
|
+
langTSX,
|
|
25
|
+
],
|
|
26
|
+
loadWasm: getWasmInlined,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return HIGHLIGHTER;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CodeViewProps {
|
|
33
|
+
fileName: string;
|
|
34
|
+
content: string;
|
|
35
|
+
line: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const RANGE = 8;
|
|
39
|
+
|
|
40
|
+
export function CodeView(props: CodeViewProps): JSX.Element | null {
|
|
41
|
+
const lines = () =>
|
|
42
|
+
props.content.split('\n').map((item, index) => ({
|
|
43
|
+
index: index + 1,
|
|
44
|
+
line: item,
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
const minLine = () => Math.max(props.line - (1 + RANGE), 0);
|
|
48
|
+
const maxLine = () => Math.min(props.line + RANGE, lines().length - 1);
|
|
49
|
+
|
|
50
|
+
let ref: HTMLDivElement | undefined;
|
|
51
|
+
|
|
52
|
+
const [data] = createResource(() => (
|
|
53
|
+
lines()
|
|
54
|
+
.slice(minLine(), maxLine())
|
|
55
|
+
.map(item => item.line)
|
|
56
|
+
.join('\n')
|
|
57
|
+
) ,async (value) => {
|
|
58
|
+
const highlighter = await loadHighlighter();
|
|
59
|
+
const lang = props.fileName
|
|
60
|
+
.split(/[#?]/)[0]
|
|
61
|
+
.split('.')
|
|
62
|
+
.pop()
|
|
63
|
+
?.trim() as BuiltinLanguage;
|
|
64
|
+
return highlighter.codeToHtml(value, {
|
|
65
|
+
theme: 'dark-plus',
|
|
66
|
+
lang,
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
createEffect(() => {
|
|
71
|
+
const result = data();
|
|
72
|
+
if (ref && result) {
|
|
73
|
+
ref.innerHTML = result;
|
|
74
|
+
|
|
75
|
+
const lines = ref.querySelectorAll('span[class="line"]');
|
|
76
|
+
|
|
77
|
+
for (let i = 0, len = lines.length; i < len; i++) {
|
|
78
|
+
const el = lines[i];
|
|
79
|
+
if ((props.line - minLine() - 1) === i) {
|
|
80
|
+
el.innerHTML = `<mark style="background-color:#aaaaaa80">${el.innerHTML}</mark>`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return <div ref={ref} class="dev-overlay-code-view" style={{
|
|
87
|
+
'--dev-overlay-code-view-start': minLine() + 1,
|
|
88
|
+
}} />;
|
|
89
|
+
}
|