@@ -126,7 +126,10 @@ function parseParametersSection(content, sectionName) {
126126
127127 // Extract default value
128128 let defaultValue = "*undefined*" ;
129- // Match default: followed by value, handling functions, strings, numbers, etc.
129+ // Regex explanation:
130+ // \bdefault\s*:\s* - Match "default:" with optional whitespace
131+ // ([\s\S]*?) - Capture the value (non-greedy, including newlines)
132+ // (?:,\s*(?:\w+\s*:|$)|$) - Stop at: comma followed by another property, or end of string
130133 const defaultMatch = propertyDef . match ( / \b d e f a u l t \s * : \s * ( [ \s \S ] * ?) (?: , \s * (?: \w + \s * : | $ ) | $ ) / ) ;
131134
132135 if ( defaultMatch ) {
@@ -160,7 +163,21 @@ function parseParametersSection(content, sectionName) {
160163 }
161164 }
162165
163- const displayType = isArray ? `array of ${ type } s` : type ;
166+ // Format array types properly (e.g., "array of strings" not "array of strings")
167+ let displayType = type ;
168+ if ( isArray ) {
169+ // Handle proper pluralization for different types
170+ const pluralForms = {
171+ string : "strings" ,
172+ boolean : "booleans" ,
173+ numeric : "numbers" ,
174+ function : "functions" ,
175+ object : "objects" ,
176+ "HTML string" : "HTML strings" ,
177+ } ;
178+ const pluralType = pluralForms [ type ] || `${ type } s` ;
179+ displayType = `array of ${ pluralType } ` ;
180+ }
164181
165182 params . push ( {
166183 name : propertyName ,
@@ -294,10 +311,14 @@ function findExamples(pluginName, rootDir) {
294311}
295312
296313/**
297- * Extracts JavaScript code from an example HTML file
314+ * Extracts JavaScript code from an example HTML file.
315+ * Note: This function parses trusted example files from the jsPsych repository,
316+ * not arbitrary user-provided HTML. The regex patterns are sufficient for
317+ * parsing well-formed HTML from our own examples.
298318 */
299319function extractExampleCode ( htmlContent ) {
300320 // Find script tags that contain jsPsych code (not src imports)
321+ // Note: This regex is for parsing trusted internal example files, not for security-critical HTML sanitization
301322 const scriptMatch = htmlContent . match ( / < s c r i p t [ ^ > ] * > (? ! [ \s \S ] * s r c = ) ( [ \s \S ] * ?) < \/ s c r i p t > / gi) ;
302323
303324 if ( scriptMatch ) {
@@ -331,8 +352,12 @@ function generateParametersTable(params) {
331352 table += "----------|------|---------------|------------\n" ;
332353
333354 for ( const param of params ) {
334- // Escape pipe characters in description
335- const description = param . description . replace ( / \| / g, "\\|" ) . replace ( / \n / g, " " ) ;
355+ // Escape special markdown characters in description for table cell
356+ // First escape backslashes, then pipes, and convert newlines to spaces
357+ const description = param . description
358+ . replace ( / \\ / g, "\\\\" )
359+ . replace ( / \| / g, "\\|" )
360+ . replace ( / \n / g, " " ) ;
336361 table += `${ param . name } | ${ param . type } | ${ param . default } | ${ description } \n` ;
337362 }
338363
@@ -354,8 +379,12 @@ function generateDataTable(data) {
354379 table += "-----|------|------\n" ;
355380
356381 for ( const item of data ) {
357- // Escape pipe characters in description
358- const description = item . description . replace ( / \| / g, "\\|" ) . replace ( / \n / g, " " ) ;
382+ // Escape special markdown characters in description for table cell
383+ // First escape backslashes, then pipes, and convert newlines to spaces
384+ const description = item . description
385+ . replace ( / \\ / g, "\\\\" )
386+ . replace ( / \| / g, "\\|" )
387+ . replace ( / \n / g, " " ) ;
359388 table += `${ item . name } | ${ item . type } | ${ description } \n` ;
360389 }
361390
@@ -465,6 +494,21 @@ function getAllPluginNames() {
465494 . map ( ( p ) => p . replace ( "plugin-" , "" ) ) ;
466495}
467496
497+ /**
498+ * Get argument value for a flag (supports both long and short forms)
499+ */
500+ function getArgValue ( args , longFlag , shortFlag ) {
501+ const longIndex = args . indexOf ( longFlag ) ;
502+ if ( longIndex !== - 1 && args [ longIndex + 1 ] && ! args [ longIndex + 1 ] . startsWith ( "-" ) ) {
503+ return args [ longIndex + 1 ] ;
504+ }
505+ const shortIndex = args . indexOf ( shortFlag ) ;
506+ if ( shortIndex !== - 1 && args [ shortIndex + 1 ] && ! args [ shortIndex + 1 ] . startsWith ( "-" ) ) {
507+ return args [ shortIndex + 1 ] ;
508+ }
509+ return null ;
510+ }
511+
468512/**
469513 * Main entry point
470514 */
@@ -497,22 +541,14 @@ Examples:
497541 }
498542
499543 let pluginNames = [ ] ;
500- let outputDir = null ;
501544
502545 if ( args . includes ( "--all" ) ) {
503546 pluginNames = getAllPluginNames ( ) ;
504547 } else {
505548 pluginNames = args . filter ( ( arg ) => ! arg . startsWith ( "-" ) ) ;
506549 }
507550
508- const outputIndex = args . indexOf ( "--output" ) ;
509- if ( outputIndex !== - 1 && args [ outputIndex + 1 ] ) {
510- outputDir = args [ outputIndex + 1 ] ;
511- }
512- const outputIndexShort = args . indexOf ( "-o" ) ;
513- if ( outputIndexShort !== - 1 && args [ outputIndexShort + 1 ] ) {
514- outputDir = args [ outputIndexShort + 1 ] ;
515- }
551+ const outputDir = getArgValue ( args , "--output" , "-o" ) ;
516552
517553 if ( pluginNames . length === 0 ) {
518554 console . error ( "No plugins specified. Use --help for usage information." ) ;
@@ -556,5 +592,8 @@ export {
556592 parameterTypeMap ,
557593} ;
558594
559- // Run main if this is the entry point
560- main ( ) ;
595+ // Run main if this is the entry point (i.e., script executed directly)
596+ const isMainModule = import . meta. url === `file://${ process . argv [ 1 ] } ` ;
597+ if ( isMainModule ) {
598+ main ( ) ;
599+ }
0 commit comments