@@ -18,10 +18,8 @@ const converter = new showdown.Converter({ simpleLineBreaks: true })
18
18
19
19
/**
20
20
* Glob patterns to locate .tsx files for documentation.
21
- * Adjust these patterns based on your project structure.
22
21
*/
23
22
const GLOB_PATTERNS = [
24
- // '**/src/components/date-picker/*.tsx',
25
23
'**/src/**/*.tsx' ,
26
24
'../node_modules/@coreui/icons-react/src/**/*.tsx' ,
27
25
'../node_modules/@coreui/react-chartjs/src/**/*.tsx' ,
@@ -39,7 +37,6 @@ const GLOBBY_OPTIONS = {
39
37
40
38
/**
41
39
* Excluded files list (currently unused).
42
- * Can be utilized for additional exclusion patterns if needed.
43
40
*/
44
41
const EXCLUDED_FILES = [ ] // Currently unused, but can be utilized if needed
45
42
@@ -69,6 +66,9 @@ const PRO_COMPONENTS = [
69
66
'CVirtualScroller' ,
70
67
]
71
68
69
+ /**
70
+ * Text replacements for certain components.
71
+ */
72
72
const TEXT_REPLACEMENTS = {
73
73
CDatePicker : {
74
74
description : [ { 'React Calendar' : 'React Date Picker' } ] ,
@@ -78,6 +78,15 @@ const TEXT_REPLACEMENTS = {
78
78
description : [ { 'React Calendar' : 'React Date Range Picker' } ] ,
79
79
example : [ { CCalendar : 'CDateRangePicker' } ] ,
80
80
} ,
81
+ CFormInput : {
82
+ example : [ { CFormControlValidation : 'CFormInput' } , { CFormControlWrapper : 'CFormInput' } ] ,
83
+ } ,
84
+ CFormTextarea : {
85
+ example : [
86
+ { CFormControlValidation : 'CFormTextarea' } ,
87
+ { CFormControlWrapper : 'CFormTextarea' } ,
88
+ ] ,
89
+ } ,
81
90
}
82
91
83
92
/**
@@ -86,7 +95,7 @@ const TEXT_REPLACEMENTS = {
86
95
* @param {string } text - The text to escape.
87
96
* @returns {string } - The escaped text.
88
97
*/
89
- function escapeMarkdown ( text ) {
98
+ const escapeMarkdown = ( text ) => {
90
99
if ( typeof text !== 'string' ) return text
91
100
return text
92
101
. replaceAll ( / ( < ) / g, String . raw `\$1` )
@@ -100,9 +109,8 @@ function escapeMarkdown(text) {
100
109
* @param {string } file - The absolute file path.
101
110
* @returns {string } - The relative filename.
102
111
*/
103
- function getRelativeFilename ( file ) {
104
- let relativePath
105
- relativePath = file . includes ( 'node_modules' )
112
+ const getRelativeFilename = ( file ) => {
113
+ let relativePath = file . includes ( 'node_modules' )
106
114
? path . relative ( path . join ( __dirname , '..' , '..' ) , file ) . replace ( 'coreui-' , '' )
107
115
: path . relative ( GLOBBY_OPTIONS . cwd , file ) . replace ( 'coreui-' , '' )
108
116
@@ -122,15 +130,15 @@ function getRelativeFilename(file) {
122
130
* @returns {string[] } An array of split parts, trimmed of whitespace.
123
131
* @throws {Error } Throws an error if there are unmatched braces or parentheses in the input.
124
132
*/
125
- function splitOutsideBracesAndParentheses ( input ) {
133
+ const splitOutsideBracesAndParentheses = ( input ) => {
126
134
if ( input . endsWith ( '...' ) ) {
127
135
return [ input ]
128
136
}
129
137
130
138
const parts = [ ]
131
139
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
134
142
135
143
for ( const char of input ) {
136
144
switch ( char ) {
@@ -161,38 +169,43 @@ function splitOutsideBracesAndParentheses(input) {
161
169
if ( braceDepth === 0 && parenthesisDepth === 0 && currentPart . trim ( ) ) {
162
170
parts . push ( currentPart . trim ( ) )
163
171
currentPart = ''
164
- continue // Skip adding the '|' to currentPart
172
+ continue
165
173
}
166
174
break
167
175
}
168
176
default : {
169
- // No action needed for other characters
170
177
break
171
178
}
172
179
}
173
180
currentPart += char
174
181
}
175
182
176
- // After processing all characters, check for unmatched opening braces or parentheses
177
183
if ( braceDepth !== 0 ) {
178
184
throw new Error ( 'Unmatched opening curly brace detected.' )
179
185
}
180
186
if ( parenthesisDepth !== 0 ) {
181
187
throw new Error ( 'Unmatched opening parenthesis detected.' )
182
188
}
183
189
184
- // Add the last accumulated part if it's not empty
185
190
if ( currentPart . trim ( ) ) {
186
191
parts . push ( currentPart . trim ( ) )
187
192
}
188
193
189
194
return parts
190
195
}
191
196
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 ) => {
193
206
const keyNames = Object . keys ( TEXT_REPLACEMENTS )
194
207
195
- if ( keyNames . includes ( componenName ) ) {
208
+ if ( keyNames . includes ( componenName ) && TEXT_REPLACEMENTS [ componenName ] [ keyName ] ) {
196
209
const replacements = TEXT_REPLACEMENTS [ componenName ] [ keyName ]
197
210
for ( const replacement of replacements ) {
198
211
for ( const [ key , value ] of Object . entries ( replacement ) ) {
@@ -210,17 +223,14 @@ function replaceText(componenName, keyName, text) {
210
223
* Creates an MDX file with the component's API documentation.
211
224
*
212
225
* @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.
214
227
*/
215
- async function createMdx ( file , component ) {
216
- if ( ! component ) {
217
- return
218
- }
228
+ const createMdx = async ( file , component ) => {
229
+ if ( ! component ) return
219
230
220
231
const filename = path . basename ( file , '.tsx' )
221
232
const relativeFilename = getRelativeFilename ( file )
222
233
223
- // Construct import statements
224
234
let content = `\n\`\`\`jsx\n`
225
235
const importPathParts = relativeFilename . split ( '/' )
226
236
if ( importPathParts . length > 1 ) {
@@ -230,11 +240,17 @@ async function createMdx(file, component) {
230
240
content += `import ${ component . displayName } from '@coreui/${ relativeFilename . replace ( '.tsx' , '' ) } '\n`
231
241
content += `\`\`\`\n\n`
232
242
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 ( ) ) {
238
254
if ( index === 0 ) {
239
255
content += `<div className="table-responsive table-api border rounded mb-3">\n`
240
256
content += ` <table className="table">\n`
@@ -248,20 +264,6 @@ async function createMdx(file, component) {
248
264
content += ` <tbody>\n`
249
265
}
250
266
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
-
265
267
// Skip props marked to be ignored
266
268
if ( propInfo . tags ?. ignore === '' ) {
267
269
continue
@@ -272,20 +274,19 @@ async function createMdx(file, component) {
272
274
? `<span className="badge bg-success">${ propInfo . tags . since } +</span>`
273
275
: ''
274
276
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>`
276
278
: ''
277
279
const description = propInfo . description
278
280
? replaceText ( component . displayName , 'description' , propInfo . description )
279
281
: '-'
280
-
281
282
const type = propInfo . type
282
283
? propInfo . type . name . includes ( 'ReactElement' )
283
284
? 'ReactElement'
284
285
: propInfo . type . name
285
286
: ''
286
287
const defaultValue = propInfo . defaultValue ? `\`${ propInfo . defaultValue . value } \`` : `undefined`
287
288
const example = propInfo . tags ?. example
288
- ? replaceText ( component . displayName , 'example' , propInfo . tags ? .example )
289
+ ? replaceText ( component . displayName , 'example' , propInfo . tags . example )
289
290
: false
290
291
291
292
// Format types as inline code
@@ -305,27 +306,24 @@ async function createMdx(file, component) {
305
306
content += ` <td colSpan="3">\n`
306
307
content += ` ${ converter
307
308
. makeHtml ( description )
308
- . replaceAll ( / < c o d e > ( .* ?) < \/ c o d e > / g, '<code>{`$1`}</code>' ) } \n`
309
+ . replaceAll ( / < c o d e > ( .* ?) < \/ c o d e > / g, '<code>{`$1`}</code>' )
310
+ . replaceAll ( / < c o d e > { ` & l t ; ( .* ?) & g t ; ` } < \/ c o d e > / g, '<code>{`<$1>`}</code>' ) } \n`
309
311
310
312
if ( example ) {
311
313
content += ` <JSXDocs code={\`${ example . trim ( ) } \`} />\n`
312
314
}
313
315
314
316
content += ` </td>\n`
315
317
content += ` </tr>\n`
316
-
317
- if ( isLast ) {
318
- content += ` </tbody>\n`
319
- content += ` </table>\n`
320
- content += `</div>\n`
321
- }
322
318
}
323
319
324
- // Define the output directory and ensure it exists
320
+ content += ` </tbody>\n`
321
+ content += ` </table>\n`
322
+ content += `</div>\n`
323
+
325
324
const outputDir = path . join ( 'content' , 'api' )
326
325
const outputPath = path . join ( outputDir , `${ filename } .api.mdx` )
327
326
328
- // Create the directory if it doesn't exist
329
327
try {
330
328
await mkdir ( outputDir , { recursive : true } )
331
329
await writeFile ( outputPath , content , { encoding : 'utf8' } )
@@ -336,19 +334,16 @@ async function createMdx(file, component) {
336
334
}
337
335
338
336
/**
339
- * Main function to execute the script .
337
+ * Main execution function .
340
338
*/
341
- async function main ( ) {
339
+ const main = async ( ) => {
342
340
try {
343
- // Retrieve all matching files based on the glob patterns
344
341
const files = await globby ( GLOB_PATTERNS , GLOBBY_OPTIONS )
345
342
346
- // Process each file concurrently
347
343
await Promise . all (
348
344
files . map ( async ( file ) => {
349
345
console . log ( `Processing file: ${ file } ` )
350
346
let components
351
-
352
347
try {
353
348
components = parse ( file , DOCGEN_OPTIONS )
354
349
} catch ( parseError ) {
0 commit comments