@solidrt/cli 0.0.17 → 0.0.18
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/package.json +8 -8
- package/scaffold/package.json +3 -3
- package/scaffold/tsconfig.json +2 -0
- package/src/bundler.ts +63 -12
- package/src/commands/bundle.ts +3 -8
- package/src/commands/server.ts +5 -5
- package/src/dev-client.ts +3 -3
- package/src/dev-server.ts +18 -0
- package/src/repl.ts +5 -8
- package/src/watcher.ts +4 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Antoine van Wel",
|
|
6
6
|
"type": "module",
|
|
@@ -14,20 +14,20 @@
|
|
|
14
14
|
"AGENTS.md"
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@babel/core": "^
|
|
18
|
-
"@babel/plugin-syntax-jsx": "^
|
|
19
|
-
"@babel/preset-typescript": "^
|
|
17
|
+
"@babel/core": "^7.0.0",
|
|
18
|
+
"@babel/plugin-syntax-jsx": "^7.0.0",
|
|
19
|
+
"@babel/preset-typescript": "^7.0.0",
|
|
20
20
|
"babel-preset-solid": "2.0.0-beta.15",
|
|
21
21
|
"bonjour-service": "^1.4.0",
|
|
22
22
|
"qrcode-generator": "^2.0.4"
|
|
23
23
|
},
|
|
24
24
|
"optionalDependencies": {
|
|
25
|
-
"@solidrt/darwin-arm64": "0.0.
|
|
26
|
-
"@solidrt/linux-x64-gnu": "0.0.
|
|
27
|
-
"@solidrt/win32-x64-msvc": "0.0.
|
|
25
|
+
"@solidrt/darwin-arm64": "0.0.18",
|
|
26
|
+
"@solidrt/linux-x64-gnu": "0.0.18",
|
|
27
|
+
"@solidrt/win32-x64-msvc": "0.0.18"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@solidrt/core": "0.0.
|
|
30
|
+
"@solidrt/core": "0.0.18",
|
|
31
31
|
"typescript": "^6"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
package/scaffold/package.json
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
"bundle": "srt bundle src/index.tsx"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@solidrt/core": "
|
|
10
|
+
"@solidrt/core": "0.0.18"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@solidrt/cli": "
|
|
14
|
-
"@solidrt/flux-types": "
|
|
13
|
+
"@solidrt/cli": "0.0.0",
|
|
14
|
+
"@solidrt/flux-types": "0.0.0",
|
|
15
15
|
"typescript": "^6"
|
|
16
16
|
}
|
|
17
17
|
}
|
package/scaffold/tsconfig.json
CHANGED
package/src/bundler.ts
CHANGED
|
@@ -2,10 +2,65 @@ import { transformAsync } from "@babel/core"
|
|
|
2
2
|
import jsx from "@babel/plugin-syntax-jsx"
|
|
3
3
|
import ts from "@babel/preset-typescript"
|
|
4
4
|
import solid from "babel-preset-solid"
|
|
5
|
-
import { type BunPlugin } from "bun"
|
|
5
|
+
import { type BunPlugin, type BuildArtifact } from "bun"
|
|
6
|
+
import { readFileSync } from "node:fs"
|
|
7
|
+
import { dirname, resolve as resolvePath } from "node:path"
|
|
6
8
|
import { values, source } from "./args"
|
|
7
9
|
import { state, print, requireBinary } from "./util"
|
|
8
10
|
|
|
11
|
+
// Babel plugin: rewrite `import data from "./x" with { type: "binary" }` into an
|
|
12
|
+
// inline Uint8Array of the file's bytes. The import attribute is invisible to
|
|
13
|
+
// Bun's bundler and its plugins in this Bun version, so we handle it here in the
|
|
14
|
+
// transform where the AST still carries it. Inlining (rather than emitting a
|
|
15
|
+
// separate asset) keeps a single bundle output and hands JS a Uint8Array, which
|
|
16
|
+
// is what createImage and friends expect. Decoded at runtime via the global
|
|
17
|
+
// atob; for ASCII-extension files (.jpg/.png/...) we may add an attribute-free
|
|
18
|
+
// path later.
|
|
19
|
+
function binaryImport({ types: t }: { types: any }) {
|
|
20
|
+
return {
|
|
21
|
+
visitor: {
|
|
22
|
+
ImportDeclaration(path: any, pluginState: any) {
|
|
23
|
+
let attrs = path.node.attributes ?? path.node.assertions
|
|
24
|
+
let isBinary = attrs?.some((a: any) => a.key.name === "type" && a.value.value === "binary")
|
|
25
|
+
if (!isBinary) return
|
|
26
|
+
|
|
27
|
+
let def = path.node.specifiers.find((s: any) => s.type === "ImportDefaultSpecifier")
|
|
28
|
+
if (!def) {
|
|
29
|
+
throw path.buildCodeFrameError(
|
|
30
|
+
'A binary import needs a default import: import data from "./file" with { type: "binary" }',
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let importer = pluginState.file.opts.filename as string
|
|
35
|
+
let abs = resolvePath(dirname(importer), path.node.source.value)
|
|
36
|
+
let b64 = readFileSync(abs).toString("base64")
|
|
37
|
+
|
|
38
|
+
// var <local> = Uint8Array.from(atob("<b64>"), c => c.charCodeAt(0))
|
|
39
|
+
let expr = t.callExpression(t.memberExpression(t.identifier("Uint8Array"), t.identifier("from")), [
|
|
40
|
+
t.callExpression(t.identifier("atob"), [t.stringLiteral(b64)]),
|
|
41
|
+
t.arrowFunctionExpression(
|
|
42
|
+
[t.identifier("c")],
|
|
43
|
+
t.callExpression(t.memberExpression(t.identifier("c"), t.identifier("charCodeAt")), [t.numericLiteral(0)]),
|
|
44
|
+
),
|
|
45
|
+
])
|
|
46
|
+
path.replaceWith(t.variableDeclaration("var", [t.variableDeclarator(t.identifier(def.local.name), expr)]))
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Concatenate only the JS outputs (entry point plus any code-split chunks) of a
|
|
53
|
+
// build, skipping emitted asset outputs. Bun's file loader emits binary assets
|
|
54
|
+
// as extra outputs; the callers that flatten outputs into a single code string
|
|
55
|
+
// must not glue those raw bytes onto the program.
|
|
56
|
+
export async function codeFromOutputs(outputs: BuildArtifact[]): Promise<string> {
|
|
57
|
+
let code = ""
|
|
58
|
+
for (let o of outputs) {
|
|
59
|
+
if (o.kind === "entry-point" || o.kind === "chunk") code += await o.text()
|
|
60
|
+
}
|
|
61
|
+
return code
|
|
62
|
+
}
|
|
63
|
+
|
|
9
64
|
// Bun build plugin that runs JSX/TSX through babel-preset-solid (universal
|
|
10
65
|
// generate, targeting @solidrt/core) plus the TS preset.
|
|
11
66
|
function solidPlugin(): BunPlugin {
|
|
@@ -18,7 +73,7 @@ function solidPlugin(): BunPlugin {
|
|
|
18
73
|
let transforms = await transformAsync(code, {
|
|
19
74
|
filename: args.path,
|
|
20
75
|
presets: [[solid, { moduleName: "@solidrt/core", generate: "universal" }], [ts]],
|
|
21
|
-
plugins: [jsx],
|
|
76
|
+
plugins: [jsx, binaryImport],
|
|
22
77
|
})
|
|
23
78
|
return { contents: transforms?.code ?? "", loader: "js" }
|
|
24
79
|
})
|
|
@@ -31,7 +86,8 @@ export async function bundle(entry = source) {
|
|
|
31
86
|
|
|
32
87
|
let devBase = state.serverUrl ?? undefined
|
|
33
88
|
let dev = !!devBase || values.dev
|
|
34
|
-
|
|
89
|
+
// Keep stdout clean when the bundle itself is written to stdout.
|
|
90
|
+
if (!values.stdout) print(`[cli] Bundling (${dev ? "development" : "production"})`)
|
|
35
91
|
let define: Record<string, string> = {
|
|
36
92
|
"process.env.NODE_ENV": dev ? "development" : "production",
|
|
37
93
|
}
|
|
@@ -45,6 +101,7 @@ export async function bundle(entry = source) {
|
|
|
45
101
|
minify: values.minify,
|
|
46
102
|
external: ["flux:*", "srt:*"],
|
|
47
103
|
define,
|
|
104
|
+
loader: { ".svg": "text" },
|
|
48
105
|
plugins: [solidPlugin()],
|
|
49
106
|
})
|
|
50
107
|
} catch (e) {
|
|
@@ -68,9 +125,7 @@ export async function bundleTo(outfile: string) {
|
|
|
68
125
|
console.error("Build failed")
|
|
69
126
|
process.exit(1)
|
|
70
127
|
}
|
|
71
|
-
|
|
72
|
-
await Bun.write(outfile, output)
|
|
73
|
-
}
|
|
128
|
+
await Bun.write(outfile, await codeFromOutputs(result.outputs))
|
|
74
129
|
return result
|
|
75
130
|
}
|
|
76
131
|
|
|
@@ -88,9 +143,7 @@ export async function bundleFlux(entry: string): Promise<string> {
|
|
|
88
143
|
console.error("Build failed")
|
|
89
144
|
process.exit(1)
|
|
90
145
|
}
|
|
91
|
-
|
|
92
|
-
for (let output of result.outputs) jsCode += await output.text()
|
|
93
|
-
return jsCode
|
|
146
|
+
return codeFromOutputs(result.outputs)
|
|
94
147
|
}
|
|
95
148
|
|
|
96
149
|
// Bundle for the SolidRT runtime via the standard Solid-aware bundler.
|
|
@@ -100,9 +153,7 @@ export async function bundleSolid(): Promise<string> {
|
|
|
100
153
|
console.error("Build failed")
|
|
101
154
|
process.exit(1)
|
|
102
155
|
}
|
|
103
|
-
|
|
104
|
-
for (let output of result.outputs) jsCode += await output.text()
|
|
105
|
-
return jsCode
|
|
156
|
+
return codeFromOutputs(result.outputs)
|
|
106
157
|
}
|
|
107
158
|
|
|
108
159
|
// Compile JS source to QuickJS bytecode via the fluxc binary.
|
package/src/commands/bundle.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { values, source, isPrebuilt } from "../args"
|
|
2
|
-
import { bundle, bundleTo, bundleFlux, compileToBytecode } from "../bundler"
|
|
2
|
+
import { bundle, bundleTo, bundleFlux, compileToBytecode, codeFromOutputs } from "../bundler"
|
|
3
3
|
import { resolve } from "path"
|
|
4
4
|
|
|
5
5
|
// Write to stdout and resolve only once the whole payload is flushed.
|
|
@@ -55,9 +55,7 @@ export async function runBundleCommand() {
|
|
|
55
55
|
console.error("Build failed")
|
|
56
56
|
process.exit(1)
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
await writeStdout(await output.text())
|
|
60
|
-
}
|
|
58
|
+
await writeStdout(await codeFromOutputs(result.outputs))
|
|
61
59
|
process.exit()
|
|
62
60
|
}
|
|
63
61
|
|
|
@@ -67,10 +65,7 @@ export async function runBundleCommand() {
|
|
|
67
65
|
console.error("Build failed")
|
|
68
66
|
process.exit(1)
|
|
69
67
|
}
|
|
70
|
-
let jsCode =
|
|
71
|
-
for (let output of result.outputs) {
|
|
72
|
-
jsCode += await output.text()
|
|
73
|
-
}
|
|
68
|
+
let jsCode = await codeFromOutputs(result.outputs)
|
|
74
69
|
await writeBytecode(jsCode, baseName + ".srt.bin")
|
|
75
70
|
process.exit()
|
|
76
71
|
}
|
package/src/commands/server.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import pkg from "../../package.json"
|
|
2
2
|
import { source, isSource, isPrebuilt, values } from "../args"
|
|
3
3
|
import { state, shutdown } from "../util"
|
|
4
|
-
import { bundle } from "../bundler"
|
|
5
|
-
import { startServer } from "../dev-server"
|
|
4
|
+
import { bundle, codeFromOutputs } from "../bundler"
|
|
5
|
+
import { startServer, showBuildFailure } from "../dev-server"
|
|
6
6
|
import { startRepl } from "../repl"
|
|
7
7
|
import { startWatcher } from "../watcher"
|
|
8
8
|
import * as cache from "../cache"
|
|
@@ -28,9 +28,9 @@ export async function runServerCommand() {
|
|
|
28
28
|
if (source && isSource) {
|
|
29
29
|
let initialResult = await bundle()
|
|
30
30
|
if (initialResult) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
state.currentCode = await codeFromOutputs(initialResult.outputs)
|
|
32
|
+
} else {
|
|
33
|
+
showBuildFailure()
|
|
34
34
|
}
|
|
35
35
|
} else if (source && isPrebuilt && source.endsWith(".srt.js")) {
|
|
36
36
|
state.currentCode = await Bun.file(resolve(source)).text()
|
package/src/dev-client.ts
CHANGED
|
@@ -17,11 +17,11 @@ function pipeAbovePrompt(stream: ReadableStream<Uint8Array>, out: NodeJS.WriteSt
|
|
|
17
17
|
|
|
18
18
|
export function spawnClient() {
|
|
19
19
|
let runner = requireBinary("solidrt-go")
|
|
20
|
-
|
|
20
|
+
// The local client and dev server share this machine, so connect straight to
|
|
21
|
+
// the loopback server: no mDNS discovery or recents lookup is needed for `run`.
|
|
22
|
+
let args: string[] = ["--dev-server", `${DEV_HOST}:${DEV_PORT}`]
|
|
21
23
|
if (values.size) args.push("--size", values.size)
|
|
22
24
|
state.child = Bun.spawn([runner, ...args], {
|
|
23
|
-
//TODO implement dev server connection
|
|
24
|
-
// state.child = Bun.spawn([runner, "--dev-server", `${DEV_HOST}:${DEV_PORT}`], {
|
|
25
25
|
stdio: ["ignore", "pipe", "pipe"],
|
|
26
26
|
})
|
|
27
27
|
|
package/src/dev-server.ts
CHANGED
|
@@ -22,6 +22,23 @@ export function broadcast(msg: object) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// Reload code that fails to start the engine on purpose. The runtime treats a
|
|
26
|
+
// startup error like any app that never called render() and falls back to its
|
|
27
|
+
// baked-in BSOD screen, so a build that doesn't compile shows the BSOD instead
|
|
28
|
+
// of leaving the previous app frozen on screen.
|
|
29
|
+
const BSOD_TRIGGER = `throw new Error("SolidRT: build failed")`
|
|
30
|
+
|
|
31
|
+
// Called when a bundle fails to compile. Latches the BSOD trigger as the
|
|
32
|
+
// current code (so a client connecting after the failure gets it too) and
|
|
33
|
+
// pushes it to every connected client.
|
|
34
|
+
export function showBuildFailure() {
|
|
35
|
+
state.currentCode = BSOD_TRIGGER
|
|
36
|
+
let msg = buildReload({ code: BSOD_TRIGGER })
|
|
37
|
+
for (let ws of state.clients.keys()) {
|
|
38
|
+
ws.send(msg)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
function headersToObject(h: Headers): Record<string, string> {
|
|
26
43
|
let out: Record<string, string> = {}
|
|
27
44
|
h.forEach((v, k) => {
|
|
@@ -51,6 +68,7 @@ async function handleProxy(req: Request): Promise<Response> {
|
|
|
51
68
|
print("[cli] proxy %s %s [cache hit]", req.method, target)
|
|
52
69
|
let respHeaders = new Headers(hit.headers)
|
|
53
70
|
respHeaders.set("x-srt-cache", "hit")
|
|
71
|
+
// await new Promise(resolve => setTimeout(resolve, 1000))
|
|
54
72
|
return new Response(hit.body, { status: hit.status, headers: respHeaders })
|
|
55
73
|
}
|
|
56
74
|
}
|
package/src/repl.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { createInterface } from "node:readline"
|
|
|
2
2
|
import { resolve, dirname } from "path"
|
|
3
3
|
import { readdirSync } from "node:fs"
|
|
4
4
|
import { state, print, printErr, shutdown } from "./util"
|
|
5
|
-
import { buildReload, broadcast } from "./dev-server"
|
|
6
|
-
import { bundle } from "./bundler"
|
|
5
|
+
import { buildReload, broadcast, showBuildFailure } from "./dev-server"
|
|
6
|
+
import { bundle, codeFromOutputs } from "./bundler"
|
|
7
7
|
import { startWatcher, stopWatcher } from "./watcher"
|
|
8
8
|
|
|
9
9
|
function cmdStop(args: string) {
|
|
@@ -32,11 +32,10 @@ async function cmdReload(args: string) {
|
|
|
32
32
|
let result = await bundle(state.source)
|
|
33
33
|
if (!result) {
|
|
34
34
|
printErr("[cli] Build failed, reload aborted")
|
|
35
|
+
showBuildFailure()
|
|
35
36
|
return
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
-
state.currentCode = await output.text()
|
|
39
|
-
}
|
|
38
|
+
state.currentCode = await codeFromOutputs(result.outputs)
|
|
40
39
|
}
|
|
41
40
|
let msg = buildReload({ code: state.currentCode })
|
|
42
41
|
if (!args) {
|
|
@@ -95,9 +94,7 @@ async function cmdLoad(file: string) {
|
|
|
95
94
|
printErr("[cli] Build failed")
|
|
96
95
|
return
|
|
97
96
|
}
|
|
98
|
-
|
|
99
|
-
state.currentCode = await output.text()
|
|
100
|
-
}
|
|
97
|
+
state.currentCode = await codeFromOutputs(result.outputs)
|
|
101
98
|
} else if (file.endsWith(".srt.js")) {
|
|
102
99
|
state.currentCode = await Bun.file(path).text()
|
|
103
100
|
} else if (file.endsWith(".srt.bin")) {
|
package/src/watcher.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { watch } from "node:fs"
|
|
2
2
|
import { resolve, dirname } from "path"
|
|
3
3
|
import { state, print, printErr } from "./util"
|
|
4
|
-
import { buildReload } from "./dev-server"
|
|
5
|
-
import { bundle } from "./bundler"
|
|
4
|
+
import { buildReload, showBuildFailure } from "./dev-server"
|
|
5
|
+
import { bundle, codeFromOutputs } from "./bundler"
|
|
6
6
|
|
|
7
7
|
let currentWatcher: ReturnType<typeof watch> | null = null
|
|
8
8
|
|
|
@@ -28,11 +28,10 @@ export function startWatcher() {
|
|
|
28
28
|
let result = await bundle(state.source)
|
|
29
29
|
if (!result) {
|
|
30
30
|
printErr("[cli] Build failed, waiting for changes...")
|
|
31
|
+
showBuildFailure()
|
|
31
32
|
return
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
state.currentCode = await output.text()
|
|
35
|
-
}
|
|
34
|
+
state.currentCode = await codeFromOutputs(result.outputs)
|
|
36
35
|
let msg = buildReload({ code: state.currentCode })
|
|
37
36
|
for (let ws of state.clients.keys()) {
|
|
38
37
|
ws.send(msg)
|