@solidrt/cli 0.0.8 → 0.0.9
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 +5 -5
- package/src/args.ts +44 -1
- package/src/{native.ts → artifacts.ts} +25 -0
- package/src/bundler.ts +117 -0
- package/src/commands/bundle.ts +59 -0
- package/src/commands/client.ts +22 -0
- package/src/commands/pack.ts +25 -0
- package/src/commands/record.ts +16 -0
- package/src/commands/server.ts +45 -0
- package/src/dev-android.ts +69 -0
- package/src/{client.ts → dev-client.ts} +2 -1
- package/src/{server.ts → dev-server.ts} +16 -1
- package/src/main.ts +22 -112
- package/src/packer.ts +27 -0
- package/src/repl.ts +3 -2
- package/src/util.ts +43 -16
- package/src/watcher.ts +3 -2
- package/src/bun-plugin-solid.ts +0 -21
- package/src/bundle.ts +0 -126
- package/src/pack.ts +0 -58
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Antoine van Wel",
|
|
6
6
|
"type": "module",
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
"qrcode-generator": "^2.0.4"
|
|
20
20
|
},
|
|
21
21
|
"optionalDependencies": {
|
|
22
|
-
"@solidrt/darwin-arm64": "0.0.
|
|
23
|
-
"@solidrt/linux-x64-gnu": "0.0.
|
|
24
|
-
"@solidrt/win32-x64-msvc": "0.0.
|
|
22
|
+
"@solidrt/darwin-arm64": "0.0.9",
|
|
23
|
+
"@solidrt/linux-x64-gnu": "0.0.9",
|
|
24
|
+
"@solidrt/win32-x64-msvc": "0.0.9"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@solidrt/core": "0.0.
|
|
27
|
+
"@solidrt/core": "0.0.9",
|
|
28
28
|
"typescript": "^5"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
package/src/args.ts
CHANGED
|
@@ -5,6 +5,7 @@ export let { values, positionals } = parseArgs({
|
|
|
5
5
|
dev: { type: "boolean", short: "d", default: false },
|
|
6
6
|
minify: { type: "boolean", short: "m", default: false },
|
|
7
7
|
compile: { type: "boolean", short: "c", default: false },
|
|
8
|
+
flux: { type: "boolean", default: false },
|
|
8
9
|
stdout: { type: "boolean", default: false },
|
|
9
10
|
output: { type: "string", short: "o" },
|
|
10
11
|
"proxy-files": { type: "boolean", default: false },
|
|
@@ -12,6 +13,8 @@ export let { values, positionals } = parseArgs({
|
|
|
12
13
|
fps: { type: "string" },
|
|
13
14
|
duration: { type: "string" },
|
|
14
15
|
size: { type: "string" },
|
|
16
|
+
android: { type: "boolean", default: false },
|
|
17
|
+
device: { type: "string" },
|
|
15
18
|
},
|
|
16
19
|
allowPositionals: true,
|
|
17
20
|
})
|
|
@@ -23,6 +26,37 @@ export let isTs = source?.endsWith(".ts") || source?.endsWith(".js")
|
|
|
23
26
|
export let isSource = isTsx || isTs
|
|
24
27
|
export let isPrebuilt = source?.endsWith(".srt.js") || source?.endsWith(".srt.bin")
|
|
25
28
|
|
|
29
|
+
function usage(line: string): never {
|
|
30
|
+
console.error("Usage: " + line)
|
|
31
|
+
process.exit(1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Per-command argument requirements. Called once before dispatch.
|
|
35
|
+
export function validateArgs() {
|
|
36
|
+
switch (command) {
|
|
37
|
+
case "bundle":
|
|
38
|
+
if (!source || (!isSource && !isPrebuilt))
|
|
39
|
+
usage("srt bundle [options] <entry.[tsx|jsx|ts|js|srt.js|srt.bin]>")
|
|
40
|
+
break
|
|
41
|
+
case "record":
|
|
42
|
+
if (!source || !isTsx) usage("srt record <entry.[tsx|jsx]>")
|
|
43
|
+
break
|
|
44
|
+
case "pack":
|
|
45
|
+
if (values.flux) {
|
|
46
|
+
if (!source || !isTs) usage("srt pack --flux [options] <entry.[ts|js]>")
|
|
47
|
+
} else if (!source || !isSource) {
|
|
48
|
+
usage("srt pack [options] <entry.[tsx|jsx|ts|js]>")
|
|
49
|
+
}
|
|
50
|
+
break
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --android installs/launches the client on a device; it is a client action,
|
|
54
|
+
// so it is only valid for `client`.
|
|
55
|
+
if (values.android && command !== "client") {
|
|
56
|
+
usage("srt client --android (--android is only valid with the client command)")
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
26
60
|
export function printUsage() {
|
|
27
61
|
console.error(`Usage: srt <command> [options] [file]
|
|
28
62
|
|
|
@@ -32,7 +66,7 @@ Commands:
|
|
|
32
66
|
client Start solidrt-go client only
|
|
33
67
|
bundle <file> Transpile TS/JS/TSX/JSX to JS or bytecode
|
|
34
68
|
record <file.tsx|jsx> Capture frames for video generation
|
|
35
|
-
pack <file
|
|
69
|
+
pack <file> Bundle + compile to a standalone executable (experimental)
|
|
36
70
|
|
|
37
71
|
run/server options:
|
|
38
72
|
--proxy-files Route file/dir access through the dev server
|
|
@@ -41,6 +75,10 @@ run/server options:
|
|
|
41
75
|
run/client options:
|
|
42
76
|
--size <WxH> Window size (default: 1280x720)
|
|
43
77
|
|
|
78
|
+
client options:
|
|
79
|
+
--android Install and launch the client on a connected Android device
|
|
80
|
+
--device <serial> Target a specific adb device (when several are connected)
|
|
81
|
+
|
|
44
82
|
bundle options:
|
|
45
83
|
-d, --dev Use development build of SolidJS (default: production)
|
|
46
84
|
-m, --minify Minify the output
|
|
@@ -48,6 +86,11 @@ bundle options:
|
|
|
48
86
|
-o, --output <name> Output filename
|
|
49
87
|
--stdout Write bundle to stdout
|
|
50
88
|
|
|
89
|
+
pack options:
|
|
90
|
+
--flux Pack for the bare Flux runtime instead of SolidRT (entry must be .ts|.js)
|
|
91
|
+
-m, --minify Minify the output
|
|
92
|
+
-o, --output <name> Output filename
|
|
93
|
+
|
|
51
94
|
record options:
|
|
52
95
|
--fps <N> Frames per second (default: 60)
|
|
53
96
|
--duration <N> Duration in seconds (default: 1)
|
|
@@ -41,5 +41,30 @@ export function resolveBinary(name: string) {
|
|
|
41
41
|
} catch {}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The Android client APK is per-ABI (it bundles native .so for one architecture)
|
|
48
|
+
// and host-independent, so it lives under dist/android/<abi>/ rather than the
|
|
49
|
+
// host triple map. Only arm64-v8a is supported for now.
|
|
50
|
+
let ANDROID_ABI = "arm64-v8a"
|
|
51
|
+
let ANDROID_PKG = "@solidrt/android-arm64-v8a"
|
|
52
|
+
|
|
53
|
+
export function resolveApk() {
|
|
54
|
+
// 1. SRT_HOME: contributor checkout, where `make dist-android` stages the APK
|
|
55
|
+
// under dist/android/<abi>/.
|
|
56
|
+
let srtRoot = process.env.SRT_HOME
|
|
57
|
+
if (srtRoot) {
|
|
58
|
+
let apk = resolve(srtRoot, "dist/android", ANDROID_ABI, "solidrt-go.apk")
|
|
59
|
+
if (existsSync(apk)) return apk
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Platform npm package
|
|
63
|
+
try {
|
|
64
|
+
let pkgDir = dirname(require.resolve(`${ANDROID_PKG}/package.json`))
|
|
65
|
+
let apk = resolve(pkgDir, "solidrt-go.apk")
|
|
66
|
+
if (existsSync(apk)) return apk
|
|
67
|
+
} catch {}
|
|
68
|
+
|
|
44
69
|
return null
|
|
45
70
|
}
|
package/src/bundler.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { transformAsync } from "@babel/core"
|
|
2
|
+
import ts from "@babel/preset-typescript"
|
|
3
|
+
import solid from "babel-preset-solid"
|
|
4
|
+
import { type BunPlugin } from "bun"
|
|
5
|
+
import { values, source } from "./args"
|
|
6
|
+
import { state, print, requireBinary } from "./util"
|
|
7
|
+
|
|
8
|
+
// Bun build plugin that runs JSX/TSX through babel-preset-solid (universal
|
|
9
|
+
// generate, targeting @solidrt/core) plus the TS preset.
|
|
10
|
+
function solidPlugin(): BunPlugin {
|
|
11
|
+
return {
|
|
12
|
+
name: "bun-plugin-solid",
|
|
13
|
+
setup: (build) => {
|
|
14
|
+
build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => {
|
|
15
|
+
let file = Bun.file(args.path)
|
|
16
|
+
let code = await file.text()
|
|
17
|
+
let transforms = await transformAsync(code, {
|
|
18
|
+
filename: args.path,
|
|
19
|
+
presets: [[solid, { moduleName: "@solidrt/core", generate: "universal" }], [ts]],
|
|
20
|
+
})
|
|
21
|
+
return { contents: transforms?.code ?? "", loader: "js" }
|
|
22
|
+
})
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function bundle(entry = source) {
|
|
28
|
+
let result = null
|
|
29
|
+
|
|
30
|
+
let devBase = state.serverUrl ?? undefined
|
|
31
|
+
let dev = !!devBase || values.dev
|
|
32
|
+
print(`[cli] Bundling (${dev ? "development" : "production"})`)
|
|
33
|
+
let define: Record<string, string> = {
|
|
34
|
+
"process.env.NODE_ENV": dev ? "development" : "production",
|
|
35
|
+
}
|
|
36
|
+
if (devBase) define.__SRT_DEV_BASE__ = devBase
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
result = await Bun.build({
|
|
40
|
+
entrypoints: [entry!],
|
|
41
|
+
target: "browser",
|
|
42
|
+
format: "esm",
|
|
43
|
+
minify: values.minify,
|
|
44
|
+
external: ["flux:*"],
|
|
45
|
+
define,
|
|
46
|
+
plugins: [solidPlugin()],
|
|
47
|
+
})
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.error("[cli] compile error:\n", e)
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (result?.success) {
|
|
54
|
+
return result
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (result) {
|
|
58
|
+
for (let msg of result?.logs) console.error(msg)
|
|
59
|
+
}
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function bundleTo(outfile: string) {
|
|
64
|
+
let result = await bundle()
|
|
65
|
+
if (!result) {
|
|
66
|
+
console.error("Build failed")
|
|
67
|
+
process.exit(1)
|
|
68
|
+
}
|
|
69
|
+
for (let output of result.outputs) {
|
|
70
|
+
await Bun.write(outfile, output)
|
|
71
|
+
}
|
|
72
|
+
return result
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Bundle for the bare Flux runtime: no Solid plugin, flux: modules stay external.
|
|
76
|
+
export async function bundleFlux(entry: string): Promise<string> {
|
|
77
|
+
let result = await Bun.build({
|
|
78
|
+
entrypoints: [entry],
|
|
79
|
+
target: "browser",
|
|
80
|
+
format: "esm",
|
|
81
|
+
minify: values.minify,
|
|
82
|
+
external: ["flux:*"],
|
|
83
|
+
})
|
|
84
|
+
if (!result.success) {
|
|
85
|
+
for (let msg of result.logs) console.error(msg)
|
|
86
|
+
console.error("Build failed")
|
|
87
|
+
process.exit(1)
|
|
88
|
+
}
|
|
89
|
+
let jsCode = ""
|
|
90
|
+
for (let output of result.outputs) jsCode += await output.text()
|
|
91
|
+
return jsCode
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Bundle for the SolidRT runtime via the standard Solid-aware bundler.
|
|
95
|
+
export async function bundleSolid(): Promise<string> {
|
|
96
|
+
let result = await bundle()
|
|
97
|
+
if (!result) {
|
|
98
|
+
console.error("Build failed")
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
let jsCode = ""
|
|
102
|
+
for (let output of result.outputs) jsCode += await output.text()
|
|
103
|
+
return jsCode
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Compile JS source to QuickJS bytecode via the fluxc binary.
|
|
107
|
+
export async function compileToBytecode(jsCode: string): Promise<Buffer> {
|
|
108
|
+
let compiler = requireBinary("fluxc")
|
|
109
|
+
let proc = Bun.spawn([compiler], {
|
|
110
|
+
stdin: new Blob([jsCode]),
|
|
111
|
+
stdout: "pipe",
|
|
112
|
+
stderr: "inherit",
|
|
113
|
+
})
|
|
114
|
+
let [bytecode, code] = await Promise.all([new Response(proc.stdout).arrayBuffer(), proc.exited])
|
|
115
|
+
if (code !== 0) process.exit(code)
|
|
116
|
+
return Buffer.from(bytecode)
|
|
117
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { values, source, isPrebuilt } from "../args"
|
|
2
|
+
import { bundle, bundleTo, compileToBytecode } from "../bundler"
|
|
3
|
+
import { resolve } from "path"
|
|
4
|
+
|
|
5
|
+
// Compile JS to a .srt.bin file and report its size.
|
|
6
|
+
async function writeBytecode(jsCode: string, outfile: string) {
|
|
7
|
+
let bytecode = await compileToBytecode(jsCode)
|
|
8
|
+
await Bun.write(outfile, bytecode)
|
|
9
|
+
let binSize = (await Bun.file(outfile).stat()).size
|
|
10
|
+
console.log(`>> wrote ${binSize} bytes to ${outfile}`)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function runBundleCommand() {
|
|
14
|
+
if (isPrebuilt) {
|
|
15
|
+
if (!source!.endsWith(".srt.js")) {
|
|
16
|
+
console.error("Can only compile .srt.js files. .srt.bin is already compiled.")
|
|
17
|
+
process.exit(1)
|
|
18
|
+
}
|
|
19
|
+
let jsFile = resolve(source!)
|
|
20
|
+
let binOut = jsFile.replace(/\.srt\.js$/, ".srt.bin").replace(/\.js$/, ".bin")
|
|
21
|
+
await writeBytecode(await Bun.file(jsFile).text(), binOut)
|
|
22
|
+
process.exit()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let baseName = values.output ?? source!.replace(/\.[jt]sx?$/, "")
|
|
26
|
+
|
|
27
|
+
if (values.stdout) {
|
|
28
|
+
let result = await bundle()
|
|
29
|
+
if (!result) {
|
|
30
|
+
console.error("Build failed")
|
|
31
|
+
process.exit(1)
|
|
32
|
+
}
|
|
33
|
+
for (let output of result.outputs) {
|
|
34
|
+
process.stdout.write(await output.text())
|
|
35
|
+
}
|
|
36
|
+
process.exit()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (values.compile) {
|
|
40
|
+
let result = await bundle()
|
|
41
|
+
if (!result) {
|
|
42
|
+
console.error("Build failed")
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
let jsCode = ""
|
|
46
|
+
for (let output of result.outputs) {
|
|
47
|
+
jsCode += await output.text()
|
|
48
|
+
}
|
|
49
|
+
await writeBytecode(jsCode, baseName + ".srt.bin")
|
|
50
|
+
process.exit()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let jsOutfile = baseName + ".srt.js"
|
|
54
|
+
let result = await bundleTo(jsOutfile)
|
|
55
|
+
for (let output of result.outputs) {
|
|
56
|
+
console.log(`>> wrote ${output.size} bytes to ${jsOutfile}`)
|
|
57
|
+
}
|
|
58
|
+
process.exit()
|
|
59
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { values } from "../args"
|
|
2
|
+
import { requireBinary, run } from "../util"
|
|
3
|
+
import { spawnAndroidClient } from "../dev-android"
|
|
4
|
+
|
|
5
|
+
// Standalone solidrt-go client (no dev server). The `run` command instead uses
|
|
6
|
+
// spawnClient() to launch a client tied to the dev-server lifecycle. Either way
|
|
7
|
+
// the client discovers a dev server over the LAN; with --android it is installed
|
|
8
|
+
// and launched on a connected Android device instead of run locally.
|
|
9
|
+
export async function runClientCommand() {
|
|
10
|
+
if (values.android) {
|
|
11
|
+
await spawnAndroidClient()
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let runner = requireBinary("solidrt-go")
|
|
16
|
+
let args: string[] = []
|
|
17
|
+
if (values.size) args.push("--size", values.size)
|
|
18
|
+
//TODO add dev server connection
|
|
19
|
+
// if (source) args.push("--dev-server", source)
|
|
20
|
+
let exit = await run(runner, args)
|
|
21
|
+
process.exit(exit)
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { values, source } from "../args"
|
|
2
|
+
import { bundleFlux, bundleSolid } from "../bundler"
|
|
3
|
+
import { packRunner } from "../packer"
|
|
4
|
+
|
|
5
|
+
// Write the packed executable, mark it runnable, and report its size.
|
|
6
|
+
async function writeExecutable(packed: Buffer, outfile: string) {
|
|
7
|
+
await Bun.write(outfile, packed)
|
|
8
|
+
if (process.platform !== "win32") {
|
|
9
|
+
Bun.spawnSync(["chmod", "+x", outfile])
|
|
10
|
+
}
|
|
11
|
+
console.log(`>> wrote ${packed.length} bytes to ${outfile}`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function runPackCommand() {
|
|
15
|
+
let outfile = values.output ?? source!.replace(/\.[jt]sx?$/, "")
|
|
16
|
+
// On Windows the packed image is a PE executable; it needs a .exe name to run.
|
|
17
|
+
if (process.platform === "win32" && !outfile.toLowerCase().endsWith(".exe")) {
|
|
18
|
+
outfile += ".exe"
|
|
19
|
+
}
|
|
20
|
+
let packed = values.flux
|
|
21
|
+
? await packRunner("fluxrt", await bundleFlux(source!))
|
|
22
|
+
: await packRunner("solidrt", await bundleSolid())
|
|
23
|
+
await writeExecutable(packed, outfile)
|
|
24
|
+
process.exit()
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { source, values } from "../args"
|
|
2
|
+
import { requireBinary, run } from "../util"
|
|
3
|
+
import { bundleTo } from "../bundler"
|
|
4
|
+
import { resolve } from "path"
|
|
5
|
+
|
|
6
|
+
export async function runRecordCommand() {
|
|
7
|
+
let jsOutfile = source!.replace(/\.[jt]sx$/, "") + ".srt.js"
|
|
8
|
+
await bundleTo(jsOutfile)
|
|
9
|
+
let runner = requireBinary("solidrt-go")
|
|
10
|
+
let recordArgs = ["--record", resolve(jsOutfile)]
|
|
11
|
+
if (values.fps) recordArgs.push("--fps", values.fps)
|
|
12
|
+
if (values.duration) recordArgs.push("--duration", values.duration)
|
|
13
|
+
if (values.size) recordArgs.push("--size", values.size)
|
|
14
|
+
let exit = await run(runner, recordArgs)
|
|
15
|
+
process.exit(exit)
|
|
16
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import pkg from "../../package.json"
|
|
2
|
+
import { source, isSource, isPrebuilt, values } from "../args"
|
|
3
|
+
import { state, shutdown } from "../util"
|
|
4
|
+
import { bundle } from "../bundler"
|
|
5
|
+
import { startServer } from "../dev-server"
|
|
6
|
+
import { startRepl } from "../repl"
|
|
7
|
+
import { startWatcher } from "../watcher"
|
|
8
|
+
import * as cache from "../cache"
|
|
9
|
+
import { resolve, dirname } from "path"
|
|
10
|
+
|
|
11
|
+
// Brings up the dev server (HTTP/WS + initial bundle + repl + watcher). The
|
|
12
|
+
// `run` command spawns a local client on top of this from main.ts.
|
|
13
|
+
export async function runServerCommand() {
|
|
14
|
+
// Initialize state from args
|
|
15
|
+
state.source = source
|
|
16
|
+
state.sourceDir = source ? dirname(resolve(source)) : process.cwd()
|
|
17
|
+
|
|
18
|
+
if (values["proxy-http"]) {
|
|
19
|
+
cache.initCache({ dir: process.cwd() })
|
|
20
|
+
console.log("[cli] HTTP cache enabled")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
startServer()
|
|
24
|
+
|
|
25
|
+
// Bundle initial code if source file given (after server start so the
|
|
26
|
+
// dev base URL is available to the bundler).
|
|
27
|
+
if (source && isSource) {
|
|
28
|
+
let initialResult = await bundle()
|
|
29
|
+
if (initialResult) {
|
|
30
|
+
for (let output of initialResult.outputs) {
|
|
31
|
+
state.currentCode = await output.text()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else if (source && isPrebuilt && source.endsWith(".srt.js")) {
|
|
35
|
+
state.currentCode = await Bun.file(resolve(source)).text()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
process.on("SIGINT", shutdown)
|
|
39
|
+
process.on("SIGTERM", shutdown)
|
|
40
|
+
|
|
41
|
+
let version = pkg.version === "0.0.0" ? "" : " version " + pkg.version
|
|
42
|
+
console.log(`[cli] Welcome to SolidRT${version}!`)
|
|
43
|
+
startRepl()
|
|
44
|
+
startWatcher()
|
|
45
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { print, requireAdb } from "./util"
|
|
2
|
+
import { resolveApk } from "./artifacts"
|
|
3
|
+
import { values } from "./args"
|
|
4
|
+
|
|
5
|
+
// Launch component of the "go" dev-client flavor (see lattice/Makefile.x-android).
|
|
6
|
+
let PACKAGE_ACTIVITY = "com.solidrt.go/com.solidrt.app.MainActivity"
|
|
7
|
+
|
|
8
|
+
// Prefix adb args with `-s <serial>` only when the user pinned a device; with a
|
|
9
|
+
// single connected device adb selects it on its own.
|
|
10
|
+
function adbArgs(extra: string[]) {
|
|
11
|
+
return values.device ? ["-s", values.device, ...extra] : extra
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Install + launch the Android client on a connected device over adb, then let
|
|
15
|
+
// the device discover the running dev server over LAN UDP (the same path as a
|
|
16
|
+
// manually launched client). Fire-and-forget: the client's lifecycle is tracked
|
|
17
|
+
// via WS connect/disconnect in dev-server.ts, not as a child process here.
|
|
18
|
+
export async function spawnAndroidClient() {
|
|
19
|
+
let adb = requireAdb()
|
|
20
|
+
|
|
21
|
+
let apk = resolveApk()
|
|
22
|
+
if (!apk) {
|
|
23
|
+
console.error("Could not find the SolidRT-Go APK.")
|
|
24
|
+
console.error("Add it with: bun add -d @solidrt/android-arm64-v8a")
|
|
25
|
+
process.exit(1)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Resolve the target device: 0 -> error, 1 -> use it, many -> require --device.
|
|
29
|
+
let target = values.device
|
|
30
|
+
if (!target) {
|
|
31
|
+
let listed = Bun.spawnSync([adb, "devices"], { stdout: "pipe", stderr: "pipe" })
|
|
32
|
+
let devices = listed.stdout
|
|
33
|
+
.toString()
|
|
34
|
+
.split("\n")
|
|
35
|
+
.slice(1)
|
|
36
|
+
.map((l) => l.trim())
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.filter((l) => l.endsWith("\tdevice"))
|
|
39
|
+
.map((l) => l.split("\t")[0])
|
|
40
|
+
|
|
41
|
+
if (devices.length === 0) {
|
|
42
|
+
console.error("No authorized Android device found. Enable USB debugging and check `adb devices`.")
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
if (devices.length > 1) {
|
|
46
|
+
console.error(`Multiple devices connected (${devices.join(", ")}); pick one with --device <serial>.`)
|
|
47
|
+
process.exit(1)
|
|
48
|
+
}
|
|
49
|
+
target = devices[0]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
print(`[cli] Installing SolidRT-Go on ${target}`)
|
|
53
|
+
let install = Bun.spawn([adb, ...adbArgs(["install", "-r", apk])], { stdout: "pipe", stderr: "pipe" })
|
|
54
|
+
if ((await install.exited) !== 0) {
|
|
55
|
+
console.error("adb install failed:\n" + (await new Response(install.stderr).text()))
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let start = Bun.spawn([adb, ...adbArgs(["shell", "am", "start", "-n", PACKAGE_ACTIVITY])], {
|
|
60
|
+
stdout: "pipe",
|
|
61
|
+
stderr: "pipe",
|
|
62
|
+
})
|
|
63
|
+
if ((await start.exited) !== 0) {
|
|
64
|
+
console.error("adb start failed:\n" + (await new Response(start.stderr).text()))
|
|
65
|
+
process.exit(1)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
print(`[cli] Launched SolidRT-Go on ${target}; waiting for it to discover the dev server...`)
|
|
69
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { state, print, requireBinary } from "./util"
|
|
2
|
+
import { DEV_HOST, DEV_PORT } from "./dev-server"
|
|
2
3
|
import { values } from "./args"
|
|
3
4
|
|
|
4
5
|
function pipeAbovePrompt(stream: ReadableStream<Uint8Array>, out: NodeJS.WriteStream) {
|
|
@@ -3,9 +3,24 @@ import { stat as fsStat, readdir } from "node:fs/promises"
|
|
|
3
3
|
import { networkInterfaces } from "node:os"
|
|
4
4
|
import { createSocket } from "node:dgram"
|
|
5
5
|
import qrcode from "qrcode-generator"
|
|
6
|
-
import {
|
|
6
|
+
import { state, print } from "./util"
|
|
7
|
+
import { values } from "./args"
|
|
7
8
|
import * as cache from "./cache"
|
|
8
9
|
|
|
10
|
+
export const DEV_HOST = "127.0.0.1"
|
|
11
|
+
export const DEV_PORT = 15194
|
|
12
|
+
|
|
13
|
+
// Dev-server WS protocol helpers: the reload message shape and the stop broadcast.
|
|
14
|
+
export function buildReload(payload: { code?: string | null; bytecode?: string }) {
|
|
15
|
+
return JSON.stringify({ type: "reload", proxyFiles: values["proxy-files"], proxyHttp: values["proxy-http"], ...payload })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function broadcastStop() {
|
|
19
|
+
for (let ws of state.clients.keys()) {
|
|
20
|
+
ws.send(JSON.stringify({ type: "stop" }))
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
9
24
|
function headersToObject(h: Headers): Record<string, string> {
|
|
10
25
|
let out: Record<string, string> = {}
|
|
11
26
|
h.forEach((v, k) => {
|
package/src/main.ts
CHANGED
|
@@ -1,52 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// srt bundle examples/hello.tsx - bundle TSX to .srt.js
|
|
11
|
-
// srt bundle -c examples/hello.tsx - bundle TSX to .srt.js + compile to .srt.bin
|
|
12
|
-
// srt bundle examples/hello.srt.js - compile .srt.js to .srt.bin
|
|
13
|
-
// srt record examples/hello.tsx - bundle TSX and run with frame capture
|
|
14
|
-
|
|
15
|
-
import pkg from "../package.json"
|
|
16
|
-
import { values, command, source, isTsx, isTs, isSource, isPrebuilt, printUsage } from "./args"
|
|
17
|
-
import { state, requireBinary, run, shutdown } from "./util"
|
|
18
|
-
import { bundle, bundleTo, runBundleCommand } from "./bundle"
|
|
19
|
-
import { runPackCommand } from "./pack"
|
|
20
|
-
import { startServer } from "./server"
|
|
21
|
-
import { spawnClient } from "./client"
|
|
22
|
-
import { startRepl } from "./repl"
|
|
23
|
-
import { startWatcher } from "./watcher"
|
|
24
|
-
import * as cache from "./cache"
|
|
25
|
-
import { resolve, dirname } from "path"
|
|
3
|
+
import { values, command, validateArgs, printUsage } from "./args"
|
|
4
|
+
import { runBundleCommand } from "./commands/bundle"
|
|
5
|
+
import { runPackCommand } from "./commands/pack"
|
|
6
|
+
import { runRecordCommand } from "./commands/record"
|
|
7
|
+
import { runServerCommand } from "./commands/server"
|
|
8
|
+
import { runClientCommand } from "./commands/client"
|
|
9
|
+
import { spawnClient } from "./dev-client"
|
|
26
10
|
|
|
27
11
|
// -- Validate args --
|
|
28
12
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (!command || !COMMANDS.includes(command)) {
|
|
32
|
-
printUsage()
|
|
33
|
-
process.exit(1)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (command === "bundle" && (!source || (!isSource && !isPrebuilt))) {
|
|
37
|
-
console.error("Usage: srt bundle [options] <entry.[tsx|jsx|ts|js|srt.js|srt.bin]>")
|
|
38
|
-
process.exit(1)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (command === "record" && (!source || !isTsx)) {
|
|
42
|
-
console.error("Usage: srt record <entry.[tsx|jsx]>")
|
|
43
|
-
process.exit(1)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (command === "pack" && (!source || !isTs)) {
|
|
47
|
-
console.error("Usage: srt pack [options] <entry.[ts|js]>")
|
|
48
|
-
process.exit(1)
|
|
49
|
-
}
|
|
13
|
+
validateArgs()
|
|
50
14
|
|
|
51
15
|
// Force the production export condition for prod bundles. Bun auto-activates the
|
|
52
16
|
// "development" condition whenever NODE_ENV != "production" (read once at startup),
|
|
@@ -69,76 +33,22 @@ if (isProdBuild && process.env.NODE_ENV !== "production") {
|
|
|
69
33
|
process.exit(proc.exitCode ?? 0)
|
|
70
34
|
}
|
|
71
35
|
|
|
72
|
-
// --
|
|
36
|
+
// -- Dispatch --
|
|
73
37
|
|
|
74
38
|
if (command === "bundle") {
|
|
75
39
|
await runBundleCommand()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (command === "pack") {
|
|
40
|
+
} else if (command === "pack") {
|
|
79
41
|
await runPackCommand()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (command === "
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
let recordArgs = ["--record", resolve(jsOutfile)]
|
|
89
|
-
if (values.fps) recordArgs.push("--fps", values.fps)
|
|
90
|
-
if (values.duration) recordArgs.push("--duration", values.duration)
|
|
91
|
-
if (values.size) recordArgs.push("--size", values.size)
|
|
92
|
-
let exit = await run(runner, recordArgs)
|
|
93
|
-
process.exit(exit)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// -- Client command --
|
|
97
|
-
|
|
98
|
-
if (command === "client") {
|
|
99
|
-
let runner = requireBinary("solidrt-go")
|
|
100
|
-
let args: string[] = []
|
|
101
|
-
if (values.size) args.push("--size", values.size)
|
|
102
|
-
//TODO add dev server connection
|
|
103
|
-
// if (source) args.push("--dev-server", source)
|
|
104
|
-
let exit = await run(runner, args)
|
|
105
|
-
process.exit(exit)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// -- Server / Run command --
|
|
109
|
-
|
|
110
|
-
// Initialize state from args
|
|
111
|
-
state.source = source
|
|
112
|
-
state.sourceDir = source ? dirname(resolve(source)) : process.cwd()
|
|
113
|
-
|
|
114
|
-
if (values["proxy-http"]) {
|
|
115
|
-
cache.initCache({ dir: process.cwd() })
|
|
116
|
-
console.log("[cli] HTTP cache enabled")
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
startServer()
|
|
120
|
-
|
|
121
|
-
// Bundle initial code if source file given (after server start so the
|
|
122
|
-
// dev base URL is available to the bundler).
|
|
123
|
-
if (source && isSource) {
|
|
124
|
-
let initialResult = await bundle()
|
|
125
|
-
if (initialResult) {
|
|
126
|
-
for (let output of initialResult.outputs) {
|
|
127
|
-
state.currentCode = await output.text()
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
} else if (source && isPrebuilt && source.endsWith(".srt.js")) {
|
|
131
|
-
state.currentCode = await Bun.file(resolve(source)).text()
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (command === "run") {
|
|
42
|
+
} else if (command === "record") {
|
|
43
|
+
await runRecordCommand()
|
|
44
|
+
} else if (command === "client") {
|
|
45
|
+
await runClientCommand()
|
|
46
|
+
} else if (command === "server") {
|
|
47
|
+
await runServerCommand()
|
|
48
|
+
} else if (command === "run") {
|
|
49
|
+
await runServerCommand()
|
|
135
50
|
spawnClient()
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
process.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
let version = pkg.version === "0.0.0" ? "" : " version " + pkg.version
|
|
142
|
-
console.log(`[cli] Welcome to SolidRT${version}!`)
|
|
143
|
-
startRepl()
|
|
144
|
-
startWatcher()
|
|
51
|
+
} else {
|
|
52
|
+
printUsage()
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
package/src/packer.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { requireBinary } from "./util"
|
|
2
|
+
import { compileToBytecode } from "./bundler"
|
|
3
|
+
|
|
4
|
+
// Trailer magic identifying the runner an embedded payload belongs to. Must match
|
|
5
|
+
// the runner-side checks: fluxrt -> flux/src/bin/fluxrt.rs, solidrt ->
|
|
6
|
+
// lattice/src/main.rs (load_embedded_bytecode).
|
|
7
|
+
const MAGIC = {
|
|
8
|
+
fluxrt: Buffer.from([0x46, 0x4c, 0x55, 0x58, 0x52, 0x54, 0x88, 0x44]), // "FLUXRT\x88\x44"
|
|
9
|
+
solidrt: Buffer.from([0x53, 0x4f, 0x4c, 0x49, 0x44, 0x52, 0x54, 0x88, 0x44]), // "SOLIDRT\x88\x44"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type Runner = keyof typeof MAGIC
|
|
13
|
+
|
|
14
|
+
// Compile JS to bytecode and append it to the runner binary, followed by a
|
|
15
|
+
// trailer of [u64 offset LE][8-byte magic]. The runner reads its own image at
|
|
16
|
+
// startup, validates the magic, and slices the bytecode back out.
|
|
17
|
+
export async function packRunner(runner: Runner, jsCode: string): Promise<Buffer> {
|
|
18
|
+
let bytecode = await compileToBytecode(jsCode)
|
|
19
|
+
|
|
20
|
+
let runnerPath = requireBinary(runner)
|
|
21
|
+
let runnerBytes = Buffer.from(await Bun.file(runnerPath).arrayBuffer())
|
|
22
|
+
|
|
23
|
+
let offsetBuf = Buffer.allocUnsafe(8)
|
|
24
|
+
offsetBuf.writeBigUInt64LE(BigInt(runnerBytes.length))
|
|
25
|
+
|
|
26
|
+
return Buffer.concat([runnerBytes, bytecode, offsetBuf, MAGIC[runner]])
|
|
27
|
+
}
|
package/src/repl.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createInterface } from "node:readline"
|
|
2
2
|
import { resolve, dirname } from "path"
|
|
3
3
|
import { readdirSync } from "node:fs"
|
|
4
|
-
import { state, print, printErr,
|
|
5
|
-
import {
|
|
4
|
+
import { state, print, printErr, shutdown } from "./util"
|
|
5
|
+
import { buildReload, broadcastStop } from "./dev-server"
|
|
6
|
+
import { bundle } from "./bundler"
|
|
6
7
|
import { startWatcher, stopWatcher } from "./watcher"
|
|
7
8
|
|
|
8
9
|
function cmdStop(args: string) {
|
package/src/util.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { resolveBinary } from "./
|
|
2
|
-
import {
|
|
1
|
+
import { resolveBinary } from "./artifacts"
|
|
2
|
+
import { existsSync } from "node:fs"
|
|
3
|
+
import { resolve } from "node:path"
|
|
3
4
|
import type { Interface as ReadlineInterface } from "node:readline"
|
|
4
5
|
import type { Server as BunServer } from "bun"
|
|
5
6
|
|
|
6
|
-
export const DEV_HOST = "127.0.0.1"
|
|
7
|
-
export const DEV_PORT = 15194
|
|
8
|
-
|
|
9
7
|
export let state = {
|
|
10
8
|
clients: new Map<any, { platform: string; version: string }>(),
|
|
11
9
|
currentCode: null as string | null,
|
|
@@ -17,11 +15,50 @@ export let state = {
|
|
|
17
15
|
rl: null as ReadlineInterface | null,
|
|
18
16
|
}
|
|
19
17
|
|
|
18
|
+
// Build target per binary, for the "not found" hint. Run from the repo root.
|
|
19
|
+
let BUILD_HINTS: Record<string, string> = {
|
|
20
|
+
"solidrt-go": "make solidrt-go",
|
|
21
|
+
solidrt: "make runtime",
|
|
22
|
+
flux: "make -C flux flux",
|
|
23
|
+
fluxc: "make -C flux fluxc",
|
|
24
|
+
fluxrt: "make -C flux fluxrt PROFILE=release-opt",
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
export function requireBinary(name: string) {
|
|
21
28
|
let path = resolveBinary(name)
|
|
22
29
|
if (path) return path
|
|
30
|
+
let hint = BUILD_HINTS[name]
|
|
23
31
|
console.error(`Could not find ${name} binary.`)
|
|
24
|
-
|
|
32
|
+
if (hint) {
|
|
33
|
+
console.error(`Build it from source: run ${hint}, with SRT_HOME pointing at your SolidRT checkout.`)
|
|
34
|
+
} else {
|
|
35
|
+
console.error("Build it from source, with SRT_HOME pointing at your SolidRT checkout.")
|
|
36
|
+
}
|
|
37
|
+
process.exit(1)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// adb is a system tool (Android Platform Tools), never bundled. Look on PATH
|
|
41
|
+
// first, then the standard SDK location.
|
|
42
|
+
export function resolveAdb() {
|
|
43
|
+
let exe = process.platform === "win32" ? "adb.exe" : "adb"
|
|
44
|
+
let onPath = Bun.which(exe)
|
|
45
|
+
if (onPath) return onPath
|
|
46
|
+
for (let root of [process.env.ANDROID_HOME, process.env.ANDROID_SDK_ROOT]) {
|
|
47
|
+
if (!root) continue
|
|
48
|
+
let candidate = resolve(root, "platform-tools", exe)
|
|
49
|
+
if (existsSync(candidate)) return candidate
|
|
50
|
+
}
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function requireAdb() {
|
|
55
|
+
let path = resolveAdb()
|
|
56
|
+
if (path) return path
|
|
57
|
+
console.error("Could not find adb (Android Platform Tools).")
|
|
58
|
+
console.error("Install it:")
|
|
59
|
+
console.error(" Windows: winget install Google.PlatformTools")
|
|
60
|
+
console.error(" macOS: brew install android-platform-tools")
|
|
61
|
+
console.error(" Linux: install your distro's android-tools / adb package")
|
|
25
62
|
process.exit(1)
|
|
26
63
|
}
|
|
27
64
|
|
|
@@ -42,16 +79,6 @@ export function printErr(...args: any[]) {
|
|
|
42
79
|
state.rl?.prompt(true)
|
|
43
80
|
}
|
|
44
81
|
|
|
45
|
-
export function buildReload(payload: { code?: string | null; bytecode?: string }) {
|
|
46
|
-
return JSON.stringify({ type: "reload", proxyFiles: values["proxy-files"], proxyHttp: values["proxy-http"], ...payload })
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function broadcastStop() {
|
|
50
|
-
for (let ws of state.clients.keys()) {
|
|
51
|
-
ws.send(JSON.stringify({ type: "stop" }))
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
82
|
export function shutdown() {
|
|
56
83
|
if (state.child) state.child.kill()
|
|
57
84
|
if (state.server) state.server.stop()
|
package/src/watcher.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { watch } from "node:fs"
|
|
2
2
|
import { resolve, dirname } from "path"
|
|
3
|
-
import { state, print, printErr
|
|
4
|
-
import {
|
|
3
|
+
import { state, print, printErr } from "./util"
|
|
4
|
+
import { buildReload } from "./dev-server"
|
|
5
|
+
import { bundle } from "./bundler"
|
|
5
6
|
|
|
6
7
|
let currentWatcher: ReturnType<typeof watch> | null = null
|
|
7
8
|
|
package/src/bun-plugin-solid.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { transformAsync } from "@babel/core"
|
|
2
|
-
import ts from "@babel/preset-typescript"
|
|
3
|
-
import solid from "babel-preset-solid"
|
|
4
|
-
import { type BunPlugin } from "bun"
|
|
5
|
-
|
|
6
|
-
export function solidPlugin(): BunPlugin {
|
|
7
|
-
return {
|
|
8
|
-
name: "bun-plugin-solid",
|
|
9
|
-
setup: (build) => {
|
|
10
|
-
build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => {
|
|
11
|
-
let file = Bun.file(args.path)
|
|
12
|
-
let code = await file.text()
|
|
13
|
-
let transforms = await transformAsync(code, {
|
|
14
|
-
filename: args.path,
|
|
15
|
-
presets: [[solid, { moduleName: "@solidrt/core", generate: "universal" }], [ts]],
|
|
16
|
-
})
|
|
17
|
-
return { contents: transforms?.code ?? "", loader: "js" }
|
|
18
|
-
})
|
|
19
|
-
},
|
|
20
|
-
}
|
|
21
|
-
}
|
package/src/bundle.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { solidPlugin } from "./bun-plugin-solid"
|
|
2
|
-
import { values, source, isPrebuilt } from "./args"
|
|
3
|
-
import { requireBinary, state, print } from "./util"
|
|
4
|
-
import { resolve } from "path"
|
|
5
|
-
|
|
6
|
-
export async function bundle(entry = source) {
|
|
7
|
-
let result = null
|
|
8
|
-
|
|
9
|
-
let devBase = state.serverUrl ?? undefined
|
|
10
|
-
let dev = !!devBase || values.dev
|
|
11
|
-
print(`[cli] Bundling (${dev ? "development" : "production"})`)
|
|
12
|
-
let define: Record<string, string> = {
|
|
13
|
-
"process.env.NODE_ENV": dev ? "development" : "production",
|
|
14
|
-
}
|
|
15
|
-
if (devBase) define.__SRT_DEV_BASE__ = devBase
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
result = await Bun.build({
|
|
19
|
-
entrypoints: [entry!],
|
|
20
|
-
target: "browser",
|
|
21
|
-
format: "esm",
|
|
22
|
-
minify: values.minify,
|
|
23
|
-
external: ["flux:*"],
|
|
24
|
-
define,
|
|
25
|
-
plugins: [solidPlugin()],
|
|
26
|
-
})
|
|
27
|
-
} catch (e) {
|
|
28
|
-
console.error("[cli] compile error:\n", e)
|
|
29
|
-
return null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (result?.success) {
|
|
33
|
-
return result
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (result) {
|
|
37
|
-
for (let msg of result?.logs) console.error(msg)
|
|
38
|
-
}
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function bundleTo(outfile: string) {
|
|
43
|
-
let result = await bundle()
|
|
44
|
-
if (!result) {
|
|
45
|
-
console.error("Build failed")
|
|
46
|
-
process.exit(1)
|
|
47
|
-
}
|
|
48
|
-
for (let output of result.outputs) {
|
|
49
|
-
await Bun.write(outfile, output)
|
|
50
|
-
}
|
|
51
|
-
return result
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function compileJs(jsCode: string, outfile: string) {
|
|
55
|
-
let compiler = requireBinary("fluxc")
|
|
56
|
-
let proc = Bun.spawn([compiler], {
|
|
57
|
-
stdin: new Blob([jsCode]),
|
|
58
|
-
stdout: "pipe",
|
|
59
|
-
stderr: "inherit",
|
|
60
|
-
})
|
|
61
|
-
let [bytecode, code] = await Promise.all([new Response(proc.stdout).arrayBuffer(), proc.exited])
|
|
62
|
-
if (code !== 0) process.exit(code)
|
|
63
|
-
await Bun.write(outfile, bytecode)
|
|
64
|
-
return outfile
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function compileToBytecode(jsFile: string, outFile?: string) {
|
|
68
|
-
let jsCode = await Bun.file(jsFile).text()
|
|
69
|
-
let dest = outFile ?? jsFile.replace(/\.srt\.js$/, ".srt.bin").replace(/\.js$/, ".bin")
|
|
70
|
-
return compileJs(jsCode, dest)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function compileFromStdin(jsCode: string, outfile: string) {
|
|
74
|
-
return compileJs(jsCode, outfile)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export async function runBundleCommand() {
|
|
78
|
-
if (isPrebuilt) {
|
|
79
|
-
if (!source!.endsWith(".srt.js")) {
|
|
80
|
-
console.error("Can only compile .srt.js files. .srt.bin is already compiled.")
|
|
81
|
-
process.exit(1)
|
|
82
|
-
}
|
|
83
|
-
let binOut = await compileToBytecode(resolve(source!))
|
|
84
|
-
let binSize = (await Bun.file(binOut).stat()).size
|
|
85
|
-
console.log(`>> wrote ${binSize} bytes to ${binOut}`)
|
|
86
|
-
process.exit()
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
let baseName = values.output ?? source!.replace(/\.[jt]sx?$/, "")
|
|
90
|
-
|
|
91
|
-
if (values.stdout) {
|
|
92
|
-
let result = await bundle()
|
|
93
|
-
if (!result) {
|
|
94
|
-
console.error("Build failed")
|
|
95
|
-
process.exit(1)
|
|
96
|
-
}
|
|
97
|
-
for (let output of result.outputs) {
|
|
98
|
-
process.stdout.write(await output.text())
|
|
99
|
-
}
|
|
100
|
-
process.exit()
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (values.compile) {
|
|
104
|
-
let result = await bundle()
|
|
105
|
-
if (!result) {
|
|
106
|
-
console.error("Build failed")
|
|
107
|
-
process.exit(1)
|
|
108
|
-
}
|
|
109
|
-
let jsCode = ""
|
|
110
|
-
for (let output of result.outputs) {
|
|
111
|
-
jsCode += await output.text()
|
|
112
|
-
}
|
|
113
|
-
let binOutfile = baseName + ".srt.bin"
|
|
114
|
-
await compileFromStdin(jsCode, binOutfile)
|
|
115
|
-
let binSize = (await Bun.file(binOutfile).stat()).size
|
|
116
|
-
console.log(`>> wrote ${binSize} bytes to ${binOutfile}`)
|
|
117
|
-
process.exit()
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
let jsOutfile = baseName + ".srt.js"
|
|
121
|
-
let result = await bundleTo(jsOutfile)
|
|
122
|
-
for (let output of result.outputs) {
|
|
123
|
-
console.log(`>> wrote ${output.size} bytes to ${jsOutfile}`)
|
|
124
|
-
}
|
|
125
|
-
process.exit()
|
|
126
|
-
}
|
package/src/pack.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { values, source } from "./args"
|
|
2
|
-
import { requireBinary } from "./util"
|
|
3
|
-
|
|
4
|
-
const MAGIC = Buffer.from([0x46, 0x4c, 0x55, 0x58, 0x52, 0x54, 0x00, 0x01]) // "FLUXRT\x00\x01"
|
|
5
|
-
|
|
6
|
-
async function bundleFlux(entry: string): Promise<string> {
|
|
7
|
-
let result = await Bun.build({
|
|
8
|
-
entrypoints: [entry],
|
|
9
|
-
target: "browser",
|
|
10
|
-
format: "esm",
|
|
11
|
-
minify: values.minify,
|
|
12
|
-
external: ["flux:*"],
|
|
13
|
-
})
|
|
14
|
-
if (!result.success) {
|
|
15
|
-
for (let msg of result.logs) console.error(msg)
|
|
16
|
-
console.error("Build failed")
|
|
17
|
-
process.exit(1)
|
|
18
|
-
}
|
|
19
|
-
let jsCode = ""
|
|
20
|
-
for (let output of result.outputs) jsCode += await output.text()
|
|
21
|
-
return jsCode
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function compileToBytecode(jsCode: string): Promise<Buffer> {
|
|
25
|
-
let compiler = requireBinary("fluxc")
|
|
26
|
-
let proc = Bun.spawn([compiler], {
|
|
27
|
-
stdin: new Blob([jsCode]),
|
|
28
|
-
stdout: "pipe",
|
|
29
|
-
stderr: "inherit",
|
|
30
|
-
})
|
|
31
|
-
let [bytecode, code] = await Promise.all([new Response(proc.stdout).arrayBuffer(), proc.exited])
|
|
32
|
-
if (code !== 0) process.exit(code)
|
|
33
|
-
return Buffer.from(bytecode)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function runPackCommand() {
|
|
37
|
-
let outfile = values.output ?? source!.replace(/\.[jt]s$/, "")
|
|
38
|
-
|
|
39
|
-
let jsCode = await bundleFlux(source!)
|
|
40
|
-
|
|
41
|
-
let bytecode = await compileToBytecode(jsCode)
|
|
42
|
-
|
|
43
|
-
let runner = requireBinary("fluxrt")
|
|
44
|
-
let runnerBytes = Buffer.from(await Bun.file(runner).arrayBuffer())
|
|
45
|
-
|
|
46
|
-
let offsetBuf = Buffer.allocUnsafe(8)
|
|
47
|
-
offsetBuf.writeBigUInt64LE(BigInt(runnerBytes.length))
|
|
48
|
-
|
|
49
|
-
let packed = Buffer.concat([runnerBytes, bytecode, offsetBuf, MAGIC])
|
|
50
|
-
await Bun.write(outfile, packed)
|
|
51
|
-
|
|
52
|
-
if (process.platform !== "win32") {
|
|
53
|
-
Bun.spawnSync(["chmod", "+x", outfile])
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
console.log(`>> wrote ${packed.length} bytes to ${outfile}`)
|
|
57
|
-
process.exit()
|
|
58
|
-
}
|