@salesforce/b2c-dx-mcp 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -370
- package/content/pwav3/components.md +400 -0
- package/content/pwav3/config.md +124 -0
- package/content/pwav3/data-fetching.md +213 -0
- package/content/pwav3/extensibility.md +167 -0
- package/content/pwav3/i18n.md +214 -0
- package/content/pwav3/quick-reference.md +169 -0
- package/content/pwav3/routing.md +107 -0
- package/content/pwav3/state-management.md +193 -0
- package/content/pwav3/styling.md +248 -0
- package/content/pwav3/testing.md +124 -0
- package/content/site-theming/theming-accessibility.md +126 -0
- package/content/site-theming/theming-questions.md +208 -0
- package/content/site-theming/theming-validation.md +174 -0
- package/dist/commands/mcp.d.ts +3 -3
- package/dist/commands/mcp.js +7 -7
- package/dist/registry.js +1 -1
- package/dist/services.d.ts +15 -15
- package/dist/services.js +21 -14
- package/dist/tools/adapter.d.ts +2 -2
- package/dist/tools/adapter.js +2 -2
- package/dist/tools/cartridges/index.js +1 -6
- package/dist/tools/index.d.ts +1 -4
- package/dist/tools/index.js +1 -4
- package/dist/tools/mrt/index.js +4 -9
- package/dist/tools/pwav3/index.d.ts +12 -3
- package/dist/tools/pwav3/index.js +5 -63
- package/dist/tools/pwav3/pwa-kit-development-guidelines.d.ts +9 -0
- package/dist/tools/pwav3/pwa-kit-development-guidelines.js +151 -0
- package/dist/tools/scapi/index.d.ts +1 -1
- package/dist/tools/scapi/index.js +6 -1
- package/dist/tools/scapi/scapi-custom-api-scaffold.d.ts +60 -0
- package/dist/tools/scapi/scapi-custom-api-scaffold.js +175 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +24 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +53 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +42 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/index.js +325 -0
- package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +40 -0
- package/dist/tools/storefrontnext/figma/generate-component/decision.js +312 -0
- package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +9 -0
- package/dist/tools/storefrontnext/figma/generate-component/formatter.js +92 -0
- package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +114 -0
- package/dist/tools/storefrontnext/figma/generate-component/index.js +98 -0
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +71 -0
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +260 -0
- package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +61 -0
- package/dist/tools/storefrontnext/figma/map-tokens/index.js +234 -0
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +65 -0
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +268 -0
- package/dist/tools/storefrontnext/index.d.ts +17 -0
- package/dist/tools/storefrontnext/index.js +10 -60
- package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +15 -0
- package/dist/tools/storefrontnext/page-designer-decorator/index.js +3 -3
- package/dist/tools/storefrontnext/{developer-guidelines.js → sfnext-development-guidelines.js} +3 -3
- package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +92 -0
- package/dist/tools/storefrontnext/site-theming/color-contrast.js +186 -0
- package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +16 -0
- package/dist/tools/storefrontnext/site-theming/color-mapping.js +131 -0
- package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +11 -0
- package/dist/tools/storefrontnext/site-theming/guidance-merger.js +78 -0
- package/dist/tools/storefrontnext/site-theming/index.d.ts +14 -0
- package/dist/tools/storefrontnext/site-theming/index.js +122 -0
- package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +16 -0
- package/dist/tools/storefrontnext/site-theming/response-builder.js +316 -0
- package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +62 -0
- package/dist/tools/storefrontnext/site-theming/theming-store.js +410 -0
- package/dist/tools/storefrontnext/site-theming/types.d.ts +35 -0
- package/dist/tools/storefrontnext/site-theming/types.js +7 -0
- package/oclif.manifest.json +8 -5
- package/package.json +9 -6
- /package/content/{auth.md → sfnext/auth.md} +0 -0
- /package/content/{components.md → sfnext/components.md} +0 -0
- /package/content/{config.md → sfnext/config.md} +0 -0
- /package/content/{data-fetching.md → sfnext/data-fetching.md} +0 -0
- /package/content/{extensions.md → sfnext/extensions.md} +0 -0
- /package/content/{i18n.md → sfnext/i18n.md} +0 -0
- /package/content/{page-designer.md → sfnext/page-designer.md} +0 -0
- /package/content/{performance.md → sfnext/performance.md} +0 -0
- /package/content/{pitfalls.md → sfnext/pitfalls.md} +0 -0
- /package/content/{quick-reference.md → sfnext/quick-reference.md} +0 -0
- /package/content/{state-management.md → sfnext/state-management.md} +0 -0
- /package/content/{styling.md → sfnext/styling.md} +0 -0
- /package/content/{testing.md → sfnext/testing.md} +0 -0
- /package/dist/tools/storefrontnext/{developer-guidelines.d.ts → sfnext-development-guidelines.d.ts} +0 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Figma-to-component workflow orchestrator tool.
|
|
8
|
+
*
|
|
9
|
+
* Parses Figma URLs, loads workflow instructions, and returns step-by-step guidance
|
|
10
|
+
* for converting Figma designs to Storefront Next components.
|
|
11
|
+
*
|
|
12
|
+
* @module tools/storefrontnext/figma/figma-to-component
|
|
13
|
+
*/
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
16
|
+
import { createToolAdapter, textResult } from '../../../adapter.js';
|
|
17
|
+
import { parseFigmaUrl } from './figma-url-parser.js';
|
|
18
|
+
// prettier-ignore
|
|
19
|
+
const DEFAULT_WORKFLOW_CONTENT = `---
|
|
20
|
+
description: Figma to StorefrontNext component conversion workflow
|
|
21
|
+
taskType: component
|
|
22
|
+
---
|
|
23
|
+
# Figma to StorefrontNext Component Workflow
|
|
24
|
+
|
|
25
|
+
IMPORTANT: The figma_to_component tool is a WORKFLOW ORCHESTRATOR that provides instructions only. It does NOT fetch design data or generate components.
|
|
26
|
+
|
|
27
|
+
After calling figma_to_component, you MUST:
|
|
28
|
+
1. Call Figma MCP tools to fetch design data
|
|
29
|
+
2. Discover similar components using Glob/Grep/Read
|
|
30
|
+
3. Call generate-component tool for REUSE/EXTEND/CREATE recommendation
|
|
31
|
+
4. Call map-tokens tool for token mapping
|
|
32
|
+
5. Implement the recommended approach
|
|
33
|
+
|
|
34
|
+
DO NOT STOP after receiving workflow instructions. Execute all steps to complete the conversion.
|
|
35
|
+
|
|
36
|
+
## WORKFLOW_GUIDELINES
|
|
37
|
+
|
|
38
|
+
### Overview
|
|
39
|
+
This workflow guides through converting a Figma design into a StorefrontNext-compliant component.
|
|
40
|
+
|
|
41
|
+
### Key Principles
|
|
42
|
+
1. Review workflow plan and list out todos to user before fetching designs
|
|
43
|
+
2. ALWAYS fetch design context and visual reference from Figma MCP tools first. Do not attempt to generate code without retrieving at minimum the design context and screenshot. Metadata is optional
|
|
44
|
+
2a. **NEVER pass dirForAssetWrites on the initial get_design_context call.** Call it first WITHOUT that parameter to inspect the response. Only pass dirForAssetWrites after the user has explicitly approved image export.
|
|
45
|
+
2b. **MANDATORY GATE - Image export requires user approval:** Do NOT call get_design_context with dirForAssetWrites for ANY node until you have: (1) identified all image-containing nodes, (2) presented the list to the user, (3) asked "Should I export these N image assets now? (yes/no)", and (4) received an explicit "yes". Do NOT export images automatically.
|
|
46
|
+
2c. **Single prompt per batch:** Ask ONCE for the entire batch of image nodes. After the user says "yes", export all of them (via one or more get_design_context calls with dirForAssetWrites). Do NOT prompt again for each individual image.
|
|
47
|
+
3. Only call the Figma MCP tools listed in this workflow. If a tool is not available or not enabled, inform the user
|
|
48
|
+
4. ALWAYS discover similar components before creating new ones. Use Glob/Grep/Read to search the codebase
|
|
49
|
+
5. ALWAYS call generate-component tool with discovered components to get REUSE/EXTEND/CREATE recommendation
|
|
50
|
+
6. ALWAYS call map-tokens tool to map Figma tokens to existing theme variables rather than hardcoding values
|
|
51
|
+
7. Follow StorefrontNext patterns. All components must adhere to StorefrontNext architecture
|
|
52
|
+
8. Present a detailed plan to the user and wait for approval before implementing
|
|
53
|
+
9. Validate thoroughly. Always run validation checks before presenting the final component to the developer
|
|
54
|
+
|
|
55
|
+
### Figma MCP Tools
|
|
56
|
+
When calling these tools, always include: clientLanguages="typescript", clientFrameworks="react"
|
|
57
|
+
- mcp__figma__get_design_context (REQUIRED): Generates UI code and returns asset URLs. **Initial call: do NOT pass dirForAssetWrites.** Call first without it to inspect the response. Only pass dirForAssetWrites after user has approved export (see Image and Asset Export below).
|
|
58
|
+
- mcp__figma__get_screenshot (REQUIRED): Provides visual reference of the design
|
|
59
|
+
- mcp__figma__get_metadata (REQUIRED when node is a section): Retrieves node hierarchy, layer types, names, positions, and sizes. Use when get_design_context returns sparse metadata (section nodes do not export assets)
|
|
60
|
+
|
|
61
|
+
### Image and Asset Export (REQUIRED)
|
|
62
|
+
**MANDATORY GATE: Do not call get_design_context with dirForAssetWrites until the user has approved. You MUST ask ONCE for the entire batch—never prompt per image.**
|
|
63
|
+
|
|
64
|
+
Section nodes return sparse metadata and do NOT export images. You MUST:
|
|
65
|
+
|
|
66
|
+
1. **Initial probe (no export)**: Call get_design_context WITHOUT dirForAssetWrites first. Never pass dirForAssetWrites on the first call.
|
|
67
|
+
2. **Detect sparse response**: If get_design_context returns "sparse metadata" or "section node", call get_metadata with the same nodeId to retrieve the XML with child node IDs
|
|
68
|
+
3. **Identify image-containing nodes**: From the metadata XML (or from the initial response if it's a leaf with images), find nodes that contain images. Include: RECTANGLE with fills; nodes named with image-like names (e.g., photo, image, banner, hero); logos and brand assets (nodes named "logo", "Logo", "brand", "icon", "header", "footer", or similar); vector/component instances that represent logos or icons; frames that visually contain photos/illustrations; any node the screenshot suggests contains a logo or brand asset
|
|
69
|
+
4. **STOP and ask for approval (MANDATORY)**: Present the list of identified nodes (names and node IDs) to the user. Ask explicitly: "I found N image-containing nodes. Should I export these assets now? (yes/no)". STOP and wait for the user to respond. Do NOT call get_design_context with dirForAssetWrites for any node until the user confirms "yes". If you proceed without user confirmation, you have violated this workflow
|
|
70
|
+
5. **Download image nodes** (ONLY after user says "yes"): For each identified image-containing node, call get_design_context with:
|
|
71
|
+
- nodeId: the node ID from metadata or initial selection (e.g., "3351:1234")
|
|
72
|
+
- dirForAssetWrites: absolute path to the project's public images folder (e.g., \`{workspace}/packages/template-retail-rsc-app/public/images/figma-exports\`)
|
|
73
|
+
6. **Track downloaded assets**: Note which exported file path corresponds to which node/component (e.g., hero banner → hero-banner.webp, logo → nettle-logo.webp, category card 1 → infused-beverages.webp)
|
|
74
|
+
7. **Set image URLs in implementation**: When implementing the component, use the downloaded file paths for any img src or imageUrl props. Replace placeholder paths with the actual exported asset paths (e.g., \`/images/figma-exports/hero-banner.webp\`)
|
|
75
|
+
|
|
76
|
+
### StorefrontNext MCP Tools (REQUIRED)
|
|
77
|
+
- generate-component: Analyzes Figma design and discovered components, recommends REUSE/EXTEND/CREATE strategy. MUST be called with discoveredComponents parameter
|
|
78
|
+
- map-tokens: Maps Figma design tokens to existing theme tokens. MUST be called to avoid hardcoded values
|
|
79
|
+
- validate_component: Validates component against StorefrontNext patterns (optional, not yet implemented)
|
|
80
|
+
|
|
81
|
+
### AI-Driven Component Discovery (Before calling generate-component)
|
|
82
|
+
Before calling generate-component, you must discover similar components using your tools:
|
|
83
|
+
|
|
84
|
+
**Discovery Strategy:**
|
|
85
|
+
1. **Name-Based Search (Primary):**
|
|
86
|
+
- Use Glob to find component files: \`**/components/**/*.tsx\`, \`**/src/**/*.tsx\`
|
|
87
|
+
- Exclude: \`**/node_modules/**\`, \`**/dist/**\`, \`**/*.test.tsx\`, \`**/*.stories.tsx\`
|
|
88
|
+
- Use Grep to search for component names similar to the Figma component name
|
|
89
|
+
- Look in: export statements, function names, interface names
|
|
90
|
+
|
|
91
|
+
2. **Structure-Based Search (Secondary):**
|
|
92
|
+
- If name search yields poor results, search by code structure
|
|
93
|
+
- Look for similar hooks (useState, useEffect, etc.)
|
|
94
|
+
- Look for similar element patterns (buttons, forms, layouts)
|
|
95
|
+
- Search for 'use client' directive if Figma code is client-side
|
|
96
|
+
|
|
97
|
+
3. **Read and Score Components:**
|
|
98
|
+
- Read each promising match
|
|
99
|
+
- Score similarity (0-100) based on:
|
|
100
|
+
* Name similarity: How close is the name?
|
|
101
|
+
* Purpose similarity: Does it serve the same function?
|
|
102
|
+
* Structure similarity: Similar JSX structure, hooks, props?
|
|
103
|
+
* Styling similarity: Similar Tailwind classes or theme usage?
|
|
104
|
+
- Assign match type: 'name', 'structure', or 'visual'
|
|
105
|
+
|
|
106
|
+
4. **Select Top Matches:**
|
|
107
|
+
- Select top 1-3 matches with similarity >= 50%
|
|
108
|
+
- Sort by similarity score (highest first)
|
|
109
|
+
- If no matches found, pass empty array to generate-component
|
|
110
|
+
|
|
111
|
+
**Discovery Tips:**
|
|
112
|
+
- Be semantic: "PrimaryButton" and "CallToAction" might serve the same purpose
|
|
113
|
+
- Consider component purpose and context, not just file names
|
|
114
|
+
- Check common directories first: components/ui/, components/shared/
|
|
115
|
+
- Read component code to understand structure, props, and behavior
|
|
116
|
+
- Trust your judgment using React patterns knowledge
|
|
117
|
+
|
|
118
|
+
### Component Requirements
|
|
119
|
+
- Use React Server Components (RSC) pattern by default
|
|
120
|
+
- Use Tailwind CSS classes with theme tokens, no inline styles or hardcoded values
|
|
121
|
+
- Follow TypeScript strict mode conventions
|
|
122
|
+
- Include proper accessibility attributes
|
|
123
|
+
- Follow existing file naming conventions
|
|
124
|
+
- Use absolute imports from '@/components', '@/lib', etc.
|
|
125
|
+
|
|
126
|
+
## General Development Guidelines
|
|
127
|
+
|
|
128
|
+
### Core Principles
|
|
129
|
+
- Thoroughly analyze requests and the existing project for successful implementation
|
|
130
|
+
- Promptly clarify ambiguous requirements
|
|
131
|
+
|
|
132
|
+
### Development Workflow
|
|
133
|
+
- **Analyze Requirements** - Clearly define the objectives and functionalities required
|
|
134
|
+
- **Review Existing Code** - Examine the current codebase to identify similar solutions and potentially reusable components
|
|
135
|
+
- **Understand Existing Hooks and Utilities** - Familiarize with hooks and utility functions available within the project
|
|
136
|
+
- **Plan Implementation** - Design component structure before coding
|
|
137
|
+
- **Implement Incrementally** - Develop and test the service in small, manageable steps
|
|
138
|
+
- **Test Thoroughly** - Ensure comprehensive testing
|
|
139
|
+
|
|
140
|
+
### After Generation
|
|
141
|
+
- Present the component code to the developer for review
|
|
142
|
+
- Provide file path suggestions based on component type
|
|
143
|
+
- Highlight any design tokens that don't have existing mappings
|
|
144
|
+
- List any validation warnings or suggestions
|
|
145
|
+
|
|
146
|
+
## WORKFLOW_STEPS
|
|
147
|
+
|
|
148
|
+
**Create and present to the user a task plan that reflects these steps while keeping the Workflow Guidelines in mind. Wait for approval before proceeding.**
|
|
149
|
+
|
|
150
|
+
1. REQUIRED: Retrieve design context using mcp__figma__get_design_context with fileKey and nodeId. **Do NOT pass dirForAssetWrites on this initial call.** If the response is sparse (section node): call get_metadata, identify image-containing nodes, then STOP and ask the user "Should I export these N image assets now? (yes/no)". WAIT for user to respond. Only after user says "yes", call get_design_context for each image-containing node with dirForAssetWrites
|
|
151
|
+
2. REQUIRED: Retrieve visual reference using mcp__figma__get_screenshot with the provided fileKey and nodeId
|
|
152
|
+
3. REQUIRED when sparse: If get_design_context returns sparse metadata (section node), call mcp__figma__get_metadata to get child node IDs, identify image-containing nodes, then STOP and ask user for approval. Do NOT call get_design_context with dirForAssetWrites until user confirms "yes"
|
|
153
|
+
4. REQUIRED: Discover similar components in the codebase:
|
|
154
|
+
- Use Glob to find component files in common directories
|
|
155
|
+
- Use Grep to search for components with similar names or structure
|
|
156
|
+
- Use Read to examine promising matches
|
|
157
|
+
- Score similarity (0-100) and select top 1-3 matches
|
|
158
|
+
- Prepare discoveredComponents array for next step
|
|
159
|
+
5. REQUIRED: Analyze component generation strategy using generate-component tool with discovered components. This provides REUSE/EXTEND/CREATE recommendation. Wait for user approval of strategy before code changes
|
|
160
|
+
6. REQUIRED: Map Figma design tokens to existing StorefrontNext theme tokens using the map-tokens tool. Extract color, spacing, and other design tokens from Figma data and pass them to this tool for matching
|
|
161
|
+
7. OPTIONAL (not implemented): Validate the generated component against StorefrontNext patterns using the validate_component tool
|
|
162
|
+
8. REQUIRED: Implement the recommended approach and present the final component code to the developer for review`;
|
|
163
|
+
export const figmaToComponentSchema = z
|
|
164
|
+
.object({
|
|
165
|
+
figmaUrl: z
|
|
166
|
+
.string()
|
|
167
|
+
.url()
|
|
168
|
+
.describe('The Figma design URL to convert to a StorefrontNext component. Must include node-id parameter.'),
|
|
169
|
+
workflowFilePath: z
|
|
170
|
+
.string()
|
|
171
|
+
.optional()
|
|
172
|
+
.describe('Optional absolute path to custom workflow .md file. If not provided, uses default built-in workflow.'),
|
|
173
|
+
})
|
|
174
|
+
.strict();
|
|
175
|
+
function extractWorkflowContent(content) {
|
|
176
|
+
const metadata = {};
|
|
177
|
+
let body = content;
|
|
178
|
+
const metadataMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
179
|
+
if (metadataMatch) {
|
|
180
|
+
const metadataText = metadataMatch[1];
|
|
181
|
+
body = metadataMatch[2];
|
|
182
|
+
for (const line of metadataText.split('\n')) {
|
|
183
|
+
const match = line.match(/^(.+?):\s*(.+)$/);
|
|
184
|
+
if (match) {
|
|
185
|
+
metadata[match[1].trim()] = match[2].trim();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return { metadata, body: body.trim() };
|
|
190
|
+
}
|
|
191
|
+
function parseWorkflowFile(filePath) {
|
|
192
|
+
let fileContent;
|
|
193
|
+
if (filePath) {
|
|
194
|
+
if (!existsSync(filePath)) {
|
|
195
|
+
throw new Error(`Workflow file not found: ${filePath}`);
|
|
196
|
+
}
|
|
197
|
+
fileContent = readFileSync(filePath, 'utf8');
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
fileContent = DEFAULT_WORKFLOW_CONTENT;
|
|
201
|
+
}
|
|
202
|
+
const { metadata, body } = extractWorkflowContent(fileContent);
|
|
203
|
+
return { metadata, content: body };
|
|
204
|
+
}
|
|
205
|
+
function formatFigmaParams(params, originalUrl) {
|
|
206
|
+
let section = '## Figma Design Parameters\n\n';
|
|
207
|
+
section += '```json\n';
|
|
208
|
+
section += JSON.stringify({
|
|
209
|
+
fileKey: params.fileKey,
|
|
210
|
+
nodeId: params.nodeId,
|
|
211
|
+
originalUrl,
|
|
212
|
+
}, null, 2);
|
|
213
|
+
section += '\n```\n\n';
|
|
214
|
+
section +=
|
|
215
|
+
'IMPORTANT: Use these exact parameters when calling Figma MCP tools. The `clientLanguages` parameter should be set to "typescript" and `clientFrameworks` should be set to "react".\n\n';
|
|
216
|
+
return section;
|
|
217
|
+
}
|
|
218
|
+
function formatWorkflowContent(content) {
|
|
219
|
+
return `${content}\n\n`;
|
|
220
|
+
}
|
|
221
|
+
function formatNextStepsReminder() {
|
|
222
|
+
return `---
|
|
223
|
+
## CRITICAL: Next Steps Required
|
|
224
|
+
|
|
225
|
+
This tool has provided workflow instructions only. You MUST now execute ALL steps below.
|
|
226
|
+
|
|
227
|
+
**EXPECTED FINAL OUTPUT:** A recommendation with confidence score from generate_component tool AND a token mapping summary from map_tokens_to_theme tool.
|
|
228
|
+
|
|
229
|
+
### Step 1: Fetch Figma Design Data (Parallel Calls)
|
|
230
|
+
Call these Figma MCP tools with the parameters above:
|
|
231
|
+
- \`mcp__figma__get_design_context\` (REQUIRED) - **Do NOT pass dirForAssetWrites on the initial call.** Call first without it to inspect the response. If response is sparse (section node): call get_metadata to get child node IDs, identify image-containing nodes, then STOP and present the list to the user. Ask "Should I export these N image assets now? (yes/no)" and WAIT for user response. Only after user says "yes", call get_design_context per image-containing node with dirForAssetWrites
|
|
232
|
+
- \`mcp__figma__get_screenshot\` (REQUIRED) - Get visual reference
|
|
233
|
+
- \`mcp__figma__get_metadata\` (REQUIRED when sparse) - Use when get_design_context returns sparse metadata. After identifying image nodes: STOP, present list to user, wait for "yes" before exporting
|
|
234
|
+
|
|
235
|
+
### Step 2: Discover Similar Components
|
|
236
|
+
Use your tools to find existing components:
|
|
237
|
+
- Use \`Glob\` to find component files: \`**/components/**/*.tsx\`
|
|
238
|
+
- Use \`Grep\` to search for similar names or patterns
|
|
239
|
+
- Use \`Read\` to examine promising matches
|
|
240
|
+
- Score each match (0-100) based on similarity
|
|
241
|
+
|
|
242
|
+
### Step 3: Analyze Component Strategy (CRITICAL - DO NOT SKIP)
|
|
243
|
+
You MUST call \`generate_component\` tool with:
|
|
244
|
+
- figmaMetadata (from step 1, or empty string if not fetched)
|
|
245
|
+
- figmaCode (from step 1)
|
|
246
|
+
- componentName (extracted from Figma)
|
|
247
|
+
- discoveredComponents (from step 2)
|
|
248
|
+
|
|
249
|
+
This tool returns the recommendation with confidence score that MUST be shown to the user.
|
|
250
|
+
|
|
251
|
+
### Step 4: Map Design Tokens (CRITICAL - DO NOT SKIP)
|
|
252
|
+
You MUST call \`map_tokens_to_theme\` tool with tokens extracted from Figma design.
|
|
253
|
+
|
|
254
|
+
This tool returns the token mapping summary that MUST be shown to the user.
|
|
255
|
+
|
|
256
|
+
### Step 5: Implement
|
|
257
|
+
After showing the recommendation and token mapping to the user, wait for approval then implement the code changes. Use the downloaded asset paths from Step 1 for any img src or imageUrl props—do not use placeholder paths.
|
|
258
|
+
|
|
259
|
+
**DO NOT STOP until you have called generate_component AND map_tokens_to_theme and shown their outputs to the user.**
|
|
260
|
+
`;
|
|
261
|
+
}
|
|
262
|
+
function formatErrorResponse(details) {
|
|
263
|
+
let response = `# Error: Invalid Figma URL\n\n${details}\n\n`;
|
|
264
|
+
response += 'Please provide a valid Figma URL in the format:\n';
|
|
265
|
+
response += 'https://figma.com/design/:fileKey/:fileName?node-id=1-2\n\n';
|
|
266
|
+
response += 'Example:\nhttps://figma.com/design/abc123/MyDesign?node-id=1-2\n';
|
|
267
|
+
return response;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Generates the workflow guide for Figma-to-component conversion.
|
|
271
|
+
*
|
|
272
|
+
* @param figmaUrl - Figma design URL with node-id query parameter
|
|
273
|
+
* @param workflowFilePath - Optional absolute path to custom workflow .md file; uses built-in default if omitted
|
|
274
|
+
* @returns Formatted workflow guide string with Figma parameters and step-by-step instructions, or error message if URL or workflow file is invalid
|
|
275
|
+
*/
|
|
276
|
+
export function generateWorkflowResponse(figmaUrl, workflowFilePath) {
|
|
277
|
+
let figmaParams;
|
|
278
|
+
try {
|
|
279
|
+
figmaParams = parseFigmaUrl(figmaUrl);
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
283
|
+
return formatErrorResponse(errorMessage);
|
|
284
|
+
}
|
|
285
|
+
let workflowConfig;
|
|
286
|
+
try {
|
|
287
|
+
workflowConfig = parseWorkflowFile(workflowFilePath);
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
291
|
+
return `# Error: Workflow File Not Found\n\n${errorMessage}\n\nPlease provide a valid workflow file path or omit the parameter to use the default workflow.\n`;
|
|
292
|
+
}
|
|
293
|
+
let response = '# Figma to StorefrontNext Workflow Guide\n\n';
|
|
294
|
+
response += formatFigmaParams(figmaParams, figmaUrl);
|
|
295
|
+
response += formatWorkflowContent(workflowConfig.content);
|
|
296
|
+
response += formatNextStepsReminder();
|
|
297
|
+
return response;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Creates the storefront_next_figma_to_component_workflow MCP tool.
|
|
301
|
+
*
|
|
302
|
+
* @param loadServices - Function that loads configuration and returns Services instance
|
|
303
|
+
* @returns MCP tool for workflow orchestration
|
|
304
|
+
*/
|
|
305
|
+
export function createFigmaToComponentTool(loadServices) {
|
|
306
|
+
return createToolAdapter({
|
|
307
|
+
name: 'storefront_next_figma_to_component_workflow',
|
|
308
|
+
description: 'WORKFLOW ORCHESTRATOR: Call this tool FIRST when converting Figma designs. ' +
|
|
309
|
+
'Parses Figma URL to extract fileKey and nodeId, returns step-by-step workflow instructions ' +
|
|
310
|
+
'and parameters for subsequent tool calls. ' +
|
|
311
|
+
'CRITICAL: This is only the FIRST step. After calling this tool, you MUST continue executing ' +
|
|
312
|
+
'the complete workflow: 1) Call Figma MCP tools, 2) Discover similar components, ' +
|
|
313
|
+
'3) Call generate_component tool, 4) Call map_tokens_to_theme tool, ' +
|
|
314
|
+
'5) Show both outputs to the user then implement the recommended approach.',
|
|
315
|
+
toolsets: ['STOREFRONTNEXT'],
|
|
316
|
+
isGA: false,
|
|
317
|
+
requiresInstance: false,
|
|
318
|
+
inputSchema: figmaToComponentSchema.shape,
|
|
319
|
+
async execute(args) {
|
|
320
|
+
return generateWorkflowResponse(args.figmaUrl, args.workflowFilePath);
|
|
321
|
+
},
|
|
322
|
+
formatOutput: (output) => textResult(output),
|
|
323
|
+
}, loadServices);
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SimilarComponent, ComponentAnalysisResult } from './index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Categorized differences between a matched component and Figma design
|
|
4
|
+
* @property {DifferenceDetail[]} styling - Visual differences (Tailwind classes, inline styles, theme tokens)
|
|
5
|
+
* @property {DifferenceDetail[]} structural - JSX hierarchy differences (elements, nesting, root element changes)
|
|
6
|
+
* @property {DifferenceDetail[]} behavioral - Interaction differences (hooks, event handlers, client/server rendering)
|
|
7
|
+
* @property {DifferenceDetail[]} props - Interface/prop definition differences (new props, type changes)
|
|
8
|
+
*/
|
|
9
|
+
export interface ComponentDifferences {
|
|
10
|
+
styling: DifferenceDetail[];
|
|
11
|
+
structural: DifferenceDetail[];
|
|
12
|
+
behavioral: DifferenceDetail[];
|
|
13
|
+
props: DifferenceDetail[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Details about a specific difference between components
|
|
17
|
+
* @property {string} description - Explanation of the difference
|
|
18
|
+
* @property {'major'|'minor'|'moderate'} severity - Impact level: 'minor' (1pt), 'moderate' (3pts), 'major' (5pts)
|
|
19
|
+
* @property {boolean} isBackwardCompatible - Whether existing code using the component would still work after this change
|
|
20
|
+
*/
|
|
21
|
+
export interface DifferenceDetail {
|
|
22
|
+
description: string;
|
|
23
|
+
severity: 'major' | 'minor' | 'moderate';
|
|
24
|
+
isBackwardCompatible: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Analyzes differences between matched component and Figma design.
|
|
28
|
+
*
|
|
29
|
+
* @param matchedComponent - The existing component to compare against
|
|
30
|
+
* @param figmaCode - The Figma-generated React code
|
|
31
|
+
* @param _figmaMetadata - Reserved for future use (e.g., component hierarchy analysis).
|
|
32
|
+
* Currently unused but kept in the signature to avoid a breaking change once metadata
|
|
33
|
+
* analysis is implemented.
|
|
34
|
+
*/
|
|
35
|
+
export declare function analyzeComponentDifferences(matchedComponent: SimilarComponent, figmaCode: string, _figmaMetadata: string): ComponentDifferences;
|
|
36
|
+
/**
|
|
37
|
+
* Determines the appropriate action based on differences
|
|
38
|
+
* Uses type of difference + impact assessment
|
|
39
|
+
*/
|
|
40
|
+
export declare function determineAction(matchedComponent: SimilarComponent, differences: ComponentDifferences): ComponentAnalysisResult;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Analyzes differences between matched component and Figma design.
|
|
8
|
+
*
|
|
9
|
+
* @param matchedComponent - The existing component to compare against
|
|
10
|
+
* @param figmaCode - The Figma-generated React code
|
|
11
|
+
* @param _figmaMetadata - Reserved for future use (e.g., component hierarchy analysis).
|
|
12
|
+
* Currently unused but kept in the signature to avoid a breaking change once metadata
|
|
13
|
+
* analysis is implemented.
|
|
14
|
+
*/
|
|
15
|
+
export function analyzeComponentDifferences(matchedComponent, figmaCode, _figmaMetadata) {
|
|
16
|
+
const differences = {
|
|
17
|
+
styling: [],
|
|
18
|
+
structural: [],
|
|
19
|
+
behavioral: [],
|
|
20
|
+
props: [],
|
|
21
|
+
};
|
|
22
|
+
// Analyze styling differences
|
|
23
|
+
differences.styling = analyzeStylingDifferences(matchedComponent.code, figmaCode);
|
|
24
|
+
// Analyze structural differences
|
|
25
|
+
differences.structural = analyzeStructuralDifferences(matchedComponent.code, figmaCode);
|
|
26
|
+
// Analyze behavioral differences (hooks, state, effects)
|
|
27
|
+
differences.behavioral = analyzeBehavioralDifferences(matchedComponent.code, figmaCode);
|
|
28
|
+
// Analyze prop differences
|
|
29
|
+
differences.props = analyzePropDifferences(matchedComponent.code, figmaCode);
|
|
30
|
+
return differences;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Analyzes styling differences (Tailwind classes, CSS, theme tokens)
|
|
34
|
+
*/
|
|
35
|
+
function analyzeStylingDifferences(existingCode, figmaCode) {
|
|
36
|
+
const differences = [];
|
|
37
|
+
// Extract className usage
|
|
38
|
+
const existingClasses = extractTailwindClasses(existingCode);
|
|
39
|
+
const figmaClasses = extractTailwindClasses(figmaCode);
|
|
40
|
+
// Check for new classes in Figma design
|
|
41
|
+
const newClasses = figmaClasses.filter((c) => !existingClasses.includes(c));
|
|
42
|
+
if (newClasses.length > 0) {
|
|
43
|
+
differences.push({
|
|
44
|
+
description: `New Tailwind classes: ${newClasses.slice(0, 5).join(', ')}${newClasses.length > 5 ? '...' : ''}`,
|
|
45
|
+
severity: newClasses.length > 10 ? 'major' : newClasses.length > 3 ? 'moderate' : 'minor',
|
|
46
|
+
isBackwardCompatible: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Check for inline styles (anti-pattern)
|
|
50
|
+
if (figmaCode.includes('style={{') || figmaCode.includes('style="')) {
|
|
51
|
+
differences.push({
|
|
52
|
+
description: 'Figma code contains inline styles (needs conversion to Tailwind)',
|
|
53
|
+
severity: 'moderate',
|
|
54
|
+
isBackwardCompatible: true,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return differences;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Analyzes structural differences (JSX hierarchy, elements)
|
|
61
|
+
*/
|
|
62
|
+
function analyzeStructuralDifferences(existingCode, figmaCode) {
|
|
63
|
+
const differences = [];
|
|
64
|
+
// Extract JSX elements
|
|
65
|
+
const existingElements = extractJSXElements(existingCode);
|
|
66
|
+
const figmaElements = extractJSXElements(figmaCode);
|
|
67
|
+
// Check for different root elements
|
|
68
|
+
if (existingElements[0] !== figmaElements[0]) {
|
|
69
|
+
differences.push({
|
|
70
|
+
description: `Different root element: ${existingElements[0]} vs ${figmaElements[0]}`,
|
|
71
|
+
severity: 'moderate',
|
|
72
|
+
isBackwardCompatible: false,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Check for additional nested elements
|
|
76
|
+
const newElements = figmaElements.filter((e) => !existingElements.includes(e));
|
|
77
|
+
if (newElements.length > 0) {
|
|
78
|
+
differences.push({
|
|
79
|
+
description: `New elements in Figma design: ${newElements.join(', ')}`,
|
|
80
|
+
severity: newElements.length > 3 ? 'major' : 'minor',
|
|
81
|
+
isBackwardCompatible: true,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return differences;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Analyzes behavioral differences (hooks, state, effects, event handlers)
|
|
88
|
+
*/
|
|
89
|
+
function analyzeBehavioralDifferences(existingCode, figmaCode) {
|
|
90
|
+
const differences = [];
|
|
91
|
+
// Check for 'use client' directive
|
|
92
|
+
const existingIsClient = existingCode.includes("'use client'") || existingCode.includes('"use client"');
|
|
93
|
+
const figmaIsClient = figmaCode.includes("'use client'") || figmaCode.includes('"use client"');
|
|
94
|
+
if (existingIsClient !== figmaIsClient) {
|
|
95
|
+
differences.push({
|
|
96
|
+
description: figmaIsClient
|
|
97
|
+
? 'Figma design requires client-side rendering'
|
|
98
|
+
: 'Existing component is client-side, Figma design could be RSC',
|
|
99
|
+
severity: 'major',
|
|
100
|
+
isBackwardCompatible: false,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
// Check for new hooks
|
|
104
|
+
const existingHooks = extractHooks(existingCode);
|
|
105
|
+
const figmaHooks = extractHooks(figmaCode);
|
|
106
|
+
const newHooks = figmaHooks.filter((h) => !existingHooks.includes(h));
|
|
107
|
+
if (newHooks.length > 0) {
|
|
108
|
+
differences.push({
|
|
109
|
+
description: `New React hooks needed: ${newHooks.join(', ')}`,
|
|
110
|
+
severity: 'moderate',
|
|
111
|
+
isBackwardCompatible: newHooks.every((h) => h.startsWith('use')),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// Check for event handlers
|
|
115
|
+
const existingHasHandlers = /on[A-Z]\w+=/g.test(existingCode);
|
|
116
|
+
const figmaHasHandlers = /on[A-Z]\w+=/g.test(figmaCode);
|
|
117
|
+
if (!existingHasHandlers && figmaHasHandlers) {
|
|
118
|
+
differences.push({
|
|
119
|
+
description: 'Figma design includes event handlers (onClick, onChange, etc.)',
|
|
120
|
+
severity: 'moderate',
|
|
121
|
+
isBackwardCompatible: true,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return differences;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Analyzes prop differences
|
|
128
|
+
* parses TypeScript interfaces to compare prop definitions
|
|
129
|
+
*/
|
|
130
|
+
function analyzePropDifferences(existingCode, figmaCode) {
|
|
131
|
+
const differences = [];
|
|
132
|
+
const existingPropCount = (existingCode.match(/interface\s+\w+Props/g) || []).length;
|
|
133
|
+
const figmaPropCount = (figmaCode.match(/interface\s+\w+Props/g) || []).length;
|
|
134
|
+
if (figmaPropCount > existingPropCount) {
|
|
135
|
+
differences.push({
|
|
136
|
+
description: 'Figma design may require additional props',
|
|
137
|
+
severity: 'minor',
|
|
138
|
+
isBackwardCompatible: true,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return differences;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Extracts Tailwind classes from code
|
|
145
|
+
*/
|
|
146
|
+
function extractTailwindClasses(code) {
|
|
147
|
+
const classRegex = /className=["']([^"']+)["']/g;
|
|
148
|
+
const classes = new Set();
|
|
149
|
+
let match;
|
|
150
|
+
while ((match = classRegex.exec(code)) !== null) {
|
|
151
|
+
const classList = match[1].split(/\s+/);
|
|
152
|
+
for (const c of classList)
|
|
153
|
+
classes.add(c);
|
|
154
|
+
}
|
|
155
|
+
return [...classes];
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Extracts JSX elements from code (simplified)
|
|
159
|
+
*/
|
|
160
|
+
function extractJSXElements(code) {
|
|
161
|
+
// Simple regex to find JSX opening tags
|
|
162
|
+
const elementRegex = /<([A-Z][a-zA-Z0-9]*|[a-z]+)[\s>]/g;
|
|
163
|
+
const elements = new Set();
|
|
164
|
+
let match;
|
|
165
|
+
while ((match = elementRegex.exec(code)) !== null) {
|
|
166
|
+
elements.add(match[1]);
|
|
167
|
+
}
|
|
168
|
+
return [...elements];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Extracts React hooks from code
|
|
172
|
+
*/
|
|
173
|
+
function extractHooks(code) {
|
|
174
|
+
const hookRegex = /\b(use[A-Z]\w+)\(/g;
|
|
175
|
+
const hooks = new Set();
|
|
176
|
+
let match;
|
|
177
|
+
while ((match = hookRegex.exec(code)) !== null) {
|
|
178
|
+
hooks.add(match[1]);
|
|
179
|
+
}
|
|
180
|
+
return [...hooks];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Determines the appropriate action based on differences
|
|
184
|
+
* Uses type of difference + impact assessment
|
|
185
|
+
*/
|
|
186
|
+
export function determineAction(matchedComponent, differences) {
|
|
187
|
+
const allDifferences = [
|
|
188
|
+
...differences.styling,
|
|
189
|
+
...differences.structural,
|
|
190
|
+
...differences.behavioral,
|
|
191
|
+
...differences.props,
|
|
192
|
+
];
|
|
193
|
+
const severityScores = { minor: 1, moderate: 3, major: 5 };
|
|
194
|
+
let differenceScore = 0;
|
|
195
|
+
for (const diff of allDifferences) {
|
|
196
|
+
differenceScore += severityScores[diff.severity];
|
|
197
|
+
}
|
|
198
|
+
// Count breaking changes
|
|
199
|
+
const breakingChanges = allDifferences.filter((d) => !d.isBackwardCompatible).length;
|
|
200
|
+
// Decision thresholds
|
|
201
|
+
const REUSE_THRESHOLD = 2; // Only minor styling differences
|
|
202
|
+
const EXTEND_THRESHOLD = 10; // Moderate differences that can be added
|
|
203
|
+
// REUSE: Minimal differences, mostly styling
|
|
204
|
+
if (differenceScore <= REUSE_THRESHOLD && breakingChanges === 0) {
|
|
205
|
+
return {
|
|
206
|
+
action: 'REUSE',
|
|
207
|
+
confidence: Math.round(matchedComponent.similarity),
|
|
208
|
+
matchedComponent: {
|
|
209
|
+
path: matchedComponent.path,
|
|
210
|
+
name: matchedComponent.name,
|
|
211
|
+
similarity: matchedComponent.similarity,
|
|
212
|
+
},
|
|
213
|
+
differences: allDifferences.map((d) => d.description),
|
|
214
|
+
recommendation: `The existing component "${matchedComponent.name}" can be reused with different props or minor styling adjustments.`,
|
|
215
|
+
suggestedApproach: `Use the existing component at ${matchedComponent.path} and customize it through props.`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// CREATE: Major structural or behavioral differences, or many breaking changes
|
|
219
|
+
if (differenceScore > EXTEND_THRESHOLD || breakingChanges > 2) {
|
|
220
|
+
return {
|
|
221
|
+
action: 'CREATE',
|
|
222
|
+
confidence: 85,
|
|
223
|
+
matchedComponent: {
|
|
224
|
+
path: matchedComponent.path,
|
|
225
|
+
name: matchedComponent.name,
|
|
226
|
+
similarity: matchedComponent.similarity,
|
|
227
|
+
},
|
|
228
|
+
differences: allDifferences.map((d) => d.description),
|
|
229
|
+
recommendation: `Differences are significant enough to warrant creating a new component.`,
|
|
230
|
+
suggestedApproach: `Create a new component. You may reference patterns from ${matchedComponent.path} but build a new component.`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// EXTEND: Moderate differences that can be added
|
|
234
|
+
// Determine extend strategy: props / variant / composition
|
|
235
|
+
const extendStrategy = determineExtendStrategy(differences, allDifferences);
|
|
236
|
+
return {
|
|
237
|
+
action: 'EXTEND',
|
|
238
|
+
confidence: Math.round((matchedComponent.similarity + 100 - differenceScore * 2) / 2),
|
|
239
|
+
matchedComponent: {
|
|
240
|
+
path: matchedComponent.path,
|
|
241
|
+
name: matchedComponent.name,
|
|
242
|
+
similarity: matchedComponent.similarity,
|
|
243
|
+
},
|
|
244
|
+
differences: allDifferences.map((d) => d.description),
|
|
245
|
+
recommendation: `The existing component "${matchedComponent.name}" can be extended to support the Figma design.`,
|
|
246
|
+
suggestedApproach: generateExtendApproach(extendStrategy, matchedComponent, differences),
|
|
247
|
+
extendStrategy,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Determines the best extend strategy based on differences
|
|
252
|
+
* Context-dependent: checks type of difference then validates with impact
|
|
253
|
+
*/
|
|
254
|
+
function determineExtendStrategy(differences, allDifferences) {
|
|
255
|
+
// Props extension: Only new optional behaviors (1-3 new props, backward compatible)
|
|
256
|
+
if (differences.props.length <= 3 &&
|
|
257
|
+
differences.structural.length === 0 &&
|
|
258
|
+
differences.behavioral.length <= 1 &&
|
|
259
|
+
allDifferences.every((d) => d.isBackwardCompatible)) {
|
|
260
|
+
return 'props';
|
|
261
|
+
}
|
|
262
|
+
// Composition: Structural changes or new child components
|
|
263
|
+
if (differences.structural.length > 0 || differences.behavioral.some((d) => !d.isBackwardCompatible)) {
|
|
264
|
+
return 'composition';
|
|
265
|
+
}
|
|
266
|
+
// Variant pattern: Visual variations (styling focused, 4+ new classes)
|
|
267
|
+
if (differences.styling.some((d) => d.severity !== 'minor')) {
|
|
268
|
+
return 'variant';
|
|
269
|
+
}
|
|
270
|
+
// Default to props for small changes
|
|
271
|
+
return 'props';
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Generates extend approach description based on strategy
|
|
275
|
+
*/
|
|
276
|
+
function generateExtendApproach(strategy, matchedComponent, differences) {
|
|
277
|
+
const componentPath = matchedComponent.path;
|
|
278
|
+
const componentName = matchedComponent.name;
|
|
279
|
+
switch (strategy) {
|
|
280
|
+
case 'composition': {
|
|
281
|
+
return `**Composition Pattern**
|
|
282
|
+
Create a new component that wraps/composes the existing one:
|
|
283
|
+
1. Create new component: ${componentName}Enhanced.tsx
|
|
284
|
+
2. Import and compose: <${componentName}>{/* new elements */}</${componentName}>
|
|
285
|
+
3. Structural changes: ${differences.structural.map((d) => d.description).join(', ')}
|
|
286
|
+
4. This preserves the existing component while adding new behavior
|
|
287
|
+
|
|
288
|
+
This approach works because there are structural changes that would break existing usage if added directly.`;
|
|
289
|
+
}
|
|
290
|
+
case 'props': {
|
|
291
|
+
return `**Props Extension Pattern**
|
|
292
|
+
Extend the existing component by adding new optional props:
|
|
293
|
+
1. Modify ${componentPath}
|
|
294
|
+
2. Add new props to the interface (${differences.props.map((d) => d.description).join(', ')})
|
|
295
|
+
3. Implement the new prop behavior while maintaining backward compatibility
|
|
296
|
+
4. Ensure existing usage is not affected
|
|
297
|
+
|
|
298
|
+
This approach works because the changes are small and backward compatible.`;
|
|
299
|
+
}
|
|
300
|
+
case 'variant': {
|
|
301
|
+
return `**Variant Pattern**
|
|
302
|
+
Add new visual variants to the existing component:
|
|
303
|
+
1. Modify ${componentPath}
|
|
304
|
+
2. Add variant definitions using your variant system (e.g., CVA)
|
|
305
|
+
3. New styling: ${differences.styling.map((d) => d.description).join(', ')}
|
|
306
|
+
4. Extend the component's visual options without breaking existing usage
|
|
307
|
+
|
|
308
|
+
This approach works because the changes are primarily styling-focused.`;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=decision.js.map
|