boltdocs 1.11.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/dist/cache-7G6D532T.mjs +1 -0
- package/dist/chunk-A4HQPEPU.mjs +1 -0
- package/dist/chunk-BA5NH5HU.mjs +1 -0
- package/dist/chunk-BQCD3DWG.mjs +1 -0
- package/dist/chunk-H63UMKYF.mjs +1 -0
- package/dist/chunk-IWHRQHS7.mjs +1 -0
- package/dist/chunk-JZXLCA2E.mjs +1 -0
- package/dist/chunk-MFU7Q6WF.mjs +1 -0
- package/dist/chunk-QYPNX5UN.mjs +1 -0
- package/dist/chunk-XEAPSFMB.mjs +1 -0
- package/dist/client/components/mdx/index.d.mts +209 -0
- package/dist/client/components/mdx/index.d.ts +209 -0
- package/dist/client/components/mdx/index.js +1 -0
- package/dist/client/components/mdx/index.mjs +1 -0
- package/dist/client/hooks/index.d.mts +133 -0
- package/dist/client/hooks/index.d.ts +133 -0
- package/dist/client/hooks/index.js +1 -0
- package/dist/client/hooks/index.mjs +1 -0
- package/dist/client/index.d.mts +212 -0
- package/dist/client/index.d.ts +212 -0
- package/dist/client/index.js +1 -0
- package/dist/client/index.mjs +1 -0
- package/dist/client/ssr.d.mts +31 -0
- package/dist/client/ssr.d.ts +31 -0
- package/dist/client/ssr.js +1 -0
- package/dist/client/ssr.mjs +1 -0
- package/dist/config-CX4l-ZNp.d.mts +166 -0
- package/dist/config-CX4l-ZNp.d.ts +166 -0
- package/dist/node/index.d.mts +89 -0
- package/dist/node/index.d.ts +89 -0
- package/dist/node/index.js +57 -0
- package/dist/node/index.mjs +57 -0
- package/dist/search-dialog-EB3N4TYM.mjs +1 -0
- package/dist/types-BuZWFT7r.d.ts +159 -0
- package/dist/types-CvT-SGbK.d.mts +159 -0
- package/dist/use-routes-5bAtAAYX.d.mts +30 -0
- package/dist/use-routes-BefRXY3v.d.ts +30 -0
- package/package.json +10 -10
- package/src/client/app/index.tsx +9 -6
- package/src/client/types.ts +1 -1
- package/src/node/plugin/index.ts +0 -1
- package/src/node/routes/parser.ts +27 -32
- package/src/node/ssg/index.ts +29 -3
- package/src/node/utils.ts +23 -0
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
capitalize,
|
|
10
10
|
stripNumberPrefix,
|
|
11
11
|
extractNumberPrefix,
|
|
12
|
+
sanitizeHtml,
|
|
13
|
+
stripHtmlTags,
|
|
12
14
|
} from '../utils'
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -118,47 +120,47 @@ export function parseDocFile(
|
|
|
118
120
|
|
|
119
121
|
const isGroupIndex = parts.length >= 2 && /^index\.mdx?$/.test(cleanFileName)
|
|
120
122
|
|
|
121
|
-
const headings: { level: number; text: string; id: string }[] = []
|
|
122
123
|
const slugger = new GithubSlugger()
|
|
124
|
+
const headings: { level: number; text: string; id: string }[] = []
|
|
123
125
|
const headingsRegex = /^(#{2,4})\s+(.+)$/gm
|
|
126
|
+
|
|
124
127
|
let match
|
|
125
128
|
while ((match = headingsRegex.exec(content)) !== null) {
|
|
126
129
|
const level = match[1].length
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
.replace(
|
|
130
|
-
.replace(/[_*`]/g, '')
|
|
131
|
-
.trim()
|
|
132
|
-
const id = slugger.slug(text)
|
|
133
|
-
// Security: Sanitize heading text for XSS
|
|
134
|
-
const sanitizedText = text
|
|
135
|
-
.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gim, '')
|
|
136
|
-
.replace(/<[^>]+on\w+="[^"]*"/gim, '')
|
|
137
|
-
.replace(/<img[^>]+>/gim, '')
|
|
130
|
+
const rawText = match[2]
|
|
131
|
+
.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1') // Strip markdown links
|
|
132
|
+
.replace(/[_*`]/g, '') // Strip markdown formatting
|
|
138
133
|
.trim()
|
|
134
|
+
|
|
135
|
+
const sanitizedText = sanitizeHtml(rawText).trim()
|
|
136
|
+
const id = slugger.slug(sanitizedText)
|
|
137
|
+
|
|
139
138
|
headings.push({ level, text: sanitizedText, id })
|
|
140
139
|
}
|
|
141
140
|
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
141
|
+
const sanitizedTitle = data.title
|
|
142
|
+
? sanitizeHtml(String(data.title))
|
|
143
|
+
: inferredTitle
|
|
144
|
+
let sanitizedDescription = data.description
|
|
145
|
+
? sanitizeHtml(String(data.description))
|
|
146
|
+
: ''
|
|
147
147
|
|
|
148
148
|
// If no description is provided, extract a summary from the content
|
|
149
149
|
if (!sanitizedDescription && content) {
|
|
150
|
-
|
|
150
|
+
const plainExcerpt = stripHtmlTags(
|
|
151
151
|
content
|
|
152
152
|
.replace(/^#+.*$/gm, '') // Remove headers
|
|
153
153
|
.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1') // Simplify links
|
|
154
154
|
.replace(/[_*`]/g, '') // Remove formatting
|
|
155
|
-
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
156
|
-
.trim()
|
|
157
|
-
.slice(0, 160),
|
|
155
|
+
.replace(/\s+/g, ' '), // Normalize whitespace
|
|
158
156
|
)
|
|
157
|
+
.trim()
|
|
158
|
+
.slice(0, 160)
|
|
159
|
+
|
|
160
|
+
sanitizedDescription = plainExcerpt
|
|
159
161
|
}
|
|
160
162
|
|
|
161
|
-
const sanitizedBadge = data.badge ?
|
|
163
|
+
const sanitizedBadge = data.badge ? sanitizeHtml(String(data.badge)) : undefined
|
|
162
164
|
const icon = data.icon ? String(data.icon) : undefined
|
|
163
165
|
|
|
164
166
|
// Extract full content as plain text for search indexing
|
|
@@ -203,24 +205,17 @@ export function parseDocFile(
|
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
207
|
|
|
206
|
-
/**
|
|
207
|
-
* Sanitizes a string by removing script tags for basic XSS protection.
|
|
208
|
-
*/
|
|
209
|
-
function sanitize(str: string): string {
|
|
210
|
-
return str.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gim, '').trim()
|
|
211
|
-
}
|
|
212
|
-
|
|
213
208
|
/**
|
|
214
209
|
* Converts markdown content to plain text for search indexing.
|
|
215
210
|
* Strips headers, links, tags, and formatting.
|
|
216
211
|
*/
|
|
217
212
|
function parseContentToPlainText(content: string): string {
|
|
218
|
-
|
|
213
|
+
const plainText = content
|
|
219
214
|
.replace(/^#+.*$/gm, '') // Remove headers
|
|
220
215
|
.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1') // Simplify links
|
|
221
|
-
.replace(/<[^>]+>/g, '') // Remove HTML/JSX tags
|
|
222
216
|
.replace(/\{[^\}]+\}/g, '') // Remove JS expressions/curly braces
|
|
223
217
|
.replace(/[_*`]/g, '') // Remove formatting
|
|
224
218
|
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
225
|
-
|
|
219
|
+
|
|
220
|
+
return stripHtmlTags(plainText).trim()
|
|
226
221
|
}
|
package/src/node/ssg/index.ts
CHANGED
|
@@ -39,8 +39,34 @@ export async function generateStaticPages(options: SSGOptions): Promise<void> {
|
|
|
39
39
|
)
|
|
40
40
|
return
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
// Mock require so Node doesn't choke on virtual modules compiled externally
|
|
44
|
+
const Module = _require('module')
|
|
45
|
+
const originalRequire = Module.prototype.require
|
|
46
|
+
;(Module.prototype as any).require = function (id: string, ...args: any[]) {
|
|
47
|
+
if (id === 'virtual:boltdocs-layout') {
|
|
48
|
+
return {
|
|
49
|
+
__esModule: true,
|
|
50
|
+
default: function SSG_Virtual_Layout(props: any) {
|
|
51
|
+
try {
|
|
52
|
+
const client = originalRequire.apply(this, [path.resolve(_dirname, '../client/index.js')])
|
|
53
|
+
const Comp = client.DefaultLayout || (({ children }: any) => children)
|
|
54
|
+
const React = originalRequire.apply(this, ['react'])
|
|
55
|
+
return React.createElement(Comp, props)
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return props.children
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return originalRequire.apply(this, [id, ...args])
|
|
63
|
+
}
|
|
64
|
+
|
|
42
65
|
const { render } = _require(ssrModulePath)
|
|
43
66
|
|
|
67
|
+
// Restore require after loading the module
|
|
68
|
+
;(Module.prototype as any).require = originalRequire
|
|
69
|
+
|
|
44
70
|
// Read the built index.html as template
|
|
45
71
|
const templatePath = path.join(outDir, 'index.html')
|
|
46
72
|
if (!fs.existsSync(templatePath)) {
|
|
@@ -57,7 +83,7 @@ export async function generateStaticPages(options: SSGOptions): Promise<void> {
|
|
|
57
83
|
|
|
58
84
|
// We mock the modules for SSR so it doesn't crash trying to dynamically import
|
|
59
85
|
const fakeModules: Record<string, any> = {}
|
|
60
|
-
fakeModules[route.
|
|
86
|
+
fakeModules[`/${docsDirName}/${route.filePath}`] = { default: () => null } // Mock MDX component
|
|
61
87
|
|
|
62
88
|
try {
|
|
63
89
|
const appHtml = await render({
|
|
@@ -83,8 +109,8 @@ export async function generateStaticPages(options: SSGOptions): Promise<void> {
|
|
|
83
109
|
html,
|
|
84
110
|
'utf-8',
|
|
85
111
|
)
|
|
86
|
-
} catch (e) {
|
|
87
|
-
console.error(`[boltdocs] Error SSR rendering route ${route.path}:`, e)
|
|
112
|
+
} catch (e: any) {
|
|
113
|
+
console.error(`[boltdocs] Error SSR rendering route ${route.path}:`, e ? e.stack || e : e)
|
|
88
114
|
}
|
|
89
115
|
}),
|
|
90
116
|
)
|
package/src/node/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import matter from 'gray-matter'
|
|
3
|
+
import DOMPurify from 'isomorphic-dompurify'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Normalizes a file path by replacing Windows backslashes with forward slashes.
|
|
@@ -136,6 +137,28 @@ export function fileToRoutePath(relativePath: string): string {
|
|
|
136
137
|
return routePath
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Sanitizes an HTML string using DOMPurify to prevent XSS.
|
|
142
|
+
* By default, it allows a safe subset of HTML tags.
|
|
143
|
+
*
|
|
144
|
+
* @param html - The raw HTML string
|
|
145
|
+
* @returns The sanitized HTML
|
|
146
|
+
*/
|
|
147
|
+
export function sanitizeHtml(html: string): string {
|
|
148
|
+
return DOMPurify.sanitize(html)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Strips all HTML tags from a string, returning only the text content.
|
|
153
|
+
* Uses DOMPurify for secure and complete tag removal.
|
|
154
|
+
*
|
|
155
|
+
* @param html - The string containing HTML tags
|
|
156
|
+
* @returns The plain text content
|
|
157
|
+
*/
|
|
158
|
+
export function stripHtmlTags(html: string): string {
|
|
159
|
+
return DOMPurify.sanitize(html, { ALLOWED_TAGS: [], KEEP_CONTENT: true })
|
|
160
|
+
}
|
|
161
|
+
|
|
139
162
|
/**
|
|
140
163
|
* Capitalizes the first letter of a given string.
|
|
141
164
|
* Used primarily for generating default group titles.
|