From 7b4640839c3b0207cc2395986652c3bc33ff8a97 Mon Sep 17 00:00:00 2001 From: Brian Heston <47367562+bheston@users.noreply.github.com> Date: Tue, 12 May 2026 12:52:10 -0400 Subject: [PATCH 1/2] AUI Figma: Added `components` CLI option and fixed processing issues --- package-lock.json | 15 +- .../src/cli/main.ts | 129 +++++++++++++----- .../src/lib/node-parser.ts | 17 ++- 3 files changed, 119 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index c05842da..266538ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17072,9 +17072,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001752", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001752.tgz", - "integrity": "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g==", + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", "funding": [ { "type": "opencollective", @@ -17088,7 +17088,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capture-exit": { "version": "2.0.0", @@ -49404,9 +49405,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001752", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001752.tgz", - "integrity": "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g==" + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==" }, "capture-exit": { "version": "2.0.0", diff --git a/packages/adaptive-ui-designer-figma/src/cli/main.ts b/packages/adaptive-ui-designer-figma/src/cli/main.ts index 16691562..c16c1f77 100644 --- a/packages/adaptive-ui-designer-figma/src/cli/main.ts +++ b/packages/adaptive-ui-designer-figma/src/cli/main.ts @@ -17,13 +17,60 @@ program .name(programName) .description('A CLI tool for generating CSS stylesheets from Figma Library Components') .requiredOption('-l, --library ', 'Path to the library configuration file.') + .option('-c, --components ', 'Comma-delimited list or wildcard pattern of components to generate (e.g., "anchor,button" or "content navigation *")') .action(main); interface ProgramOptions { library: string; + components?: string; } -async function main({ library }: ProgramOptions) { +/** + * Matches a component name against a pattern that may include wildcards. + * @param componentName - The component name to test + * @param pattern - The pattern to match against (supports * wildcard) + * @returns true if the component name matches the pattern + */ +function matchesPattern(componentName: string, pattern: string): boolean { + const normalizedComponent = componentName.toLowerCase().trim(); + const normalizedPattern = pattern.toLowerCase().trim(); + + // Convert wildcard pattern to regex + const regexPattern = normalizedPattern + .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except * + .replace(/\*/g, '.*'); // Convert * to .* + + const regex = new RegExp(`^${regexPattern}$`); + return regex.test(normalizedComponent); +} + +/** + * Filters component names based on comma-delimited patterns or wildcards. + * @param componentNames - Array of all available component names + * @param filterString - Comma-delimited patterns (e.g., "button,card" or "content navigation *") + * @returns Array of matching component names + */ +function filterComponentNames(componentNames: string[], filterString: string): string[] { + const patterns = filterString.split(',').map(p => p.trim()).filter(p => p.length > 0); + + if (patterns.length === 0) { + return componentNames; + } + + const matchedNames = new Set(); + + for (const pattern of patterns) { + for (const componentName of componentNames) { + if (matchesPattern(componentName, pattern)) { + matchedNames.add(componentName); + } + } + } + + return Array.from(matchedNames).sort(alphabetize); +} + +async function main({ library, components }: ProgramOptions) { const configPath = path.resolve(process.cwd(), library); logger.neutral('Validating library config file: ' + configPath); const libraryConfig = await LibraryConfig.create(configPath); @@ -60,14 +107,14 @@ async function main({ library }: ProgramOptions) { const libraryComponentSetsResponse = await client.getFileComponentSets(libraryConfig.file); if (libraryComponentSetsResponse.error || libraryComponentSetsResponse.status !== 200) { - logger.fail(`Accessing Figma library component sets failed with status code ${libraryComponentSetsResponse.status}`); + logger.fail(`Accessing Figma library component sets failed with status code ${libraryComponentSetsResponse.status}: ${(libraryComponentSetsResponse as any).err}`); process.exit(1); } const libraryComponentsResponse = await client.getFileComponents(libraryConfig.file); if (libraryComponentsResponse.error || libraryComponentsResponse.status !== 200) { - logger.fail(`Accessing Figma library components failed with status code ${libraryComponentsResponse.status}`); + logger.fail(`Accessing Figma library components failed with status code ${libraryComponentsResponse.status}: ${(libraryComponentsResponse as any).err}`); process.exit(1); } @@ -84,32 +131,48 @@ async function main({ library }: ProgramOptions) { // Also filter out components which aren't in a container frame (assume they are helper/utility for now) const hasContainingFrame = component.containing_frame !== undefined; - return !hasComponentSet && !hasContainingFrame; + return !hasComponentSet && hasContainingFrame; }); const allComponents = libraryComponentSets.concat(uniqueComponents); const componentNames = allComponents.map((value) => value.name).sort(alphabetize); - const pickComponentsRequest = { - type: 'list', - name: 'all', - message: 'Which component stylesheets would you like to generate?', - choices: ['All', 'Choose which'], - }; - - const pickComponentsResponse = await inquirer.prompt([pickComponentsRequest]); - const componentNamesToRender: string[] = []; - - if (pickComponentsResponse.all !== 'All') { - const chooseComponentsRequest = { - type: 'checkbox', - name: 'which', - message: 'Choose components:', - choices: componentNames, - }; - const components = await inquirer.prompt([chooseComponentsRequest]); - componentNamesToRender.push(...components.which); + let componentNamesToRender: string[] = []; + + // If components filter is provided via CLI, use it directly + if (components) { + componentNamesToRender = filterComponentNames(componentNames, components); + + if (componentNamesToRender.length === 0) { + logger.fail(`No components matched the pattern: "${components}"`); + logger.neutral(`Available components:\n${componentNames.join(', ')}`); + process.exit(1); + } + + logger.success(`Found ${componentNamesToRender.length} component${componentNamesToRender.length === 1 ? '' : 's'} matching "${components}":`); + logger.neutral(componentNamesToRender.join(', ')); } else { - componentNamesToRender.push(...componentNames); + // Interactive mode when no -c parameter is provided + const pickComponentsRequest = { + type: 'list', + name: 'all', + message: 'Which component stylesheets would you like to generate?', + choices: ['All', 'Choose which'], + }; + + const pickComponentsResponse = await inquirer.prompt([pickComponentsRequest]); + + if (pickComponentsResponse.all !== 'All') { + const chooseComponentsRequest = { + type: 'checkbox', + name: 'which', + message: 'Choose components:', + choices: componentNames, + }; + const chosenComponents = await inquirer.prompt([chooseComponentsRequest]); + componentNamesToRender.push(...chosenComponents.which); + } else { + componentNamesToRender.push(...componentNames); + } } if (componentNamesToRender.length === 0) { @@ -123,10 +186,17 @@ async function main({ library }: ProgramOptions) { }\n${componentNamesToRender.join(', ')}` ); - const confirm = await inquirer.prompt({ type: 'confirm', message: 'Would you like to continue?', name: 'confirm' }); + // Only prompt for confirmation in interactive mode + if (!components) { + const confirm = await inquirer.prompt({ type: 'confirm', message: 'Would you like to continue?', name: 'confirm' }); + + if (!confirm.confirm) { + logger.neutral(`Exiting ${programName}`); + process.exit(0); + } + } - if (confirm.confirm) { - logger.success('Generating component stylesheets. This may take a moment.'); + logger.success('Generating component stylesheets. This may take a moment.'); const nameLookup = new Set(componentNamesToRender); const componentsToRender = allComponents.filter((value) => { @@ -159,11 +229,6 @@ async function main({ library }: ProgramOptions) { }) ); - // process components - } else { - logger.neutral(`Exiting ${programName}`); - } - process.exit(0); } diff --git a/packages/adaptive-ui-designer-figma/src/lib/node-parser.ts b/packages/adaptive-ui-designer-figma/src/lib/node-parser.ts index 5a848ac3..46e70e34 100644 --- a/packages/adaptive-ui-designer-figma/src/lib/node-parser.ts +++ b/packages/adaptive-ui-designer-figma/src/lib/node-parser.ts @@ -48,9 +48,20 @@ export function parseNode(node: FigmaRestAPI.Node): PluginUINodeData { const appliedDesignTokens: AppliedDesignTokens = appliedTokensPluginData ? deserializeMap(appliedTokensPluginData) : new AppliedDesignTokens(); - const appliedStyleModules: AppliedStyleModules = appliedStylesPluginData - ? JSON.parse(appliedStylesPluginData) - : new AppliedStyleModules(); + + // Parse appliedStyleModules and ensure it's an array + let appliedStyleModules: AppliedStyleModules = new AppliedStyleModules(); + if (appliedStylesPluginData) { + try { + const parsed = JSON.parse(appliedStylesPluginData); + // Ensure we have an array - if parsed data is not an array, ignore it + if (Array.isArray(parsed)) { + appliedStyleModules = new AppliedStyleModules(...parsed); + } + } catch (e) { + console.warn('Failed to parse appliedStyleModules:', e); + } + } return { id: node.id, From 38a558464b8b897b7f1c2cc0f686fd33415625be Mon Sep 17 00:00:00 2001 From: Brian Heston <47367562+bheston@users.noreply.github.com> Date: Tue, 12 May 2026 12:54:58 -0400 Subject: [PATCH 2/2] changes --- .changeset/silent-trains-spend.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silent-trains-spend.md diff --git a/.changeset/silent-trains-spend.md b/.changeset/silent-trains-spend.md new file mode 100644 index 00000000..fd842605 --- /dev/null +++ b/.changeset/silent-trains-spend.md @@ -0,0 +1,5 @@ +--- +"@adaptive-web/adaptive-ui-designer-figma": patch +--- + +Added `components` CLI option and fixed processing issues