@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.
Files changed (84) hide show
  1. package/README.md +82 -370
  2. package/content/pwav3/components.md +400 -0
  3. package/content/pwav3/config.md +124 -0
  4. package/content/pwav3/data-fetching.md +213 -0
  5. package/content/pwav3/extensibility.md +167 -0
  6. package/content/pwav3/i18n.md +214 -0
  7. package/content/pwav3/quick-reference.md +169 -0
  8. package/content/pwav3/routing.md +107 -0
  9. package/content/pwav3/state-management.md +193 -0
  10. package/content/pwav3/styling.md +248 -0
  11. package/content/pwav3/testing.md +124 -0
  12. package/content/site-theming/theming-accessibility.md +126 -0
  13. package/content/site-theming/theming-questions.md +208 -0
  14. package/content/site-theming/theming-validation.md +174 -0
  15. package/dist/commands/mcp.d.ts +3 -3
  16. package/dist/commands/mcp.js +7 -7
  17. package/dist/registry.js +1 -1
  18. package/dist/services.d.ts +15 -15
  19. package/dist/services.js +21 -14
  20. package/dist/tools/adapter.d.ts +2 -2
  21. package/dist/tools/adapter.js +2 -2
  22. package/dist/tools/cartridges/index.js +1 -6
  23. package/dist/tools/index.d.ts +1 -4
  24. package/dist/tools/index.js +1 -4
  25. package/dist/tools/mrt/index.js +4 -9
  26. package/dist/tools/pwav3/index.d.ts +12 -3
  27. package/dist/tools/pwav3/index.js +5 -63
  28. package/dist/tools/pwav3/pwa-kit-development-guidelines.d.ts +9 -0
  29. package/dist/tools/pwav3/pwa-kit-development-guidelines.js +151 -0
  30. package/dist/tools/scapi/index.d.ts +1 -1
  31. package/dist/tools/scapi/index.js +6 -1
  32. package/dist/tools/scapi/scapi-custom-api-scaffold.d.ts +60 -0
  33. package/dist/tools/scapi/scapi-custom-api-scaffold.js +175 -0
  34. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +24 -0
  35. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +53 -0
  36. package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +42 -0
  37. package/dist/tools/storefrontnext/figma/figma-to-component/index.js +325 -0
  38. package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +40 -0
  39. package/dist/tools/storefrontnext/figma/generate-component/decision.js +312 -0
  40. package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +9 -0
  41. package/dist/tools/storefrontnext/figma/generate-component/formatter.js +92 -0
  42. package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +114 -0
  43. package/dist/tools/storefrontnext/figma/generate-component/index.js +98 -0
  44. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +71 -0
  45. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +260 -0
  46. package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +61 -0
  47. package/dist/tools/storefrontnext/figma/map-tokens/index.js +234 -0
  48. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +65 -0
  49. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +268 -0
  50. package/dist/tools/storefrontnext/index.d.ts +17 -0
  51. package/dist/tools/storefrontnext/index.js +10 -60
  52. package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +15 -0
  53. package/dist/tools/storefrontnext/page-designer-decorator/index.js +3 -3
  54. package/dist/tools/storefrontnext/{developer-guidelines.js → sfnext-development-guidelines.js} +3 -3
  55. package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +92 -0
  56. package/dist/tools/storefrontnext/site-theming/color-contrast.js +186 -0
  57. package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +16 -0
  58. package/dist/tools/storefrontnext/site-theming/color-mapping.js +131 -0
  59. package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +11 -0
  60. package/dist/tools/storefrontnext/site-theming/guidance-merger.js +78 -0
  61. package/dist/tools/storefrontnext/site-theming/index.d.ts +14 -0
  62. package/dist/tools/storefrontnext/site-theming/index.js +122 -0
  63. package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +16 -0
  64. package/dist/tools/storefrontnext/site-theming/response-builder.js +316 -0
  65. package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +62 -0
  66. package/dist/tools/storefrontnext/site-theming/theming-store.js +410 -0
  67. package/dist/tools/storefrontnext/site-theming/types.d.ts +35 -0
  68. package/dist/tools/storefrontnext/site-theming/types.js +7 -0
  69. package/oclif.manifest.json +8 -5
  70. package/package.json +9 -6
  71. /package/content/{auth.md → sfnext/auth.md} +0 -0
  72. /package/content/{components.md → sfnext/components.md} +0 -0
  73. /package/content/{config.md → sfnext/config.md} +0 -0
  74. /package/content/{data-fetching.md → sfnext/data-fetching.md} +0 -0
  75. /package/content/{extensions.md → sfnext/extensions.md} +0 -0
  76. /package/content/{i18n.md → sfnext/i18n.md} +0 -0
  77. /package/content/{page-designer.md → sfnext/page-designer.md} +0 -0
  78. /package/content/{performance.md → sfnext/performance.md} +0 -0
  79. /package/content/{pitfalls.md → sfnext/pitfalls.md} +0 -0
  80. /package/content/{quick-reference.md → sfnext/quick-reference.md} +0 -0
  81. /package/content/{state-management.md → sfnext/state-management.md} +0 -0
  82. /package/content/{styling.md → sfnext/styling.md} +0 -0
  83. /package/content/{testing.md → sfnext/testing.md} +0 -0
  84. /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