Skip to content

Commit 0670ec3

Browse files
committed
feat(icon-generator): rebase
1 parent f9674bd commit 0670ec3

File tree

15 files changed

+357
-229
lines changed

15 files changed

+357
-229
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ Please ensure that all React UI components contributed meet the following guidel
159159
### Code consistency
160160

161161
- All files and folders under your package's `src/components` should name with PascalCase except `index.js` files
162-
- If you need a constant file, it should be called `{Component_Name}constants.js` (Component_Name with PascalCase)
162+
- If you need a constant file, it should be called `{Component_Name}Constants.js` (Component_Name with PascalCase)
163163
- Each component should treat as a standalone package and live under its own folder. If you think your component can be made completely independent from other components, create it as a new package. This reduces the dependency tree and improves performance downstream. It also makes exported bundles lighter.
164164
- Single file per component with **default export**
165165
- Avoid using the bindMethods syntax for attaching methods to a class. Instead use class properties for example `testMethod = () => { return 'test'}`
@@ -329,5 +329,4 @@ The process for revoking someone's maintainer status is a discussion limited to
329329

330330
[1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/
331331
[2]: mailto:patternfly@redhat.com
332-
[3]: https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#type
333-
332+
[3]: https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#type
Lines changed: 139 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,58 @@
1+
/**
2+
* Enhanced Icon Connector Script
3+
* Generates Figma connections for PatternFly React icons
4+
*/
15
import fs from 'fs/promises';
26
import path from 'path';
37
import getIconConfig from '../utils/config.mjs';
8+
import {
9+
extractIconNames,
10+
loadIconData,
11+
findIconByName,
12+
generateConnectStatement,
13+
generateSummary
14+
} from '../utils/iconUtils.mjs';
415

516
// Get the configuration
617
const config = getIconConfig();
718
const { logger } = config;
819

920
/**
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
4022
*/
4123
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+
4234
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+
});
4642

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);
5645

57-
// Resolve the output file path using existing configuration
46+
// Resolve the output file path
5847
const connectionFilePath = path.resolve(config.iconsFigmaDir, config.figmaOutputFile);
48+
logger.debug(`Output file path: ${connectionFilePath}`, { source: 'IconConnector' });
5949

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";
6356
import {
6457
AddCircleOIcon, AngleDoubleLeftIcon, AngleDoubleRightIcon, AngleDownIcon
6558
} from "./generated";
@@ -69,68 +62,132 @@ import figma from "@figma/code-connect";
6962
* -- This file was auto-generated by the figmaConnector script --
7063
* This file connects icon components to their corresponding Figma components.
7164
*/
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+
}
7478

7579
// 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+
});
7794

7895
// Generate import statement
7996
const importStatement = `import {
8097
${uniqueIconNames.join(', ')}
8198
} from "./generated";`;
8299

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
84131
const connectionContent = `import React from "react";
85132
${importStatement}
86133
import figma from "@figma/code-connect";
87134
88135
/**
89136
* -- This file was auto-generated by the figmaConnector script --
90137
* This file connects icon components to their corresponding Figma components.
138+
* Generated on: ${new Date().toISOString()}
91139
*/
92140
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')}
111142
`;
112143

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+
}
115162

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' });
123166

124167
return true;
125168
} catch (error) {
169+
stats.endTime = Date.now();
170+
stats.errors++;
171+
126172
logger.error('Error generating icon connections', error, {
127-
source: 'IconConnectionGenerator'
173+
source: 'IconConnector'
128174
});
175+
176+
logger.info(generateSummary(stats), { source: 'IconConnector' });
129177
return false;
130178
}
131179
}
132180

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

Comments
 (0)