@salesforce/b2c-dx-mcp 2.0.0 → 2.1.0

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 (108) hide show
  1. package/README.md +15 -62
  2. package/dist/commands/mcp.d.ts +1 -0
  3. package/dist/commands/mcp.js +47 -9
  4. package/dist/registry.d.ts +3 -4
  5. package/dist/registry.js +7 -14
  6. package/dist/services.d.ts +21 -1
  7. package/dist/services.js +38 -1
  8. package/dist/tools/adapter.d.ts +19 -2
  9. package/dist/tools/adapter.js +49 -10
  10. package/dist/tools/cartridges/index.js +11 -3
  11. package/dist/tools/diagnostics/config-inspect.js +2 -2
  12. package/dist/tools/diagnostics/debug-list-sessions.js +1 -0
  13. package/dist/tools/diagnostics/debug-start-session.js +13 -2
  14. package/dist/tools/diagnostics/log-watch-registry.d.ts +3 -0
  15. package/dist/tools/diagnostics/log-watch-registry.js +1 -0
  16. package/dist/tools/diagnostics/logs-watch-list.js +1 -0
  17. package/dist/tools/diagnostics/logs-watch-start.js +6 -1
  18. package/dist/tools/diagnostics/mrt-log-watch-registry.d.ts +3 -0
  19. package/dist/tools/diagnostics/mrt-log-watch-registry.js +0 -0
  20. package/dist/tools/diagnostics/mrt-logs-watch-list.js +1 -0
  21. package/dist/tools/diagnostics/mrt-logs-watch-start.js +7 -1
  22. package/dist/tools/diagnostics/session-registry.d.ts +3 -0
  23. package/dist/tools/diagnostics/session-registry.js +2 -1
  24. package/dist/tools/index.d.ts +0 -1
  25. package/dist/tools/index.js +0 -1
  26. package/dist/tools/mrt/index.js +4 -0
  27. package/dist/tools/project-context.d.ts +42 -11
  28. package/dist/tools/project-context.js +48 -15
  29. package/dist/tools/scapi/metrics-get.js +1 -1
  30. package/dist/tools/scapi/scapi-custom-api-generate-scaffold.d.ts +4 -0
  31. package/dist/tools/scapi/scapi-custom-api-generate-scaffold.js +27 -7
  32. package/dist/tools/scapi/scapi-custom-apis-get-status.js +1 -1
  33. package/dist/tools/scapi/scapi-schemas-list.js +1 -0
  34. package/dist/utils/constants.d.ts +2 -13
  35. package/dist/utils/constants.js +1 -20
  36. package/oclif.manifest.json +2 -2
  37. package/package.json +3 -3
  38. package/content/sfnext/auth.md +0 -62
  39. package/content/sfnext/components.md +0 -123
  40. package/content/sfnext/config.md +0 -180
  41. package/content/sfnext/data-fetching.md +0 -323
  42. package/content/sfnext/extensions.md +0 -80
  43. package/content/sfnext/i18n.md +0 -121
  44. package/content/sfnext/page-designer.md +0 -78
  45. package/content/sfnext/performance.md +0 -80
  46. package/content/sfnext/pitfalls.md +0 -141
  47. package/content/sfnext/quick-reference.md +0 -226
  48. package/content/sfnext/state-management.md +0 -75
  49. package/content/sfnext/styling.md +0 -51
  50. package/content/sfnext/testing.md +0 -232
  51. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +0 -24
  52. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +0 -53
  53. package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +0 -45
  54. package/dist/tools/storefrontnext/figma/figma-to-component/index.js +0 -332
  55. package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +0 -40
  56. package/dist/tools/storefrontnext/figma/generate-component/decision.js +0 -312
  57. package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +0 -9
  58. package/dist/tools/storefrontnext/figma/generate-component/formatter.js +0 -92
  59. package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +0 -117
  60. package/dist/tools/storefrontnext/figma/generate-component/index.js +0 -102
  61. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +0 -71
  62. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +0 -260
  63. package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +0 -64
  64. package/dist/tools/storefrontnext/figma/map-tokens/index.js +0 -242
  65. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +0 -65
  66. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +0 -268
  67. package/dist/tools/storefrontnext/index.d.ts +0 -30
  68. package/dist/tools/storefrontnext/index.js +0 -28
  69. package/dist/tools/storefrontnext/page-designer-decorator/analyzer.d.ts +0 -169
  70. package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +0 -567
  71. package/dist/tools/storefrontnext/page-designer-decorator/index.d.ts +0 -259
  72. package/dist/tools/storefrontnext/page-designer-decorator/index.js +0 -622
  73. package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.d.ts +0 -8
  74. package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.js +0 -65
  75. package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.d.ts +0 -13
  76. package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.js +0 -87
  77. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.d.ts +0 -4
  78. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.js +0 -55
  79. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.d.ts +0 -22
  80. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.js +0 -109
  81. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.d.ts +0 -21
  82. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.js +0 -60
  83. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.d.ts +0 -27
  84. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.js +0 -68
  85. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.d.ts +0 -4
  86. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.js +0 -65
  87. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.d.ts +0 -11
  88. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.js +0 -92
  89. package/dist/tools/storefrontnext/page-designer-decorator/rules.d.ts +0 -51
  90. package/dist/tools/storefrontnext/page-designer-decorator/rules.js +0 -70
  91. package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.d.ts +0 -116
  92. package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.js +0 -350
  93. package/dist/tools/storefrontnext/sfnext-development-guidelines.d.ts +0 -9
  94. package/dist/tools/storefrontnext/sfnext-development-guidelines.js +0 -141
  95. package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +0 -92
  96. package/dist/tools/storefrontnext/site-theming/color-contrast.js +0 -186
  97. package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +0 -16
  98. package/dist/tools/storefrontnext/site-theming/color-mapping.js +0 -131
  99. package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +0 -11
  100. package/dist/tools/storefrontnext/site-theming/guidance-merger.js +0 -78
  101. package/dist/tools/storefrontnext/site-theming/index.d.ts +0 -14
  102. package/dist/tools/storefrontnext/site-theming/index.js +0 -128
  103. package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +0 -16
  104. package/dist/tools/storefrontnext/site-theming/response-builder.js +0 -316
  105. package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +0 -64
  106. package/dist/tools/storefrontnext/site-theming/theming-store.js +0 -417
  107. package/dist/tools/storefrontnext/site-theming/types.d.ts +0 -36
  108. package/dist/tools/storefrontnext/site-theming/types.js +0 -7
