Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions docs/mcp/tools/storefront-next-page-designer-decorator.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The `storefront_next_page_designer_decorator` tool analyzes React components and
1. **Auto Mode**: Quick setup with sensible defaults-automatically selects suitable props, infers types, and generates decorators immediately.
2. **Interactive Mode**: Multi-step workflow for fine-tuned control over decorator configuration.

The tool uses component discovery to find components by name (e.g., "ProductCard") without requiring exact file paths, making it easy to add Page Designer support to existing components.
The tool uses component discovery to find components by name (e.g., "ProductItem", "ProductTile") without requiring exact file paths, making it easy to add Page Designer support to existing components.

## Authentication

Expand All @@ -27,7 +27,7 @@ No authentication required. This tool operates on local files only.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `component` | string | Yes | Component name (for example, `"ProductCard"`, `"Hero"`) or file path (for example, `"src/components/ProductCard.tsx"`). When a name is provided, the tool automatically searches common component directories. |
| `component` | string | Yes | Component name (for example, `"ProductItem"`, `"ProductTile"`) or file path (for example, `"src/components/ProductItem.tsx"`). When a name is provided, the tool automatically searches common component directories. |
| `searchPaths` | string[] | No | Additional directories to search for components (for example, `["packages/retail/src", "app/features"]`). Only used when a component is specified by name (not path). |
| `autoMode` | boolean | No | Auto-generate all configurations with sensible defaults (skip interactive workflow). When enabled, automatically selects suitable props, infers types, and generates decorators without user confirmation. |
| `componentId` | string | No | Override component ID (default: auto-generated from component name). |
Expand Down Expand Up @@ -60,14 +60,14 @@ Auto mode generates decorators immediately with sensible defaults:
**Usage:**

```
Use the MCP tool to add Page Designer decorators to my ProductCard component with auto mode.
Use the MCP tool to add Page Designer decorators to my ProductItem component with auto mode.
```

**Example:**

```json
{
"component": "ProductCard",
"component": "ProductItem",
"autoMode": true
}
```
Expand All @@ -85,14 +85,14 @@ Interactive mode provides a multi-step workflow for fine-tuned control:
**Usage:**

```
Use the MCP tool to add Page Designer decorators to my Hero component interactively.
Use the MCP tool to add Page Designer decorators to my ProductTile component interactively.
```

**Example:**

```json
{
"component": "Hero",
"component": "ProductTile",
"conversationContext": {
"step": "analyze"
}
Expand All @@ -110,13 +110,13 @@ The tool automatically searches for components in these locations (in order):
5. Custom paths (if provided via `searchPaths`)

**Project Directory:**
Component discovery uses the project directory resolved from `--project-directory` flag or `SFCC_PROJECT_DIRECTORY` environment variable. This ensures searches start from the correct project directory, especially when MCP clients spawn servers from the home directory.
Component discovery uses the project directory from `--project-directory` flag or `SFCC_PROJECT_DIRECTORY` environment variable. This ensures searches start from the correct project directory, especially when MCP clients spawn servers from the home directory.

**Examples:**

- `"ProductCard"` → finds `src/components/product-tile/ProductCard.tsx`
- `"Hero"` → finds `src/components/hero/Hero.tsx` or `app/components/hero.tsx`
- `"product-card"` → finds `src/components/product-card.tsx` or `product-card/index.tsx`
- `"ProductItem"` → finds `src/components/product-item/index.tsx` or `ProductItem.tsx`
- `"ProductTile"` → finds `src/components/product-tile/ProductTile.tsx` or `product-tile/index.tsx`
- `"product-item"` → finds `src/components/product-item.tsx` or `product-item/index.tsx`

**Tips:**

Expand All @@ -132,31 +132,31 @@ Component discovery uses the project directory resolved from `--project-director
Add Page Designer support with auto-generated defaults:

