mcpField
W
weaverse-integration

weaverse-integration

github.com/weaverse开发92129 次安装2 周前更新
代码仓库

关于 weaverse-integration

面向尚未接入 Weaverse 的 Hydrogen 项目,指导开发者完成现状分析、SDK 配置、组件转换、路由迁移与验证。强调沿用项目既有代码风格,并可分阶段迁移。

主要功能

  1. 01分析 Hydrogen 版本与项目结构
  2. 02配置 Weaverse SDK 和环境变量
  3. 03将组件转换为可编辑区块
  4. 04注册区块并迁移路由
  5. 05按需逐步迁移页面

适用场景

  1. 01为现有 Hydrogen 商店接入 Weaverse
  2. 02将促销横幅等组件转换为可编辑区块
  3. 03逐步为首页和商品页启用 Weaverse

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/weaverse/shopify-hydrogen-skills --skill weaverse-integration

安装后在 Claude Code 中输入/weaverse-integration确认已加载。

详细介绍

Weaverse 集成 — 现有 Hydrogen 项目

目的: 将 Weaverse 集成到当前尚未使用它的 Hydrogen 项目中。本指南将引导你完成整个流程:分析 → SDK 设置 → 组件转换 → 路由迁移 → 验证。

核心原则: 尊重现有项目。遵循其编码风格、模式和约定。不要重构已有的可用代码,而是通过 Weaverse 对其进行扩展。

实时文档

在本技能所在的文件夹中运行以下命令。辅助工具包含在同级的 shopify-hydrogen 技能中,只要安装了完整工具包,../shopify-hydrogen/scripts/ 就会存在。

如果你单独安装了本技能,则该文件夹不存在,以下命令会因 MODULE_NOT_FOUND 而失败。请添加一次辅助工具:

npx skills add Weaverse/shopify-hydrogen-skills --skill shopify-hydrogen

本技能没有 references/ 目录,因此以下阶段是本技能仅有的离线指南;实时文档命令则是可选补充。

# Always fetch latest docs before making decisions
node ../shopify-hydrogen/scripts/search_weaverse_docs.mjs "existing hydrogen integration"
node ../shopify-hydrogen/scripts/get_weaverse_page.mjs "migration-advanced/existing-hydrogen-integration"
node ../shopify-hydrogen/scripts/search_shopify_docs.mjs "createHydrogenContext"

阶段 1:分析现有项目

动手修改任何代码之前,先阅读并记录项目的当前状态。

1.1 检测框架版本
# Check package.json
cat package.json | grep -E '"react-router"|"@remix-run"|"@shopify/hydrogen"|"@weaverse"'
检测结果要安装的 Weaverse 版本
react-router + @shopify/[email protected]+@weaverse/hydrogen@latest(v5,React Router v7)
@remix-run/react@weaverse/hydrogen@4(v4,Remix)

如果项目使用 Remix(早于 2025.5.0):

  • 先询问用户是否要迁移到 React Router v7,然后再集成 Weaverse v5
  • 或在当前 Remix 配置中集成 Weaverse v4
  • 推荐迁移到 v7——这是未来的发展方向
1.2 梳理代码库

阅读以下文件并记录其模式:

