关于 sales-app-extensibility
面向 FastStore 项目的 VTEX Sales App 扩展开发与部署技能,指导 Agent 按步骤检查环境、规划需求、生成代码并完成验证和部署。
主要功能
- 01检查 FastStore 与 Sales App 前置条件
- 02七步扩展开发工作流
- 03选择扩展点与 Hook
- 04提供界面和 API 扩展模板
- 05TypeScript 与静态分析验证
- 06文档和部署指导
适用场景
- 01搭建购物车促销扩展
- 02添加商品详情徽章或推荐
- 03将 API 集成到 Sales App 扩展
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vtex/ skills --skill sales-app-extensibility安装后在 Claude Code 中输入/sales-app-extensibility确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/code-templates-and-patterns.mddesign-guidelines.mddiscovery-and-use-cases.mddocumentation-template.mdextension-points-hooks-and-types.mdlocal-dev-build-and-deploy.mdstatic-analysis-rules.md详细介绍
销售应用扩展
适用场景
在构建、自定义或部署 VTEX Sales App 扩展时使用此技能。
- 为购物车页面添加功能(促销、忠诚度计划、服务)
- 为商品详情页添加功能(徽章、推荐、保修)
- 为菜单添加功能(用户资料、导航、指标)
- 将外部 API 集成到 Sales App 扩展中
- 生成、搭建或验证 Sales App 扩展代码
以下场景不要使用此技能:
- 常规 FastStore 店面自定义(使用
faststore-storefront) - 构建 VTEX IO 应用(使用
vtex-io-*技能) - Sales App 核心开发或框架修改
前置条件:FastStore 项目
项目根目录必须包含:biome.json、faststore.json、package.json、tsconfig.json、turbo.json。
如果缺少其中任何文件,请停止。用户必须先安装 FastStore:
npx @vtex/fsp-cli init
# Prompt: "What is the application name?" → enter name or press Enter for default
cd <application-name> && yarn
文档:https://beta.fast.store/getting-started
前置条件:Sales App 模块
FastStore 项目中必须存在 Sales App 工作区(通常位于 packages/sales-app),并包含 src/、package.json 和 tsconfig.json。检查根目录 package.json 中的 "workspaces",确认是否包含路径中带有 sales-app 的工作区。若缺少,请停止:
yarn add @vtex/sales-app -D -W
npx fsp create
# Prompts: account name → "Sales App" → path (default or custom)
# Then add the path to root package.json "workspaces" array
yarn install
文档:https://beta.fast.store/sales-app/setting-up
在确认两项前置条件都满足之前,不要继续进行发现或代码生成。
决策规则
严格按照 7 步工作流(步骤 0–6)的顺序执行。不要跳过步骤。
| 步骤 | 目的 | 条件 |
|---|---|---|
| 0 | 检查前置条件 | 已安装 FastStore + Sales App → 继续;否则停止 |
| 1 | 需求发现 | 了解用户想要构建什么 |
| 2 | 需求与计划 | 梳理需求 → 生成计划 → 等待用户批准 |
| 3 | 代码生成与验证 | 生成 Component.tsx + Component.css(纯 CSS,绝不使用 .module.css)+ index.tsx → 验证 |
| 4 | 文档 | 生成 docs/<ExtensionName>.md,说明该扩展 |
| 5 | 本地测试 | 提供开发命令和 URL |
| 6 | 构建与部署 | 构建命令 → 部署指南 |
扩展点选择
| 扩展点 | 类别 | 可用钩子 | 布局偏移 |
|---|---|---|---|
cart.cart-list.after | 购物车 | useCart、useExtension | 否 |
cart.cart-item.after | 购物车 | useCart、useCartItem、useExtension | 是 |
cart.order-summary.after | 购物车 | useCart、useExtension | 是 |
pdp.sidebar.before | PDP | usePDP、useCart、useExtension | 是 |
pdp.sidebar.after | PDP | usePDP、useCart、useExtension | 是 |
pdp.content.after | PDP | usePDP、useCart、useExtension | 是 |
menu.item | 菜单 | useExtension | 否 |
menu.drawer-content | 菜单 | useCurrentUser、useExtension | 否 |
钩子可用性
- useCart → 所有购物车 + PDP 扩展
- useCartItem → 仅适用于
cart.cart-item.after - useCurrentUser → 仅适用于
menu.drawer-content - usePDP → 仅适用于 PDP 扩展
- useExtension → 所有扩展点
模板选择
- 无 API + 无钩子 → 简单模板
- 无 API + 有钩子 → 钩子模板
- 有 API + 无身份验证 → API 模板
- 有 API + VTEX IO 代理 → IO 代理模板(推荐)
- 有 API + 直接身份验证 → 直接身份验证模板(不安全,警告用户)
- 提供了 API 文档 → 根据提取的响应结构生成 TypeScript 接口;未提供 API 文档 → 使用
${DATA_INTERFACE}占位符
API 身份验证策略
- 推荐:VTEX IO 代理应用 — IO 应用在服务端存储密钥。扩展使用
credentials: 'include',并通过相对路径访问(/_v/my-api/data)。 - 不安全:直接身份验证 — 密钥存储在前端代码中,可在浏览器中查看。仅用于测试和开发。
硬性限制
约束:组件必须返回 JSX.Element,不能返回 null
defineExtensions 需要返回 Element 的 ExtensionPointComponent,而不是 Element | null。
为何重要
返回 null 会导致 TypeScript 编译错误。构建将失败。
检测方式
在任何使用 defineExtensions 注册的组件中出现 return null。
正确
export function MyExtension(): JSX.Element {
if (!data) return (<></>);
return <div>{data.value}</div>;
}
错误
export function MyExtension(): JSX.Element | null {
if (!data) return null;
return <div>{data.value}</div>;
}
约束:使用前检查可选属性
必须对 CartItem.manualPrice (number | undefined)、CartItem.productRefId (string | undefined)、CartItem.attachments (Attachment[] | undefined) 进行检查。
为何重要 TypeScript 严格模式会拒绝访问可能为 undefined 的值。
检测方式
使用 item.manualPrice、item.productRefId 或 item.attachments 时,没有使用 ?.、??、&& 或 != null。
正确
const price = item.manualPrice ?? item.sellingPrice;
const refId = item.productRefId ?? 'N/A';
const count = item.attachments?.length ?? 0;
错误
const price = item.manualPrice;
const refId = item.productRefId.toUpperCase();
const count = item.attachments.length;
约束:useCartItem().item 可能为 undefined
useCartItem() 返回的 item 类型为 CartItem | undefined。
为何重要 访问 undefined 的属性会导致运行时崩溃。
检测方式
从 useCartItem() 解构出的 item,未经过 if (!item) 检查便被使用。
正确
const { item } = useCartItem();
if (!item) return (<></>);
return <div>{item.name}</div>;
错误
const { item } = useCartItem();
return <div>{item.name}</div>;
约束:index.tsx 中必须使用 defineExtensions
入口点必须使用来自 @vtex/sales-app 的 defineExtensions。
为何重要 如果没有它,构建会成功,但不会渲染任何扩展。
检测方式
index.tsx 中缺少 defineExtensions 导入或调用。
正确
import { defineExtensions } from '@vtex/sales-app';
import { MyExtension } from './components/MyExtension';
export default defineExtensions({ 'cart.cart-list.after': MyExtension });
错误
import { MyExtension } from './components/MyExtension';
export default { 'cart.cart-list.after': MyExtension };
约束:扩展点名称必须完全匹配
ID 是固定集合。不存在的名称会静默失败。
为何重要 扩展不会在任何位置渲染。不会出现构建错误,但运行时不可见。
检测方式
名称不在以下列表中:cart.cart-list.after、cart.cart-item.after、cart.order-summary.after、pdp.sidebar.before、pdp.sidebar.after、pdp.content.after、menu.item、menu.drawer-content。
正确
defineExtensions({ 'cart.cart-list.after': MyExtension });
错误
defineExtensions({ 'cart.list.after': MyExtension });
约束:必须在兼容的扩展点中使用 Hook
每个 Hook 都有 available_in 限制。
为何重要 Hook 上下文只会在特定扩展点中挂载。在其他位置使用会引发运行时错误。
检测方式
在 PDP/menu 中使用 useCartItem。在 cart 中使用 useCurrentUser。在 menu/cart-list 中使用 usePDP。
正确
// useCartItem in cart.cart-item.after ✓
defineExtensions({ 'cart.cart-item.after': ItemWarranty });
错误
// useCartItem in pdp.sidebar.after ✗ — will fail at runtime
defineExtensions({ 'pdp.sidebar.after': ItemWarranty });
推荐模式
工作流概览
步骤 0 — 检查 FastStore + Sales App 的先决条件。若缺少,则停止。
步骤 1 — 探索。根据关键词识别用例,提出后续问题,并确定 API 身份验证策略。如果用户提供了 API 文档(URL、OpenAPI/Swagger 文件、Markdown 或内联文本),则导入文档以提取端点详情和响应结构——跳过相应的手动提问。与用户确认提取的信息。加载探索参考文档,了解详细的问题流程和 API 文档导入部分。
步骤 2 — 将需求映射到扩展点 + hooks + 模板。提供计划,列出要创建的文件:components/<ComponentName>.tsx、components/<ComponentName>.css(纯 CSS——绝不使用 .module.css)和 index.tsx。等待批准。
步骤 3 — 生成 <ComponentName>.tsx、<ComponentName>.css(纯 CSS,绝不使用 .module.css)和 index.tsx。
此步骤所需的参考文档(生成前加载):
- code-templates-and-patterns.md — 所有组件模板、CSS 模板(内含完整的 Sales App 设计系统:设计令牌、排版、间距、响应式设计)、
index.tsx模板、类型生成规则和自定义 fetch hook 模式。 - extension-points-hooks-and-types.md — hook 返回类型和 TypeScript 定义。
- static-analysis-rules.md —
@vtex/fsp-analyzer中的沙箱安全、CSS 隔离和 React 性能规则。对所有生成的文件运行这些检查。 - design-guidelines.md — 扩展会渲染任何 UI 文本(句首字母大写规则)或使用图标(Phosphor Icons)时加载。
如果在步骤 1 中导入了 API 文档,则根据提取的响应结构生成 TypeScript 接口,并在组件中使用这些接口,而不是 ${DATA_INTERFACE} 占位符。如果扩展会调用 2 个或更多端点,则将 fetch 逻辑提取到自定义 hook 中。根据 code-templates-and-patterns.md 中的 12 项检查清单进行验证。在展示代码之前修复所有违规项;向用户指出警告。
步骤 4 — 在 Sales App 包内生成 docs/<ExtensionName>.md(如有需要,创建 docs/ 文件夹)。加载文档模板参考,了解所需的 9 部分结构和待填写的 Markdown 框架。
步骤 5 — 提供本地开发命令和测试 URL。加载开发/构建/部署参考文档。
步骤 6 — 提供构建命令和部署指南。加载开发/构建/部署参考文档。
参考文件
根据任务需要按需加载这些文件。不要一次性全部加载。
| 文件 | 加载时机 |
|---|---|
| references/extension-points-hooks-and-types.md | 选择扩展点、选择钩子、查询 TypeScript 类型(CartItem、ProductSku、Totalizers、Attachment),或检查钩子返回值及其在各扩展点中的可用性时 |
| references/code-templates-and-patterns.md | 生成扩展代码时——简单、钩子、API、IO Proxy 或 Direct Auth 模板;内嵌完整 Sales App 设计系统(令牌、排版、间距、响应式)的 CSS 模板;包含 defineExtensions 的 index.tsx;钩子初始化;验证清单 |
| references/discovery-and-use-cases.md | 执行步骤 1(发现)时——用例检测关键词、后续问题、API 身份验证决策树、IO Proxy 与 Direct Auth 流程 |
| references/local-dev-build-and-deploy.md | 执行步骤 5–6 时——开发服务器命令、测试 URL、构建命令、常见构建错误、FastStore WebOps 部署、监控、回滚 |
| references/static-analysis-rules.md | 验证生成的代码(步骤 3)时——来自 @vtex/fsp-analyzer 的沙箱安全、CSS 限制和 React 性能规则;包含违规 ID、检测模式及正确/错误示例的完整规则目录 |
| references/design-guidelines.md | 撰写 UI 文本(句式大小写规则)或使用图标(Phosphor Icons)时。CSS 相关设计规则——令牌、排版、间距、响应式——已直接内嵌在 code-templates-and-patterns.md 的 CSS 模板中,因此生成 CSS 时无需此文件。 |
| references/documentation-template.md | 在步骤 4 中编写 docs/<ExtensionName>.md 文件时——9 部分结构和 Markdown 模板 |
常见失败模式
- 跳过前置检查——未安装 FastStore/Sales App 就生成代码。务必在步骤 1 前确认两者均已安装。
- 未提供计划——用户可能希望采用其他方案。生成代码前,务必在步骤 2 确认计划。
- 跳过文档——生成扩展时未附带
docs/<ExtensionName>.md。加载 documentation-template.md 获取必需的结构。 - 臆造 API 响应类型——生成的接口与实际 API 不符。如果用户提供了文档,请据此推导类型;如果没有,请向用户索取示例 JSON 响应。
- 忽略用户提供的 API 文档——用户提供了 URL 或文件,但 Agent 仍然手动提问。务必先检查文档,并使用 API 文档导入流程。
- 使用内联 fetch 请求 2 个或更多端点——在组件主体内调用多个
fetch。将其提取到hooks/use{Purpose}.ts中的自定义钩子。 - 将代码放在 packages/sales-app/src/ 之外——此路径之外的文件不会纳入构建。
代码级违规(DOM API、Node 导入、eval、CSS 限制、React 性能、设计令牌)由
@vtex/fsp-analyzer强制检查,并会在步骤 3 验证时被捕获。请参阅 static-analysis-rules.md 和 design-guidelines.md。
审核清单
工作流检查点
- 已安装 FastStore(biome.json、faststore.json、package.json、tsconfig.json、turbo.json)?
- 已安装 Sales App 模块(sales-app 目录中的 src/、package.json、tsconfig.json)?
- 已完成探索并确定用例?
- 用户已批准执行计划?
- 扩展点有效(来自 8 点参考)?
- Hooks 与所选扩展点兼容?
- 已在
docs/<ExtensionName>.md生成文档(9 个部分,使用 documentation-template.md)? - 如果提供了 API 文档,TypeScript 接口是否与文档中的响应结构匹配?
- 如果使用了 2 个或更多 API 端点,是否已将 fetch 逻辑提取到
hooks/中的自定义 Hook? - 构建通过:
yarn fsp build {account} sales-app? - 已在本地测试:
yarn fsp dev {account}?
TypeScript / 运行时防护(fsp-analyzer 无法检测)
- 组件是否返回 JSX.Element,而非 null?
- 是否对可选属性进行了防护(manualPrice、productRefId、attachments)?
- 是否检查了 useCartItem().item 是否为 undefined?
- 是否在 index.tsx 中配置了 defineExtensions?
其他所有代码级规则(沙箱 API、CSS 隔离、React 性能、设计令牌)均由
@vtex/fsp-analyzer强制执行,并在步骤 3 中检查。请参阅 static-analysis-rules.md 和 design-guidelines.md。
相关技能
faststore-storefront— Sales App 之外的 storefront 自定义vtex-io-app-contract— 构建 VTEX IO 代理应用,以实现安全的 API 集成
参考资料
- Sales App 设置:https://beta.fast.store/sales-app/setting-up
- FastStore 入门指南:https://beta.fast.store/getting-started
- VTEX Sales App 文档:https://help.vtex.com/en/tracks/instore-getting-started-and-setting-up
- VTEX IO 应用开发:https://developers.vtex.com/docs/guides/vtex-io-documentation-developing-an-app