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.
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
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.replace(/^file:\/\//, "");
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
- fs.copyFileSync(path.join(src, file), path.join(dest, file));
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);
@@ -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,iBA+F1C"}
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
- let filePath = path.join(outDir, url.pathname);
49
- if (filePath.endsWith("/")) {
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 += "index.html";
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
- text = text.replace("</body>", `<script>
73
- const ws = new WebSocket("ws://localhost:3000/");
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 srcPath = path.join(process.cwd(), dir, url.pathname);
81
- if (fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) {
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 });
@@ -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":
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAGA,wBAAsB,OAAO,kBA6F5B"}
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 __dirname = path.dirname(new URL(import.meta.url).pathname);
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;
@@ -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.replace(`@@BK_CODE_${id}@@`, () => match);
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.replace(`<p>@@BK_MATH_BLOCK_${id}@@</p>`, () => `<div class="bk-math-block">${rendered}</div>`);
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.replace(`@@BK_MATH_BLOCK_${id}@@`, () => `<div class="bk-math-block">${rendered}</div>`);
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.replace(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
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.replace(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
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,CA8DR;AAED,iBAAS,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CA0CnE;AAED,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC"}
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"}
@@ -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
- if (!filePath.match(/^[a-zA-Z0-9_\-./\\]+$/)) {
39
- console.warn(`\n ⚠ Invalid characters in file path for bun build: ${filePath}`);
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
- const out = spawnSync("bun", ["build", "--target=browser", filePath]);
43
- if (out.status === 0) {
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
- let isWebAbsolute = src.startsWith("/") && !fs.existsSync(src);
65
- let filePath = path.isAbsolute(src)
66
- ? src
67
- : path.resolve(options.contentBase ?? ".", src);
68
- if (src.startsWith("/") && !fs.existsSync(filePath)) {
69
- const fallbackPath = path.resolve(options.contentBase ?? ".", src.slice(1));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mr-md",
3
- "version": "2.1.0-beta",
3
+ "version": "2.1.2-beta",
4
4
  "description": "Mr Markdown is an opinionated TypeScript SDK for building interactive learning pages.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
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.replace(/^file:\/\//, "");
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
- fs.copyFileSync(path.join(src, file), path.join(dest, file));
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
- let filePath = path.join(outDir, url.pathname);
56
+ const decodedPath = decodeURIComponent(url.pathname);
57
+ let filePath = path.resolve(outDir, "." + decodedPath);
57
58
 
58
- if (filePath.endsWith("/")) {
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 += "index.html";
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
- text = text.replace("</body>", `<script>
80
- const ws = new WebSocket("ws://localhost:3000/");
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 srcPath = path.join(process.cwd(), dir, url.pathname);
89
- if (fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) {
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
 
@@ -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 __dirname = path.dirname(new URL(import.meta.url).pathname);
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;
@@ -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');
@@ -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.replace(`@@BK_CODE_${id}@@`, () => match);
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.replace(
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.replace(
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.replace(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
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.replace(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
153
+ html = html.replaceAll(`@@BK_MATH_INLINE_${id}@@`, () => rendered);
154
154
  });
155
155
 
156
156
  return html;
@@ -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
- if (!filePath.match(/^[a-zA-Z0-9_\-./\\]+$/)) {
58
- console.warn(`\n ⚠ Invalid characters in file path for bun build: ${filePath}`);
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
- const out = spawnSync("bun", ["build", "--target=browser", filePath]);
61
- if (out.status === 0) {
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
- let isWebAbsolute = src.startsWith("/") && !fs.existsSync(src);
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 filePath = path.isAbsolute(src)
89
- ? src
90
- : path.resolve(options.contentBase ?? ".", src);
95
+ let isWebAbsolute = cleanSrc.startsWith("/") && !fs.existsSync(cleanSrc);
91
96
 
92
- if (src.startsWith("/") && !fs.existsSync(filePath)) {
93
- const fallbackPath = path.resolve(options.contentBase ?? ".", src.slice(1));
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 };