@@ -1,567 +0,0 @@
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
- import { existsSync, readFileSync } from 'node:fs';
7
- import path from 'node:path';
8
- import { globSync } from 'glob';
9
- import { Project, InterfaceDeclaration, PropertySignature } from 'ts-morph';
10
- /**
11
- * Lazily-initialized, reusable ts-morph Project for component analysis.
12
- * Creating a new Project (~40ms) on every analyzeComponent call is expensive;
13
- * reusing one with an in-memory file system avoids repeated TypeScript compiler init.
14
- */
15
- let cachedProject;
16
- function getProject() {
17
- if (!cachedProject) {
18
- cachedProject = new Project({
19
- useInMemoryFileSystem: true,
20
- skipAddingFilesFromTsConfig: true,
21
- });
22
- }
23
- return cachedProject;
24
- }
25
- // ============================================================================
26
- // TYPE INFERENCE
27
- // ============================================================================
28
- /**
29
- * Type mapping from TypeScript to SFCC Page Designer attribute types
30
- */
31
- const TYPE_MAPPING = {
32
- String: 'string',
33
- string: 'string',
34
- Number: 'integer',
35
- number: 'integer',
36
- Boolean: 'boolean',
37
- boolean: 'boolean',
38
- Date: 'string',
39
- URL: 'url',
40
- CMSRecord: 'cms_record',
41
- };
42
- /**
43
- * Valid SFCC Page Designer attribute types
44
- */
45
- export const VALID_ATTRIBUTE_TYPES = [
46
- 'string',
47
- 'text',
48
- 'markup',
49
- 'integer',
50
- 'boolean',
51
- 'product',
52
- 'category',
53
- 'file',
54
- 'page',
55
- 'image',
56
- 'url',
57
- 'enum',
58
- 'custom',
59
- 'cms_record',
60
- ];
61
- /**
62
- * Infer Page Designer attribute type from TypeScript type
63
- */
64
- export function inferPageDesignerType(tsType) {
65
- if (TYPE_MAPPING[tsType]) {
66
- return TYPE_MAPPING[tsType];
67
- }
68
- if (tsType.includes('|')) {
69
- const firstType = tsType.split('|')[0].trim();
70
- return inferPageDesignerType(firstType);
71
- }
72
- if (tsType.includes('[]') || tsType.includes('Array<')) {
73
- return 'string';
74
- }
75
- return 'string';
76
- }
77
- /**
78
- * Check if TypeScript type can be auto-inferred
79
- */
80
- export function isAutoInferredType(tsType) {
81
- return Boolean(TYPE_MAPPING[tsType]);
82
- }
83
- /**
84
- * Check if type is too complex for Page Designer
85
- */
86
- export function isComplexType(tsType) {
87
- return (tsType.includes('{') ||
88
- tsType.includes('<') ||
89
- tsType.includes('.') ||
90
- tsType.includes('=>') ||
91
- tsType.includes('React.') ||
92
- tsType.startsWith('('));
93
- }
94
- /**
95
- * Check if property is UI-only
96
- */
97
- export function isUIOnlyProp(propName) {
98
- const uiPatterns = [
99
- 'classname',
100
- 'style',
101
- 'theme',
102
- 'variant',
103
- 'size',
104
- 'color',
105
- 'loading',
106
- 'disabled',
107
- 'readonly',
108
- 'onclick',
109
- 'onchange',
110
- 'onsubmit',
111
- 'children',
112
- 'key',
113
- 'ref',
114
- ];
115
- const nameLower = propName.toLowerCase();
116
- return uiPatterns.some((pattern) => nameLower.includes(pattern));
117
- }
118
- /**
119
- * Generate Page Designer attribute type suggestions for a component prop
120
- *
121
- * **Inference Strategy:**
122
- * Uses naming patterns and TypeScript types to suggest appropriate Page Designer types.
123
- * This reduces manual configuration by auto-detecting common patterns.
124
- *
125
- * **Page Designer Types:**
126
- * - `string`: Default text input
127
- * - `url`: URL/link inputs (validates URL format)
128
- * - `image`: Image asset picker
129
- * - `html`: Rich text editor
130
- * - `markup`: HTML/markdown editor
131
- * - `enum`: Dropdown with predefined values
132
- * - `boolean`: Checkbox
133
- * - `number`: Numeric input
134
- * - `product`: Product picker (SFCC-specific)
135
- * - `category`: Category picker (SFCC-specific)
136
- *
137
- * **Heuristics (by priority):**
138
- * 1. **High Priority**: Strong patterns (url, image, product)
139
- * 2. **Medium Priority**: Contextual patterns (html, markup)
140
- * 3. **Low Priority**: Weak signals (description → markup)
141
- *
142
- * Multiple suggestions allow developers to choose the best fit.
143
- *
144
- * @param propName - Property name from component interface
145
- * @param tsType - TypeScript type string
146
- * @returns Array of type suggestions with reasoning and priority
147
- *
148
- * @example
149
- * // URL detection:
150
- * generateTypeSuggestions('imageUrl', 'string')
151
- * // => [{ type: 'url', reason: '...', priority: 'high' }]
152
- *
153
- * @example
154
- * // Image detection:
155
- * generateTypeSuggestions('heroImage', 'string')
156
- * // => [{ type: 'image', reason: '...', priority: 'high' }]
157
- *
158
- * @example
159
- * // Multiple suggestions:
160
- * generateTypeSuggestions('description', 'string')
161
- * // => [
162
- * // { type: 'markup', reason: '...', priority: 'low' },
163
- * // { type: 'html', reason: '...', priority: 'medium' }
164
- * // ]
165
- *
166
- * @example
167
- * // Product reference:
168
- * generateTypeSuggestions('product', 'string')
169
- * // => [{ type: 'product', reason: '...', priority: 'high' }]
170
- *
171
- * @public
172
- */
173
- export function generateTypeSuggestions(propName, tsType) {
174
- const suggestions = [];
175
- const nameLower = propName.toLowerCase();
176
- // URL patterns
177
- if (nameLower.includes('url') || nameLower.includes('link') || nameLower.includes('href')) {
178
- suggestions.push({
179
- type: 'url',
180
- reason: 'Property name suggests URL/link',
181
- priority: 'high',
182
- });
183
- }
184
- // Image patterns
185
- if (nameLower.includes('image') ||
186
- nameLower.includes('img') ||
187
- nameLower.includes('picture') ||
188
- nameLower.includes('background')) {
189
- suggestions.push({
190
- type: 'image',
191
- reason: 'Property name suggests image asset',
192
- priority: 'high',
193
- });
194
- }
195
- // Rich text patterns
196
- if (nameLower.includes('html') ||
197
- nameLower.includes('richtext') ||
198
- nameLower.includes('content') ||
199
- nameLower.includes('body')) {
200
- suggestions.push({
201
- type: 'markup',
202
- reason: 'Property name suggests rich content',
203
- priority: 'medium',
204
- });
205
- }
206
- // Multi-line text patterns
207
- if (nameLower.includes('description') || nameLower.includes('bio') || nameLower.includes('message')) {
208
- suggestions.push({
209
- type: 'text',
210
- reason: 'Property name suggests multi-line text',
211
- priority: 'medium',
212
- });
213
- }
214
- // Array patterns
215
- if (tsType.includes('[]') || tsType.includes('Array<')) {
216
- suggestions.push({
217
- type: 'enum',
218
- reason: 'Array types work best as enums for selection in Page Designer',
219
- priority: 'high',
220
- });
221
- }
222
- // Product/Category references
223
- if (nameLower.includes('product') && !nameLower.includes('products')) {
224
- suggestions.push({
225
- type: 'product',
226
- reason: 'Property name suggests product reference',
227
- priority: 'high',
228
- });
229
- }
230
- if (nameLower.includes('category')) {
231
- suggestions.push({
232
- type: 'category',
233
- reason: 'Property name suggests category reference',
234
- priority: 'high',
235
- });
236
- }
237
- return suggestions;
238
- }
239
- // ============================================================================
240
- // COMPONENT FILE PARSING
241
- // ============================================================================
242
- /**
243
- * Extract component name from file content
244
- *
245
- * Priority order:
246
- * 1. export default function X (inline default function)
247
- * 2. export default X (default export of named identifier, e.g. export default ProductItem)
248
- * 3. export function X (first named function export)
249
- * 4. export const X =
250
- * 5. fallback: 'Component'
251
- *
252
- * Note: (2) must be checked before (3) because files may have both "export function Foo"
253
- * and "export default Bar" — the default export is the primary component.
254
- */
255
- function extractComponentName(content) {
256
- const defaultFunctionMatch = content.match(/export\s+default\s+function\s+(\w+)/);
257
- if (defaultFunctionMatch) {
258
- return defaultFunctionMatch[1];
259
- }
260
- // export default X where X is a named identifier (not "function")
261
- const defaultNamedMatch = content.match(/export\s+default\s+(?!function\s)(\w+)/);
262
- if (defaultNamedMatch) {
263
- return defaultNamedMatch[1];
264
- }
265
- const namedFunctionMatch = content.match(/export\s+function\s+(\w+)/);
266
- if (namedFunctionMatch) {
267
- return namedFunctionMatch[1];
268
- }
269
- const namedConstMatch = content.match(/export\s+const\s+(\w+)\s*=/);
270
- if (namedConstMatch) {
271
- return namedConstMatch[1];
272
- }
273
- return 'Component';
274
- }
275
- /**
276
- * Detect export type
277
- */
278
- function detectExportType(content) {
279
- return content.includes('export default') ? 'default' : 'named';
280
- }
281
- /**
282
- * Parse component file and extract structure
283
- */
284
- function parseComponentFile(filePath) {
285
- const content = readFileSync(filePath, 'utf8');
286
- const hasDecorators = content.includes('@Component') || content.includes('@PageType');
287
- if (hasDecorators) {
288
- return {
289
- componentName: extractComponentName(content),
290
- interfaceName: null,
291
- hasDecorators: true,
292
- props: [],
293
- exportType: detectExportType(content),
294
- filePath,
295
- };
296
- }
297
- const project = getProject();
298
- const sourceFile = project.createSourceFile(filePath, content, { overwrite: true });
299
- try {
300
- const interfaces = sourceFile.getInterfaces();
301
- const propsInterface = interfaces.find((i) => i.getName().includes('Props'));
302
- if (!propsInterface) {
303
- return {
304
- componentName: extractComponentName(content),
305
- interfaceName: null,
306
- hasDecorators: false,
307
- props: [],
308
- exportType: detectExportType(content),
309
- filePath,
310
- };
311
- }
312
- const props = propsInterface.getProperties().map((prop) => {
313
- const name = prop.getName();
314
- const type = prop.getType().getText();
315
- const optional = prop.hasQuestionToken();
316
- return {
317
- name,
318
- type,
319
- optional,
320
- isComplex: isComplexType(type),
321
- isUIOnly: isUIOnlyProp(name),
322
- };
323
- });
324
- return {
325
- componentName: extractComponentName(content),
326
- interfaceName: propsInterface.getName(),
327
- hasDecorators: false,
328
- props,
329
- exportType: detectExportType(content),
330
- filePath,
331
- };
332
- }
333
- finally {
334
- project.removeSourceFile(sourceFile);
335
- }
336
- }
337
- // ============================================================================
338
- // COMPONENT ANALYZER
339
- // ============================================================================
340
- /**
341
- * Component analyzer for Page Designer decorator generation
342
- */
343
- class ComponentAnalyzer {
344
- cache = new Map();
345
- analyzeComponent(filePath) {
346
- const cached = this.cache.get(filePath);
347
- if (cached) {
348
- return cached;
349
- }
350
- const analysis = parseComponentFile(filePath);
351
- this.cache.set(filePath, analysis);
352
- return analysis;
353
- }
354
- clearCache() {
355
- this.cache.clear();
356
- }
357
- }
358
- export const componentAnalyzer = new ComponentAnalyzer();
359
- // ============================================================================
360
- // COMPONENT RESOLUTION (Name-Based Lookup)
361
- // ============================================================================
362
- /**
363
- * Convert PascalCase or camelCase to kebab-case
364
- *
365
- * Used for finding components with different naming conventions.
366
- * React components are typically PascalCase, but file names may be kebab-case.
367
- *
368
- * @param str - String to convert (e.g., "ProductCard", "myComponent")
369
- * @returns Kebab-case string (e.g., "product-card", "my-component")
370
- *
371
- * @example
372
- * toKebabCase('ProductCard') // => 'product-card'
373
- * toKebabCase('MyButtonComponent') // => 'my-button-component'
374
- * toKebabCase('heroSection') // => 'hero-section'
375
- *
376
- * @internal
377
- */
378
- function toKebabCase(str) {
379
- return str
380
- .replaceAll(/([a-z0-9])([A-Z])/g, '$1-$2')
381
- .replaceAll(/([A-Z])([A-Z][a-z])/g, '$1-$2')
382
- .toLowerCase();
383
- }
384
- /**
385
- * Search for component file by name using smart discovery patterns
386
- *
387
- * **Search Strategy (in priority order):**
388
- * 1. Common component directories with exact name (PascalCase)
389
- * 2. Kebab-case variants of the name
390
- * 3. Index file patterns (for directory-based components)
391
- * 4. Broader search in src/
392
- * 5. Custom search paths (if provided)
393
- *
394
- * **Why this order:**
395
- * - Most projects follow conventions (src/components/)
396
- * - PascalCase is React standard, checked first
397
- * - Kebab-case is common for file names
398
- * - Index files are common for complex components
399
- * - Fallback to broader search if not in standard locations
400
- *
401
- * **Disambiguation:**
402
- * If multiple files match, prefers the shortest path (closest to root).
403
- * This typically selects the main component over similar named test/story files.
404
- *
405
- * @param componentName - Component name without extension (e.g., "ProductCard", "Hero")
406
- * @param workspaceRoot - Absolute path to workspace root
407
- * @param customPaths - Additional directories to search (e.g., ["packages/retail/src"])
408
- * @returns Absolute file path or null if not found
409
- *
410
- * @example
411
- * // Finds: src/components/product-tile/ProductCard.tsx
412
- * findComponentByName('ProductCard', '/workspace', undefined)
413
- *
414
- * @example
415
- * // Finds: src/components/hero.tsx or src/components/hero/index.tsx
416
- * findComponentByName('hero', '/workspace', undefined)
417
- *
418
- * @example
419
- * // Searches in custom paths first
420
- * findComponentByName('ProductCard', '/workspace', ['packages/retail/src'])
421
- *
422
- * @internal
423
- */
424
- function findComponentByName(componentName, workspaceRoot, customPaths) {
425
- // Normalize component name (remove file extensions)
426
- const cleanName = componentName.replace(/\.(tsx?|jsx?)$/, '');
427
- const kebabName = toKebabCase(cleanName);
428
- // Search patterns (in order of priority)
429
- const searchPatterns = [
430
- // Common component directories (PascalCase)
431
- `src/components/**/${cleanName}.tsx`,
432
- `src/components/**/${cleanName}.ts`,
433
- `app/components/**/${cleanName}.tsx`,
434
- `components/**/${cleanName}.tsx`,
435
- // Kebab-case variants
436
- `src/components/**/${kebabName}.tsx`,
437
- `app/components/**/${kebabName}.tsx`,
438
- `components/**/${kebabName}.tsx`,
439
- // Index file patterns
440
- `src/components/**/${kebabName}/index.tsx`,
441
- `app/components/**/${kebabName}/index.tsx`,
442
- // Anywhere in src/ (broader search)
443
- `src/**/${cleanName}.tsx`,
444
- `src/**/${cleanName}.ts`,
445
- `src/**/${kebabName}.tsx`,
446
- // Custom search paths (if provided)
447
- ...(customPaths?.flatMap((path) => [
448
- `${path}/**/${cleanName}.tsx`,
449
- `${path}/**/${cleanName}.ts`,
450
- `${path}/**/${kebabName}.tsx`,
451
- `${path}/**/${kebabName}/index.tsx`,
452
- ]) || []),
453
- ];
454
- // Search with glob
455
- for (const pattern of searchPatterns) {
456
- try {
457
- const matches = globSync(pattern, {
458
- cwd: workspaceRoot,
459
- absolute: true,
460
- ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.next/**', '**/out/**'],
461
- });
462
- if (matches.length > 0) {
463
- // If multiple matches, prefer shortest path (closest to root)
464
- const sorted = matches.sort((a, b) => a.length - b.length);
465
- return sorted[0];
466
- }
467
- }
468
- catch {
469
- // Ignore glob errors and try next pattern
470
- continue;
471
- }
472
- }
473
- return null;
474
- }
475
- /**
476
- * Resolve component input (name or path) to absolute file path
477
- *
478
- * **This is the main entry point for component discovery.**
479
- *
480
- * Supports two input modes:
481
- * 1. **Name-based** (recommended): Just provide the component name
482
- * 2. **Path-based** (backward compatible): Provide relative path from workspace
483
- *
484
- * **Name-based detection:**
485
- * Input is treated as a name if it:
486
- * - Does NOT contain path separators (/ or \)
487
- * - Does NOT have a file extension (.tsx, .ts, etc.)
488
- *
489
- * **Path-based detection:**
490
- * Input is treated as a path if it:
491
- * - Contains / or \
492
- * - Has a file extension
493
- *
494
- * @param input - Component name or relative path
495
- * @param workspaceRoot - Absolute path to workspace root
496
- * @param searchPaths - Additional directories to search (only used for name-based)
497
- * @returns Absolute file path to component
498
- * @throws {Error} If component cannot be found, with detailed search information
499
- *
500
- * @example
501
- * // Name-based (finds automatically):
502
- * resolveComponent('ProductCard', '/workspace')
503
- * // => '/workspace/src/components/product-tile/ProductCard.tsx'
504
- *
505
- * @example
506
- * // Path-based (backward compatible):
507
- * resolveComponent('src/components/ProductCard.tsx', '/workspace')
508
- * // => '/workspace/src/components/ProductCard.tsx'
509
- *
510
- * @example
511
- * // With custom search paths (for monorepos):
512
- * resolveComponent('Hero', '/workspace', ['packages/retail/src', 'packages/shared'])
513
- * // => '/workspace/packages/retail/src/components/Hero.tsx'
514
- *
515
- * @example
516
- * // Error handling:
517
- * try {
518
- * resolveComponent('NonExistent', '/workspace')
519
- * } catch (err) {
520
- * // Error includes:
521
- * // - List of searched locations
522
- * // - Tried name variations
523
- * // - Helpful tips for resolution
524
- * }
525
- *
526
- * @public
527
- */
528
- export function resolveComponent(input, workspaceRoot, searchPaths) {
529
- // Check if input looks like a path (has / or \ or file extension)
530
- const looksLikePath = input.includes('/') || input.includes('\\') || input.match(/\.(tsx?|jsx?|mjs|cjs|js)$/);
531
- if (looksLikePath) {
532
- // Treat as path (backward compatible)
533
- const fullPath = path.join(workspaceRoot, input);
534
- if (existsSync(fullPath)) {
535
- return fullPath;
536
- }
537
- throw new Error(`Component file not found at path: ${input}\n\n` +
538
- `Full path checked: ${fullPath}\n\n` +
539
- `Tips:\n` +
540
- ` 1. Use component name instead (e.g., "ProductCard") for automatic discovery\n` +
541
- ` 2. If components are in a different repo, set --project-directory flag or SFCC_PROJECT_DIRECTORY env var`);
542
- }
543
- // Treat as component name - search for it
544
- const found = findComponentByName(input, workspaceRoot, searchPaths);
545
- if (!found) {
546
- const searchLocations = [
547
- 'src/components/**',
548
- 'app/components/**',
549
- 'components/**',
550
- 'src/**',
551
- ...(searchPaths || []),
552
- ];
553
- throw new Error(`Component "${input}" not found.\n\n` +
554
- `Searched in:\n${searchLocations.map((loc) => ` - ${loc}`).join('\n')}\n\n` +
555
- `Tried variations:\n` +
556
- ` - ${input}.tsx\n` +
557
- ` - ${toKebabCase(input)}.tsx\n` +
558
- ` - ${toKebabCase(input)}/index.tsx\n\n` +
559
- `Tips:\n` +
560
- ` 1. Provide full path: component: "src/components/ProductCard.tsx"\n` +
561
- ` 2. Add custom search: searchPaths: ["packages/retail/src"]\n` +
562
- ` 3. Check component name spelling and casing\n` +
563
- ` 4. If components are in a different repo, set --project-directory flag or SFCC_PROJECT_DIRECTORY env var`);
564
- }
565
- return found;
566
- }
567
- //# sourceMappingURL=analyzer.js.map