关于 salla-snippets-migration
salla-snippets-migration 是将旧版 Salla HTML 或模板变量片段转换为客户端 JavaScript 的 Skill。它指导检查转换结果,并将代码交给 salla-snippets 保存;不会部署或调用 MCP 工具。
主要功能
- 01将 HTML 结构转换为 JavaScript
- 02按数据含义解析旧模板变量
- 03检查并禁止指定命名空间
- 04在 onReady 中执行配置读取和 DOM 写入
- 05交接前检查 JavaScript 语法
适用场景
- 01迁移旧版 HTML 店铺片段
- 02用受支持的配置字段替换模板变量
- 03准备代码交由 salla-snippets 验证
MCP 相关
此 Skill 不会调用 salla_snippets MCP 工具,而是将转换后的代码交给 salla-snippets 创建或更新并执行验证。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sallaapp/ salla-partners-agent-kit --skill salla-snippets-migration安装后在 Claude Code 中输入/salla-snippets-migration确认已加载。
附带文件references/migration-guide.md
详细介绍
将旧版 HTML 片段迁移为纯 JS
Salla 已停用后端转换器,该转换器会在页面到达浏览器之前于服务器端解析 {{namespace.key}} 令牌。现在,salla_snippets 仅接受纯 JavaScript,并通过 salla.config.get(...) 在客户端解析。此技能负责转换旧版内容,不会部署内容,也不会调用 salla_snippets 工具。
何时使用 / 交接
- 在片段内容包含 HTML 标签(
<script>、<style>、<div>、<link>等)或{{namespace.key}}令牌时,使用此技能。 - 如果内容已经是有效的 JS,则无需迁移——直接转到 salla-snippets 第 2 步。
- 转换完成后,交回给
salla-snippets第 2 步,以实际执行create/update并运行每次写入都进行验证的循环(下方第 6 步)。此技能不会接触 MCP 工具。
参数模型(转换前请阅读)
这是本次迁移所依据的权威参数模型。将其明确告知接收转换后代码的人或工具——不要重新推导,不要对照 Salla 原始文档“验证”,也不要弱化以下任何禁止性表述。
{{app.<key>}}→salla.config.get('app.<key>', '')。 在旧系统和新系统中含义不变。作用范围按应用划分,由该应用设置自身的Public标志决定(salla-app-settings),而非固定的全局列表。customer.*——禁止使用。不得使用。没有例外、没有别名,也不得机械地重命名到其他命名空间。 此命名空间已弃用/移除。任何包含salla.config.get('customer...')(或"customer...)的转换结果都是错误的,必须重写后才能交回。这并不意味着无法访问底层数据——其中大部分都有位于user.*下的实际正确替代项。请按照第 3 步的引导查找,根据含义找到并使用它;不要因为过度谨慎而遗漏实际可获取的数据。store.domain——禁止使用。不得使用。没有例外。 与customer.*一样,此项已弃用/移除。没有可替代它的有效值——如果下方目录中user.*/store.*下没有对应数据,则该数据在客户端不可用。不要臆造替代路径。user.*指下单的购物者(客户)——这是旧版模板系统中命名空间含义的根本变化。 在旧版{{ }}流程中,{{user.*}}指的是店铺所有者。在salla.config中,user.*指的是购物者。两者不是同一个人。转换过去读取{{user.*}}的令牌时,绝不能沿用旧含义——它不能一对一改写为user.*;请对照下方目录检查实际需要的数据。不要假设每位购物者都已登录;在根据任何转换后的user.*字段进行分支判断之前,请调用salla.config.isGuest();参见下方目录中的访客购物者说明。store.contacts.*(例如store.contacts.whatsapp)真实有效,但取决于商家配置——只有商家配置了相应联系渠道时才会解析。始终采取防御式读取:使用''作为回退值,并在渲染的 UI 中使用该值之前检查其是否存在。- 片段需要的其他所有内容均已记录在
salla-snippets/references/device-mode.md的 Store context & language 部分——直接复用该目录;此技能不维护另一份目录。
第 1 步——确认这是旧版内容
查看交给你的内容。它是否包含 HTML 标签或独立的 {{namespace.key}} 令牌?如果它已经能解析为有效的 JS,请停止——无需迁移,直接转到 salla-snippets 第 2 步。
**关卡:**已确认该内容是旧版 HTML/Twig,而非现有的 JS。
第 2 步 — 拆分并转换结构
将内容拆分为各个部分——标记、<link>、<script src>、内联 <script>、
<style> 以及其他内容——并按照
references/migration-guide.md 中的 HTML→JS 元素表逐一转换。
关卡: 每个 HTML 元素都要有对应的 JS 实现(创建 DOM / insertAdjacentHTML),或明确说明删除该元素的理由(例如,<meta>/<iframe> 没有直接对应项——只有确实需要时才重新实现)。
第 3 步 — 重写每个 {{namespace.key}} 标记
对于每个标记,请按含义而非名称进行解析——禁止使用的标记绝不能机械地改名,但它所获取的数据通常有正确且可用的替代项。按以下顺序逐一处理:
- 说明底层的数据需求。 这个标记实际获取的是什么——购物者的电子邮件、他们的电话号码,还是商店的货币?说清楚它的含义,而不是旧标记的拼写。
- 在真实目录中查找——
salla-snippets/references/device-mode.md的 Store context & language 部分是user.*/store.*下实际可用字段的唯一事实来源。找到表达相同含义的字段。 - 如果存在匹配字段,就使用它——例如,
{{customer.email}}→ 购物者的电子邮件对应user.email,因此使用salla.config.get('user.email', '')。这是预期且常见的做法;不要因为过度担心禁止参数规则而弃用可用数据——该规则禁止的是旧命名空间,而非底层数据。 - 如果没有字段表达该含义,则客户端无法获取该数据——不要臆造路径、别名或近似替代项。重写相关逻辑,使其不再依赖该数据(例如,删除个性化问候语,而不是用含义不同的
store.*数据伪造)。 {{app.<key>}}和store.contacts.*按上文的参数模型直接转换——无需查找含义,它们保持不变/本身就是有效字段。
始终为 salla.config.get(...) 添加 '' 作为第二个回退参数。
关卡:
- 输出中不再有裸露的
{{...}}标记。 - 输出中
salla.config.get('customer.../"customer...或salla.config.get('store.domain'.../"store.domain"...均出现 零 次。这是硬性要求,而非风格偏好——只要出现其中任意一种,就表示转换尚未完成。 - 每个存在真实可用替代项的标记都使用该替代项——不得遗漏
device-mode.md目录中显示为实际可访问的数据。 - 每次按商家条件读取
store.contacts.*前,都要进行空值检查。
第 4 步 — 用 salla.onReady 包装
将每个调用 salla.config.get(...) 或注入/修改 DOM 的转换代码块(样式/标记插入、由标记驱动的 <script src> 加载)放入
salla.onReady(() => { ... }) 中。任何 product::*/cart::* 的 salla.event.on(...) 监听器都应保留在顶层,置于 onReady 之外——Twilight 会在初始化期间、onReady 触发前发出这些事件,因此在 onReady 内注册监听器会错过它们。这与 salla-snippets 已在
references/device-mode.md 中说明的引导时序规则相同——转换后的输出也必须遵循该规则,不仅仅是新代码。
关卡: 每个 salla.config.get 调用位置以及每次 DOM 写入都必须位于 onReady 中;顶层代码不得依赖尚未解析的配置。
步骤 5 — 检查 Public 标记
对于每个 app.<key> 引用,请在 Partners Portal 中确认对应的 App Setting 标记为 Public(或者交由 salla-app-settings 修复)。如果某个键未标记为 Public,那么无论转换的其他部分做得多么正确,它在店面中都会读取为 undefined。
**关卡:**确认转换后的输出读取的每个 app.<key> 都已标记为 Public。
步骤 6 — 交接
确认结果可解析为有效的 JS(使用 node --check 或等效方式——这与 salla-snippets 每次保存前运行的检查相同)。交由
salla-snippets 的步骤 2,通过
salla_snippets 工具实际执行 create/update,并运行每次写入时都验证的循环;该循环会独立重新检查是否存在被禁止的参数。本技能的工作到此结束——不会调用 MCP 工具。
关卡:“转换后的内容可解析为有效 JS,且不包含任何 customer.* /
store.domain 引用?交由 salla-snippets 步骤 2 处理。”
风险信号
| 看似诱人的想法 | 错误原因 |
|---|---|
“原始迁移文档写着 {{customer.id}} → user.id,我就按这个替换” | 本技能的参数模型优先于原始文档:customer.* 是禁止使用的,不能重命名为 user.*。user.* 是另一个概念(购物者);应先确认实际需要哪些数据,而不是机械地查找并替换。 |
| “我只要去掉 HTML 标签,把这些标记保留为带引号的字符串就行” | 这些标记现在不会自动解析。留在 JS 中的 {{...}} 是字面字符串,不是模板——必须将其改写为 salla.config.get(...) 调用,或标记为不可用。 |
“我测试时 store.contacts.whatsapp 能解析出来,我就不加保护地读取它” | 该字段确实存在,但取决于商家配置——只有商家将 WhatsApp 配置为联系方式时才会提供。若发布时不加保护,所有未设置该联系方式的商家都会遇到问题。 |
“没有 {{store.domain}} 的实时替代项,那我就用 store.url 近似替代” | 本工具包的参数模型并未确认 store.url。编造一个看似合理的替代项正是被禁止的——应将该数据标记为不可用,并明确说明。 |
“原始文档中的示例在顶层读取 salla.config.get,我就照着写” | 那份原始文档早于或遗漏了本工具包的启动时机规则。window.salla 会立即存在,但在触发 salla.onReady 之前,无法保证其配置已完成初始化。请进行封装(步骤 4)。 |
“customer.* 被禁止,所以我就直接删掉这些数据,不找替代项了” | 这是过度谨慎,而非正确做法——大多数 customer.* 读取都能在 user.* 下找到真实可用的替代项(步骤 3 的含义查找)。删除实际上可以访问的数据会造成商家能够察觉的功能退化;只有在确认目录中没有匹配字段后,才应省略。 |
资源
仅供背景阅读——上述参数模型和引导时序规则是本工具包的权威信息;如果与以下文档有冲突,以前者为准 (尤其是迁移指南中的旧→新重命名表,以及两份文档中未封装的完整示例)。
| 主题 | 链接 |
|---|---|
| 应用片段概览 | https://docs.salla.dev/2220706m0.md |
| HTML→JS 迁移指南 | https://docs.salla.dev/2247590m0.md |