1
+ /**
2
+ * Enhanced Icon Connector Script
3
+ * Generates Figma connections for PatternFly React icons
4
+ */
1
5
import fs from 'fs/promises' ;
2
6
import path from 'path' ;
3
7
import getIconConfig from '../utils/config.mjs' ;
8
+ import {
9
+ extractIconNames ,
10
+ loadIconData ,
11
+ findIconByName ,
12
+ generateConnectStatement ,
13
+ generateSummary
14
+ } from '../utils/iconUtils.mjs' ;
4
15
5
16
// Get the configuration
6
17
const config = getIconConfig ( ) ;
7
18
const { logger } = config ;
8
19
9
20
/**
10
- * Extract icon names from import statement
11
- * @param {string } importStatement - The full import statement
12
- * @returns {string[] } Array of unique icon names
13
- */
14
- function extractIconNames ( importStatement ) {
15
- // Remove comments and extra whitespace
16
- const cleanImport = importStatement
17
- . replace ( / \/ \/ .* | \/ \* [ \s \S ] * ?\* \/ / g, '' )
18
- . replace ( / \s + / g, ' ' )
19
- . trim ( ) ;
20
-
21
- // Extract icons between { }
22
- const matchIcons = cleanImport . match ( / { \s * ( .+ ?) \s * } / ) ;
23
- if ( ! matchIcons ) {
24
- return [ ] ;
25
- }
26
-
27
- // Split icons and clean up
28
- return [
29
- ...new Set (
30
- matchIcons [ 1 ]
31
- . split ( ',' )
32
- . map ( ( icon ) => icon . trim ( ) )
33
- . filter ( ( icon ) => icon && ! icon . includes ( '=' ) && ! icon . startsWith ( 'Icon Size' ) && ! icon . includes ( '(' ) )
34
- )
35
- ] ;
36
- }
37
-
38
- /**
39
- * Generate Figma connections for icons
21
+ * Generate Figma connections for icons with enhanced error handling and performance tracking
40
22
*/
41
23
async function generateIconConnections ( ) {
24
+ // Track performance and statistics
25
+ const stats = {
26
+ startTime : Date . now ( ) ,
27
+ endTime : 0 ,
28
+ totalIcons : 0 ,
29
+ newIcons : 0 ,
30
+ updatedIcons : 0 ,
31
+ errors : 0
32
+ } ;
33
+
42
34
try {
43
- // Read the existing icons data
44
- const iconsDataPath = config . iconsDataPath ;
45
- let iconsData = [ ] ;
35
+ logger . info ( 'Starting icon connection generation' , {
36
+ source : 'IconConnector' ,
37
+ context : {
38
+ configPath : config . iconsDataPath ,
39
+ outputPath : path . resolve ( config . iconsFigmaDir , config . figmaOutputFile )
40
+ }
41
+ } ) ;
46
42
47
- try {
48
- const iconsDataContent = await fs . readFile ( iconsDataPath , 'utf8' ) ;
49
- iconsData = JSON . parse ( iconsDataContent ) ;
50
- } catch ( error ) {
51
- logger . warn ( 'Could not read icons data, using minimal configuration' , {
52
- source : 'IconConnectionGenerator' ,
53
- context : { path : iconsDataPath , error : error . message }
54
- } ) ;
55
- }
43
+ // Read the existing icons data
44
+ const iconsData = await loadIconData ( config . iconsDataPath , config , logger ) ;
56
45
57
- // Resolve the output file path using existing configuration
46
+ // Resolve the output file path
58
47
const connectionFilePath = path . resolve ( config . iconsFigmaDir , config . figmaOutputFile ) ;
48
+ logger . debug ( `Output file path: ${ connectionFilePath } ` , { source : 'IconConnector' } ) ;
59
49
60
- // Read the current icon connection file
61
- const existingContent = await fs . readFile ( connectionFilePath , 'utf8' ) . catch (
62
- ( ) => `import React from "react";
50
+ // Create output directory if it doesn't exist
51
+ const outputDir = path . dirname ( connectionFilePath ) ;
52
+ await fs . mkdir ( outputDir , { recursive : true } ) ;
53
+
54
+ // Default content for new files
55
+ const defaultContent = `import React from "react";
63
56
import {
64
57
AddCircleOIcon, AngleDoubleLeftIcon, AngleDoubleRightIcon, AngleDownIcon
65
58
} from "./generated";
@@ -69,68 +62,132 @@ import figma from "@figma/code-connect";
69
62
* -- This file was auto-generated by the figmaConnector script --
70
63
* This file connects icon components to their corresponding Figma components.
71
64
*/
72
- `
73
- ) ;
65
+ ` ;
66
+
67
+ // Read the current icon connection file
68
+ let existingContent ;
69
+ try {
70
+ existingContent = await fs . readFile ( connectionFilePath , 'utf8' ) ;
71
+ logger . debug ( 'Read existing connection file' , { source : 'IconConnector' } ) ;
72
+ } catch ( error ) {
73
+ logger . info ( 'No existing connection file, using default template' , {
74
+ source : 'IconConnector'
75
+ } ) ;
76
+ existingContent = defaultContent ;
77
+ }
74
78
75
79
// Extract unique icon names
76
- const uniqueIconNames = extractIconNames ( existingContent ) ;
80
+ const uniqueIconNames = extractIconNames ( existingContent , logger ) ;
81
+ stats . totalIcons = uniqueIconNames . length ;
82
+
83
+ if ( uniqueIconNames . length === 0 ) {
84
+ logger . warn ( 'No icon names found in existing file, using defaults' , {
85
+ source : 'IconConnector'
86
+ } ) ;
87
+ uniqueIconNames . push ( 'AddCircleOIcon' , 'AngleDoubleLeftIcon' ) ;
88
+ stats . totalIcons = uniqueIconNames . length ;
89
+ }
90
+
91
+ logger . info ( `Found ${ uniqueIconNames . length } unique icon names` , {
92
+ source : 'IconConnector'
93
+ } ) ;
77
94
78
95
// Generate import statement
79
96
const importStatement = `import {
80
97
${ uniqueIconNames . join ( ', ' ) }
81
98
} from "./generated";` ;
82
99
83
- // Generate connection content
100
+ // Create connections for each icon
101
+ const iconConnections = [ ] ;
102
+
103
+ for ( const iconName of uniqueIconNames ) {
104
+ try {
105
+ // Find matching icon configuration
106
+ const iconConfig = findIconByName ( iconName , iconsData ) ;
107
+
108
+ // Use URL from icon data or fallback to default
109
+ let url ;
110
+ if ( iconConfig ?. url ) {
111
+ url = iconConfig . url ;
112
+ stats . updatedIcons ++ ;
113
+ } else {
114
+ url = `${ config . figmaBaseUrl } ?node-id=${ config . defaultNodeId } &m=dev` ;
115
+ stats . newIcons ++ ;
116
+ logger . debug ( `No URL found for ${ iconName } , using default` , { source : 'IconConnector' } ) ;
117
+ }
118
+
119
+ // Generate connection statement
120
+ const connectStatement = generateConnectStatement ( iconName , url ) ;
121
+ iconConnections . push ( connectStatement ) ;
122
+ } catch ( error ) {
123
+ logger . error ( `Error generating connection for ${ iconName } ` , error , {
124
+ source : 'IconConnector'
125
+ } ) ;
126
+ stats . errors ++ ;
127
+ }
128
+ }
129
+
130
+ // Generate complete connection file content
84
131
const connectionContent = `import React from "react";
85
132
${ importStatement }
86
133
import figma from "@figma/code-connect";
87
134
88
135
/**
89
136
* -- This file was auto-generated by the figmaConnector script --
90
137
* This file connects icon components to their corresponding Figma components.
138
+ * Generated on: ${ new Date ( ) . toISOString ( ) }
91
139
*/
92
140
93
- ${ uniqueIconNames
94
- . map ( ( iconName ) => {
95
- // Try to find a matching icon in iconsData
96
- const iconConfig = iconsData . find (
97
- ( icon ) =>
98
- icon . reactName === iconName || icon . fileName . replace ( '-icon' , '' ) === iconName . replace ( 'Icon' , '' ) . toLowerCase ( )
99
- ) ;
100
-
101
- // Use the URL directly from iconsData if available
102
- const url = iconConfig ?. url || `${ config . figmaBaseUrl } ?node-id=${ config . defaultNodeId } &m=dev` ;
103
-
104
- // Use a more compact format for figma.connect()
105
- return `figma.connect(${ iconName } , "${ url } ", {
106
- props: {},
107
- example: (props) => <${ iconName } {...props} />
108
- });` ;
109
- } )
110
- . join ( '\n\n' ) }
141
+ ${ iconConnections . join ( '\n\n' ) }
111
142
` ;
112
143
113
- // Write the updated connection file
114
- await fs . writeFile ( connectionFilePath , connectionContent , 'utf8' ) ;
144
+ // Write the file unless in dry-run mode
145
+ if ( ! config . dryRun ) {
146
+ await fs . writeFile ( connectionFilePath , connectionContent , 'utf8' ) ;
147
+ logger . success ( `Generated icon connections file at ${ connectionFilePath } ` , {
148
+ source : 'IconConnector' ,
149
+ context : {
150
+ iconCount : uniqueIconNames . length
151
+ }
152
+ } ) ;
153
+ } else {
154
+ logger . info ( 'Dry run: would have written icon connections file' , {
155
+ source : 'IconConnector' ,
156
+ context : {
157
+ path : connectionFilePath ,
158
+ iconCount : uniqueIconNames . length
159
+ }
160
+ } ) ;
161
+ }
115
162
116
- logger . success ( 'Generated icon connections' , {
117
- source : 'IconConnectionGenerator' ,
118
- context : {
119
- uniqueIconCount : uniqueIconNames . length ,
120
- outputPath : connectionFilePath
121
- }
122
- } ) ;
163
+ // Calculate final statistics
164
+ stats . endTime = Date . now ( ) ;
165
+ logger . info ( generateSummary ( stats ) , { source : 'IconConnector' } ) ;
123
166
124
167
return true ;
125
168
} catch ( error ) {
169
+ stats . endTime = Date . now ( ) ;
170
+ stats . errors ++ ;
171
+
126
172
logger . error ( 'Error generating icon connections' , error , {
127
- source : 'IconConnectionGenerator '
173
+ source : 'IconConnector '
128
174
} ) ;
175
+
176
+ logger . info ( generateSummary ( stats ) , { source : 'IconConnector' } ) ;
129
177
return false ;
130
178
}
131
179
}
132
180
133
- // Run the script
134
- generateIconConnections ( )
135
- . then ( ( success ) => process . exit ( success ? 0 : 1 ) )
136
- . catch ( ( ) => process . exit ( 1 ) ) ;
181
+ // Execute directly if run as a script
182
+ if ( import . meta. url === `file://${ process . argv [ 1 ] } ` ) {
183
+ generateIconConnections ( )
184
+ . then ( ( success ) => process . exit ( success ? 0 : 1 ) )
185
+ . catch ( ( error ) => {
186
+ logger . error ( 'Unhandled error in icon generation' , error , {
187
+ source : 'IconConnector'
188
+ } ) ;
189
+ process . exit ( 1 ) ;
190
+ } ) ;
191
+ }
192
+
193
+ export default generateIconConnections ;
0 commit comments