boltdocs 1.3.0 → 1.3.2

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.
Files changed (103) hide show
  1. package/dist/{cache-EHR7SXRU.mjs → cache-GQHF6BXI.mjs} +1 -1
  2. package/dist/{chunk-GSYECEZY.mjs → chunk-CYBWLFOG.mjs} +5 -1
  3. package/dist/node/index.js +36 -20
  4. package/dist/node/index.mjs +34 -22
  5. package/package.json +1 -1
  6. package/src/client/app/index.tsx +344 -344
  7. package/src/client/app/preload.tsx +56 -56
  8. package/src/client/index.ts +40 -40
  9. package/src/client/ssr.tsx +51 -51
  10. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +76 -76
  11. package/src/client/theme/components/CodeBlock/index.ts +1 -1
  12. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +154 -154
  13. package/src/client/theme/components/PackageManagerTabs/index.ts +1 -1
  14. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +64 -64
  15. package/src/client/theme/components/Playground/Playground.tsx +124 -124
  16. package/src/client/theme/components/Playground/index.ts +1 -1
  17. package/src/client/theme/components/Playground/playground.css +168 -168
  18. package/src/client/theme/components/Video/Video.tsx +84 -84
  19. package/src/client/theme/components/Video/index.ts +1 -1
  20. package/src/client/theme/components/Video/video.css +41 -41
  21. package/src/client/theme/components/mdx/Admonition.tsx +80 -80
  22. package/src/client/theme/components/mdx/Badge.tsx +31 -31
  23. package/src/client/theme/components/mdx/Button.tsx +50 -50
  24. package/src/client/theme/components/mdx/Card.tsx +80 -80
  25. package/src/client/theme/components/mdx/List.tsx +57 -57
  26. package/src/client/theme/components/mdx/Tabs.tsx +94 -94
  27. package/src/client/theme/components/mdx/index.ts +18 -18
  28. package/src/client/theme/components/mdx/mdx-components.css +424 -424
  29. package/src/client/theme/icons/bun.tsx +62 -62
  30. package/src/client/theme/icons/deno.tsx +20 -20
  31. package/src/client/theme/icons/discord.tsx +12 -12
  32. package/src/client/theme/icons/github.tsx +15 -15
  33. package/src/client/theme/icons/npm.tsx +13 -13
  34. package/src/client/theme/icons/pnpm.tsx +72 -72
  35. package/src/client/theme/icons/twitter.tsx +12 -12
  36. package/src/client/theme/styles/markdown.css +343 -343
  37. package/src/client/theme/styles/variables.css +162 -162
  38. package/src/client/theme/styles.css +37 -37
  39. package/src/client/theme/ui/BackgroundGradient/BackgroundGradient.tsx +10 -10
  40. package/src/client/theme/ui/BackgroundGradient/index.ts +1 -1
  41. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +68 -68
  42. package/src/client/theme/ui/Breadcrumbs/index.ts +1 -1
  43. package/src/client/theme/ui/Footer/footer.css +32 -32
  44. package/src/client/theme/ui/Head/Head.tsx +69 -69
  45. package/src/client/theme/ui/Head/index.ts +1 -1
  46. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +125 -125
  47. package/src/client/theme/ui/LanguageSwitcher/index.ts +1 -1
  48. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +98 -98
  49. package/src/client/theme/ui/Layout/Layout.tsx +202 -202
  50. package/src/client/theme/ui/Layout/base.css +76 -76
  51. package/src/client/theme/ui/Layout/index.ts +2 -2
  52. package/src/client/theme/ui/Layout/pagination.css +72 -72
  53. package/src/client/theme/ui/Layout/responsive.css +36 -36
  54. package/src/client/theme/ui/Link/Link.tsx +254 -254
  55. package/src/client/theme/ui/Link/index.ts +2 -2
  56. package/src/client/theme/ui/Loading/Loading.tsx +10 -10
  57. package/src/client/theme/ui/Loading/index.ts +1 -1
  58. package/src/client/theme/ui/Loading/loading.css +30 -30
  59. package/src/client/theme/ui/Navbar/GithubStars.tsx +27 -27
  60. package/src/client/theme/ui/Navbar/Navbar.tsx +145 -145
  61. package/src/client/theme/ui/Navbar/index.ts +2 -2
  62. package/src/client/theme/ui/Navbar/navbar.css +233 -233
  63. package/src/client/theme/ui/NotFound/NotFound.tsx +19 -19
  64. package/src/client/theme/ui/NotFound/index.ts +1 -1
  65. package/src/client/theme/ui/NotFound/not-found.css +64 -64
  66. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +235 -235
  67. package/src/client/theme/ui/OnThisPage/index.ts +1 -1
  68. package/src/client/theme/ui/OnThisPage/toc.css +132 -132
  69. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +18 -18
  70. package/src/client/theme/ui/PoweredBy/index.ts +1 -1
  71. package/src/client/theme/ui/PoweredBy/powered-by.css +76 -76
  72. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +199 -199
  73. package/src/client/theme/ui/SearchDialog/index.ts +1 -1
  74. package/src/client/theme/ui/SearchDialog/search.css +152 -152
  75. package/src/client/theme/ui/Sidebar/Sidebar.tsx +204 -204
  76. package/src/client/theme/ui/Sidebar/index.ts +1 -1
  77. package/src/client/theme/ui/Sidebar/sidebar.css +236 -236
  78. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +69 -69
  79. package/src/client/theme/ui/ThemeToggle/index.ts +1 -1
  80. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +136 -136
  81. package/src/client/theme/ui/VersionSwitcher/index.ts +1 -1
  82. package/src/client/types.ts +50 -50
  83. package/src/client/utils.ts +26 -26
  84. package/src/node/cache.ts +408 -408
  85. package/src/node/config.ts +192 -192
  86. package/src/node/index.ts +21 -21
  87. package/src/node/mdx.ts +120 -120
  88. package/src/node/plugin/entry.ts +58 -58
  89. package/src/node/plugin/html.ts +55 -55
  90. package/src/node/plugin/index.ts +193 -193
  91. package/src/node/plugin/types.ts +11 -11
  92. package/src/node/routes/cache.ts +28 -28
  93. package/src/node/routes/index.ts +167 -167
  94. package/src/node/routes/parser.ts +153 -127
  95. package/src/node/routes/sorter.ts +42 -42
  96. package/src/node/routes/types.ts +49 -49
  97. package/src/node/ssg/index.ts +114 -114
  98. package/src/node/ssg/meta.ts +33 -34
  99. package/src/node/ssg/options.ts +13 -13
  100. package/src/node/ssg/sitemap.ts +55 -54
  101. package/src/node/utils.ts +145 -134
  102. package/tsconfig.json +20 -20
  103. package/tsup.config.ts +22 -22