```
Use the MCP tool to add Page Designer decorators to my ProductCard component.
Use the MCP tool to add Page Designer decorators to my ProductItem component.
```

### Auto Mode with Custom Search Paths

Search in custom directories:

```
Use the MCP tool to add Page Designer decorators to ProductCard, searching in packages/retail/src and app/features.
Use the MCP tool to add Page Designer decorators to ProductItem, searching in packages/retail/src and app/features.
```

### Interactive Mode - Start Analysis

Begin interactive workflow:

```
Use the MCP tool to analyze my Hero component for Page Designer decorators.
Use the MCP tool to analyze my ProductTile component for Page Designer decorators.
```

### Path-Based Usage

Specify exact component path:

```
Use the MCP tool to add Page Designer decorators to src/components/ProductCard.tsx with auto mode.
Use the MCP tool to add Page Designer decorators to src/components/ProductItem.tsx with auto mode.
```

## Output
Expand All @@ -174,12 +174,12 @@ The tool returns generated decorator code that includes:
import { Component, AttributeDefinition, RegionDefinition } from '@salesforce/page-designer';

@Component({
id: 'product-card',
name: 'Product Card',
id: 'product-item',
name: 'Product Item',
description: 'Displays product information',
group: 'Commerce'
})
export class ProductCardMetadata {
export class ProductItemMetadata {
@AttributeDefinition({
id: 'product-id',
name: 'Product ID',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This tool analyzes React components and generates Page Designer decorators (`@Co

## ✨ Key Features

- **Name-Based Lookup**: Find components by name (e.g., "ProductCard") without knowing paths
- **Name-Based Lookup**: Find components by name (e.g., "ProductItem", "ProductTile") without knowing paths
- **Auto-Discovery**: Automatically searches common component directories
- **Type-Safe**: Full TypeScript type inference for all contexts
- **Fast**: Direct function execution, no file I/O or compilation overhead
Expand Down Expand Up @@ -42,19 +42,19 @@ page-designer-decorator/
```bash
# By component name (automatically finds the file)
storefront_next_page_designer_decorator({
component: "ProductCard",
component: "ProductItem",
autoMode: true
})

# Interactive mode
storefront_next_page_designer_decorator({
component: "Hero",
component: "ProductTile",
conversationContext: { step: "analyze" }
})

# With custom search paths (for unusual locations)
storefront_next_page_designer_decorator({
component: "ProductCard",
component: "ProductItem",
searchPaths: ["packages/retail/src", "app/features"],
autoMode: true
})
Expand All @@ -65,14 +65,14 @@ storefront_next_page_designer_decorator({
```bash
# If you prefer to specify the exact path
storefront_next_page_designer_decorator({
component: "src/components/ProductCard.tsx",
component: "src/components/ProductItem.tsx",
autoMode: true
})
```

### Workflow

1. **Component Discovery**: Provide name (e.g., "ProductCard") or path
1. **Component Discovery**: Provide name (e.g., "ProductItem") or path
2. **Mode Selection**: Choose Auto or Interactive mode
3. **Analysis** (Interactive only): Review component props
4. **Selection** (Interactive only): Select which props to expose
Expand All @@ -91,13 +91,13 @@ The tool automatically searches for components in these locations (in order):
5. Custom paths (if provided via `searchPaths`)

**Project Directory:**
Component discovery uses the project directory resolved from `--project-directory` flag or `SFCC_PROJECT_DIRECTORY` environment variable (via Services). This ensures searches start from the correct project directory, especially when MCP clients spawn servers from the home directory.
Component discovery uses the project directory from `--project-directory` flag or `SFCC_PROJECT_DIRECTORY` environment variable (via Services). This ensures searches start from the correct project directory, especially when MCP clients spawn servers from the home directory.

**Examples:**

- `"ProductCard"` → finds `src/components/product-tile/ProductCard.tsx`
- `"Hero"` → finds `src/components/hero/Hero.tsx` or `app/components/hero.tsx`
- `"product-card"` → finds `src/components/product-card.tsx` or `product-card/index.tsx`
- `"ProductItem"` → finds `src/components/product-item/index.tsx` or `ProductItem.tsx`
- `"ProductTile"` → finds `src/components/product-tile/ProductTile.tsx` or `product-tile/index.tsx`
- `"product-item"` → finds `src/components/product-item.tsx` or `product-item/index.tsx`

**Tips:**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,29 @@ export function generateTypeSuggestions(propName: string, tsType: string): TypeS

/**
* Extract component name from file content
*
* Priority order:
* 1. export default function X (inline default function)
* 2. export default X (default export of named identifier, e.g. export default ProductItem)
* 3. export function X (first named function export)
* 4. export const X =
* 5. fallback: 'Component'
*
* Note: (2) must be checked before (3) because files may have both "export function Foo"
* and "export default Bar" — the default export is the primary component.
*/
function extractComponentName(content: string): string {
const defaultFunctionMatch = content.match(/export\s+default\s+function\s+(\w+)/);
if (defaultFunctionMatch) {
return defaultFunctionMatch[1];
}

// export default X where X is a named identifier (not "function")
const defaultNamedMatch = content.match(/export\s+default\s+(?!function\s)(\w+)/);
if (defaultNamedMatch) {
return defaultNamedMatch[1];
}

const namedFunctionMatch = content.match(/export\s+function\s+(\w+)/);
if (namedFunctionMatch) {
return namedFunctionMatch[1];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const pageDesignerDecoratorSchema = z
component: z
.string()
.describe(
'Component name (e.g., "ProductCard", "Hero") or file path (e.g., "src/components/ProductCard.tsx"). ' +
'Component name (e.g., "ProductItem", "ProductTile") or file path (e.g., "src/components/ProductItem.tsx"). ' +
'When a name is provided, the tool automatically searches common component directories. ' +
'For backward compatibility, file paths are also supported.',
),
Expand Down Expand Up @@ -634,7 +634,7 @@ export function createPageDesignerDecoratorTool(loadServices: () => Services): M
description:
'Adds Page Designer decorators (@Component, @AttributeDefinition, @RegionDefinition) to React components. ' +
'Two modes: autoMode=true for quick setup with defaults, or interactive mode via conversationContext.step. ' +
'Component discovery uses projectDirectory from flags/env. ' +
'Component discovery uses --project-directory flag or SFCC_PROJECT_DIRECTORY env var. ' +
'Auto mode: selects suitable props, infers types, generates code immediately. ' +
'Interactive mode: multi-step workflow (analyze → select_props → configure_attrs → configure_regions → confirm_generation).',

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,40 @@ export default function CollisionComponent({title}: CollisionComponentProps) { r
const text = getResultText(result);
expect(text).to.include('CollisionComponent');
});

it('should pick default export over first named export (export default X pattern)', async () => {
const tool = createPageDesignerDecoratorTool(getServices);
// Simulate product-item/index.tsx: has export function X (helper) and export default Y (main)
const productItemDir = path.join(testDir, 'src', 'components', 'product-item');
mkdirSync(productItemDir, {recursive: true});
const indexPath = path.join(productItemDir, 'index.tsx');
writeFileSync(
indexPath,
`export interface ProductItemProps { productId: string; }
export function ProductItemVariantImage() { return null; }
export function ProductItemVariantAttributes() { return null; }
function ProductItem({ productId }: ProductItemProps) {
return <div>{productId}</div>;
}
export default ProductItem;
`,
'utf8',
);

const result = await tool.handler({
component: 'ProductItem',
autoMode: true,
});

expect(result.isError).to.be.undefined;
const text = getResultText(result);
// Must target ProductItem (default export), not ProductItemVariantImage (first named export)
expect(text).to.include('ProductItem');
expect(text).not.to.include('ProductItemVariantImage');
});
});

describe('input validation', () => {
Expand Down
Loading