1- import { existsSync , mkdirSync , rmSync } from ' node:fs' ;
2- import { readFile , readdir , writeFile } from ' node:fs/promises' ;
3- import { basename , join } from ' node:path' ;
4- import matter from ' gray-matter' ;
5-
6- const ROOT_DIR = join ( import . meta. dirname , ' ../../..' ) ;
7- const DOCS_DIR = join ( ROOT_DIR , ' apps/docs/content/docs' ) ;
8- const COMPONENTS_DIR = join ( DOCS_DIR , ' components' ) ;
9- const EXAMPLES_DIR = join ( ROOT_DIR , ' packages/examples/src' ) ;
10- const SKILLS_DIR = join ( ROOT_DIR , ' skills' ) ;
11- const SKILL_DIR = join ( SKILLS_DIR , ' ai-elements' ) ;
1+ import { existsSync , mkdirSync , rmSync } from " node:fs" ;
2+ import { readdir , readFile , writeFile } from " node:fs/promises" ;
3+ import { basename , join } from " node:path" ;
4+ import matter from " gray-matter" ;
5+
6+ const ROOT_DIR = join ( import . meta. dirname , " ../../.." ) ;
7+ const DOCS_DIR = join ( ROOT_DIR , " apps/docs/content/docs" ) ;
8+ const COMPONENTS_DIR = join ( DOCS_DIR , " components" ) ;
9+ const EXAMPLES_DIR = join ( ROOT_DIR , " packages/examples/src" ) ;
10+ const SKILLS_DIR = join ( ROOT_DIR , " skills" ) ;
11+ const SKILL_DIR = join ( SKILLS_DIR , " ai-elements" ) ;
1212
1313const discoverMdxFiles = async ( dir : string ) : Promise < string [ ] > => {
1414 const results : string [ ] = [ ] ;
@@ -18,7 +18,7 @@ const discoverMdxFiles = async (dir: string): Promise<string[]> => {
1818 const fullPath = join ( dir , entry . name ) ;
1919 if ( entry . isDirectory ( ) ) {
2020 results . push ( ...( await discoverMdxFiles ( fullPath ) ) ) ;
21- } else if ( entry . name . endsWith ( ' .mdx' ) ) {
21+ } else if ( entry . name . endsWith ( " .mdx" ) ) {
2222 results . push ( fullPath ) ;
2323 }
2424 }
@@ -35,18 +35,25 @@ const replacePreviews = (content: string): string => {
3535
3636const removeCustomComponents = ( content : string ) : string => {
3737 return content
38- . replace ( / < E l e m e n t s I n s t a l l e r \s * \/ > / g, '' )
39- . replace ( / < E l e m e n t s D e m o \s * \/ > / g, '' )
40- . replace ( / < C a l l o u t > \s * [ \s \S ] * ?< \/ C a l l o u t > / g, '' ) ;
38+ . replace ( / < E l e m e n t s I n s t a l l e r \s * \/ > / g, "" )
39+ . replace ( / < E l e m e n t s D e m o \s * \/ > / g, "" )
40+ . replace ( / < C a l l o u t > \s * [ \s \S ] * ?< \/ C a l l o u t > / g, "" ) ;
4141} ;
4242
4343const replaceInstaller = ( content : string ) : string => {
4444 return content . replace (
4545 / < E l e m e n t s I n s t a l l e r \s + p a t h = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] \s * \/ > / g,
46- ( _ , component ) => '```bash\nnpx ai-elements@latest add ' + component + '\n```'
46+ ( _ , component ) =>
47+ `\`\`\`bash\nnpx ai-elements@latest add ${ component } \n\`\`\``
4748 ) ;
4849} ;
4950
51+ const PROP_REGEX = / [ ' " ] ? ( [ ^ ' " : \s ] + ) [ ' " ] ? \s * : \s * \{ ( [ ^ } ] + ) \} / g;
52+ const DESC_REGEX = / d e s c r i p t i o n : \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / ;
53+ const TYPE_REGEX = / t y p e : \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / ;
54+ const DEFAULT_REGEX = / d e f a u l t : \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / ;
55+ const REQUIRED_REGEX = / r e q u i r e d : \s * t r u e / ;
56+
5057const parseTypeTableProps = (
5158 typeContent : string
5259) : Array < {
@@ -64,22 +71,21 @@ const parseTypeTableProps = (
6471 default ?: string ;
6572 } > = [ ] ;
6673
67- const propRegex = / [ ' " ] ? ( [ ^ ' " : \s ] + ) [ ' " ] ? \s * : \s * \{ ( [ ^ } ] + ) \} / g;
68- let match ;
74+ const matches = typeContent . matchAll ( PROP_REGEX ) ;
6975
70- while ( ( match = propRegex . exec ( typeContent ) ) !== null ) {
76+ for ( const match of matches ) {
7177 const propName = match [ 1 ] ;
7278 const propBody = match [ 2 ] ;
7379
74- const descMatch = propBody . match ( / d e s c r i p t i o n : \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / ) ;
75- const typeMatch = propBody . match ( / t y p e : \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / ) ;
76- const defaultMatch = propBody . match ( / d e f a u l t : \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / ) ;
77- const requiredMatch = propBody . match ( / r e q u i r e d : \s * t r u e / ) ;
80+ const descMatch = propBody . match ( DESC_REGEX ) ;
81+ const typeMatch = propBody . match ( TYPE_REGEX ) ;
82+ const defaultMatch = propBody . match ( DEFAULT_REGEX ) ;
83+ const requiredMatch = propBody . match ( REQUIRED_REGEX ) ;
7884
7985 props . push ( {
8086 name : propName ,
81- type : typeMatch ?. [ 1 ] || ' unknown' ,
82- description : descMatch ?. [ 1 ] || '' ,
87+ type : typeMatch ?. [ 1 ] || " unknown" ,
88+ description : descMatch ?. [ 1 ] || "" ,
8389 required : ! ! requiredMatch ,
8490 default : defaultMatch ?. [ 1 ] ,
8591 } ) ;
@@ -95,30 +101,31 @@ const replaceTypeTables = (content: string): string => {
95101 const props = parseTypeTableProps ( typeContent ) ;
96102
97103 if ( props . length === 0 ) {
98- return '' ;
104+ return "" ;
99105 }
100106
101107 const rows = props . map ( ( prop ) => {
102108 const name = `\`${ prop . name } \`` ;
103109 const type = `\`${ prop . type } \`` ;
104- const defaultVal = prop . required
105- ? 'Required'
106- : prop . default
107- ? `\`${ prop . default } \``
108- : '-' ;
110+ let defaultVal = "-" ;
111+ if ( prop . required ) {
112+ defaultVal = "Required" ;
113+ } else if ( prop . default ) {
114+ defaultVal = `\`${ prop . default } \`` ;
115+ }
109116 return `| ${ name } | ${ type } | ${ defaultVal } | ${ prop . description } |` ;
110117 } ) ;
111118
112119 return [
113- ' | Prop | Type | Default | Description |' ,
114- ' |------|------|---------|-------------|' ,
120+ " | Prop | Type | Default | Description |" ,
121+ " |------|------|---------|-------------|" ,
115122 ...rows ,
116- ] . join ( '\n' ) ;
123+ ] . join ( "\n" ) ;
117124 } ) ;
118125} ;
119126
120127const removeCallouts = ( content : string ) : string => {
121- return content . replace ( / < C a l l o u t [ ^ > ] * > [ \s \S ] * ?< \/ C a l l o u t > / g, '' ) ;
128+ return content . replace ( / < C a l l o u t [ ^ > ] * > [ \s \S ] * ?< \/ C a l l o u t > / g, "" ) ;
122129} ;
123130
124131const transformComponentMdx = ( fileContent : string ) : string => {
@@ -147,9 +154,9 @@ const findMatchingExamples = async (
147154 const files = await readdir ( EXAMPLES_DIR ) ;
148155
149156 return files . filter ( ( file ) => {
150- const fileBasename = file . replace ( ' .tsx' , '' ) ;
157+ const fileBasename = file . replace ( " .tsx" , "" ) ;
151158 return (
152- file . endsWith ( ' .tsx' ) &&
159+ file . endsWith ( " .tsx" ) &&
153160 ( fileBasename === componentName ||
154161 fileBasename . startsWith ( `${ componentName } -` ) )
155162 ) ;
@@ -164,17 +171,13 @@ const cleanSkillsDir = (): void => {
164171} ;
165172
166173const generateOverviewSkill = async ( ) : Promise < void > => {
167- const indexContent = await readFile ( join ( DOCS_DIR , ' index.mdx' ) , ' utf-8' ) ;
168- const usageContent = await readFile ( join ( DOCS_DIR , ' usage.mdx' ) , ' utf-8' ) ;
174+ const indexContent = await readFile ( join ( DOCS_DIR , " index.mdx" ) , " utf-8" ) ;
175+ const usageContent = await readFile ( join ( DOCS_DIR , " usage.mdx" ) , " utf-8" ) ;
169176 const troubleshootingContent = await readFile (
170- join ( DOCS_DIR , ' troubleshooting.mdx' ) ,
171- ' utf-8'
177+ join ( DOCS_DIR , " troubleshooting.mdx" ) ,
178+ " utf-8"
172179 ) ;
173180
174- const indexData = matter ( indexContent ) ;
175- const usageData = matter ( usageContent ) ;
176- const troubleshootingData = matter ( troubleshootingContent ) ;
177-
178181 const skillContent = `---
179182name: ai-elements
180183description: Create new AI chat interface components for the ai-elements library following established composable patterns, shadcn/ui integration, and Vercel AI SDK conventions. Use when creating new components in packages/elements/src or when the user asks to add a new component to ai-elements.
@@ -198,16 +201,16 @@ See the \`references/\` folder for detailed documentation on each component.
198201` ;
199202
200203 mkdirSync ( SKILL_DIR , { recursive : true } ) ;
201- await writeFile ( join ( SKILL_DIR , ' SKILL.md' ) , skillContent ) ;
202- console . log ( ' Generated: SKILL.md (overview)' ) ;
204+ await writeFile ( join ( SKILL_DIR , " SKILL.md" ) , skillContent ) ;
205+ console . log ( " Generated: SKILL.md (overview)" ) ;
203206} ;
204207
205208const processComponent = async ( mdxPath : string ) : Promise < number > => {
206- const componentName = basename ( mdxPath , ' .mdx' ) ;
207- const referencesDir = join ( SKILL_DIR , ' references' ) ;
208- const scriptsDir = join ( SKILL_DIR , ' scripts' ) ;
209+ const componentName = basename ( mdxPath , " .mdx" ) ;
210+ const referencesDir = join ( SKILL_DIR , " references" ) ;
211+ const scriptsDir = join ( SKILL_DIR , " scripts" ) ;
209212
210- const fileContent = await readFile ( mdxPath , ' utf-8' ) ;
213+ const fileContent = await readFile ( mdxPath , " utf-8" ) ;
211214 const { data } = matter ( fileContent ) ;
212215
213216 const referenceContent = `# ${ data . title }
@@ -225,20 +228,25 @@ ${transformComponentMdx(fileContent)}
225228 if ( examples . length > 0 ) {
226229 mkdirSync ( scriptsDir , { recursive : true } ) ;
227230 for ( const example of examples ) {
228- const exampleContent = await readFile ( join ( EXAMPLES_DIR , example ) , 'utf-8' ) ;
231+ const exampleContent = await readFile (
232+ join ( EXAMPLES_DIR , example ) ,
233+ "utf-8"
234+ ) ;
229235 const transformedContent = exampleContent
230- . replace ( / @ r e p o \/ s h a d c n - u i \/ / g, '@/' )
231- . replace ( / @ r e p o \/ e l e m e n t s \/ / g, ' @/components/ai-elements/' ) ;
236+ . replace ( / @ r e p o \/ s h a d c n - u i \/ / g, "@/" )
237+ . replace ( / @ r e p o \/ e l e m e n t s \/ / g, " @/components/ai-elements/" ) ;
232238 await writeFile ( join ( scriptsDir , example ) , transformedContent ) ;
233239 }
234240 }
235241
236- console . log ( `Generated: references/${ componentName } .md (${ examples . length } examples)` ) ;
242+ console . log (
243+ `Generated: references/${ componentName } .md (${ examples . length } examples)`
244+ ) ;
237245 return examples . length ;
238246} ;
239247
240248const main = async ( ) : Promise < void > => {
241- console . log ( ' Generating ai-elements skill from docs and examples...\n' ) ;
249+ console . log ( " Generating ai-elements skill from docs and examples...\n" ) ;
242250
243251 cleanSkillsDir ( ) ;
244252
@@ -252,7 +260,9 @@ const main = async (): Promise<void> => {
252260 totalExamples += await processComponent ( mdxPath ) ;
253261 }
254262
255- console . log ( `\nDone! Generated ${ mdxFiles . length } references with ${ totalExamples } examples.` ) ;
263+ console . log (
264+ `\nDone! Generated ${ mdxFiles . length } references with ${ totalExamples } examples.`
265+ ) ;
256266} ;
257267
258268main ( ) . catch ( console . error ) ;
0 commit comments