Skip to content

Commit 842f2e2

Browse files
committed
build: update API generator
1 parent cde36ae commit 842f2e2

File tree

1 file changed

+53
-58
lines changed

1 file changed

+53
-58
lines changed

packages/docs/build/api.mjs

+53-58
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ const converter = new showdown.Converter({ simpleLineBreaks: true })
1818

1919
/**
2020
* Glob patterns to locate .tsx files for documentation.
21-
* Adjust these patterns based on your project structure.
2221
*/
2322
const GLOB_PATTERNS = [
24-
// '**/src/components/date-picker/*.tsx',
2523
'**/src/**/*.tsx',
2624
'../node_modules/@coreui/icons-react/src/**/*.tsx',
2725
'../node_modules/@coreui/react-chartjs/src/**/*.tsx',
@@ -39,7 +37,6 @@ const GLOBBY_OPTIONS = {
3937

4038
/**
4139
* Excluded files list (currently unused).
42-
* Can be utilized for additional exclusion patterns if needed.
4340
*/
4441
const EXCLUDED_FILES = [] // Currently unused, but can be utilized if needed
4542

@@ -69,6 +66,9 @@ const PRO_COMPONENTS = [
6966
'CVirtualScroller',
7067
]
7168

69+
/**
70+
* Text replacements for certain components.
71+
*/
7272
const TEXT_REPLACEMENTS = {
7373
CDatePicker: {
7474
description: [{ 'React Calendar': 'React Date Picker' }],
@@ -78,6 +78,15 @@ const TEXT_REPLACEMENTS = {
7878
description: [{ 'React Calendar': 'React Date Range Picker' }],
7979
example: [{ CCalendar: 'CDateRangePicker' }],
8080
},
81+
CFormInput: {
82+
example: [{ CFormControlValidation: 'CFormInput' }, { CFormControlWrapper: 'CFormInput' }],
83+
},
84+
CFormTextarea: {
85+
example: [
86+
{ CFormControlValidation: 'CFormTextarea' },
87+
{ CFormControlWrapper: 'CFormTextarea' },
88+
],
89+
},
8190
}
8291

8392
/**
@@ -86,7 +95,7 @@ const TEXT_REPLACEMENTS = {
8695
* @param {string} text - The text to escape.
8796
* @returns {string} - The escaped text.
8897
*/
89-
function escapeMarkdown(text) {
98+
const escapeMarkdown = (text) => {
9099
if (typeof text !== 'string') return text
91100
return text
92101
.replaceAll(/(<)/g, String.raw`\$1`)
@@ -100,9 +109,8 @@ function escapeMarkdown(text) {
100109
* @param {string} file - The absolute file path.
101110
* @returns {string} - The relative filename.
102111
*/
103-
function getRelativeFilename(file) {
104-
let relativePath
105-
relativePath = file.includes('node_modules')
112+
const getRelativeFilename = (file) => {
113+
let relativePath = file.includes('node_modules')
106114
? path.relative(path.join(__dirname, '..', '..'), file).replace('coreui-', '')
107115
: path.relative(GLOBBY_OPTIONS.cwd, file).replace('coreui-', '')
108116

@@ -122,15 +130,15 @@ function getRelativeFilename(file) {
122130
* @returns {string[]} An array of split parts, trimmed of whitespace.
123131
* @throws {Error} Throws an error if there are unmatched braces or parentheses in the input.
124132
*/
125-
function splitOutsideBracesAndParentheses(input) {
133+
const splitOutsideBracesAndParentheses = (input) => {
126134
if (input.endsWith('...')) {
127135
return [input]
128136
}
129137

130138
const parts = []
131139
let currentPart = ''
132-
let braceDepth = 0 // Tracks depth of curly braces {}
133-
let parenthesisDepth = 0 // Tracks depth of parentheses ()
140+
let braceDepth = 0
141+
let parenthesisDepth = 0
134142

135143
for (const char of input) {
136144
switch (char) {
@@ -161,38 +169,43 @@ function splitOutsideBracesAndParentheses(input) {
161169
if (braceDepth === 0 && parenthesisDepth === 0 && currentPart.trim()) {
162170
parts.push(currentPart.trim())
163171
currentPart = ''
164-
continue // Skip adding the '|' to currentPart
172+
continue
165173
}
166174
break
167175
}
168176
default: {
169-
// No action needed for other characters
170177
break
171178
}
172179
}
173180
currentPart += char
174181
}
175182

176-
// After processing all characters, check for unmatched opening braces or parentheses
177183
if (braceDepth !== 0) {
178184
throw new Error('Unmatched opening curly brace detected.')
179185
}
180186
if (parenthesisDepth !== 0) {
181187
throw new Error('Unmatched opening parenthesis detected.')
182188
}
183189

184-
// Add the last accumulated part if it's not empty
185190
if (currentPart.trim()) {
186191
parts.push(currentPart.trim())
187192
}
188193

189194
return parts
190195
}
191196

192-
function replaceText(componenName, keyName, text) {
197+
/**
198+
* Replaces specified text within component documentation.
199+
*
200+
* @param {string} componenName - The name of the component.
201+
* @param {string} keyName - The key of the text replacement (e.g., 'description', 'example').
202+
* @param {string} text - The text to be replaced.
203+
* @returns {string} The replaced text.
204+
*/
205+
const replaceText = (componenName, keyName, text) => {
193206
const keyNames = Object.keys(TEXT_REPLACEMENTS)
194207

195-
if (keyNames.includes(componenName)) {
208+
if (keyNames.includes(componenName) && TEXT_REPLACEMENTS[componenName][keyName]) {
196209
const replacements = TEXT_REPLACEMENTS[componenName][keyName]
197210
for (const replacement of replacements) {
198211
for (const [key, value] of Object.entries(replacement)) {
@@ -210,17 +223,14 @@ function replaceText(componenName, keyName, text) {
210223
* Creates an MDX file with the component's API documentation.
211224
*
212225
* @param {string} file - The absolute path to the component file.
213-
* @param {object} component - The component information extracted by react-docgen-typescript.
226+
* @param {object} component - The component info extracted by react-docgen-typescript.
214227
*/
215-
async function createMdx(file, component) {
216-
if (!component) {
217-
return
218-
}
228+
const createMdx = async (file, component) => {
229+
if (!component) return
219230

220231
const filename = path.basename(file, '.tsx')
221232
const relativeFilename = getRelativeFilename(file)
222233

223-
// Construct import statements
224234
let content = `\n\`\`\`jsx\n`
225235
const importPathParts = relativeFilename.split('/')
226236
if (importPathParts.length > 1) {
@@ -230,11 +240,17 @@ async function createMdx(file, component) {
230240
content += `import ${component.displayName} from '@coreui/${relativeFilename.replace('.tsx', '')}'\n`
231241
content += `\`\`\`\n\n`
232242

233-
const sortedProps = Object.entries(component.props).sort(([a], [b]) => a.localeCompare(b))
234-
235-
// Initialize table headers
236-
for (const [index, [propName, propInfo]] of sortedProps.entries()) {
237-
const isLast = index === sortedProps.length - 1
243+
const filteredProps = Object.entries(component.props)
244+
.filter(([_, value]) => {
245+
if (!value.parent?.fileName) return true
246+
return (
247+
!value.parent.fileName.includes('@types/react/index.d.ts') &&
248+
!value.parent.fileName.includes('@types/react/ts5.0/index.d.ts')
249+
)
250+
})
251+
.sort(([a], [b]) => a.localeCompare(b))
252+
253+
for (const [index, [propName, propInfo]] of filteredProps.entries()) {
238254
if (index === 0) {
239255
content += `<div className="table-responsive table-api border rounded mb-3">\n`
240256
content += ` <table className="table">\n`
@@ -248,20 +264,6 @@ async function createMdx(file, component) {
248264
content += ` <tbody>\n`
249265
}
250266

251-
// Skip props from React's type definitions
252-
if (
253-
propInfo.parent?.fileName?.includes('@types/react/index.d.ts') ||
254-
propInfo.parent?.fileName?.includes('@types/react/ts5.0/index.d.ts')
255-
) {
256-
if (isLast) {
257-
content += ` </tbody>\n`
258-
content += ` </table>\n`
259-
content += `</div>\n`
260-
}
261-
262-
continue
263-
}
264-
265267
// Skip props marked to be ignored
266268
if (propInfo.tags?.ignore === '') {
267269
continue
@@ -272,20 +274,19 @@ async function createMdx(file, component) {
272274
? `<span className="badge bg-success">${propInfo.tags.since}+</span>`
273275
: ''
274276
const deprecated = propInfo.tags?.deprecated
275-
? `<span className="badge bg-success">Deprecated ${propInfo.tags.since}</span>`
277+
? `<span className="badge bg-danger">Deprecated ${propInfo.tags.deprecated}</span>`
276278
: ''
277279
const description = propInfo.description
278280
? replaceText(component.displayName, 'description', propInfo.description)
279281
: '-'
280-
281282
const type = propInfo.type
282283
? propInfo.type.name.includes('ReactElement')
283284
? 'ReactElement'
284285
: propInfo.type.name
285286
: ''
286287
const defaultValue = propInfo.defaultValue ? `\`${propInfo.defaultValue.value}\`` : `undefined`
287288
const example = propInfo.tags?.example
288-
? replaceText(component.displayName, 'example', propInfo.tags?.example)
289+
? replaceText(component.displayName, 'example', propInfo.tags.example)
289290
: false
290291

291292
// Format types as inline code
@@ -305,27 +306,24 @@ async function createMdx(file, component) {
305306
content += ` <td colSpan="3">\n`
306307
content += ` ${converter
307308
.makeHtml(description)
308-
.replaceAll(/<code>(.*?)<\/code>/g, '<code>{`$1`}</code>')}\n`
309+
.replaceAll(/<code>(.*?)<\/code>/g, '<code>{`$1`}</code>')
310+
.replaceAll(/<code>{`&lt;(.*?)&gt;`}<\/code>/g, '<code>{`<$1>`}</code>')}\n`
309311

310312
if (example) {
311313
content += ` <JSXDocs code={\`${example.trim()}\`} />\n`
312314
}
313315

314316
content += ` </td>\n`
315317
content += ` </tr>\n`
316-
317-
if (isLast) {
318-
content += ` </tbody>\n`
319-
content += ` </table>\n`
320-
content += `</div>\n`
321-
}
322318
}
323319

324-
// Define the output directory and ensure it exists
320+
content += ` </tbody>\n`
321+
content += ` </table>\n`
322+
content += `</div>\n`
323+
325324
const outputDir = path.join('content', 'api')
326325
const outputPath = path.join(outputDir, `${filename}.api.mdx`)
327326

328-
// Create the directory if it doesn't exist
329327
try {
330328
await mkdir(outputDir, { recursive: true })
331329
await writeFile(outputPath, content, { encoding: 'utf8' })
@@ -336,19 +334,16 @@ async function createMdx(file, component) {
336334
}
337335

338336
/**
339-
* Main function to execute the script.
337+
* Main execution function.
340338
*/
341-
async function main() {
339+
const main = async () => {
342340
try {
343-
// Retrieve all matching files based on the glob patterns
344341
const files = await globby(GLOB_PATTERNS, GLOBBY_OPTIONS)
345342

346-
// Process each file concurrently
347343
await Promise.all(
348344
files.map(async (file) => {
349345
console.log(`Processing file: ${file}`)
350346
let components
351-
352347
try {
353348
components = parse(file, DOCGEN_OPTIONS)
354349
} catch (parseError) {

0 commit comments

Comments
 (0)