文件观察内容
package.json依赖项、脚本、包管理器(npm/yarn/pnpm/bun)
app/root.tsx布局结构、提供程序、全局样式
app/entry.server.tsxCSP 设置、渲染方式
server.ts(或 server/index.ts)上下文创建、中间件
app/routes/**路由结构、loader 模式、命名约定
app/components/**组件模式、props、样式方案
app/sections/**如果存在 sections,了解其结构
app/styles/** 或 tailwind.config.*样式方案(Tailwind、CSS modules、styled-components 等)
vite.config.ts构建插件、别名
.env 或 .env.example现有环境变量(不要读取密钥)
tsconfig.json路径别名、严格性设置
1.3 记录编码约定

记录并保留以下模式:

  • 组件风格: 函数声明、箭头函数还是 forwardRef
  • 导出风格: export default、命名导出还是 export let
  • 样式: Tailwind 类、CSS modules、styled-components、内联样式
  • 状态管理: Hooks、上下文、存储
  • 数据获取: Loader 模式、GraphQL 查询结构、错误处理
  • 文件命名: kebab-case、PascalCase、camelCase
  • 导入风格: 绝对路径(~/)还是相对路径,命名空间导入还是默认导入
  • TypeScript: 严格性、interface 还是 type、泛型用法
  • 注释: 文档说明的详细程度、JSDoc 用法

⚠️ 重要: 转换后的 Weaverse 组件必须遵循这些约定。如果项目使用 function 声明,就不要使用 const X = () =>。如果项目使用 Tailwind,就不要引入 CSS modules。

1.4 找出可转换的组件

找出适合作为 Weaverse sections 的组件:

适合的组件:

  • Hero 横幅/轮播图
  • 精选系列/商品网格
  • 用户评价/评论
  • 新闻简报订阅
  • 促销横幅
  • 自定义内容区块(图片 + 文本、视频等)
  • 页脚区块(新闻简报、社交链接)
  • 内容可编辑的导航组件

跳过(不要转换):

  • 布局基础组件(Button、Input、Modal——保留为共享组件)
  • 路由级包装器
  • 购物车/结账逻辑组件
  • 分析/跟踪组件
  • 服务端工具

第 2 阶段:安装和配置 Weaverse SDK

2.1 安装软件包
# Use whatever package manager the project already uses
# React Router v7 (Hydrogen 2025.5.0+)
npm install @weaverse/hydrogen@latest

# Remix (legacy)
npm install @weaverse/hydrogen@4
2.2 环境变量

添加到 .env:

WEAVERSE_PROJECT_ID="provided-by-user"
WEAVERSE_API_KEY="provided-by-user"

同时添加到 .env.example(不填写真实值),并更新 TypeScript 环境类型:

// Add to the Env type in env.d.ts or wherever the project defines it
WEAVERSE_PROJECT_ID: string;
WEAVERSE_API_KEY: string;
WEAVERSE_HOST?: string;
2.3 创建 app/weaverse/ 目录

创建以下 5 个文件。遵循项目现有的代码风格(导出方式、命名、引号、分号)。

app/weaverse/schema.server.ts — 主题架构
import type { HydrogenThemeSchema } from "@weaverse/hydrogen";
import pkg from "../../package.json";

export let themeSchema: HydrogenThemeSchema = {
  info: {
    version: pkg.version,
    author: "Your Store Name",
    name: "Your Theme Name",
  },
  settings: [
    // Start minimal — add more as you convert components
    {
      group: "Colors",
      inputs: [
        {
          type: "color",
          name: "colorPrimary",
          label: "Primary Color",
          defaultValue: "#000000",
        },
        {
          type: "color",
          name: "colorBackground",
          label: "Background Color",
          defaultValue: "#ffffff",
        },
        {
          type: "color",
          name: "colorText",
          label: "Text Color",
          defaultValue: "#1a1a1a",
        },
      ],
    },
    {
      group: "Typography",
      inputs: [
        {
          type: "range",
          name: "headingBaseSize",
          label: "Heading Base Size",
          configs: { min: 14, max: 32, step: 1, unit: "px" },
          defaultValue: 28,
        },
        {
          type: "range",
          name: "bodyBaseSize",
          label: "Body Base Size",
          configs: { min: 12, max: 20, step: 1, unit: "px" },
          defaultValue: 16,
        },
      ],
    },
  ],
};
app/weaverse/style.tsx — 全局样式

阅读项目现有的全局样式(CSS 文件、Tailwind 配置、根布局),并创建能与现有设置集成的 CSS 变量。

import { useThemeSettings } from "@weaverse/hydrogen";

export function GlobalStyle() {
  let settings = useThemeSettings();
  if (!settings) return null;

  return (
    <style
      id="weaverse-global-style"
      key="weaverse-global-style"
      suppressHydrationWarning
      dangerouslySetInnerHTML={{
        __html: `
          :root {
            --color-primary: ${settings.colorPrimary};
            --color-bg: ${settings.colorBackground};
            --color-text: ${settings.colorText};
            --heading-base: ${settings.headingBaseSize}px;
            --body-base: ${settings.bodyBaseSize}px;
          }
        `,
      }}
    />
  );
}
app/weaverse/components.ts — 组件注册表
import type { HydrogenComponent } from "@weaverse/hydrogen";

// Import converted sections here — start empty, add as you convert
// MUST use namespace imports: import * as X, NOT import X from
export let components: HydrogenComponent[] = [
  // Sections will be added during Phase 3
];
app/weaverse/index.tsx — WeaverseContent
import { WeaverseHydrogenRoot } from "@weaverse/hydrogen";
import { components } from "./components";

export function WeaverseContent() {
  return (
    <WeaverseHydrogenRoot
      components={components}
      errorComponent={GenericError}
    />
  );
}

如果项目已有错误组件,请使用该组件;否则创建一个精简的 GenericError。

app/weaverse/csp.ts — 内容安全策略
export function getWeaverseCsp(request: Request, context: any) {
  let url = new URL(request.url);
  let weaverseHost = context.env?.WEAVERSE_HOST || "https://weaverse.io";
  let isDesignMode = url.searchParams.get("weaverse_design_mode") === "true";

  let weaverseHosts = [
    new URL(weaverseHost).host,
    "weaverse.io",
    "*.weaverse.io",
    "shopify.com",
    "*.shopify.com",
    "*.myshopify.com",
  ];

  let updatedCsp: Record<string, string[] | string | boolean> = {
    frameAncestors: weaverseHosts,
    defaultSrc: ["'self'", "data:", ...weaverseHosts],
    scriptSrc: ["'self'", "'unsafe-inline'", ...weaverseHosts],
    styleSrc: ["'self'", "'unsafe-inline'", ...weaverseHosts],
    connectSrc: ["'self'", ...weaverseHosts],
  };

  if (isDesignMode) {
    updatedCsp.frameAncestors = ["*"];
  }

  return updatedCsp;
}
2.4 更新服务器上下文

修改现有的上下文文件(通常是 server.ts 或 app/lib/context.ts):

// ADD these imports
import { WeaverseClient } from "@weaverse/hydrogen";
import { themeSchema } from "~/weaverse/schema.server"; // adjust path alias
import { components } from "~/weaverse/components";

// INSIDE createAppLoadContext, after hydrogenContext is created:
return {
  ...hydrogenContext,
  weaverse: new WeaverseClient({
    ...hydrogenContext,
    request,
    cache,
    themeSchema,
    components,
  }),
};

不要重构其上下文创建逻辑——只需将 Weaverse 客户端展开到现有的返回对象中。

2.5 更新根文件和入口文件
app/root.tsx — 使用 withWeaverse 包装
import { withWeaverse } from "@weaverse/hydrogen";
import { GlobalStyle } from "~/weaverse/style";

// Keep their existing Layout/App components untouched
// Only change: wrap the default export

// BEFORE:
// export default function App() { ... }

// AFTER:
function App() {
  // ... their existing App code, unchanged
}
export default withWeaverse(App);

在 Layout 组件的 <head> 内添加 <GlobalStyle />。

app/entry.server.tsx — 添加 Weaverse CSP
import { getWeaverseCsp } from "~/weaverse/csp";

// Inside handleRequest, merge Weaverse CSP with existing:
const { nonce, header, NonceProvider } = createContentSecurityPolicy({
  ...getWeaverseCsp(request, context),
  shop: {
    checkoutDomain: context.env?.PUBLIC_CHECKOUT_DOMAIN || context.env?.PUBLIC_STORE_DOMAIN,
    storeDomain: context.env?.PUBLIC_STORE_DOMAIN,
  },
});

与现有的 CSP 配置合并,不要替换它。


第 3 阶段:将现有组件转换为 Weaverse 区块

这是最重要的阶段。将用户现有的组件转换为可通过 Weaverse 编辑的区块,同时保留其完全一致的视觉效果和行为。

3.1 转换策略

对于每个可转换的组件:

  1. 阅读原始组件——了解属性、状态、数据获取和样式
  2. 创建 Weaverse 包装器——添加架构和可选的加载器
  3. 保留原始渲染——不要重写 JSX,只需将其包装起来
  4. 在 components.ts 中注册——添加命名空间导入
3.2 转换模式

原始组件(转换前):

// app/components/hero-banner.tsx
interface HeroBannerProps {
  heading: string;
  subtitle: string;
  backgroundImage: string;
  alignment: "left" | "center";
}

function HeroBanner({ heading, subtitle, backgroundImage, alignment }: HeroBannerProps) {
  return (
    <section className="relative bg-cover bg-center" style={{ backgroundImage: `url(${backgroundImage})` }}>
      <div className={`flex flex-col ${alignment === 'center' ? 'items-center text-center' : 'items-start'}`}>
        <h1 className="text-4xl font-bold">{heading}</h1>
        <p className="text-xl mt-4">{subtitle}</p>
      </div>
    </section>
  );
}
export default HeroBanner;

转换后的组件:

// app/sections/hero-banner.tsx (or keep original path, just add schema)
import type { HydrogenComponentProps } from "@weaverse/hydrogen";
import { createSchema } from "@weaverse/hydrogen";

interface HeroBannerProps extends HydrogenComponentProps {
  heading: string;
  subtitle: string;
  backgroundImage: string;
  alignment: "left" | "center";
}

function HeroBanner({ heading, subtitle, backgroundImage, alignment, children, ...rest }: HeroBannerProps) {
  return (
    <section {...rest} className="relative bg-cover bg-center" style={{ backgroundImage: `url(${backgroundImage})` }}>
      <div className={`flex flex-col ${alignment === 'center' ? 'items-center text-center' : 'items-start'}`}>
        <h1 className="text-4xl font-bold">{heading}</h1>
        <p className="text-xl mt-4">{subtitle}</p>
      </div>
      {children}
    </section>
  );
}
export default HeroBanner;

export let schema = createSchema({
  type: "hero-banner",
  title: "Hero Banner",
  settings: [
    {
      group: "Content",
      inputs: [
        { type: "text", name: "heading", label: "Heading", defaultValue: "Welcome" },
        { type: "textarea", name: "subtitle", label: "Subtitle" },
        { type: "image", name: "backgroundImage", label: "Background Image" },
        {
          type: "toggle-group",
          name: "alignment",
          label: "Alignment",
          configs: {
            options: [
              { value: "left", label: "Left" },
              { value: "center", label: "Center" },
            ],
          },
          defaultValue: "center",
        },
      ],
    },
  ],
  presets: {
    heading: "Welcome",
    alignment: "center",
  },
});

主要变更:

  • 扩展 HydrogenComponentProps(添加 children、loaderData 和 Weaverse 内部属性)
  • 在根元素上展开 {...rest}(Studio 交互所必需)
  • 如果组件可以接受嵌套区块,则渲染 {children}
  • 使用 createSchema() 添加 schema 导出
  • 保持 JSX 结构和样式完全一致
3.3 架构设计规则

为转换后的组件创建架构时:

原始属性类型架构输入类型
string(短文本)text
string(长文本)textarea 或 richtext
string(URL)url
string(图片路径)image
string(十六进制颜色)color
numberrange(设置适当的最小值、最大值和步长)
booleanswitch
string(枚举)select 或 toggle-group
包含 Shopify 数据的对象product、collection、blog 等

按逻辑对设置分组——根据组件的用途进行分组,通常包括:

  • “Content”组——文本、图片、链接
  • “Design”组——颜色、间距、布局选项
  • “Advanced”组——可见性、自定义 CSS 类
3.4 转换数据获取组件

如果原始组件会获取数据(例如,精选商品集合),请将数据获取转换为 Weaverse 加载器:

import type { ComponentLoaderArgs } from "@weaverse/hydrogen";

type SectionData = {
  collectionHandle: string;
  count: number;
};

export let loader = async ({ weaverse, data }: ComponentLoaderArgs<SectionData>) => {
  if (!data?.collectionHandle) return null;
  return weaverse.storefront.query(COLLECTION_QUERY, {
    variables: { handle: data.collectionHandle, first: data.count ?? 8 },
  });
};

使用项目现有的 GraphQL 查询和片段,不要重写。如果项目有包含共享查询的 graphql/ 目录,请从该目录导入。

3.5 注册转换后的组件

更新 app/weaverse/components.ts:

import * as HeroBanner from "~/sections/hero-banner";
import * as FeaturedCollection from "~/sections/featured-collection";
// ... other converted components

export let components: HydrogenComponent[] = [
  HeroBanner,
  FeaturedCollection,
  // ...
];

必须使用 import * as X,不要使用 import X from。这是最常见的错误。


第 4 阶段:更新路由

对于每个应支持 Weaverse 编辑的路由:

4.1 路由转换模式
// BEFORE — original route
import { useLoaderData } from "react-router"; // or "@remix-run/react"
import HeroBanner from "~/components/hero-banner";
import ProductGrid from "~/components/product-grid";

export async function loader({ context, params }: LoaderFunctionArgs) {
  const productData = await context.storefront.query(PRODUCT_QUERY, { ... });
  return { product: productData.product };
}

export default function Homepage() {
  const { product } = useLoaderData<typeof loader>();
  return (
    <div>
      <HeroBanner heading="Sale" subtitle="Big deals" />
      <ProductGrid products={product} />
    </div>
  );
}
// AFTER — Weaverse-enabled route
import { WeaverseContent } from "~/weaverse";

export async function loader({ context, params }: LoaderFunctionArgs) {
  // Keep existing data loading
  const productData = await context.storefront.query(PRODUCT_QUERY, { ... });

  // ADD Weaverse page loading
  const weaverseData = await context.weaverse.loadPage({ type: "INDEX" });

  return {
    product: productData.product,
    weaverseData,
  };
}

export default function Homepage() {
  // Render Weaverse content — it will use the registered components
  return <WeaverseContent />;
}
4.2 页面类型
路由type 值
首页(_index)"INDEX"
产品页面"PRODUCT"(带有 handle: params.productHandle)
产品系列页面"COLLECTION"(带有 handle: params.collectionHandle)
产品系列列表"ALL_PRODUCTS"
博客"BLOG"
文章"ARTICLE"(带有 handle: params.articleHandle)
自定义页面"PAGE"(带有 handle: params.pageHandle)
自定义模板"CUSTOM"
4.3 逐步迁移(推荐)

无需一次性转换所有路由。请从以下内容开始:

  1. 首页 — 影响最大,通常也最简单
  2. 产品页面 — 对商家的价值很高
  3. 产品系列页面 — 支持视觉陈列
  4. 其他页面 — 按需转换

对于尚未转换的路由,保持原样即可。Weaverse 不会干扰非 Weaverse 路由。


阶段 5:处理边缘情况

5.1 现有布局组件

如果项目有共享布局组件(页眉、页脚、导航):

  • 页眉/页脚 — 暂时将其保留在 Weaverse 外部,在 root.tsx Layout 中渲染
  • 或者,如果用户希望能够编辑这些组件,则将它们转换为 Weaverse 组件
  • 询问用户倾向于采用哪种方式
5.2 共享组件(按钮、输入框等)

不要转换基础/共享组件。将它们保留在 app/components/ 中,并在 Weaverse 区块中使用。

5.3 第三方集成

如果项目使用:

  • 分析工具(GA4、Segment 等)— 保留在路由加载器或根布局中
  • A/B 测试 — 保留在 Weaverse 页面渲染之外
  • 自定义中间件 — 确保 Weaverse CSP 不会阻止第三方脚本
  • CMS 数据(Contentful、Sanity 等)— 可以共存;Weaverse 负责视觉布局,外部 CMS 负责结构化内容
5.4 TypeScript 类型

将以下内容添加到项目的类型定义中:

// Wherever the project defines AppLoadContext
interface AppLoadContext {
  // ... existing properties
  weaverse: import("@weaverse/hydrogen").WeaverseClient;
}

阶段 6:验证清单

集成后,请验证:

  • npm run dev 启动时没有错误
  • 现有路由仍能正确渲染(视觉回归检查)
  • Weaverse Studio 预览可通过本地开发 URL 加载
  • 转换后的组件显示在 Weaverse Studio 组件选择器中
  • Studio > Theme > Customize 中显示主题设置
  • 转换后的组件渲染结果与原组件完全一致
  • 组件架构暴露了正确的可编辑设置
  • 数据获取区块能正确加载数据
  • npm run build 执行完成且没有错误
  • TypeScript 编译时没有新增错误
  • 购物车、结账和客户账户流程仍可正常使用
  • 控制台中没有与 CSP 相关的错误

应避免的常见错误

错误修复方法
使用 import X from 而非 import * as X注册组件时始终使用命名空间导入
忘记在根元素上使用 {...rest}Weaverse Studio 的拖放交互需要此属性
未在容器组件中渲染 {children}嵌套区块需要此属性
覆盖现有 CSP,而不是合并将 Weaverse CSP 展开并合并到现有配置中
重写组件 JSX,“让它更好”保持原有渲染完全不变
一次性转换所有组件先转换区块,将基础组件保留为共享组件
在架构中使用 inspector使用 settings — inspector 已弃用
在 app/ 外创建 app/weaverse/必须位于 app 目录内
忘记在类型定义中添加环境变量将 WEAVERSE_* 添加到 Env 类型中

快速参考 — 文件变更摘要

文件操作
package.json添加 @weaverse/hydrogen 依赖
.env添加 WEAVERSE_PROJECT_ID、WEAVERSE_API_KEY
app/weaverse/schema.server.ts创建 — 主题架构
app/weaverse/style.tsx创建 — 全局样式组件
app/weaverse/components.ts创建 — 组件注册表
app/weaverse/index.tsx创建 — WeaverseContent 包装器
app/weaverse/csp.ts创建 — CSP 配置
server.ts(或 context 文件)修改 — 将 WeaverseClient 添加到 context
app/root.tsx修改 — 使用 withWeaverse 包装,添加 <GlobalStyle />
app/entry.server.tsx修改 — 合并 Weaverse CSP
app/sections/*.tsx创建/修改 — 转换后的组件
Route 文件修改 — 添加 weaverse.loadPage(),渲染 <WeaverseContent />
类型定义修改 — 添加 WEAVERSE_* 环境变量、weaverse context
.gitignore验证 — 确认 .react-router/ 已被忽略(RRv7 项目)

评论

支持 Markdown