@@ -7,18 +7,21 @@ import { writeFile, mkdir } from 'node:fs/promises'
|
7 | 7 | import path from 'node:path'
|
8 | 8 | import { fileURLToPath } from 'node:url'
|
9 | 9 | import { parse } from 'react-docgen-typescript'
|
| 10 | +import showdown from 'showdown' |
10 | 11 |
|
11 | 12 | /**
|
12 | 13 | * Derive __dirname in ESM
|
13 | 14 | */
|
14 | 15 | const __filename = fileURLToPath(import.meta.url)
|
15 | 16 | const __dirname = path.dirname(__filename)
|
| 17 | +const converter = new showdown.Converter({ simpleLineBreaks: true }) |
16 | 18 |
|
17 | 19 | /**
|
18 | 20 | * Glob patterns to locate .tsx files for documentation.
|
19 | 21 | * Adjust these patterns based on your project structure.
|
20 | 22 | */
|
21 | 23 | const GLOB_PATTERNS = [
|
| 24 | +// '**/src/components/date-picker/*.tsx', |
22 | 25 | '**/src/**/*.tsx',
|
23 | 26 | '../node_modules/@coreui/icons-react/src/**/*.tsx',
|
24 | 27 | '../node_modules/@coreui/react-chartjs/src/**/*.tsx',
|
@@ -44,26 +47,39 @@ const EXCLUDED_FILES = [] // Currently unused, but can be utilized if needed
|
44 | 47 | * Options for react-docgen-typescript parser.
|
45 | 48 | */
|
46 | 49 | const DOCGEN_OPTIONS = {
|
47 |
| -savePropValueAsString: true, |
48 | 50 | shouldIncludePropTagMap: true,
|
49 | 51 | }
|
50 | 52 |
|
51 | 53 | /**
|
52 | 54 | * List of pro components that require special handling.
|
53 | 55 | */
|
54 | 56 | const PRO_COMPONENTS = [
|
| 57 | +'CCalendar', |
55 | 58 | 'CDatePicker',
|
56 | 59 | 'CDateRangePicker',
|
57 | 60 | 'CFormMask',
|
58 | 61 | 'CLoadingButton',
|
59 | 62 | 'CMultiSelect',
|
60 | 63 | 'CRating',
|
| 64 | +'CRangeSlider', |
| 65 | +'CRating', |
61 | 66 | 'CSmartPagination',
|
62 | 67 | 'CSmartTable',
|
63 | 68 | 'CTimePicker',
|
64 | 69 | 'CVirtualScroller',
|
65 | 70 | ]
|
66 | 71 |
|
| 72 | +const TEXT_REPLACEMENTS = { |
| 73 | +CDatePicker: { |
| 74 | +description: [{ 'React Calendar': 'React Date Picker' }], |
| 75 | +example: [{ CCalendar: 'CDatePicker' }], |
| 76 | +}, |
| 77 | +CDateRangePicker: { |
| 78 | +description: [{ 'React Calendar': 'React Date Range Picker' }], |
| 79 | +example: [{ CCalendar: 'CDateRangePicker' }], |
| 80 | +}, |
| 81 | +} |
| 82 | + |
67 | 83 | /**
|
68 | 84 | * Escapes special characters in text to prevent Markdown rendering issues.
|
69 | 85 | *
|
@@ -72,13 +88,10 @@ const PRO_COMPONENTS = [
|
72 | 88 | */
|
73 | 89 | function escapeMarkdown(text) {
|
74 | 90 | if (typeof text !== 'string') return text
|
75 |
| -return ( |
76 |
| -text |
77 |
| -.replaceAll(/(<)/g, String.raw`\$1`) |
78 |
| -// .replaceAll(/<C(.*)\/>/g, '`<C$1/>`') |
79 |
| -.replaceAll('\n', '<br/>') |
80 |
| -.replaceAll(/`([^`]+)`/g, '<code>{`$1`}</code>') |
81 |
| -) |
| 91 | +return text |
| 92 | +.replaceAll(/(<)/g, String.raw`\$1`) |
| 93 | +.replaceAll('\n', '<br/>') |
| 94 | +.replaceAll(/`([^`]+)`/g, '<code>{`$1`}</code>') |
82 | 95 | }
|
83 | 96 |
|
84 | 97 | /**
|
@@ -110,6 +123,10 @@ function getRelativeFilename(file) {
|
110 | 123 | * @throws {Error} Throws an error if there are unmatched braces or parentheses in the input.
|
111 | 124 | */
|
112 | 125 | function splitOutsideBracesAndParentheses(input) {
|
| 126 | +if (input.endsWith('...')) { |
| 127 | +return [input] |
| 128 | +} |
| 129 | + |
113 | 130 | const parts = []
|
114 | 131 | let currentPart = ''
|
115 | 132 | let braceDepth = 0 // Tracks depth of curly braces {}
|
@@ -172,6 +189,23 @@ function splitOutsideBracesAndParentheses(input) {
|
172 | 189 | return parts
|
173 | 190 | }
|
174 | 191 |
|
| 192 | +function replaceText(componenName, keyName, text) { |
| 193 | +const keyNames = Object.keys(TEXT_REPLACEMENTS) |
| 194 | + |
| 195 | +if (keyNames.includes(componenName)) { |
| 196 | +const replacements = TEXT_REPLACEMENTS[componenName][keyName] |
| 197 | +for (const replacement of replacements) { |
| 198 | +for (const [key, value] of Object.entries(replacement)) { |
| 199 | +if (text && key && value) { |
| 200 | +return text.replaceAll(key, value) |
| 201 | +} |
| 202 | +} |
| 203 | +} |
| 204 | +} else { |
| 205 | +return text |
| 206 | +} |
| 207 | +} |
| 208 | + |
175 | 209 | /**
|
176 | 210 | * Creates an MDX file with the component's API documentation.
|
177 | 211 | *
|
@@ -190,10 +224,10 @@ async function createMdx(file, component) {
|
190 | 224 | let content = `\n\`\`\`jsx\n`
|
191 | 225 | const importPathParts = relativeFilename.split('/')
|
192 | 226 | if (importPathParts.length > 1) {
|
193 |
| -content += `import { ${component.displayName} } from '@coreui/${importPathParts[1]}'\n` |
| 227 | +content += `import { ${component.displayName} } from '@coreui/${importPathParts[0]}'\n` |
194 | 228 | }
|
195 | 229 | content += `// or\n`
|
196 |
| -content += `import ${component.displayName} from '@coreui${relativeFilename.replace('.tsx', '')}'\n` |
| 230 | +content += `import ${component.displayName} from '@coreui/${relativeFilename.replace('.tsx', '')}'\n` |
197 | 231 | content += `\`\`\`\n\n`
|
198 | 232 |
|
199 | 233 | const sortedProps = Object.entries(component.props).sort(([a], [b]) => a.localeCompare(b))
|
@@ -240,14 +274,19 @@ async function createMdx(file, component) {
|
240 | 274 | const deprecated = propInfo.tags?.deprecated
|
241 | 275 | ? `<span className="badge bg-success">Deprecated ${propInfo.tags.since}</span>`
|
242 | 276 | : ''
|
243 |
| -const description = propInfo.description || '-' |
| 277 | +const description = propInfo.description |
| 278 | +? replaceText(component.displayName, 'description', propInfo.description) |
| 279 | +: '-' |
244 | 280 |
|
245 | 281 | const type = propInfo.type
|
246 | 282 | ? propInfo.type.name.includes('ReactElement')
|
247 | 283 | ? 'ReactElement'
|
248 | 284 | : propInfo.type.name
|
249 | 285 | : ''
|
250 | 286 | const defaultValue = propInfo.defaultValue ? `\`${propInfo.defaultValue.value}\`` : `undefined`
|
| 287 | +const example = propInfo.tags?.example |
| 288 | +? replaceText(component.displayName, 'example', propInfo.tags?.example) |
| 289 | +: false |
251 | 290 |
|
252 | 291 | // Format types as inline code
|
253 | 292 | const types = splitOutsideBracesAndParentheses(type)
|
@@ -263,7 +302,16 @@ async function createMdx(file, component) {
|
263 | 302 | content += ` <td>${escapeMarkdown(types)}</td>\n`
|
264 | 303 | content += ` </tr>\n`
|
265 | 304 | content += ` <tr>\n`
|
266 |
| -content += ` <td colSpan="3">${escapeMarkdown(description)}${propInfo.tags?.example ? `<br /><JSXDocs code={\`${propInfo.tags.example}\`} />` : ''}</td>\n` |
| 305 | +content += ` <td colSpan="3">\n` |
| 306 | +content += ` ${converter |
| 307 | +.makeHtml(description) |
| 308 | +.replaceAll(/<code>(.*?)<\/code>/g, '<code>{`$1`}</code>')}\n` |
| 309 | + |
| 310 | +if (example) { |
| 311 | +content += ` <JSXDocs code={\`${example.trim()}\`} />\n` |
| 312 | +} |
| 313 | + |
| 314 | +content += ` </td>\n` |
267 | 315 | content += ` </tr>\n`
|
268 | 316 |
|
269 | 317 | if (isLast) {
|
|
0 commit comments