buninu 0.3.0 → 0.4.0
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/CHANGELOG.md +71 -0
- package/README.md +334 -37
- package/apps/bunmsh/CHANGELOG.md +262 -0
- package/apps/bunmsh/LICENSE +21 -0
- package/apps/bunmsh/LICENSE-MKSH +295 -0
- package/apps/bunmsh/PORTING.md +192 -0
- package/apps/bunmsh/README.md +801 -0
- package/apps/bunmsh/bunmsh +7 -0
- package/apps/bunmsh/bunshell.md +61 -0
- package/apps/bunmsh/help/README.md +1945 -0
- package/apps/bunmsh/help/alias.md +22 -0
- package/apps/bunmsh/help/basename.md +38 -0
- package/apps/bunmsh/help/builtin.md +25 -0
- package/apps/bunmsh/help/bun.md +27 -0
- package/apps/bunmsh/help/bunmsh.md +47 -0
- package/apps/bunmsh/help/cat.md +31 -0
- package/apps/bunmsh/help/catfancy.md +35 -0
- package/apps/bunmsh/help/cd.md +22 -0
- package/apps/bunmsh/help/chmod.md +27 -0
- package/apps/bunmsh/help/clear.md +21 -0
- package/apps/bunmsh/help/colon.md +22 -0
- package/apps/bunmsh/help/command.md +27 -0
- package/apps/bunmsh/help/cp.md +28 -0
- package/apps/bunmsh/help/cut.md +25 -0
- package/apps/bunmsh/help/date.md +25 -0
- package/apps/bunmsh/help/dirname.md +25 -0
- package/apps/bunmsh/help/echo.md +25 -0
- package/apps/bunmsh/help/env.md +27 -0
- package/apps/bunmsh/help/eval.md +22 -0
- package/apps/bunmsh/help/exec.md +22 -0
- package/apps/bunmsh/help/exit.md +21 -0
- package/apps/bunmsh/help/export.md +22 -0
- package/apps/bunmsh/help/false.md +22 -0
- package/apps/bunmsh/help/find.md +33 -0
- package/apps/bunmsh/help/getopts.md +28 -0
- package/apps/bunmsh/help/grep.md +34 -0
- package/apps/bunmsh/help/head.md +27 -0
- package/apps/bunmsh/help/kill.md +27 -0
- package/apps/bunmsh/help/ln.md +28 -0
- package/apps/bunmsh/help/ls.md +37 -0
- package/apps/bunmsh/help/lsbun.md +29 -0
- package/apps/bunmsh/help/lsfancy.md +37 -0
- package/apps/bunmsh/help/md5sum.md +21 -0
- package/apps/bunmsh/help/mkdir.md +27 -0
- package/apps/bunmsh/help/mktemp.md +26 -0
- package/apps/bunmsh/help/mv.md +26 -0
- package/apps/bunmsh/help/print.md +30 -0
- package/apps/bunmsh/help/printf.md +25 -0
- package/apps/bunmsh/help/pwd.md +21 -0
- package/apps/bunmsh/help/read.md +27 -0
- package/apps/bunmsh/help/readonly.md +27 -0
- package/apps/bunmsh/help/realpath.md +21 -0
- package/apps/bunmsh/help/rm.md +33 -0
- package/apps/bunmsh/help/rmdir.md +27 -0
- package/apps/bunmsh/help/sed.md +29 -0
- package/apps/bunmsh/help/seq.md +29 -0
- package/apps/bunmsh/help/serve.md +50 -0
- package/apps/bunmsh/help/set.md +26 -0
- package/apps/bunmsh/help/sha256sum.md +21 -0
- package/apps/bunmsh/help/shift.md +23 -0
- package/apps/bunmsh/help/sleep.md +25 -0
- package/apps/bunmsh/help/sort.md +29 -0
- package/apps/bunmsh/help/source.md +23 -0
- package/apps/bunmsh/help/tab.md +30 -0
- package/apps/bunmsh/help/tac.md +29 -0
- package/apps/bunmsh/help/tail.md +27 -0
- package/apps/bunmsh/help/tee.md +25 -0
- package/apps/bunmsh/help/test.md +30 -0
- package/apps/bunmsh/help/time.md +29 -0
- package/apps/bunmsh/help/touch.md +26 -0
- package/apps/bunmsh/help/tr.md +26 -0
- package/apps/bunmsh/help/true.md +22 -0
- package/apps/bunmsh/help/type.md +21 -0
- package/apps/bunmsh/help/umask.md +22 -0
- package/apps/bunmsh/help/unalias.md +28 -0
- package/apps/bunmsh/help/uname.md +31 -0
- package/apps/bunmsh/help/unset.md +28 -0
- package/apps/bunmsh/help/wc.md +27 -0
- package/apps/bunmsh/help/whence.md +26 -0
- package/apps/bunmsh/help/which.md +41 -0
- package/apps/bunmsh/help/yes.md +23 -0
- package/apps/bunmsh/package.json +26 -0
- package/apps/bunmsh/serve.js +468 -0
- package/apps/bunmsh/single-exe/README.md +402 -0
- package/apps/bunmsh/single-exe/assetsHelper.js +371 -0
- package/apps/bunmsh/single-exe/assetsLoader.mjs +85 -0
- package/apps/bunmsh/single-exe/assetsPacker.js +218 -0
- package/apps/bunmsh/single-exe/compiled.js +527 -0
- package/apps/bunmsh/single-exe/entry.mjs +17 -0
- package/apps/bunmsh/src/cat-fancy.js +117 -0
- package/apps/bunmsh/src/cat-operands.js +31 -0
- package/apps/bunmsh/src/completion.js +247 -0
- package/apps/bunmsh/src/environment.js +61 -0
- package/apps/bunmsh/src/fancy-ls.js +186 -0
- package/apps/bunmsh/src/find.js +119 -0
- package/apps/bunmsh/src/history.js +198 -0
- package/apps/bunmsh/src/main.js +776 -0
- package/apps/bunmsh/src/mouse.js +103 -0
- package/apps/bunmsh/src/shell.js +3557 -0
- package/apps/bunmsh/test/catfancy.test.js +102 -0
- package/apps/bunmsh/test/reference-source.sh +1 -0
- package/apps/bunmsh/test/reference.test.js +677 -0
- package/apps/bunmsh/test/serve/README.md +3 -0
- package/apps/bunmsh/test/serve/bad.json +1 -0
- package/apps/bunmsh/test/serve/data.json +1 -0
- package/apps/bunmsh/test/serve/data.json5 +1 -0
- package/apps/bunmsh/test/serve/data.jsonc +2 -0
- package/apps/bunmsh/test/serve/data.jsonl +2 -0
- package/apps/bunmsh/test/serve/data.ndjson +1 -0
- package/apps/bunmsh/test/serve/data.toml +3 -0
- package/apps/bunmsh/test/serve/data.ts +15 -0
- package/apps/bunmsh/test/serve/data.xml +1 -0
- package/apps/bunmsh/test/serve/data.yaml +3 -0
- package/apps/bunmsh/test/serve/data.yml +1 -0
- package/apps/bunmsh/test/serve.test.js +241 -0
- package/apps/bunmsh/test/shell.test.js +1778 -0
- package/apps/bunmsh/test/t.sh +12 -0
- package/apps/cmdlist +6 -1
- package/apps/jsgotty/gotty.js +65 -76
- package/apps/jsgotty/package.json +6 -1
- package/apps/jsgotty/single-exe/README.md +358 -54
- package/apps/jsgotty/single-exe/assetsHelper.js +293 -12
- package/apps/jsgotty/single-exe/assetsLoader.mjs +1 -1
- package/apps/jsgotty/single-exe/assetsPacker.js +218 -0
- package/apps/jsgotty/single-exe/compiled.js +379 -19
- package/apps/jsgotty/single-exe/entry.mjs +16 -2
- package/apps/jsgotty/zmodem-node.js +14 -12
- package/apps/jsmdcui/CHANGELOG.md +56 -0
- package/apps/jsmdcui/package.json +12 -2
- package/apps/jsmdcui/single-exe/README.md +298 -375
- package/apps/jsmdcui/single-exe/assetsHelper.js +198 -18
- package/apps/jsmdcui/single-exe/assetsLoader.mjs +1 -1
- package/apps/jsmdcui/single-exe/assetsPacker.js +218 -0
- package/apps/jsmdcui/single-exe/compiled.js +291 -5
- package/apps/jsmdcui/single-exe/entry.mjs +7 -0
- package/apps/jsmdcui/src/index.js +11 -8
- package/apps/jsmdcui/tests/jmigt.js +16 -0
- package/bin/bunmsh.bat +3 -0
- package/bin/bunmsh.sh +5 -0
- package/bin/init.js +48 -5
- package/bin/install.js +541 -0
- package/package.json +4 -2
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { basename, dirname, resolve } from "node:path";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import child_process from "node:child_process";
|
|
5
|
+
import { ASSETS_ROOT } from "./assetsPacker.js";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
export const IS_COMPILED = isCompiledBinary();
|
|
8
9
|
export const REPO_ROOT = IS_COMPILED
|
|
9
10
|
? getExeDirname()
|
|
10
|
-
: resolve(import.meta.dirname,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
: resolve(import.meta.dirname, ASSETS_ROOT);
|
|
12
|
+
// This file's own directory. Not derived from REPO_ROOT: the only user
|
|
13
|
+
// is buildExecutable(), which already refuses to run when IS_COMPILED,
|
|
14
|
+
// so REPO_ROOT there is always the source tree, and going back down
|
|
15
|
+
// through it would hard-code this folder name.
|
|
16
|
+
const SINGLE_EXE_DIR = import.meta.dirname;
|
|
13
17
|
|
|
14
18
|
|
|
15
|
-
export function isCompiledBinary() {
|
|
16
|
-
const
|
|
17
|
-
return Boolean(
|
|
19
|
+
export function isCompiledBinary(argv = process.argv) {
|
|
20
|
+
const entry = argv?.[1];
|
|
21
|
+
return Boolean(
|
|
22
|
+
entry?.startsWith?.("/$bunfs/") ||
|
|
23
|
+
entry?.startsWith?.("B:/~BUN")
|
|
24
|
+
);
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
export function getExeDirname() {
|
|
@@ -35,10 +42,63 @@ export function getDirnameFromUrl(importMetaUrl) {
|
|
|
35
42
|
return dirname(fileURLToPath(importMetaUrl));
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
export
|
|
45
|
+
export function stringifyNonPrimitiveDefineValues(argv, name) {
|
|
46
|
+
const definePrefix = `${name}=`;
|
|
47
|
+
const numberLiteral = /^[+-]?(?:(?:0[xX][\dA-Fa-f](?:_?[\dA-Fa-f])*)|(?:0[bB][01](?:_?[01])*)|(?:0[oO][0-7](?:_?[0-7])*)|(?:(?:\d(?:_?\d)*)(?:\.(?:\d(?:_?\d)*)?)?|\.(?:\d(?:_?\d)*))(?:[eE][+-]?\d(?:_?\d)*)?)$/;
|
|
48
|
+
|
|
49
|
+
// Parse scalar literals as data. Never evaluate a user-provided expression.
|
|
50
|
+
const normalize = (definition) => {
|
|
51
|
+
if (!definition.startsWith(definePrefix)) return definition;
|
|
52
|
+
const source = definition.slice(definePrefix.length);
|
|
53
|
+
const trimmed = source.trim();
|
|
54
|
+
let value;
|
|
55
|
+
let parsed = false;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
value = JSON.parse(source);
|
|
59
|
+
parsed = true;
|
|
60
|
+
} catch {
|
|
61
|
+
try {
|
|
62
|
+
if (globalThis.Bun?.JSON5) {
|
|
63
|
+
value = globalThis.Bun.JSON5.parse(source);
|
|
64
|
+
parsed = true;
|
|
65
|
+
}
|
|
66
|
+
} catch {}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (parsed && typeof value === "string") {
|
|
70
|
+
return definePrefix + JSON.stringify(value);
|
|
71
|
+
}
|
|
72
|
+
if (
|
|
73
|
+
(parsed && (
|
|
74
|
+
value === null
|
|
75
|
+
|| typeof value === "number"
|
|
76
|
+
|| typeof value === "boolean"
|
|
77
|
+
))
|
|
78
|
+
|| trimmed === "undefined"
|
|
79
|
+
|| numberLiteral.test(trimmed)
|
|
80
|
+
) {
|
|
81
|
+
return definition;
|
|
82
|
+
}
|
|
83
|
+
return definePrefix + JSON.stringify(source);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
for (let i = 0; i < argv.length; i++) {
|
|
87
|
+
if (argv[i] === "--define" && typeof argv[i + 1] === "string") {
|
|
88
|
+
argv[i + 1] = normalize(argv[i + 1]);
|
|
89
|
+
} else if (argv[i]?.startsWith?.("--define=")) {
|
|
90
|
+
argv[i] = "--define="
|
|
91
|
+
+ normalize(argv[i].slice("--define=".length));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return argv;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function buildExecutable(target = "",build_outfile="single.exe", bunArgs = []) {
|
|
39
98
|
|
|
40
|
-
|
|
99
|
+
let outfile = resolve(process.cwd(), build_outfile);
|
|
41
100
|
const normalizedTarget = String(target || "").trim();
|
|
101
|
+
const extraBunArgs = Array.from(bunArgs ?? [], String);
|
|
42
102
|
if(!globalThis.Bun || IS_COMPILED)
|
|
43
103
|
{
|
|
44
104
|
console.log("Build exe can only be run by Bun in the source tree");
|
|
@@ -56,16 +116,19 @@ export async function buildExecutable(target = "",build_outfile="single.exe") {
|
|
|
56
116
|
},
|
|
57
117
|
{
|
|
58
118
|
label: "Compile executable",
|
|
59
|
-
cwd:
|
|
119
|
+
cwd: SINGLE_EXE_DIR,
|
|
60
120
|
cmd: bunBin,
|
|
61
121
|
args: [
|
|
62
122
|
"build",
|
|
123
|
+
"--format=esm",
|
|
63
124
|
"--compile",
|
|
64
|
-
"--bytecode",
|
|
65
125
|
"--minify",
|
|
66
|
-
|
|
67
|
-
|
|
126
|
+
"--bytecode",
|
|
127
|
+
"./entry.mjs",
|
|
128
|
+
`--outfile=${outfile}`,
|
|
129
|
+
`--metafile-md=${outfile}.meta.md`,
|
|
68
130
|
...(normalizedTarget ? [`--target=${normalizedTarget}`] : []),
|
|
131
|
+
...extraBunArgs,
|
|
69
132
|
],
|
|
70
133
|
},
|
|
71
134
|
];
|
|
@@ -73,7 +136,9 @@ export async function buildExecutable(target = "",build_outfile="single.exe") {
|
|
|
73
136
|
for (const step of steps) {
|
|
74
137
|
|
|
75
138
|
console.log('');
|
|
76
|
-
console.log(Bun?.markdown?.ansi?.('## '+step.label)||step.label);
|
|
139
|
+
console.log(Bun?.markdown?.ansi?.('## '+step.label)||step.label);
|
|
140
|
+
|
|
141
|
+
console.log("Running: ",step.cmd,step.args)
|
|
77
142
|
|
|
78
143
|
const result = child_process.spawnSync(step.cmd, step.args, {
|
|
79
144
|
cwd: step.cwd,
|
|
@@ -103,6 +168,12 @@ export async function buildExecutable(target = "",build_outfile="single.exe") {
|
|
|
103
168
|
|
|
104
169
|
} // for steps of build
|
|
105
170
|
|
|
171
|
+
const isWindows = normalizedTarget
|
|
172
|
+
? normalizedTarget.toLowerCase().includes("windows")
|
|
173
|
+
: process.platform === "win32";
|
|
174
|
+
if (isWindows && !outfile.toLowerCase().endsWith(".exe")) {
|
|
175
|
+
outfile += ".exe";
|
|
176
|
+
}
|
|
106
177
|
if(await Bun.file(outfile).exists())
|
|
107
178
|
{
|
|
108
179
|
console.log(`Built executable: ${outfile}`);
|
|
@@ -115,24 +186,313 @@ export async function buildExecutable(target = "",build_outfile="single.exe") {
|
|
|
115
186
|
}
|
|
116
187
|
}
|
|
117
188
|
|
|
118
|
-
export const buildExe =
|
|
189
|
+
export const buildExe = buildExeAssets;
|
|
190
|
+
|
|
191
|
+
export async function buildEarlyExit(argv,build_outfile) {
|
|
192
|
+
argv = argv || process.argv
|
|
119
193
|
|
|
120
|
-
export async function buildEarlyExit(argv = process.argv,build_outfile) {
|
|
121
194
|
const buildExeIndex = argv.indexOf("--build-exe");
|
|
122
195
|
const buildForIndex = argv.indexOf("--build-for");
|
|
123
196
|
|
|
124
197
|
if (buildExeIndex === -1 && buildForIndex === -1) {
|
|
125
198
|
return false;
|
|
126
199
|
}
|
|
200
|
+
|
|
201
|
+
if (IS_COMPILED) {
|
|
202
|
+
console.error("--build-exe & --build-for are only available in the source tree");
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
127
205
|
|
|
128
206
|
if (buildForIndex !== -1) {
|
|
129
207
|
const target = argv[buildForIndex + 1];
|
|
130
208
|
if (!target || target.startsWith("-")) {
|
|
131
|
-
console.error("Missing value for --build-for");
|
|
209
|
+
console.error("Missing target value for --build-for");
|
|
132
210
|
process.exit(2);
|
|
133
211
|
}
|
|
134
|
-
process.exit(await buildExe(target,build_outfile));
|
|
212
|
+
process.exit(await buildExe(target, build_outfile, argv.slice(buildForIndex + 2)));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
process.exit(await buildExe(null, build_outfile, argv.slice(buildExeIndex + 1)));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
// Multi-package variant of buildExecutable().
|
|
220
|
+
//
|
|
221
|
+
// Every package that ships assets vendors its own single-exe/, and its
|
|
222
|
+
// assetsHelper imports its assetsPacker, so a packer shows up in the
|
|
223
|
+
// bundle's module graph exactly when that package is actually reachable
|
|
224
|
+
// from this entry point. A scan build writes that graph to a metafile,
|
|
225
|
+
// and each packer found there appends its own namespaced members to one
|
|
226
|
+
// shared assets.tar before the real compile runs.
|
|
227
|
+
export async function buildExeAssets(target = "", build_outfile = "single.exe", bunArgs = []) {
|
|
228
|
+
let outfile = resolve(process.cwd(), build_outfile);
|
|
229
|
+
const normalizedTarget = String(target || "").trim();
|
|
230
|
+
const extraBunArgs = Array.from(bunArgs ?? [], String);
|
|
231
|
+
|
|
232
|
+
if (!globalThis.Bun || IS_COMPILED) {
|
|
233
|
+
console.log("Build exe can only be run by Bun in the source tree");
|
|
234
|
+
return 1;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const bunBin = Bun.which("bun") || process.argv0;
|
|
238
|
+
const ownPacker = resolve(SINGLE_EXE_DIR, "assetsPacker.js");
|
|
239
|
+
const archive = resolve(SINGLE_EXE_DIR, "assets.tar");
|
|
240
|
+
const metafile = `${outfile}.meta.json`;
|
|
241
|
+
const scanDir = `${outfile}.scan`;
|
|
242
|
+
|
|
243
|
+
// ASSETS_BUNFS swaps the tar for `--compile --asset`: every packer
|
|
244
|
+
// copies into one fixed build/assets, and --asset keeps only that
|
|
245
|
+
// folder's basename, so the members land at assets/<name>@<version>/
|
|
246
|
+
// exactly as the tar back end writes them.
|
|
247
|
+
const bunfs = Boolean(process.env.ASSETS_BUNFS);
|
|
248
|
+
const staging = resolve(REPO_ROOT, "build", "assets");
|
|
249
|
+
|
|
250
|
+
// Nothing loads a tar in bunfs mode, so the asset loader — and the
|
|
251
|
+
// `assets.tar` it imports — cannot be in the graph. Everything else in
|
|
252
|
+
// entry.mjs still has to run, so the compile entry is a generated copy
|
|
253
|
+
// of it with that one import removed.
|
|
254
|
+
//
|
|
255
|
+
// Handing the main program to `bun build` directly would be simpler and
|
|
256
|
+
// breaks two ways. Whatever entry.mjs does after the import is lost, and
|
|
257
|
+
// a CommonJS program relying on a `require.main` guard the bundler
|
|
258
|
+
// inlines to false would never start. Worse, `--bytecode` only marks the
|
|
259
|
+
// chunk as an async module when a top-level await appears in an
|
|
260
|
+
// unwrapped part's own source; a main program that reaches top-level
|
|
261
|
+
// await only through `__esm`-wrapped dependencies is then evaluated on
|
|
262
|
+
// the sync path, its suspended generator is dropped, and the binary
|
|
263
|
+
// exits 0 having run nothing. entry.mjs awaits in its own source, so the
|
|
264
|
+
// copy carries that flag the way the tar build always has.
|
|
265
|
+
const bunfsEntry = resolve(SINGLE_EXE_DIR, ".bunfs-entry.mjs");
|
|
266
|
+
const dropBunfsEntry = () => bunfs && rmSync(bunfsEntry, { force: true });
|
|
267
|
+
|
|
268
|
+
if (bunfs && !writeBunfsEntry(resolve(SINGLE_EXE_DIR, "entry.mjs"), bunfsEntry)) {
|
|
269
|
+
return 1;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const compileEntry = bunfs ? bunfsEntry : "./entry.mjs";
|
|
273
|
+
|
|
274
|
+
const step = (label, args, cwd) => {
|
|
275
|
+
console.log("");
|
|
276
|
+
console.log(Bun?.markdown?.ansi?.("## " + label) || label);
|
|
277
|
+
console.log("Running: ", bunBin, args);
|
|
278
|
+
|
|
279
|
+
const result = child_process.spawnSync(bunBin, args, {
|
|
280
|
+
cwd,
|
|
281
|
+
stdio: "inherit",
|
|
282
|
+
env: process.env,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
console.log("");
|
|
286
|
+
console.log(Bun?.markdown?.ansi?.("- Status: " + result.status + " for " + label) || result.status);
|
|
287
|
+
|
|
288
|
+
if (result.error) console.error(result.error);
|
|
289
|
+
return result.status ?? 1;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// What each packer is told to write into, and in which mode.
|
|
293
|
+
const packInto = bunfs ? ["-f", staging] : ["-f", archive, "-p"];
|
|
294
|
+
|
|
295
|
+
if (bunfs) {
|
|
296
|
+
mkdirSync(staging, { recursive: true });
|
|
297
|
+
console.log("");
|
|
298
|
+
console.log(`Staging assets into ${staging}`);
|
|
299
|
+
} else if (step("Pack own assets", [ownPacker, ...packInto], SINGLE_EXE_DIR) !== 0) {
|
|
300
|
+
// Tar mode packs first: the scan below has to resolve entry.mjs's
|
|
301
|
+
// `with { type: "file" }` import of the archive.
|
|
302
|
+
console.error("Own assets failed to pack");
|
|
303
|
+
return 1;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Bundle only. The binary is thrown away, so skip --compile, --minify
|
|
307
|
+
// and --bytecode; the module graph is the same and it is much faster.
|
|
308
|
+
if (step("Scan module graph", ["build", compileEntry, "--target=bun", `--metafile=${metafile}`, `--outdir=${scanDir}`], SINGLE_EXE_DIR) !== 0) {
|
|
309
|
+
console.error("Scan build failed");
|
|
310
|
+
dropBunfsEntry();
|
|
311
|
+
return 1;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const packers = findAssetPackers(metafile, SINGLE_EXE_DIR, bunfs ? "" : ownPacker);
|
|
315
|
+
rmSync(scanDir, { recursive: true, force: true });
|
|
316
|
+
|
|
317
|
+
console.log("");
|
|
318
|
+
console.log(`Found ${packers.length} asset packer(s) to run`);
|
|
319
|
+
|
|
320
|
+
for (const packer of packers) {
|
|
321
|
+
const name = dirname(dirname(packer)).split("/").pop();
|
|
322
|
+
|
|
323
|
+
// Tar mode appends to a shared archive; folder mode needs no -r
|
|
324
|
+
// because each package owns its own <name>@<version> subtree.
|
|
325
|
+
const args = bunfs ? [packer, ...packInto] : [packer, ...packInto, "-r"];
|
|
326
|
+
|
|
327
|
+
if (step(`Pack ${name}`, args, dirname(packer)) !== 0) {
|
|
328
|
+
console.error(`Failed to pack assets from ${packer}`);
|
|
329
|
+
dropBunfsEntry();
|
|
330
|
+
return 1;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Compress last. Every packer above reads the archive and writes it
|
|
335
|
+
// back uncompressed, so this has to happen after the final append.
|
|
336
|
+
// libarchive sniffs the format on read, so assetsLoader needs no
|
|
337
|
+
// change to consume it. Nothing to compress in bunfs mode.
|
|
338
|
+
if (bunfs) {
|
|
339
|
+
// no archive in this mode
|
|
340
|
+
} else if (process.env.ASSETS_NO_GZIP) {
|
|
341
|
+
console.log("");
|
|
342
|
+
console.log("ASSETS_NO_GZIP is set, leaving the archive uncompressed");
|
|
343
|
+
} else {
|
|
344
|
+
console.log("");
|
|
345
|
+
console.log(Bun?.markdown?.ansi?.("## Compress assets") || "Compress assets");
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
const raw = await Bun.file(archive).bytes();
|
|
349
|
+
const gzipped = Bun.gzipSync(raw, { level: 9 });
|
|
350
|
+
|
|
351
|
+
await Bun.write(archive, gzipped);
|
|
352
|
+
|
|
353
|
+
const pct = raw.byteLength ? Math.round((1 - gzipped.byteLength / raw.byteLength) * 100) : 0;
|
|
354
|
+
console.log(`${raw.byteLength} -> ${gzipped.byteLength} bytes (-${pct}%)`);
|
|
355
|
+
} catch (e) {
|
|
356
|
+
console.error("Could not compress the archive; continuing uncompressed");
|
|
357
|
+
console.error(e);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (step("Compile executable", [
|
|
362
|
+
"build",
|
|
363
|
+
"--format=esm",
|
|
364
|
+
"--compile",
|
|
365
|
+
"--minify",
|
|
366
|
+
"--bytecode",
|
|
367
|
+
compileEntry,
|
|
368
|
+
...(bunfs ? ["--asset", staging] : []),
|
|
369
|
+
`--outfile=${outfile}`,
|
|
370
|
+
`--metafile-md=${outfile}.meta.md`,
|
|
371
|
+
...(normalizedTarget ? [`--target=${normalizedTarget}`] : []),
|
|
372
|
+
...extraBunArgs,
|
|
373
|
+
], SINGLE_EXE_DIR) !== 0) {
|
|
374
|
+
console.error("Compile failed");
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
dropBunfsEntry();
|
|
378
|
+
|
|
379
|
+
const isWindows = normalizedTarget ? normalizedTarget.toLowerCase().includes("windows") : process.platform === "win32";
|
|
380
|
+
if (isWindows && !outfile.toLowerCase().endsWith(".exe")) {
|
|
381
|
+
outfile += ".exe";
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (await Bun.file(outfile).exists()) {
|
|
385
|
+
console.log(`Built executable: ${outfile}`);
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
console.log(`Error while building executable: ${outfile}`);
|
|
390
|
+
return 1;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
// entry.mjs, minus the import of the asset loader, written to `outFile`
|
|
395
|
+
// as the compile entry for bunfs builds. Returns whether it was written.
|
|
396
|
+
//
|
|
397
|
+
// A textual copy, not a generated stub: entry.mjs may do more than import
|
|
398
|
+
// the main program, and all of it has to survive.
|
|
399
|
+
export function writeBunfsEntry(entryFile, outFile) {
|
|
400
|
+
let src;
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
src = readFileSync(entryFile, "utf8");
|
|
404
|
+
} catch (e) {
|
|
405
|
+
console.error(`Could not read ${entryFile}`);
|
|
406
|
+
console.error(e);
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Whole-line side-effect import of the loader, in any of the shapes a
|
|
411
|
+
// project might have written it. Nothing else may reference it: the
|
|
412
|
+
// loader is what pulls in `assets.tar`, which no bunfs build produces.
|
|
413
|
+
const loaderImport = /^[ \t]*import\s+(?:[^"'\n]*\bfrom\s+)?["'][^"'\n]*assetsLoader\.mjs["'][ \t]*;?[ \t]*$/gm; // "
|
|
414
|
+
const stripped = src.replace(loaderImport, "");
|
|
415
|
+
|
|
416
|
+
if (stripped === src) {
|
|
417
|
+
console.log("");
|
|
418
|
+
console.log(`No assetsLoader import found in ${entryFile}; compiling it as is`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const header =
|
|
422
|
+
"// Generated by single-exe/compiled.js for ASSETS_BUNFS=1 builds.\n"
|
|
423
|
+
+ "// A copy of entry.mjs without the asset loader; edit entry.mjs.\n";
|
|
424
|
+
|
|
425
|
+
// A hashbang is only a hashbang on the first line; anywhere else it is
|
|
426
|
+
// a syntax error. Keep it there and put the header underneath, unless
|
|
427
|
+
// the file is nothing but a hashbang and there is no underneath.
|
|
428
|
+
const firstBreak = stripped.indexOf("\n");
|
|
429
|
+
const hasHashbang = stripped.startsWith("#!");
|
|
430
|
+
const hashbang = hasHashbang && firstBreak !== -1 ? stripped.slice(0, firstBreak + 1) : "";
|
|
431
|
+
|
|
432
|
+
try {
|
|
433
|
+
writeFileSync(
|
|
434
|
+
outFile,
|
|
435
|
+
hasHashbang && !hashbang
|
|
436
|
+
? stripped
|
|
437
|
+
: hashbang + header + stripped.slice(hashbang.length),
|
|
438
|
+
);
|
|
439
|
+
} catch (e) {
|
|
440
|
+
console.error(`Could not write ${outFile}`);
|
|
441
|
+
console.error(e);
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
// The main program, as declared by the last import of entry.mjs. That
|
|
450
|
+
// file is already the one place an adopting project edits.
|
|
451
|
+
export function lastEntryImport(entryFile) {
|
|
452
|
+
let src;
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
src = readFileSync(entryFile, "utf8");
|
|
456
|
+
} catch {
|
|
457
|
+
return "";
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Drop comments first, or a commented-out import would win.
|
|
461
|
+
src = src.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^[ \t]*\/\/.*$/gm, "");
|
|
462
|
+
|
|
463
|
+
const re = /\bimport\b[^"'\n]*["']([^"']+)["']/g; // "
|
|
464
|
+
let last = "";
|
|
465
|
+
|
|
466
|
+
for (let m; (m = re.exec(src)) !== null; ) last = m[1];
|
|
467
|
+
|
|
468
|
+
return last;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
// Absolute, de-duplicated paths of every assetsPacker.js in the metafile,
|
|
473
|
+
// minus `own`. Metafile input keys are relative to the build's cwd.
|
|
474
|
+
export function findAssetPackers(metafilePath, baseDir, own = "") {
|
|
475
|
+
if (!existsSync(metafilePath)) return [];
|
|
476
|
+
|
|
477
|
+
let meta;
|
|
478
|
+
try {
|
|
479
|
+
meta = JSON.parse(readFileSync(metafilePath, "utf8"));
|
|
480
|
+
} catch (e) {
|
|
481
|
+
console.error(`Could not read metafile ${metafilePath}`);
|
|
482
|
+
console.error(e);
|
|
483
|
+
return [];
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const found = new Set();
|
|
487
|
+
|
|
488
|
+
for (const key of Object.keys(meta?.inputs ?? {})) {
|
|
489
|
+
if (basename(key) !== "assetsPacker.js") continue;
|
|
490
|
+
|
|
491
|
+
const abs = resolve(baseDir, key);
|
|
492
|
+
if (abs === own || !existsSync(abs)) continue;
|
|
493
|
+
|
|
494
|
+
found.add(abs);
|
|
135
495
|
}
|
|
136
496
|
|
|
137
|
-
|
|
497
|
+
return [...found].sort();
|
|
138
498
|
}
|
|
@@ -3,7 +3,21 @@
|
|
|
3
3
|
// 1. Injects assets to global.internalAssets
|
|
4
4
|
// as { "./path/in/tar":file.bytes() }
|
|
5
5
|
// 2. Sets global.assetsLoaderPromise
|
|
6
|
-
|
|
6
|
+
// 3. Starts the main program only after the assets are ready
|
|
7
|
+
import "./assetsLoader.mjs";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
// Keep these as `await`, not `.then()`. Besides the sequencing, they are
|
|
10
|
+
// the only top-level await this program writes anywhere, and `--bytecode`
|
|
11
|
+
// reads exactly that to decide the program starts asynchronously. With
|
|
12
|
+
// none written, the executable is evaluated synchronously, its suspended
|
|
13
|
+
// startup is dropped, and it exits 0 having run nothing — no error, at any
|
|
14
|
+
// size. Awaits the bundler generates for us do not count; see the README's
|
|
15
|
+
// "The await in entry.mjs is load-bearing".
|
|
16
|
+
await globalThis.assetsLoaderPromise;
|
|
9
17
|
|
|
18
|
+
// gotty.js starts itself from `require.main === module`, which the
|
|
19
|
+
// bundler inlines to false because this file is the entry point instead.
|
|
20
|
+
// Call its exported starter directly.
|
|
21
|
+
const { bootstrap } = await import("../gotty.js");
|
|
22
|
+
|
|
23
|
+
await bootstrap();
|
|
@@ -10,12 +10,12 @@ let ttyModeDepth = 0;
|
|
|
10
10
|
let rawOutputMode = false;
|
|
11
11
|
const defaultDebugLogPath = path.join(os.tmpdir(), "zmodem", "zmodem.log");
|
|
12
12
|
const debugLogPath = resolveDebugLogPath();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
: Promise.resolve(
|
|
13
|
+
const ZMODEM_ASSET = "static/js/zmodem.js";
|
|
14
|
+
// ESM, and Bun-only in practice: under Node there is nothing embedded, so
|
|
15
|
+
// the source checkout's own copy is the only answer anyway.
|
|
16
|
+
const assetsHelperPromise = globalThis.Bun
|
|
17
|
+
? import("./single-exe/assetsHelper.js").catch(() => null)
|
|
18
|
+
: Promise.resolve(null);
|
|
19
19
|
|
|
20
20
|
async function loadZmodem() {
|
|
21
21
|
if (cachedZmodem) {
|
|
@@ -23,12 +23,14 @@ async function loadZmodem() {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
await globalThis.assetsLoaderPromise;
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const bundlePath =
|
|
26
|
+
const assets = await assetsHelperPromise;
|
|
27
|
+
const source = assets
|
|
28
|
+
? assets.readAssetTextSync(ZMODEM_ASSET)
|
|
29
|
+
: fs.readFileSync(path.join(__dirname, ZMODEM_ASSET), "utf8");
|
|
30
|
+
// Only a label for stack traces out of the vm context.
|
|
31
|
+
const bundlePath = assets
|
|
32
|
+
? assets.assetDiskPath(ZMODEM_ASSET)
|
|
33
|
+
: path.join(__dirname, ZMODEM_ASSET);
|
|
32
34
|
const quietConsole = {
|
|
33
35
|
debug() {},
|
|
34
36
|
log() {},
|
|
@@ -2,6 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-visible changes to jsmdcui are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.19.0] - 2026-08-24
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `--assets-external` now works in a compiled binary. Every embedded asset
|
|
10
|
+
was affected: `--demo-<name>` reported `demos/<name>.md was not found`,
|
|
11
|
+
`--demo-list` listed only `testapp.md`, `--readme` and `--changelog` failed
|
|
12
|
+
with `ENOENT`, and the runtime registry (colorschemes, syntax, help,
|
|
13
|
+
jsplugins) looked in the wrong directory. Two causes, both in the
|
|
14
|
+
bootstrap:
|
|
15
|
+
* `assetsHelper`'s `--asset` branch stayed live in tar builds, rooted at
|
|
16
|
+
the binary's own `/$bunfs/root`. With the embedded store switched off it
|
|
17
|
+
enumerated the executable and its own archive as if they were assets, so
|
|
18
|
+
`hasInternalAssets()` answered `true` and listings came back empty. A
|
|
19
|
+
store of `null` (the loader ran and was told to stay external) is now
|
|
20
|
+
told apart from `undefined` (no loader in the graph, the `--asset` back
|
|
21
|
+
end).
|
|
22
|
+
* The disk fallback looked for `<exe dir>/<key>`, but `--assets-extract`
|
|
23
|
+
writes the archive exactly as packed, namespace included:
|
|
24
|
+
`<exe dir>/assets/<name>@<version>/<key>`.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `assetsHelper` exports `getAssetKey(path)` and `assetDiskPath(path)` — the
|
|
29
|
+
key an asset is stored under, and the file its disk fallback reads. A
|
|
30
|
+
program that writes its own fallback needs the same two answers.
|
|
31
|
+
- `assetsHelper` exports `readAssetTextSync(path)` and
|
|
32
|
+
`readAssetBytesSync(path)`, sync twins of `readAssetText` /
|
|
33
|
+
`readAssetBytes` for callers that cannot await, such as argument parsing.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- jsmdcui reads its own assets through `readAssetText` / `assetDiskPath`
|
|
38
|
+
instead of joining `REPO_ROOT` by hand, so every read follows the
|
|
39
|
+
extracted tree as well as the embedded one.
|
|
40
|
+
|
|
41
|
+
## [0.18.2] - 2026-08-23
|
|
42
|
+
|
|
43
|
+
- Added ASSETS_BUNFS=1 for the build system
|
|
44
|
+
* Use --asset to pack assets
|
|
45
|
+
* See single-exe/README.md
|
|
46
|
+
- Changed the tar build-exe to gzip compressed by default
|
|
47
|
+
|
|
48
|
+
## [0.18.1] - 2026-08-19
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- Fix `switchBackend`'s unix-socket endpoint losing a leading NUL byte, which
|
|
53
|
+
broke reaching a Linux abstract-namespace socket (a name starting with
|
|
54
|
+
`"\0"`, as opposed to a filesystem path). `new URL()` percent-encodes a NUL
|
|
55
|
+
byte in the path to `%00`; `rpc.mjs` now runs the extracted path through
|
|
56
|
+
`decodeURIComponent()` before handing it to Bun's `unix` fetch option, so
|
|
57
|
+
the exact original byte reaches Bun rather than the three literal
|
|
58
|
+
characters `%00`. Filesystem-path endpoints are unaffected, since they have
|
|
59
|
+
nothing that needs decoding.
|
|
60
|
+
|
|
5
61
|
## [0.18.0] - 2026-08-19
|
|
6
62
|
|
|
7
63
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsmdcui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Markdown as a Common UI for Terminals and Web Browsers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -15,5 +15,15 @@
|
|
|
15
15
|
"edit": "bun ./edit",
|
|
16
16
|
"check": "bun ./src/index.js --check",
|
|
17
17
|
"clean": "bun ./clean.sh"
|
|
18
|
-
}
|
|
18
|
+
},
|
|
19
|
+
"assets": [
|
|
20
|
+
"demos",
|
|
21
|
+
"runtime",
|
|
22
|
+
"README.md",
|
|
23
|
+
"CHANGELOG.md",
|
|
24
|
+
"cdp-maze.js",
|
|
25
|
+
"src/cui/server.mjs",
|
|
26
|
+
"src/cui/rpc.mjs",
|
|
27
|
+
"testapp.md"
|
|
28
|
+
]
|
|
19
29
|
}
|