jsmdcui 0.9.0 → 0.11.1
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 +119 -0
- package/README.md +104 -0
- package/package.json +7 -5
- package/runmd.mjs +71 -18
- package/runtime/help/help.md +104 -0
- package/runtime/jsplugins/example/example.js +3 -1
- package/single-exe/README.md +102 -0
- package/single-exe/README.md-rpc.js +623 -0
- package/single-exe/README.md-server.js +127 -0
- package/single-exe/README.md.back.js +2 -0
- package/single-exe/README.md.front.js +27 -0
- package/single-exe/README.md.html +325 -0
- package/single-exe/compiled.js +57 -3
- package/src/index.js +381 -76
- package/src/plugins/js-bridge.js +5 -0
- package/tests/cat-markdown.test.js +59 -0
- package/tests/compiled-runtime.test.js +68 -1
- package/tests/demo.test.js +177 -3
- package/tests/js-plugin-prompts.test.js +30 -0
- package/tests/wui-responsive-images.test.js +21 -2
- package/tests/wui.test.js +31 -0
package/src/plugins/js-bridge.js
CHANGED
|
@@ -1622,6 +1622,11 @@ function _makeBufAPI(buffer) {
|
|
|
1622
1622
|
get Type() { return buffer.Type; },
|
|
1623
1623
|
get Settings() { return buffer.Settings; },
|
|
1624
1624
|
get Modified() { return buffer.modified; },
|
|
1625
|
+
get MdcuiModuleSource() {
|
|
1626
|
+
return global.MDCUI_MAIN && buffer._useBundledMdcuiModules
|
|
1627
|
+
? "embedded"
|
|
1628
|
+
: "external";
|
|
1629
|
+
},
|
|
1625
1630
|
|
|
1626
1631
|
Line: (n) => buffer.Line(n),
|
|
1627
1632
|
LinesNum: () => buffer.LinesNum(),
|
|
@@ -78,3 +78,62 @@ test("--edit overrides the .md mdcui default with utf8", async () => {
|
|
|
78
78
|
await rm(dir, { recursive: true, force: true });
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
|
+
|
|
82
|
+
test("--mdcui and --tui are equivalent to -encoding mdcui", async () => {
|
|
83
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-cat-mdcui-"));
|
|
84
|
+
const markdownPath = join(dir, "sample.md");
|
|
85
|
+
await writeFile(markdownPath, "# Heading\n\n- one\n- two\n");
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const flag = Bun.spawnSync([bunBin, tui, "-cat", "--mdcui", markdownPath], {
|
|
89
|
+
cwd: dir,
|
|
90
|
+
stdout: "pipe",
|
|
91
|
+
stderr: "pipe",
|
|
92
|
+
});
|
|
93
|
+
const encoding = Bun.spawnSync([bunBin, tui, "-cat", "-encoding", "mdcui", markdownPath], {
|
|
94
|
+
cwd: dir,
|
|
95
|
+
stdout: "pipe",
|
|
96
|
+
stderr: "pipe",
|
|
97
|
+
});
|
|
98
|
+
const tuiFlag = Bun.spawnSync([bunBin, tui, "-cat", "--tui", markdownPath], {
|
|
99
|
+
cwd: dir,
|
|
100
|
+
stdout: "pipe",
|
|
101
|
+
stderr: "pipe",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(flag.exitCode).toBe(0);
|
|
105
|
+
expect(encoding.exitCode).toBe(0);
|
|
106
|
+
expect(tuiFlag.exitCode).toBe(0);
|
|
107
|
+
expect(flag.stdout.toString()).toBe(encoding.stdout.toString());
|
|
108
|
+
expect(tuiFlag.stdout.toString()).toBe(encoding.stdout.toString());
|
|
109
|
+
} finally {
|
|
110
|
+
await rm(dir, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("-filetype forces syntax highlighting independently of the filename", async () => {
|
|
115
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-cat-filetype-"));
|
|
116
|
+
const textPath = join(dir, "sample.txt");
|
|
117
|
+
await writeFile(textPath, "const answer = 42;\n");
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const automatic = Bun.spawnSync([bunBin, tui, "-cat", textPath], {
|
|
121
|
+
cwd: dir,
|
|
122
|
+
stdout: "pipe",
|
|
123
|
+
stderr: "pipe",
|
|
124
|
+
});
|
|
125
|
+
const javascript = Bun.spawnSync([bunBin, tui, "-cat", "-filetype", "javascript", textPath], {
|
|
126
|
+
cwd: dir,
|
|
127
|
+
stdout: "pipe",
|
|
128
|
+
stderr: "pipe",
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(automatic.exitCode).toBe(0);
|
|
132
|
+
expect(javascript.exitCode).toBe(0);
|
|
133
|
+
expect(automatic.stdout.toString()).not.toContain("\x1b[");
|
|
134
|
+
expect(javascript.stdout.toString()).toContain("\x1b[");
|
|
135
|
+
expect(Bun.stripANSI(javascript.stdout.toString()).trimEnd()).toBe("const answer = 42;");
|
|
136
|
+
} finally {
|
|
137
|
+
await rm(dir, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
@@ -1,8 +1,75 @@
|
|
|
1
1
|
import { expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
isCompiledBinary as isSingleExeCompiled,
|
|
4
|
+
stringifyNonPrimitiveDefineValues,
|
|
5
|
+
} from "../single-exe/compiled.js";
|
|
3
6
|
|
|
4
7
|
test("single-exe recognizes Bun compiled virtual paths", () => {
|
|
5
8
|
expect(isSingleExeCompiled(["bun", "/$bunfs/root/app.js"])).toBe(true);
|
|
6
9
|
expect(isSingleExeCompiled(["bun", "B:/~BUN/root/app.js"])).toBe(true);
|
|
7
10
|
expect(isSingleExeCompiled(["bun", "/project/src/index.js"])).toBe(false);
|
|
8
11
|
});
|
|
12
|
+
|
|
13
|
+
test("non-primitive MDCUI_MAIN define values become strings", () => {
|
|
14
|
+
const split = [
|
|
15
|
+
"bun",
|
|
16
|
+
"src/index.js",
|
|
17
|
+
"--build-exe",
|
|
18
|
+
"--define",
|
|
19
|
+
"global.MDCUI_MAIN=../中文=工具.md",
|
|
20
|
+
];
|
|
21
|
+
expect(
|
|
22
|
+
stringifyNonPrimitiveDefineValues(split, "global.MDCUI_MAIN"),
|
|
23
|
+
).toBe(split);
|
|
24
|
+
expect(split.at(-1)).toBe(
|
|
25
|
+
'global.MDCUI_MAIN="../中文=工具.md"',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const inline = [
|
|
29
|
+
"--define=global.MDCUI_MAIN=../m.md",
|
|
30
|
+
"--define",
|
|
31
|
+
"OTHER=../other.md",
|
|
32
|
+
"--define",
|
|
33
|
+
"global.MDCUI_MAIN_BASE=m.md",
|
|
34
|
+
];
|
|
35
|
+
stringifyNonPrimitiveDefineValues(inline, "global.MDCUI_MAIN");
|
|
36
|
+
expect(inline).toEqual([
|
|
37
|
+
'--define=global.MDCUI_MAIN="../m.md"',
|
|
38
|
+
"--define",
|
|
39
|
+
"OTHER=../other.md",
|
|
40
|
+
"--define",
|
|
41
|
+
"global.MDCUI_MAIN_BASE=m.md",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
for (const source of ["", "[]", "{}", "makePath()"]) {
|
|
45
|
+
const argv = ["--define", `global.MDCUI_MAIN=${source}`];
|
|
46
|
+
stringifyNonPrimitiveDefineValues(argv, "global.MDCUI_MAIN");
|
|
47
|
+
expect(argv[1]).toBe(
|
|
48
|
+
`global.MDCUI_MAIN=${JSON.stringify(source)}`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("primitive MDCUI_MAIN define values keep their type", () => {
|
|
54
|
+
const cases = new Map([
|
|
55
|
+
['"../m.md"', '"../m.md"'],
|
|
56
|
+
["'../m.md'", '"../m.md"'],
|
|
57
|
+
["true", "true"],
|
|
58
|
+
["false", "false"],
|
|
59
|
+
["null", "null"],
|
|
60
|
+
["undefined", "undefined"],
|
|
61
|
+
["0", "0"],
|
|
62
|
+
["-1.5", "-1.5"],
|
|
63
|
+
["NaN", "NaN"],
|
|
64
|
+
["Infinity", "Infinity"],
|
|
65
|
+
["0xff", "0xff"],
|
|
66
|
+
["0b10", "0b10"],
|
|
67
|
+
["1_000", "1_000"],
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
for (const [source, normalized] of cases) {
|
|
71
|
+
const argv = ["--define", `global.MDCUI_MAIN=${source}`];
|
|
72
|
+
stringifyNonPrimitiveDefineValues(argv, "global.MDCUI_MAIN");
|
|
73
|
+
expect(argv[1]).toBe(`global.MDCUI_MAIN=${normalized}`);
|
|
74
|
+
}
|
|
75
|
+
});
|
package/tests/demo.test.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { expect, test } from "bun:test";
|
|
2
|
-
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { readMarkdownInput } from "../runmd.mjs";
|
|
5
6
|
|
|
6
7
|
const tui = join(import.meta.dir, "..", "tui");
|
|
8
|
+
const indexEntry = join(import.meta.dir, "..", "src", "index.js");
|
|
9
|
+
const demosDirectory = join(import.meta.dir, "..", "demos");
|
|
7
10
|
const bunBin = Bun.which("bun") || process.argv0;
|
|
8
11
|
|
|
9
12
|
test("--help describes the non-overwriting demo behavior", () => {
|
|
@@ -14,8 +17,8 @@ test("--help describes the non-overwriting demo behavior", () => {
|
|
|
14
17
|
|
|
15
18
|
expect(result.exitCode).toBe(0);
|
|
16
19
|
const output = result.stdout.toString();
|
|
17
|
-
expect(output).toContain("
|
|
18
|
-
expect(output).toContain("
|
|
20
|
+
expect(output).toContain("Execute an existing ./testapp.md");
|
|
21
|
+
expect(output).toContain("Or write the bundled demo if missing");
|
|
19
22
|
expect(output).toContain("--demo-<filename>");
|
|
20
23
|
expect(output).toContain("demos/<filename>.md");
|
|
21
24
|
expect(output.match(/Open it in the TUI and write 5 generated files beside it/g)?.length).toBe(2);
|
|
@@ -39,6 +42,139 @@ test("--demo-list lists root and automatically discovered demos", () => {
|
|
|
39
42
|
expect(output).toContain("--demo-imgtool --demo-image-processor");
|
|
40
43
|
});
|
|
41
44
|
|
|
45
|
+
test("--version lists every MDCUI build define", () => {
|
|
46
|
+
const result = Bun.spawnSync([bunBin, tui, "--version"], {
|
|
47
|
+
stdout: "pipe",
|
|
48
|
+
stderr: "pipe",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(result.exitCode).toBe(0);
|
|
52
|
+
const output = result.stdout.toString();
|
|
53
|
+
expect(output).toContain("MDCUI_DEFAULT_EDIT:");
|
|
54
|
+
expect(output).toContain("MDCUI_DEFAULT_DEMO:");
|
|
55
|
+
expect(output).toContain("MDCUI_DEFAULT_DEMO_WUI:");
|
|
56
|
+
expect(output).toContain("MDCUI_OVERWRITE_DEMO:");
|
|
57
|
+
expect(output).toContain("global.MDCUI_MAIN:");
|
|
58
|
+
expect(output).toContain("global.MDCUI_MAIN_BASE:");
|
|
59
|
+
|
|
60
|
+
const presenceResult = Bun.spawnSync(
|
|
61
|
+
[
|
|
62
|
+
bunBin,
|
|
63
|
+
"--define",
|
|
64
|
+
"MDCUI_DEFAULT_DEMO_WUI=0",
|
|
65
|
+
indexEntry,
|
|
66
|
+
"--version",
|
|
67
|
+
],
|
|
68
|
+
{
|
|
69
|
+
stdout: "pipe",
|
|
70
|
+
stderr: "pipe",
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
expect(presenceResult.exitCode).toBe(0);
|
|
74
|
+
expect(presenceResult.stdout.toString()).toContain(
|
|
75
|
+
"MDCUI_DEFAULT_DEMO_WUI: enabled",
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("--demo names allow Chinese but reject whitespace", () => {
|
|
80
|
+
const unicodeResult = Bun.spawnSync(
|
|
81
|
+
[bunBin, tui, "--demo-不存在的程式", "-cat", "-encoding", "utf8"],
|
|
82
|
+
{
|
|
83
|
+
stdout: "pipe",
|
|
84
|
+
stderr: "pipe",
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
expect(unicodeResult.exitCode).toBe(2);
|
|
88
|
+
expect(unicodeResult.stderr.toString()).toContain(
|
|
89
|
+
"Unknown demo --demo-不存在的程式",
|
|
90
|
+
);
|
|
91
|
+
expect(unicodeResult.stderr.toString()).not.toContain("Invalid demo option");
|
|
92
|
+
|
|
93
|
+
const whitespaceResult = Bun.spawnSync(
|
|
94
|
+
[bunBin, tui, "--demo-中文 程式", "-cat", "-encoding", "utf8"],
|
|
95
|
+
{
|
|
96
|
+
stdout: "pipe",
|
|
97
|
+
stderr: "pipe",
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
expect(whitespaceResult.exitCode).toBe(2);
|
|
101
|
+
expect(whitespaceResult.stderr.toString()).toContain("Invalid demo option");
|
|
102
|
+
expect(whitespaceResult.stderr.toString()).toContain(
|
|
103
|
+
"whitespace is not allowed",
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("Unicode MDCUI_MAIN overwrite starts the embedded WUI server", async () => {
|
|
108
|
+
const directory = await mkdtemp(join(tmpdir(), "jsmdcui-main-unicode-"));
|
|
109
|
+
const sourceDirectory = join(directory, "source");
|
|
110
|
+
const workDirectory = join(directory, "work");
|
|
111
|
+
const demoName = `中文工具-${crypto.randomUUID()}`;
|
|
112
|
+
const markdownName = `${demoName}.md`;
|
|
113
|
+
const sourcePath = join(sourceDirectory, markdownName);
|
|
114
|
+
const workPath = join(workDirectory, markdownName);
|
|
115
|
+
const bundledDemoPath = join(demosDirectory, markdownName);
|
|
116
|
+
const preloadPath = join(directory, "mock-serve.mjs");
|
|
117
|
+
const markdown = `# 中文工具
|
|
118
|
+
|
|
119
|
+
\`\`\`js front
|
|
120
|
+
export async function run() {
|
|
121
|
+
return await rpc.answer();
|
|
122
|
+
}
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
\`\`\`js back
|
|
126
|
+
import { helper } from "./helper.js";
|
|
127
|
+
export function answer() {
|
|
128
|
+
return helper();
|
|
129
|
+
}
|
|
130
|
+
\`\`\`
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
await mkdir(sourceDirectory);
|
|
135
|
+
await mkdir(workDirectory);
|
|
136
|
+
await writeFile(sourcePath, markdown);
|
|
137
|
+
await writeFile(
|
|
138
|
+
join(sourceDirectory, "helper.js"),
|
|
139
|
+
'export function helper() { return "ok"; }\n',
|
|
140
|
+
);
|
|
141
|
+
await writeFile(workPath, "# stale file with a different byte length\n");
|
|
142
|
+
await writeFile(
|
|
143
|
+
preloadPath,
|
|
144
|
+
`Bun.serve = () => ({ port: 34567, stop() {} });\n`,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const result = Bun.spawnSync(
|
|
148
|
+
[
|
|
149
|
+
bunBin,
|
|
150
|
+
"--preload",
|
|
151
|
+
preloadPath,
|
|
152
|
+
"--define",
|
|
153
|
+
`global.MDCUI_MAIN=${JSON.stringify(sourcePath)}`,
|
|
154
|
+
indexEntry,
|
|
155
|
+
"--wui",
|
|
156
|
+
`--demo-${demoName}`,
|
|
157
|
+
"--overwrite-demo",
|
|
158
|
+
],
|
|
159
|
+
{
|
|
160
|
+
cwd: workDirectory,
|
|
161
|
+
stdout: "pipe",
|
|
162
|
+
stderr: "pipe",
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
expect(result.exitCode).toBe(0);
|
|
167
|
+
expect(await readFile(workPath, "utf8")).toBe(markdown);
|
|
168
|
+
const stderr = Bun.stripANSI(result.stderr.toString());
|
|
169
|
+
expect(stderr).toContain("[mdcui] Starting embedded WUI server:");
|
|
170
|
+
expect(stderr).toContain(sourcePath);
|
|
171
|
+
expect(stderr).not.toContain("Cannot find module");
|
|
172
|
+
} finally {
|
|
173
|
+
await rm(bundledDemoPath, { force: true });
|
|
174
|
+
await rm(directory, { recursive: true, force: true });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
42
178
|
test("--export-cdp-maze writes and overwrites the bundled solver", async () => {
|
|
43
179
|
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-export-cdp-maze-"));
|
|
44
180
|
const outputPath = join(dir, "cdp-maze.js");
|
|
@@ -97,6 +233,44 @@ test("--demo preserves an existing testapp.md", async () => {
|
|
|
97
233
|
}
|
|
98
234
|
});
|
|
99
235
|
|
|
236
|
+
test("--overwrite-demo replaces an existing demo with the bundled copy", async () => {
|
|
237
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-demo-overwrite-"));
|
|
238
|
+
const existing = "# Replace my demo\n";
|
|
239
|
+
try {
|
|
240
|
+
await writeFile(join(dir, "testapp.md"), existing);
|
|
241
|
+
const result = Bun.spawnSync(
|
|
242
|
+
[bunBin, tui, "--overwrite-demo", "--demo", "-cat", "-encoding", "utf8"],
|
|
243
|
+
{
|
|
244
|
+
cwd: dir,
|
|
245
|
+
stdout: "pipe",
|
|
246
|
+
stderr: "pipe",
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
expect(result.exitCode).toBe(0);
|
|
251
|
+
const written = await readFile(join(dir, "testapp.md"), "utf8");
|
|
252
|
+
expect(written).toContain("計算機");
|
|
253
|
+
expect(written).not.toBe(existing);
|
|
254
|
+
expect(Bun.stripANSI(result.stdout.toString())).toContain("計算機");
|
|
255
|
+
} finally {
|
|
256
|
+
await rm(dir, { recursive: true, force: true });
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("WUI demo loading can overwrite an existing testapp.md", async () => {
|
|
261
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-wui-demo-overwrite-"));
|
|
262
|
+
const demoPath = join(dir, "testapp.md");
|
|
263
|
+
try {
|
|
264
|
+
await writeFile(demoPath, "# Replace my WUI demo\n");
|
|
265
|
+
const source = await readMarkdownInput(demoPath, true);
|
|
266
|
+
|
|
267
|
+
expect(source).toContain("計算機");
|
|
268
|
+
expect(await readFile(demoPath, "utf8")).toBe(source);
|
|
269
|
+
} finally {
|
|
270
|
+
await rm(dir, { recursive: true, force: true });
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
100
274
|
test("--demo-<filename> automatically loads a matching demos file", async () => {
|
|
101
275
|
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-demo-generic-"));
|
|
102
276
|
try {
|
|
@@ -44,3 +44,33 @@ test("micro prompt APIs delegate and return synchronously", () => {
|
|
|
44
44
|
else globalThis.$ = previousSelector;
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
|
+
|
|
48
|
+
test("current buffer exposes its actual MDCUI module source", () => {
|
|
49
|
+
const previousMicro = globalThis.micro;
|
|
50
|
+
const previousSelector = globalThis.$;
|
|
51
|
+
const previousMain = global.MDCUI_MAIN;
|
|
52
|
+
const buffer = {
|
|
53
|
+
path: "/tmp/app.md",
|
|
54
|
+
_useBundledMdcuiModules: true,
|
|
55
|
+
};
|
|
56
|
+
const app = { buffer };
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
global.MDCUI_MAIN = "/build/app.md";
|
|
60
|
+
const micro = buildMicroGlobal({ _app: app, _ctx: null, on() {} });
|
|
61
|
+
|
|
62
|
+
expect(micro.CurPane().Buf.MdcuiModuleSource).toBe("embedded");
|
|
63
|
+
buffer._useBundledMdcuiModules = false;
|
|
64
|
+
expect(micro.CurPane().Buf.MdcuiModuleSource).toBe("external");
|
|
65
|
+
delete global.MDCUI_MAIN;
|
|
66
|
+
buffer._useBundledMdcuiModules = true;
|
|
67
|
+
expect(micro.CurPane().Buf.MdcuiModuleSource).toBe("external");
|
|
68
|
+
} finally {
|
|
69
|
+
if (previousMain === undefined) delete global.MDCUI_MAIN;
|
|
70
|
+
else global.MDCUI_MAIN = previousMain;
|
|
71
|
+
if (previousMicro === undefined) delete globalThis.micro;
|
|
72
|
+
else globalThis.micro = previousMicro;
|
|
73
|
+
if (previousSelector === undefined) delete globalThis.$;
|
|
74
|
+
else globalThis.$ = previousSelector;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { afterEach, expect, test } from "bun:test";
|
|
2
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { createWui } from "../runmd.mjs";
|
|
5
|
+
import { createWui, extractJs } from "../runmd.mjs";
|
|
6
6
|
|
|
7
7
|
const temporaryDirectories = [];
|
|
8
8
|
|
|
@@ -33,3 +33,22 @@ test("createWui injects the image rule into an existing document head", async ()
|
|
|
33
33
|
expect(html.indexOf("max-width: 100%")).toBeLessThan(html.indexOf("</head>"));
|
|
34
34
|
expect(html.match(/document\.md\.front\.js/g)).toHaveLength(1);
|
|
35
35
|
});
|
|
36
|
+
|
|
37
|
+
test("bundling mode creates a browser-only front entry", async () => {
|
|
38
|
+
const directory = mkdtempSync(join(tmpdir(), "jsmdcui-wui-bundling-"));
|
|
39
|
+
temporaryDirectories.push(directory);
|
|
40
|
+
const markdownPath = join(directory, "bundle.md");
|
|
41
|
+
const source = "```js front\nexport function pav() { return rpc }\n```";
|
|
42
|
+
const markdown = await extractJs(source, markdownPath, { bundling: true });
|
|
43
|
+
const html = await createWui(markdown, markdownPath, { bundling: true });
|
|
44
|
+
const frontSource = readFileSync(`${markdownPath}.tmpfs.js`, "utf8");
|
|
45
|
+
const installerSource = readFileSync(`${markdownPath}.tmpfi.js`, "utf8");
|
|
46
|
+
|
|
47
|
+
expect(frontSource).toContain("const rpc = wuiRpcClient");
|
|
48
|
+
expect(frontSource).not.toContain("globalThis.process");
|
|
49
|
+
expect(frontSource).not.toContain('import("./bundle.md.front.js")');
|
|
50
|
+
expect(installerSource).toContain('import * as frontMod from "./bundle.md.tmpfs.js"');
|
|
51
|
+
expect(installerSource).toContain("Object.assign(window, frontMod)");
|
|
52
|
+
expect(html).toContain('src="./bundle.md.tmpfi.js"');
|
|
53
|
+
expect(html).not.toContain("bundle.md.front.js");
|
|
54
|
+
});
|
package/tests/wui.test.js
CHANGED
|
@@ -4,6 +4,9 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { readMarkdownInput, writeRuntimeFiles } from "../runmd.mjs";
|
|
6
6
|
|
|
7
|
+
const indexEntry = join(import.meta.dir, "..", "src", "index.js");
|
|
8
|
+
const bunBin = Bun.which("bun") || process.argv0;
|
|
9
|
+
|
|
7
10
|
test("WUI writes the bundled testapp.md when it is missing", async () => {
|
|
8
11
|
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-wui-"));
|
|
9
12
|
const mdpath = join(dir, "testapp.md");
|
|
@@ -43,3 +46,31 @@ test("generated WUI server falls back to a system port when 3000 is occupied", a
|
|
|
43
46
|
await rm(dir, { recursive: true, force: true });
|
|
44
47
|
}
|
|
45
48
|
});
|
|
49
|
+
|
|
50
|
+
test("WUI reports an external server before starting it", async () => {
|
|
51
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-wui-source-report-"));
|
|
52
|
+
const mdpath = join(dir, "external.md");
|
|
53
|
+
const preloadPath = join(dir, "mock-serve.mjs");
|
|
54
|
+
try {
|
|
55
|
+
await writeFile(mdpath, "# External WUI\n");
|
|
56
|
+
await writeFile(
|
|
57
|
+
preloadPath,
|
|
58
|
+
`Bun.serve = () => ({ port: 34567, stop() {} });\n`,
|
|
59
|
+
);
|
|
60
|
+
const result = Bun.spawnSync(
|
|
61
|
+
[bunBin, "--preload", preloadPath, indexEntry, "--wui", mdpath],
|
|
62
|
+
{
|
|
63
|
+
cwd: dir,
|
|
64
|
+
stdout: "pipe",
|
|
65
|
+
stderr: "pipe",
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(result.exitCode).toBe(0);
|
|
70
|
+
const stderr = Bun.stripANSI(result.stderr.toString());
|
|
71
|
+
expect(stderr).toContain("[mdcui] Starting external WUI server:");
|
|
72
|
+
expect(stderr).toContain(`${mdpath}-server.js`);
|
|
73
|
+
} finally {
|
|
74
|
+
await rm(dir, { recursive: true, force: true });
|
|
75
|
+
}
|
|
76
|
+
});
|