@@ -3,7 +3,7 @@ import {
3
3
  FileCache,
4
4
  TransformCache,
5
5
  flushCache
6
- } from "./chunk-GSYECEZY.mjs";
6
+ } from "./chunk-CYBWLFOG.mjs";
7
7
  export {
8
8
  AssetCache,
9
9
  FileCache,
@@ -34,7 +34,10 @@ function parseFrontmatter(filePath) {
34
34
  return { data, content };
35
35
  }
36
36
  function escapeHtml(str) {
37
- return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
37
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
38
+ }
39
+ function escapeXml(str) {
40
+ return escapeHtml(str);
38
41
  }
39
42
  function fileToRoutePath(relativePath) {
40
43
  let cleanedPath = relativePath.split("/").map(stripNumberPrefix).join("/");
@@ -372,6 +375,7 @@ export {
372
375
  isDocFile,
373
376
  parseFrontmatter,
374
377
  escapeHtml,
378
+ escapeXml,
375
379
  fileToRoutePath,
376
380
  capitalize,
377
381
  FileCache,
@@ -57,7 +57,10 @@ function parseFrontmatter(filePath) {
57
57
  return { data, content };
58
58
  }
59
59
  function escapeHtml(str) {
60
- return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
60
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
61
+ }
62
+ function escapeXml(str) {
63
+ return escapeHtml(str);
61
64
  }
62
65
  function fileToRoutePath(relativePath) {
63
66
  let cleanedPath = relativePath.split("/").map(stripNumberPrefix).join("/");
@@ -445,8 +448,18 @@ var import_path2 = __toESM(require("path"));
445
448
  var import_github_slugger = __toESM(require("github-slugger"));
446
449
  init_utils();
447
450
  function parseDocFile(file, docsDir, basePath, config) {
451
+ const decodedFile = decodeURIComponent(file);
452
+ const absoluteFile = import_path2.default.resolve(decodedFile);
453
+ const absoluteDocsDir = import_path2.default.resolve(docsDir);
454
+ const relativePath = normalizePath(
455
+ import_path2.default.relative(absoluteDocsDir, absoluteFile)
456
+ );
457
+ if (relativePath.startsWith("../") || relativePath === ".." || absoluteFile.includes("\0")) {
458
+ throw new Error(
459
+ `Security breach: File is outside of docs directory or contains null bytes: ${file}`
460
+ );
461
+ }
448
462
  const { data, content } = parseFrontmatter(file);
449
- const relativePath = normalizePath(import_path2.default.relative(docsDir, file));
450
463
  let parts = relativePath.split("/");
451
464
  let locale;
452
465
  let version;
@@ -492,25 +505,30 @@ function parseDocFile(file, docsDir, basePath, config) {
492
505
  const level = match[1].length;
493
506
  const text = match[2].replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/[_*`]/g, "").trim();
494
507
  const id = slugger.slug(text);
495
- headings.push({ level, text, id });
508
+ headings.push({ level, text: escapeHtml(text), id });
496
509
  }
510
+ const sanitizedTitle = data.title ? escapeHtml(data.title) : inferredTitle;
511
+ const sanitizedDescription = data.description ? escapeHtml(data.description) : "";
512
+ const sanitizedBadge = data.badge ? escapeHtml(data.badge) : void 0;
497
513
  return {
498
514
  route: {
499
515
  path: finalPath,
500
516
  componentPath: file,
501
517
  filePath: relativePath,
502
- title: data.title || inferredTitle,
503
- description: data.description || "",
518
+ title: sanitizedTitle,
519
+ description: sanitizedDescription,
504
520
  sidebarPosition,
505
521
  headings,
506
522
  locale,
507
523
  version,
508
- badge: data.badge
524
+ badge: sanitizedBadge
509
525
  },
510
526
  relativeDir: cleanDirName,
511
527
  isGroupIndex,
512
528
  groupMeta: isGroupIndex ? {
513
- title: data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : ""),
529
+ title: escapeHtml(
530
+ data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : "")
531
+ ),
514
532
  position: data.groupPosition ?? data.sidebarPosition ?? (rawDirName ? extractNumberPrefix(rawDirName) : void 0)
515
533
  } : void 0,
516
534
  inferredGroupPosition: rawDirName ? extractNumberPrefix(rawDirName) : void 0
@@ -707,26 +725,24 @@ var import_url2 = require("url");
707
725
  var import_module = require("module");
708
726
 
709
727
  // src/node/ssg/meta.ts
728
+ init_utils();
710
729
  function replaceMetaTags(html, meta) {
711
- return html.replace(/<title>.*?<\/title>/, `<title>${meta.title}</title>`).replace(
730
+ const title = escapeHtml(meta.title);
731
+ const description = escapeHtml(meta.description);
732
+ return html.replace(/<title>.*?<\/title>/, `<title>${title}</title>`).replace(
712
733
  /(<meta name="description" content=")[^"]*(")/,
713
- `$1${meta.description}$2`
714
- ).replace(
715
- /(<meta property="og:title" content=")[^"]*(")/,
716
- `$1${meta.title}$2`
717
- ).replace(
734
+ `$1${description}$2`
735
+ ).replace(/(<meta property="og:title" content=")[^"]*(")/, `$1${title}$2`).replace(
718
736
  /(<meta property="og:description" content=")[^"]*(")/,
719
- `$1${meta.description}$2`
720
- ).replace(
721
- /(<meta name="twitter:title" content=")[^"]*(")/,
722
- `$1${meta.title}$2`
723
- ).replace(
737
+ `$1${description}$2`
738
+ ).replace(/(<meta name="twitter:title" content=")[^"]*(")/, `$1${title}$2`).replace(
724
739
  /(<meta name="twitter:description" content=")[^"]*(")/,
725
- `$1${meta.description}$2`
740
+ `$1${description}$2`
726
741
  );
727
742
  }
728
743
 
729
744
  // src/node/ssg/sitemap.ts
745
+ init_utils();
730
746
  function generateSitemap(routePaths, config) {
731
747
  const baseUrl = config?.siteUrl?.replace(/\/$/, "") || "https://example.com";
732
748
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -755,7 +771,7 @@ function generateSitemap(routePaths, config) {
755
771
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
756
772
  ${entries.map(
757
773
  (e) => ` <url>
758
- <loc>${baseUrl}${e.url}</loc>
774
+ <loc>${escapeXml(baseUrl)}${escapeXml(e.url)}</loc>
759
775
  <lastmod>${today}</lastmod>
760
776
  <changefreq>${e.changefreq}</changefreq>
761
777
  <priority>${e.priority}</priority>
@@ -3,13 +3,14 @@ import {
3
3
  TransformCache,
4
4
  capitalize,
5
5
  escapeHtml,
6
+ escapeXml,
6
7
  extractNumberPrefix,
7
8
  fileToRoutePath,
8
9
  isDocFile,
9
10
  normalizePath,
10
11
  parseFrontmatter,
11
12
  stripNumberPrefix
12
- } from "../chunk-GSYECEZY.mjs";
13
+ } from "../chunk-CYBWLFOG.mjs";
13
14
 
14
15
  // src/node/plugin/index.ts
15
16
  import { loadEnv } from "vite";
@@ -30,8 +31,18 @@ function invalidateFile(filePath) {
30
31
  import path from "path";
31
32
  import GithubSlugger from "github-slugger";
32
33
  function parseDocFile(file, docsDir, basePath, config) {
34
+ const decodedFile = decodeURIComponent(file);
35
+ const absoluteFile = path.resolve(decodedFile);
36
+ const absoluteDocsDir = path.resolve(docsDir);
37
+ const relativePath = normalizePath(
38
+ path.relative(absoluteDocsDir, absoluteFile)
39
+ );
40
+ if (relativePath.startsWith("../") || relativePath === ".." || absoluteFile.includes("\0")) {
41
+ throw new Error(
42
+ `Security breach: File is outside of docs directory or contains null bytes: ${file}`
43
+ );
44
+ }
33
45
  const { data, content } = parseFrontmatter(file);
34
- const relativePath = normalizePath(path.relative(docsDir, file));
35
46
  let parts = relativePath.split("/");
36
47
  let locale;
37
48
  let version;
@@ -77,25 +88,30 @@ function parseDocFile(file, docsDir, basePath, config) {
77
88
  const level = match[1].length;
78
89
  const text = match[2].replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/[_*`]/g, "").trim();
79
90
  const id = slugger.slug(text);
80
- headings.push({ level, text, id });
91
+ headings.push({ level, text: escapeHtml(text), id });
81
92
  }
93
+ const sanitizedTitle = data.title ? escapeHtml(data.title) : inferredTitle;
94
+ const sanitizedDescription = data.description ? escapeHtml(data.description) : "";
95
+ const sanitizedBadge = data.badge ? escapeHtml(data.badge) : void 0;
82
96
  return {
83
97
  route: {
84
98
  path: finalPath,
85
99
  componentPath: file,
86
100
  filePath: relativePath,
87
- title: data.title || inferredTitle,
88
- description: data.description || "",
101
+ title: sanitizedTitle,
102
+ description: sanitizedDescription,
89
103
  sidebarPosition,
90
104
  headings,
91
105
  locale,
92
106
  version,
93
- badge: data.badge
107
+ badge: sanitizedBadge
94
108
  },
95
109
  relativeDir: cleanDirName,
96
110
  isGroupIndex,
97
111
  groupMeta: isGroupIndex ? {
98
- title: data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : ""),
112
+ title: escapeHtml(
113
+ data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : "")
114
+ ),
99
115
  position: data.groupPosition ?? data.sidebarPosition ?? (rawDirName ? extractNumberPrefix(rawDirName) : void 0)
100
116
  } : void 0,
101
117
  inferredGroupPosition: rawDirName ? extractNumberPrefix(rawDirName) : void 0
@@ -292,21 +308,17 @@ import { createRequire } from "module";
292
308
 
293
309
  // src/node/ssg/meta.ts
294
310
  function replaceMetaTags(html, meta) {
295
- return html.replace(/<title>.*?<\/title>/, `<title>${meta.title}</title>`).replace(
311
+ const title = escapeHtml(meta.title);
312
+ const description = escapeHtml(meta.description);
313
+ return html.replace(/<title>.*?<\/title>/, `<title>${title}</title>`).replace(
296
314
  /(<meta name="description" content=")[^"]*(")/,
297
- `$1${meta.description}$2`
298
- ).replace(
299
- /(<meta property="og:title" content=")[^"]*(")/,
300
- `$1${meta.title}$2`
301
- ).replace(
315
+ `$1${description}$2`
316
+ ).replace(/(<meta property="og:title" content=")[^"]*(")/, `$1${title}$2`).replace(
302
317
  /(<meta property="og:description" content=")[^"]*(")/,
303
- `$1${meta.description}$2`
304
- ).replace(
305
- /(<meta name="twitter:title" content=")[^"]*(")/,
306
- `$1${meta.title}$2`
307
- ).replace(
318
+ `$1${description}$2`
319
+ ).replace(/(<meta name="twitter:title" content=")[^"]*(")/, `$1${title}$2`).replace(
308
320
  /(<meta name="twitter:description" content=")[^"]*(")/,
309
- `$1${meta.description}$2`
321
+ `$1${description}$2`
310
322
  );
311
323
  }
312
324
 
@@ -339,7 +351,7 @@ function generateSitemap(routePaths, config) {
339
351
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
340
352
  ${entries.map(
341
353
  (e) => ` <url>
342
- <loc>${baseUrl}${e.url}</loc>
354
+ <loc>${escapeXml(baseUrl)}${escapeXml(e.url)}</loc>
343
355
  <lastmod>${today}</lastmod>
344
356
  <changefreq>${e.changefreq}</changefreq>
345
357
  <priority>${e.priority}</priority>
@@ -418,7 +430,7 @@ async function generateStaticPages(options) {
418
430
  console.log(
419
431
  `[boltdocs] Generated ${routes.length} static pages + sitemap.xml`
420
432
  );
421
- const { flushCache } = await import("../cache-EHR7SXRU.mjs");
433
+ const { flushCache } = await import("../cache-GQHF6BXI.mjs");
422
434
  await flushCache();
423
435
  }
424
436
 
@@ -600,7 +612,7 @@ function boltdocsPlugin(options = {}, passedConfig) {
600
612
  if (!isBuild) return;
601
613
  const outDir = viteConfig?.build?.outDir ? path4.resolve(viteConfig.root, viteConfig.build.outDir) : path4.resolve(process.cwd(), "dist");
602
614
  await generateStaticPages({ docsDir, outDir, config });
603
- const { flushCache } = await import("../cache-EHR7SXRU.mjs");
615
+ const { flushCache } = await import("../cache-GQHF6BXI.mjs");
604
616
  await flushCache();
605
617
  }
606
618
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "boltdocs",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "A lightweight documentation generator for React projects.",
5
5
  "main": "dist/node/index.js",
6
6
  "module": "dist/node/index.mjs",