mr-md 2.1.0-beta → 2.1.1-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
@@ -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))
@@ -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 {
@@ -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 (filePath.endsWith(path.sep)) {
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>
76
+ const lastBodyIndex = text.lastIndexOf("</body>");
77
+ if (lastBodyIndex !== -1) {
78
+ text = text.slice(0, lastBodyIndex) + `<script>
73
79
  const ws = new WebSocket("ws://localhost:3000/");
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":
@@ -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"}
@@ -35,18 +35,13 @@ function resolveContent(src, options, expectedType = "text") {
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.1-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
@@ -80,8 +80,15 @@ function copyAssets(outDir: string) {
80
80
 
81
81
  const copyDir = (src: string, dest: string) => {
82
82
  if (!fs.existsSync(src)) return;
83
+ if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
83
84
  for (const file of fs.readdirSync(src)) {
84
- fs.copyFileSync(path.join(src, file), path.join(dest, file));
85
+ const srcFile = path.join(src, file);
86
+ const destFile = path.join(dest, file);
87
+ if (fs.statSync(srcFile).isDirectory()) {
88
+ copyDir(srcFile, destFile);
89
+ } else {
90
+ fs.copyFileSync(srcFile, destFile);
91
+ }
85
92
  }
86
93
  };
87
94
 
@@ -525,9 +532,11 @@ export class LessonBuilder {
525
532
  const html = render(lesson, this.options);
526
533
 
527
534
  const outDir = path.resolve(this.options.outDir as string);
528
- if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
529
-
530
535
  const outPath = path.join(outDir, `${this.meta.slug}.html`);
536
+ const outPathDir = path.dirname(outPath);
537
+
538
+ if (!fs.existsSync(outPathDir)) fs.mkdirSync(outPathDir, { recursive: true });
539
+
531
540
  fs.writeFileSync(outPath, html, "utf-8");
532
541
 
533
542
  if (this.options.standalone === false) {
@@ -750,9 +759,11 @@ export class ChapterBuilder {
750
759
  const html = renderChapter(chapterData, this.options);
751
760
 
752
761
  const outDir = path.resolve(this.options.outDir as string);
753
- if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
754
-
755
762
  const outPath = path.join(outDir, `${this.meta.slug}.html`);
763
+ const outPathDir = path.dirname(outPath);
764
+
765
+ if (!fs.existsSync(outPathDir)) fs.mkdirSync(outPathDir, { recursive: true });
766
+
756
767
  fs.writeFileSync(outPath, html, "utf-8");
757
768
 
758
769
  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.");
@@ -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 (filePath.endsWith(path.sep)) {
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>
84
+ const lastBodyIndex = text.lastIndexOf("</body>");
85
+ if (lastBodyIndex !== -1) {
86
+ text = text.slice(0, lastBodyIndex) + `<script>
80
87
  const ws = new WebSocket("ws://localhost:3000/");
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) {
@@ -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;
@@ -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 };