mr-md 2.1.0-beta → 2.1.2-beta
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/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +19 -8
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/dev.js +18 -10
- package/dist/cli/generate.js +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +3 -1
- package/dist/renderer/blocks.js +1 -1
- package/dist/renderer/markdown.js +5 -5
- package/dist/renderer/utils.d.ts.map +1 -1
- package/dist/renderer/utils.js +24 -18
- package/package.json +1 -1
- package/src/builder.ts +19 -9
- package/src/cli/dev.ts +19 -10
- package/src/cli/generate.ts +1 -1
- package/src/cli/init.ts +3 -1
- package/src/renderer/blocks.ts +1 -1
- package/src/renderer/markdown.ts +5 -5
- package/src/renderer/utils.ts +26 -17
package/dist/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEX,gBAAgB,EAEhB,YAAY,EACZ,YAAY,EACZ,OAAO,EAGP,UAAU,EAEV,cAAc,EAId,YAAY,EACZ,MAAM,EACN,UAAU,EAGV,YAAY,EACZ,SAAS,EAIT,iBAAiB,EAEjB,cAAc,EACd,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/dist/builder.js
CHANGED
|
@@ -16,9 +16,9 @@ function getCallerDir() {
|
|
|
16
16
|
if (match) {
|
|
17
17
|
let p = match[1];
|
|
18
18
|
if (p.startsWith("file://")) {
|
|
19
|
-
p = p
|
|
19
|
+
p = fileURLToPath(p);
|
|
20
20
|
}
|
|
21
|
-
if (p.startsWith("/") && p[2] === ":") {
|
|
21
|
+
else if (p.startsWith("/") && p[2] === ":") {
|
|
22
22
|
p = p.substring(1);
|
|
23
23
|
}
|
|
24
24
|
if (!builderFilePath) {
|
|
@@ -44,8 +44,17 @@ function copyAssets(outDir) {
|
|
|
44
44
|
const copyDir = (src, dest) => {
|
|
45
45
|
if (!fs.existsSync(src))
|
|
46
46
|
return;
|
|
47
|
+
if (!fs.existsSync(dest))
|
|
48
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
47
49
|
for (const file of fs.readdirSync(src)) {
|
|
48
|
-
|
|
50
|
+
const srcFile = path.join(src, file);
|
|
51
|
+
const destFile = path.join(dest, file);
|
|
52
|
+
if (fs.statSync(srcFile).isDirectory()) {
|
|
53
|
+
copyDir(srcFile, destFile);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
fs.copyFileSync(srcFile, destFile);
|
|
57
|
+
}
|
|
49
58
|
}
|
|
50
59
|
};
|
|
51
60
|
if (fs.existsSync(srcStylesDir))
|
|
@@ -298,7 +307,7 @@ export class LessonBuilder {
|
|
|
298
307
|
const base = this.options.contentBase ?? process.cwd();
|
|
299
308
|
const resolved = path.resolve(base, src);
|
|
300
309
|
const ext = path.extname(resolved);
|
|
301
|
-
const configPath = `${resolved.slice(0, -ext.length)}.config.json`;
|
|
310
|
+
const configPath = `${ext.length > 0 ? resolved.slice(0, -ext.length) : resolved}.config.json`;
|
|
302
311
|
if (fs.existsSync(configPath)) {
|
|
303
312
|
return JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
304
313
|
}
|
|
@@ -408,9 +417,10 @@ export class LessonBuilder {
|
|
|
408
417
|
const lesson = { meta: this.meta, blocks: this.blocks };
|
|
409
418
|
const html = render(lesson, this.options);
|
|
410
419
|
const outDir = path.resolve(this.options.outDir);
|
|
411
|
-
if (!fs.existsSync(outDir))
|
|
412
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
413
420
|
const outPath = path.join(outDir, `${this.meta.slug}.html`);
|
|
421
|
+
const outPathDir = path.dirname(outPath);
|
|
422
|
+
if (!fs.existsSync(outPathDir))
|
|
423
|
+
fs.mkdirSync(outPathDir, { recursive: true });
|
|
414
424
|
fs.writeFileSync(outPath, html, "utf-8");
|
|
415
425
|
if (this.options.standalone === false) {
|
|
416
426
|
copyAssets(outDir);
|
|
@@ -586,9 +596,10 @@ export class ChapterBuilder {
|
|
|
586
596
|
const chapterData = { meta: this.meta, lessons };
|
|
587
597
|
const html = renderChapter(chapterData, this.options);
|
|
588
598
|
const outDir = path.resolve(this.options.outDir);
|
|
589
|
-
if (!fs.existsSync(outDir))
|
|
590
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
591
599
|
const outPath = path.join(outDir, `${this.meta.slug}.html`);
|
|
600
|
+
const outPathDir = path.dirname(outPath);
|
|
601
|
+
if (!fs.existsSync(outPathDir))
|
|
602
|
+
fs.mkdirSync(outPathDir, { recursive: true });
|
|
592
603
|
fs.writeFileSync(outPath, html, "utf-8");
|
|
593
604
|
if (this.options.standalone === false) {
|
|
594
605
|
copyAssets(outDir);
|
package/dist/cli/dev.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAMA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAMA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,iBAwG1C"}
|
package/dist/cli/dev.js
CHANGED
|
@@ -12,7 +12,7 @@ export async function runDev(args) {
|
|
|
12
12
|
for (const entry of entryPoints) {
|
|
13
13
|
const entryPath = path.join(dir, entry);
|
|
14
14
|
if (fs.existsSync(entryPath)) {
|
|
15
|
-
exec(`NODE_ENV=development bun ${entryPath}`, (err, stdout, stderr) => {
|
|
15
|
+
exec(`NODE_ENV=development bun "${entryPath}"`, (err, stdout, stderr) => {
|
|
16
16
|
if (err)
|
|
17
17
|
console.error("Build failed:", stderr);
|
|
18
18
|
else {
|
|
@@ -29,7 +29,7 @@ export async function runDev(args) {
|
|
|
29
29
|
if (fs.existsSync(dir)) {
|
|
30
30
|
let timeout;
|
|
31
31
|
fs.watch(dir, { recursive: true }, (eventType, filename) => {
|
|
32
|
-
if (!filename || filename.includes("out/") || filename.includes(".git/"))
|
|
32
|
+
if (!filename || filename.includes("out/") || filename.includes("out\\") || filename.includes(".git/") || filename.includes(".git\\"))
|
|
33
33
|
return;
|
|
34
34
|
clearTimeout(timeout);
|
|
35
35
|
timeout = setTimeout(() => {
|
|
@@ -45,8 +45,9 @@ export async function runDev(args) {
|
|
|
45
45
|
if (srv.upgrade(req))
|
|
46
46
|
return;
|
|
47
47
|
const url = new URL(req.url);
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const decodedPath = decodeURIComponent(url.pathname);
|
|
49
|
+
let filePath = path.resolve(outDir, "." + decodedPath);
|
|
50
|
+
if (decodedPath.endsWith("/")) {
|
|
50
51
|
const files = fs.existsSync(outDir) ? fs.readdirSync(outDir) : [];
|
|
51
52
|
const htmlFiles = files.filter(f => f.endsWith(".html"));
|
|
52
53
|
if (htmlFiles.includes("index.html")) {
|
|
@@ -61,24 +62,31 @@ export async function runDev(args) {
|
|
|
61
62
|
filePath = path.join(outDir, htmlFiles[0]);
|
|
62
63
|
}
|
|
63
64
|
else {
|
|
64
|
-
filePath
|
|
65
|
+
filePath = path.join(outDir, "index.html");
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
}
|
|
69
|
+
if (!filePath.startsWith(outDir + path.sep) && filePath !== outDir) {
|
|
70
|
+
return new Response("Forbidden", { status: 403 });
|
|
71
|
+
}
|
|
68
72
|
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
69
73
|
if (filePath.endsWith(".html")) {
|
|
70
74
|
const file = Bun.file(filePath);
|
|
71
75
|
let text = await file.text();
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
const lastBodyIndex = text.lastIndexOf("</body>");
|
|
77
|
+
if (lastBodyIndex !== -1) {
|
|
78
|
+
text = text.slice(0, lastBodyIndex) + `<script>
|
|
79
|
+
const ws = new WebSocket(\`ws://\${location.host}/\`);
|
|
74
80
|
ws.onmessage = (e) => { if (e.data === "reload") location.reload(); };
|
|
75
|
-
</script></body>`);
|
|
81
|
+
</script></body>` + text.slice(lastBodyIndex + 7);
|
|
82
|
+
}
|
|
76
83
|
return new Response(text, { headers: { "Content-Type": "text/html" } });
|
|
77
84
|
}
|
|
78
85
|
return new Response(Bun.file(filePath));
|
|
79
86
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
87
|
+
const baseDir = path.resolve(process.cwd(), dir);
|
|
88
|
+
const srcPath = path.resolve(baseDir, "." + decodedPath);
|
|
89
|
+
if (srcPath.startsWith(baseDir + path.sep) && fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) {
|
|
82
90
|
return new Response(Bun.file(srcPath));
|
|
83
91
|
}
|
|
84
92
|
return new Response("Not found", { status: 404 });
|
package/dist/cli/generate.js
CHANGED
|
@@ -26,7 +26,7 @@ export async function runGenerate(args) {
|
|
|
26
26
|
console.error("Usage: md g <ch|le|qu|chapter|lesson|quiz> <name>");
|
|
27
27
|
process.exit(1);
|
|
28
28
|
}
|
|
29
|
-
const name = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
29
|
+
const name = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "item";
|
|
30
30
|
const cwd = process.cwd();
|
|
31
31
|
switch (type) {
|
|
32
32
|
case "ch":
|
package/dist/cli/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAIA,wBAAsB,OAAO,kBA8F5B"}
|
package/dist/cli/init.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
3
4
|
export async function runInit() {
|
|
4
5
|
console.log("Initializing md project structure...");
|
|
5
6
|
const dirs = [
|
|
@@ -71,7 +72,8 @@ export const firstLesson = lesson("First Lesson", { contentBase: import.meta.dir
|
|
|
71
72
|
let mrMdVersion = "latest";
|
|
72
73
|
try {
|
|
73
74
|
// Find mr-md's own package.json to get its version
|
|
74
|
-
const
|
|
75
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
76
|
+
const __dirname = path.dirname(__filename);
|
|
75
77
|
const ownPkgPath = path.resolve(__dirname, "../../package.json");
|
|
76
78
|
const ownPkg = JSON.parse(fs.readFileSync(ownPkgPath, "utf-8"));
|
|
77
79
|
mrMdVersion = ownPkg.version;
|
package/dist/renderer/blocks.js
CHANGED
|
@@ -434,7 +434,7 @@ window.addEventListener("message", (event) => {
|
|
|
434
434
|
window.dispatchEvent(new CustomEvent("bk:props", { detail: window.__simProps }));
|
|
435
435
|
});
|
|
436
436
|
try {
|
|
437
|
-
${js}
|
|
437
|
+
${js.replace(/<\/script>/gi, "<\\/script>")}
|
|
438
438
|
} catch (e) {
|
|
439
439
|
console.error("Simulation Error:", e);
|
|
440
440
|
const errDiv = document.createElement('div');
|
|
@@ -39,7 +39,7 @@ function mdToHtml(md) {
|
|
|
39
39
|
});
|
|
40
40
|
// Restore code blocks
|
|
41
41
|
codeBlocks.forEach((match, id) => {
|
|
42
|
-
processedMd = processedMd.
|
|
42
|
+
processedMd = processedMd.replaceAll(`@@BK_CODE_${id}@@`, () => match);
|
|
43
43
|
});
|
|
44
44
|
const headings = [];
|
|
45
45
|
const idPrefix = Math.random().toString(36).substring(2, 6);
|
|
@@ -61,16 +61,16 @@ function mdToHtml(md) {
|
|
|
61
61
|
displayMode: true,
|
|
62
62
|
});
|
|
63
63
|
// marked might wrap block placeholders in <p>
|
|
64
|
-
html = html.
|
|
64
|
+
html = html.replaceAll(`<p>@@BK_MATH_BLOCK_${id}@@</p>`, () => `<div class="bk-math-block">${rendered}</div>`);
|
|
65
65
|
// Fallback if not wrapped in <p>
|
|
66
|
-
html = html.
|
|
66
|
+
html = html.replaceAll(`@@BK_MATH_BLOCK_${id}@@`, () => `<div class="bk-math-block">${rendered}</div>`);
|
|
67
67
|
});
|
|
68
68
|
mathInlines.forEach((tex, id) => {
|
|
69
69
|
const rendered = katex.renderToString(tex, {
|
|
70
70
|
throwOnError: false,
|
|
71
71
|
displayMode: false,
|
|
72
72
|
});
|
|
73
|
-
html = html.
|
|
73
|
+
html = html.replaceAll(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
|
|
74
74
|
});
|
|
75
75
|
return { html, title, headings };
|
|
76
76
|
}
|
|
@@ -111,7 +111,7 @@ function mdInline(text) {
|
|
|
111
111
|
throwOnError: false,
|
|
112
112
|
displayMode: false,
|
|
113
113
|
});
|
|
114
|
-
html = html.
|
|
114
|
+
html = html.replaceAll(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
|
|
115
115
|
});
|
|
116
116
|
return html;
|
|
117
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/renderer/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,YAAY,CAAC;CACpD;AAID,iBAAS,cAAc,CACtB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,EACrB,YAAY,GAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAe,GAClD,MAAM,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/renderer/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,YAAY,CAAC;CACpD;AAID,iBAAS,cAAc,CACtB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,EACrB,YAAY,GAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAe,GAClD,MAAM,CA0DR;AAED,iBAAS,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAuDnE;AAED,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/renderer/utils.js
CHANGED
|
@@ -28,25 +28,20 @@ function resolveContent(src, options, expectedType = "text") {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
const baseDir = path.resolve(options.contentBase ?? ".");
|
|
31
|
-
if (!filePath.startsWith(baseDir) && options.strict !== false) {
|
|
31
|
+
if (!filePath.startsWith(baseDir + path.sep) && filePath !== baseDir && options.strict !== false) {
|
|
32
32
|
throw new Error(`Security Error: Path traversal attempt outside contentBase: ${filePath}`);
|
|
33
33
|
}
|
|
34
34
|
if (fs.existsSync(filePath)) {
|
|
35
35
|
const stat = fs.statSync(filePath);
|
|
36
36
|
if (stat.isFile()) {
|
|
37
37
|
if (expectedType === "js" && (filePath.endsWith(".js") || filePath.endsWith(".ts") || filePath.endsWith(".jsx") || filePath.endsWith(".tsx"))) {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const out = spawnSync("bun", ["build", "--target=browser", filePath]);
|
|
39
|
+
if (out.status === 0) {
|
|
40
|
+
return out.stdout.toString("utf-8");
|
|
40
41
|
}
|
|
41
42
|
else {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return out.stdout.toString("utf-8");
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
console.warn(`\n ⚠ Bun build failed for ${filePath}:\n${out.stderr.toString("utf-8")}`);
|
|
48
|
-
// fallback to reading raw
|
|
49
|
-
}
|
|
43
|
+
console.warn(`\n ⚠ Bun build failed for ${filePath}:\n${out.stderr.toString("utf-8")}`);
|
|
44
|
+
// fallback to reading raw
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
47
|
return fs.readFileSync(filePath, "utf-8");
|
|
@@ -61,12 +56,23 @@ function resolveContent(src, options, expectedType = "text") {
|
|
|
61
56
|
function resolveAssetSrc(src, options) {
|
|
62
57
|
if (/^(https?:|data:)/.test(src))
|
|
63
58
|
return src;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
const hashIndex = src.indexOf("#");
|
|
60
|
+
const queryIndex = src.indexOf("?");
|
|
61
|
+
const breakIndex = hashIndex !== -1 && queryIndex !== -1
|
|
62
|
+
? Math.min(hashIndex, queryIndex)
|
|
63
|
+
: Math.max(hashIndex, queryIndex);
|
|
64
|
+
let cleanSrc = src;
|
|
65
|
+
let suffix = "";
|
|
66
|
+
if (breakIndex !== -1) {
|
|
67
|
+
cleanSrc = src.substring(0, breakIndex);
|
|
68
|
+
suffix = src.substring(breakIndex);
|
|
69
|
+
}
|
|
70
|
+
let isWebAbsolute = cleanSrc.startsWith("/") && !fs.existsSync(cleanSrc);
|
|
71
|
+
let filePath = path.isAbsolute(cleanSrc)
|
|
72
|
+
? cleanSrc
|
|
73
|
+
: path.resolve(options.contentBase ?? ".", cleanSrc);
|
|
74
|
+
if (cleanSrc.startsWith("/") && !fs.existsSync(filePath)) {
|
|
75
|
+
const fallbackPath = path.resolve(options.contentBase ?? ".", cleanSrc.slice(1));
|
|
70
76
|
if (fs.existsSync(fallbackPath)) {
|
|
71
77
|
filePath = fallbackPath;
|
|
72
78
|
isWebAbsolute = false; // We found it locally, so don't treat it as a web URL
|
|
@@ -93,6 +99,6 @@ function resolveAssetSrc(src, options) {
|
|
|
93
99
|
if (!fs.existsSync(outPath)) {
|
|
94
100
|
fs.copyFileSync(filePath, outPath);
|
|
95
101
|
}
|
|
96
|
-
return `assets/${filename}`;
|
|
102
|
+
return `assets/${filename}${suffix}`;
|
|
97
103
|
}
|
|
98
104
|
export { resolveAssetSrc, resolveContent };
|
package/package.json
CHANGED
package/src/builder.ts
CHANGED
|
@@ -48,9 +48,8 @@ function getCallerDir(): string | undefined {
|
|
|
48
48
|
if (match) {
|
|
49
49
|
let p = match[1];
|
|
50
50
|
if (p.startsWith("file://")) {
|
|
51
|
-
p = p
|
|
52
|
-
}
|
|
53
|
-
if (p.startsWith("/") && p[2] === ":") {
|
|
51
|
+
p = fileURLToPath(p);
|
|
52
|
+
} else if (p.startsWith("/") && p[2] === ":") {
|
|
54
53
|
p = p.substring(1);
|
|
55
54
|
}
|
|
56
55
|
if (!builderFilePath) {
|
|
@@ -80,8 +79,15 @@ function copyAssets(outDir: string) {
|
|
|
80
79
|
|
|
81
80
|
const copyDir = (src: string, dest: string) => {
|
|
82
81
|
if (!fs.existsSync(src)) return;
|
|
82
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
83
83
|
for (const file of fs.readdirSync(src)) {
|
|
84
|
-
|
|
84
|
+
const srcFile = path.join(src, file);
|
|
85
|
+
const destFile = path.join(dest, file);
|
|
86
|
+
if (fs.statSync(srcFile).isDirectory()) {
|
|
87
|
+
copyDir(srcFile, destFile);
|
|
88
|
+
} else {
|
|
89
|
+
fs.copyFileSync(srcFile, destFile);
|
|
90
|
+
}
|
|
85
91
|
}
|
|
86
92
|
};
|
|
87
93
|
|
|
@@ -398,7 +404,7 @@ export class LessonBuilder {
|
|
|
398
404
|
const base = this.options.contentBase ?? process.cwd();
|
|
399
405
|
const resolved = path.resolve(base, src);
|
|
400
406
|
const ext = path.extname(resolved);
|
|
401
|
-
const configPath = `${resolved.slice(0, -ext.length)}.config.json`;
|
|
407
|
+
const configPath = `${ext.length > 0 ? resolved.slice(0, -ext.length) : resolved}.config.json`;
|
|
402
408
|
if (fs.existsSync(configPath)) {
|
|
403
409
|
return JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
404
410
|
}
|
|
@@ -525,9 +531,11 @@ export class LessonBuilder {
|
|
|
525
531
|
const html = render(lesson, this.options);
|
|
526
532
|
|
|
527
533
|
const outDir = path.resolve(this.options.outDir as string);
|
|
528
|
-
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
529
|
-
|
|
530
534
|
const outPath = path.join(outDir, `${this.meta.slug}.html`);
|
|
535
|
+
const outPathDir = path.dirname(outPath);
|
|
536
|
+
|
|
537
|
+
if (!fs.existsSync(outPathDir)) fs.mkdirSync(outPathDir, { recursive: true });
|
|
538
|
+
|
|
531
539
|
fs.writeFileSync(outPath, html, "utf-8");
|
|
532
540
|
|
|
533
541
|
if (this.options.standalone === false) {
|
|
@@ -750,9 +758,11 @@ export class ChapterBuilder {
|
|
|
750
758
|
const html = renderChapter(chapterData, this.options);
|
|
751
759
|
|
|
752
760
|
const outDir = path.resolve(this.options.outDir as string);
|
|
753
|
-
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
754
|
-
|
|
755
761
|
const outPath = path.join(outDir, `${this.meta.slug}.html`);
|
|
762
|
+
const outPathDir = path.dirname(outPath);
|
|
763
|
+
|
|
764
|
+
if (!fs.existsSync(outPathDir)) fs.mkdirSync(outPathDir, { recursive: true });
|
|
765
|
+
|
|
756
766
|
fs.writeFileSync(outPath, html, "utf-8");
|
|
757
767
|
|
|
758
768
|
if (this.options.standalone === false) {
|
package/src/cli/dev.ts
CHANGED
|
@@ -18,7 +18,7 @@ export async function runDev(args: string[]) {
|
|
|
18
18
|
for (const entry of entryPoints) {
|
|
19
19
|
const entryPath = path.join(dir, entry);
|
|
20
20
|
if (fs.existsSync(entryPath)) {
|
|
21
|
-
exec(`NODE_ENV=development bun ${entryPath}`, (err, stdout, stderr) => {
|
|
21
|
+
exec(`NODE_ENV=development bun "${entryPath}"`, (err, stdout, stderr) => {
|
|
22
22
|
if (err) console.error("Build failed:", stderr);
|
|
23
23
|
else {
|
|
24
24
|
console.log("Build successful.");
|
|
@@ -36,7 +36,7 @@ export async function runDev(args: string[]) {
|
|
|
36
36
|
if (fs.existsSync(dir)) {
|
|
37
37
|
let timeout: NodeJS.Timeout;
|
|
38
38
|
fs.watch(dir, { recursive: true }, (eventType, filename) => {
|
|
39
|
-
if (!filename || filename.includes("out/") || filename.includes(".git/")) return;
|
|
39
|
+
if (!filename || filename.includes("out/") || filename.includes("out\\") || filename.includes(".git/") || filename.includes(".git\\")) return;
|
|
40
40
|
|
|
41
41
|
clearTimeout(timeout);
|
|
42
42
|
timeout = setTimeout(() => {
|
|
@@ -53,9 +53,10 @@ export async function runDev(args: string[]) {
|
|
|
53
53
|
if (srv.upgrade(req)) return;
|
|
54
54
|
|
|
55
55
|
const url = new URL(req.url);
|
|
56
|
-
|
|
56
|
+
const decodedPath = decodeURIComponent(url.pathname);
|
|
57
|
+
let filePath = path.resolve(outDir, "." + decodedPath);
|
|
57
58
|
|
|
58
|
-
if (
|
|
59
|
+
if (decodedPath.endsWith("/")) {
|
|
59
60
|
const files = fs.existsSync(outDir) ? fs.readdirSync(outDir) : [];
|
|
60
61
|
const htmlFiles = files.filter(f => f.endsWith(".html"));
|
|
61
62
|
if (htmlFiles.includes("index.html")) {
|
|
@@ -67,26 +68,34 @@ export async function runDev(args: string[]) {
|
|
|
67
68
|
} else if (htmlFiles.length > 0) {
|
|
68
69
|
filePath = path.join(outDir, htmlFiles[0]);
|
|
69
70
|
} else {
|
|
70
|
-
filePath
|
|
71
|
+
filePath = path.join(outDir, "index.html");
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
if (!filePath.startsWith(outDir + path.sep) && filePath !== outDir) {
|
|
77
|
+
return new Response("Forbidden", { status: 403 });
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
76
81
|
if (filePath.endsWith(".html")) {
|
|
77
82
|
const file = Bun.file(filePath);
|
|
78
83
|
let text = await file.text();
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
const lastBodyIndex = text.lastIndexOf("</body>");
|
|
85
|
+
if (lastBodyIndex !== -1) {
|
|
86
|
+
text = text.slice(0, lastBodyIndex) + `<script>
|
|
87
|
+
const ws = new WebSocket(\`ws://\${location.host}/\`);
|
|
81
88
|
ws.onmessage = (e) => { if (e.data === "reload") location.reload(); };
|
|
82
|
-
</script></body>`);
|
|
89
|
+
</script></body>` + text.slice(lastBodyIndex + 7);
|
|
90
|
+
}
|
|
83
91
|
return new Response(text, { headers: { "Content-Type": "text/html" } });
|
|
84
92
|
}
|
|
85
93
|
return new Response(Bun.file(filePath));
|
|
86
94
|
}
|
|
87
95
|
|
|
88
|
-
const
|
|
89
|
-
|
|
96
|
+
const baseDir = path.resolve(process.cwd(), dir);
|
|
97
|
+
const srcPath = path.resolve(baseDir, "." + decodedPath);
|
|
98
|
+
if (srcPath.startsWith(baseDir + path.sep) && fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) {
|
|
90
99
|
return new Response(Bun.file(srcPath));
|
|
91
100
|
}
|
|
92
101
|
|
package/src/cli/generate.ts
CHANGED
|
@@ -32,7 +32,7 @@ export async function runGenerate(args: string[]) {
|
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const name = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
35
|
+
const name = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "item";
|
|
36
36
|
const cwd = process.cwd();
|
|
37
37
|
|
|
38
38
|
switch (type) {
|
package/src/cli/init.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
3
4
|
|
|
4
5
|
export async function runInit() {
|
|
5
6
|
console.log("Initializing md project structure...");
|
|
@@ -77,7 +78,8 @@ export const firstLesson = lesson("First Lesson", { contentBase: import.meta.dir
|
|
|
77
78
|
let mrMdVersion = "latest";
|
|
78
79
|
try {
|
|
79
80
|
// Find mr-md's own package.json to get its version
|
|
80
|
-
const
|
|
81
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
82
|
+
const __dirname = path.dirname(__filename);
|
|
81
83
|
const ownPkgPath = path.resolve(__dirname, "../../package.json");
|
|
82
84
|
const ownPkg = JSON.parse(fs.readFileSync(ownPkgPath, "utf-8"));
|
|
83
85
|
mrMdVersion = ownPkg.version;
|
package/src/renderer/blocks.ts
CHANGED
|
@@ -522,7 +522,7 @@ window.addEventListener("message", (event) => {
|
|
|
522
522
|
window.dispatchEvent(new CustomEvent("bk:props", { detail: window.__simProps }));
|
|
523
523
|
});
|
|
524
524
|
try {
|
|
525
|
-
${js}
|
|
525
|
+
${js.replace(/<\/script>/gi, "<\\/script>")}
|
|
526
526
|
} catch (e) {
|
|
527
527
|
console.error("Simulation Error:", e);
|
|
528
528
|
const errDiv = document.createElement('div');
|
package/src/renderer/markdown.ts
CHANGED
|
@@ -52,7 +52,7 @@ function mdToHtml(md: string): { html: string; title: string; headings: { id: st
|
|
|
52
52
|
|
|
53
53
|
// Restore code blocks
|
|
54
54
|
codeBlocks.forEach((match, id) => {
|
|
55
|
-
processedMd = processedMd.
|
|
55
|
+
processedMd = processedMd.replaceAll(`@@BK_CODE_${id}@@`, () => match);
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
const headings: { id: string; text: string; level: number }[] = [];
|
|
@@ -78,12 +78,12 @@ function mdToHtml(md: string): { html: string; title: string; headings: { id: st
|
|
|
78
78
|
displayMode: true,
|
|
79
79
|
});
|
|
80
80
|
// marked might wrap block placeholders in <p>
|
|
81
|
-
html = html.
|
|
81
|
+
html = html.replaceAll(
|
|
82
82
|
`<p>@@BK_MATH_BLOCK_${id}@@</p>`,
|
|
83
83
|
() => `<div class="bk-math-block">${rendered}</div>`,
|
|
84
84
|
);
|
|
85
85
|
// Fallback if not wrapped in <p>
|
|
86
|
-
html = html.
|
|
86
|
+
html = html.replaceAll(
|
|
87
87
|
`@@BK_MATH_BLOCK_${id}@@`,
|
|
88
88
|
() => `<div class="bk-math-block">${rendered}</div>`,
|
|
89
89
|
);
|
|
@@ -94,7 +94,7 @@ function mdToHtml(md: string): { html: string; title: string; headings: { id: st
|
|
|
94
94
|
throwOnError: false,
|
|
95
95
|
displayMode: false,
|
|
96
96
|
});
|
|
97
|
-
html = html.
|
|
97
|
+
html = html.replaceAll(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
|
|
98
98
|
});
|
|
99
99
|
|
|
100
100
|
return { html, title, headings };
|
|
@@ -150,7 +150,7 @@ function mdInline(text: string): string {
|
|
|
150
150
|
throwOnError: false,
|
|
151
151
|
displayMode: false,
|
|
152
152
|
});
|
|
153
|
-
html = html.
|
|
153
|
+
html = html.replaceAll(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
return html;
|
package/src/renderer/utils.ts
CHANGED
|
@@ -46,7 +46,7 @@ function resolveContent(
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const baseDir = path.resolve(options.contentBase ?? ".");
|
|
49
|
-
if (!filePath.startsWith(baseDir) && options.strict !== false) {
|
|
49
|
+
if (!filePath.startsWith(baseDir + path.sep) && filePath !== baseDir && options.strict !== false) {
|
|
50
50
|
throw new Error(`Security Error: Path traversal attempt outside contentBase: ${filePath}`);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -54,16 +54,12 @@ function resolveContent(
|
|
|
54
54
|
const stat = fs.statSync(filePath);
|
|
55
55
|
if (stat.isFile()) {
|
|
56
56
|
if (expectedType === "js" && (filePath.endsWith(".js") || filePath.endsWith(".ts") || filePath.endsWith(".jsx") || filePath.endsWith(".tsx"))) {
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const out = spawnSync("bun", ["build", "--target=browser", filePath]);
|
|
58
|
+
if (out.status === 0) {
|
|
59
|
+
return out.stdout.toString("utf-8");
|
|
59
60
|
} else {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return out.stdout.toString("utf-8");
|
|
63
|
-
} else {
|
|
64
|
-
console.warn(`\n ⚠ Bun build failed for ${filePath}:\n${out.stderr.toString("utf-8")}`);
|
|
65
|
-
// fallback to reading raw
|
|
66
|
-
}
|
|
61
|
+
console.warn(`\n ⚠ Bun build failed for ${filePath}:\n${out.stderr.toString("utf-8")}`);
|
|
62
|
+
// fallback to reading raw
|
|
67
63
|
}
|
|
68
64
|
}
|
|
69
65
|
return fs.readFileSync(filePath, "utf-8");
|
|
@@ -83,14 +79,27 @@ function resolveContent(
|
|
|
83
79
|
function resolveAssetSrc(src: string, options: BuildOptions): string {
|
|
84
80
|
if (/^(https?:|data:)/.test(src)) return src;
|
|
85
81
|
|
|
86
|
-
|
|
82
|
+
const hashIndex = src.indexOf("#");
|
|
83
|
+
const queryIndex = src.indexOf("?");
|
|
84
|
+
const breakIndex = hashIndex !== -1 && queryIndex !== -1
|
|
85
|
+
? Math.min(hashIndex, queryIndex)
|
|
86
|
+
: Math.max(hashIndex, queryIndex);
|
|
87
|
+
|
|
88
|
+
let cleanSrc = src;
|
|
89
|
+
let suffix = "";
|
|
90
|
+
if (breakIndex !== -1) {
|
|
91
|
+
cleanSrc = src.substring(0, breakIndex);
|
|
92
|
+
suffix = src.substring(breakIndex);
|
|
93
|
+
}
|
|
87
94
|
|
|
88
|
-
let
|
|
89
|
-
? src
|
|
90
|
-
: path.resolve(options.contentBase ?? ".", src);
|
|
95
|
+
let isWebAbsolute = cleanSrc.startsWith("/") && !fs.existsSync(cleanSrc);
|
|
91
96
|
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
let filePath = path.isAbsolute(cleanSrc)
|
|
98
|
+
? cleanSrc
|
|
99
|
+
: path.resolve(options.contentBase ?? ".", cleanSrc);
|
|
100
|
+
|
|
101
|
+
if (cleanSrc.startsWith("/") && !fs.existsSync(filePath)) {
|
|
102
|
+
const fallbackPath = path.resolve(options.contentBase ?? ".", cleanSrc.slice(1));
|
|
94
103
|
if (fs.existsSync(fallbackPath)) {
|
|
95
104
|
filePath = fallbackPath;
|
|
96
105
|
isWebAbsolute = false; // We found it locally, so don't treat it as a web URL
|
|
@@ -121,7 +130,7 @@ function resolveAssetSrc(src: string, options: BuildOptions): string {
|
|
|
121
130
|
fs.copyFileSync(filePath, outPath);
|
|
122
131
|
}
|
|
123
132
|
|
|
124
|
-
return `assets/${filename}`;
|
|
133
|
+
return `assets/${filename}${suffix}`;
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
export { resolveAssetSrc, resolveContent };
|