Communitygithub.com

sijad/astro-wordpress

将静态的Astro模板转换为WordPress主题,通过astro-wordpress适配器完成。适用于#add-astro-wordpress、#turn-this-astro-site-into-a-wp-theme等场景。

astro-wordpress 是什么?

该技能提供了一套经过端到端验证的配方,用于将静态Astro网站转换为可激活的WordPress主题。它通过安装`astro-wordpress`适配器、配置`astro.config.mjs`、添加主题`style.css`标头以及将`.astro`页面重命名为`.php.astro`来实现,使得`npm run dev`可以代理到本地WordPress实例,实时预览主题。该技能支持两种WordPress运行方式:使用Docker Compose(`docker compose up -d`)或已存在的本地WordPress安装。它详细说明了构建步骤,包括处理Docker环境中的路径映射、避免常见陷阱,以及验证构建输出的方法。该技能还涉及了部分导出(`export const partial = true;`)、避免OOM崩溃的构建排除配置,以及处理动态路由受限等高级话题。它明确指出,该技能仅涵盖基础适配器设置,不包含通过ACF使主题内容在WP后台可编辑的功能(相关功能由`astro-wordpress-acf`技能提供)。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/sijad/astro-wordpress/tree/HEAD/skills/astro-wordpress

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

将静态 Astro 模板转换为 WordPress 主题 通过 astro-wordpress 实现:src/pages/*.php.astro 下的页面会编译为真实的 WordPress 模板文件(index.php、page-template-*.php 等),而 npm run dev 则会代理到真实的本地 WordPress 实例,以便主题始终能够针对它进行实时预览。

这是一个经过验证的可靠配方,而不是对适配器 README 的转述: 以下步骤已应用于一个真实的静态 Astro 模板,并经过了端到端验证 —— 开发服务器实时代理到 WordPress、npm run build 生成可激活的主题、wp theme activate 成功执行。

前提条件

  • 一个可通过 HTTP 访问的本地 WordPress 安装。有两个已验证的选项:
    • Docker Compose(推荐 —— 完全不需要本地 PHP/MySQL/wp-cli,而且主机上很可能没有安装 wp-cli)。请参考步骤 0。
    • 一个已经正在运行的本地 WP 安装(任何 PHP 服务器 + MySQL/MariaDB,由 devProxyTarget 指定地址)。本技能无论如何只转换 Astro 这一侧。
  • 用于 Astro 项目的 Node/npm。
  • 在本技能的全部步骤中,wp-cli 是 可选的 —— 下面的每个 wp-cli 命令都有一个不支持 wp-cli 的回退方案(通过浏览器/curl 提交表单,或使用 docker 化的 wp-cli 辅助容器)。不要将其作为硬性要求。

0. (Docker 选项) 使用 Docker Compose 启动 WordPress

这个精确的 compose.yaml(来自 astro-wordpress 启动器)无需修改即可使用 —— 已通过实际运行验证:

services:
  wordpress:
    image: wordpress
    restart: always
    ports:
      - 8088:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - ./wp:/var/www/html
      - ./public/:/var/www/html/public

  db:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_RANDOM_ROOT_PASSWORD: "1"
    volumes:
      - db:/var/lib/mysql

volumes:
  db:
docker compose up -d

默认情况下,wordpress 镜像中没有 wp-cli —— 通过浏览器(http://localhost:8088)完成安装和所有其他操作,或者使用一个共享相同网络和卷的一次性 docker 化 wp-cli 容器(请参阅验证/注意事项 —— 每当下面的某个步骤提到 wp ... 且本地未安装 wp-cli 时,都应该使用此模式):

docker run --rm --network container:<wordpress-container-name> \
  --volumes-from <wordpress-container-name> -w /var/www/html \
  -e WORDPRESS_DB_HOST=db -e WORDPRESS_DB_USER=exampleuser \
  -e WORDPRESS_DB_PASSWORD=examplepass -e WORDPRESS_DB_NAME=exampledb \
  wordpress:cli <wp-cli-command>

(-e 变量必须与 wordpress 服务的环境变量匹配 —— CLI 容器会读取相同的 wp-config.php,该文件通过 getenv() 解析这些变量,但不会继承另一个容器的环境。)

步骤

1. 安装适配器

npm install astro-wordpress

(下面使用的 passthroughImageService 随 Astro 核心一起提供 —— 不需要额外的包。)

2. 配置 astro.config.mjs

裸机 WP(Astro 和 WP 在同一个主机上,没有容器边界):

// @ts-check
import { defineConfig, fontProviders, passthroughImageService } from "astro/config";
import wordpress from "astro-wordpress";

export default defineConfig({
  server: {
    host: "127.0.0.1",
  },
  adapter: wordpress({
    devProxyTarget: "http://localhost:7001", // 你的本地 WP URL
    outDir: "./wp/wp-content/themes/<theme-name>", // 构建后的主题输出到这里
  }),
  image: {
    service: passthroughImageService(), // 必需 —— WP 自行提供/优化图片
  },
});

使用 Docker Compose WP(使用上面的 compose.yaml)需要额外三个选项 —— 每个都是经过实际验证,缺少哪一个都会导致问题(请参阅注意事项):

export default defineConfig({
  server: {
    host: "0.0.0.0", // 绑定所有接口,以便容器可以访问进来
    allowedHosts: ["host.docker.internal"],
  },
  adapter: wordpress({
    devProxyTarget: "http://localhost:8088",
    outDir: "./wp/wp-content/themes/<theme-name>",
    // WP 容器自身对绑定挂载的 public/ 目录的视图 ——
    // 不是主机路径(请参阅注意事项)
    publicDirPath: "/var/www/html/public",
    // 让在 *容器内部* 运行的 PHP 开发模板存根能够
    // 重新访问主机上运行的 Astro 开发服务器
    devServerTarget: "http://host.docker.internal:4321",
  }),
  image: { service: passthroughImageService() },
});

devProxyTarget 必须与 WordPress 实际监听的地址匹配。outDir 是 npm run build 写入完成主题的位置;它不一定需要位于 Astro 项目内部。如果它位于内部(例如,一个完整的本地 WP 检出嵌套在 ./wp/ 下),请将该目录添加到 .gitignore —— WordPress 核心及其上传文件不属于 Astro 项目的仓库。

3. 添加主题样式表

WordPress 只识别包含包含此标头的 style.css 的主题目录 —— 创建 public/style.css(适配器会自动将其符号链接/复制到 outDir):

/*!
Theme Name: <Theme Name>
Version: 0.0.1
*/

4. 将页面转换为 .php.astro

将你想要由 WordPress 提供服务的每个 src/pages/*.astro 路由重命名为 src/pages/*.php.astro —— 文件的默认导出仍然通过 Astro/你现有的布局和组件进行渲染,无需更改;只有扩展名和最终的输出格式会变化(index.astro → index.php)。页面正确构建和服务不需要其他代码更改。

非索引的 WP 模板文件遵循相同的模式:page-template- 前缀会被自动检测并转换为 WordPress 识别的 /* Template Name: ... */ 注释,用于自定义页面模板,例如 src/pages/page-template-landing.php.astro → 一个可选择的“着陆页”模板。(在适配器的 README 中有文档说明;此配方未进行实践。)

对于必须输出原始 PHP 而没有任何 周围 HTML 文档的文件(例如用于注册钩子/字段的 functions.php.astro —— 请参阅 astro-wordpress-acf 技能),请在 frontmatter 中添加 export const partial = true;。如果没有它,Astro 会自动在输出前添加 <!DOCTYPE html> 并破坏 PHP 文件。

5. 激活主题

通用方法:WP 管理后台 → 外观 → 主题 → 激活。使用 wp-cli(本地或 docker 化,请参阅步骤 0):

npm run build
wp theme activate <theme-name> --path=<wp-dir>

验证

# 1. 开发服务器和 WP 都已启动?每个请求都通过 devProxyTarget 代理,
#    因此如果 WP 无法访问,即使是静态资源也会返回 502
npm run dev &
curl -o /dev/null -s -w "%{http_code}\n" http://127.0.0.1:4321/

# 2. 绕过 WP 代理,直接从开发服务器查看原始的、由 Astro 渲染的、PHP 风格的输出
#    —— 有助于隔离“这是 Astro 的问题还是 WordPress 的问题”
curl -s -H "by-pass-proxy: 1" http://127.0.0.1:4321/index.php | head -c 300

# 3. 生产构建将真正的普通 PHP 文件输出到 outDir
npm run build
grep -c '<?php' wp/wp-content/themes/<theme-name>/index.php

# 4. 主题已激活 —— 通过 wp-cli,或者如果 wp-cli 不可用,只需加载首页并检查特定于主题的标记
wp theme list --path=<wp-dir> | grep <theme-name>

还没有可供测试的本地 WP 安装,并且想无头(无 wp-cli,无浏览器)确认 WP 安装本身是否正常?安装向导只是一个普通的表单 POST —— 这正是本技能验证全新 WP 安装的方式:

curl "http://localhost:8088/wp-admin/install.php?step=2" \
  --data-urlencode "weblog_title=My Site" \
  --data-urlencode "user_name=admin" \
  --data-urlencode "admin_password=<password>" \
  --data-urlencode "admin_password2=<password>" \
  --data-urlencode "pw_weak=1" \
  --data-urlencode "[email protected]" \
  --data-urlencode "Submit=Install WordPress"
# 然后以相同的方式登录(使用 cookie jar)以用于后续的任何管理后台检查:
curl -c cookies.txt \
  --data-urlencode "log=admin" --data-urlencode "pwd=<password>" \
  --data-urlencode "wp-submit=Log In" \
  --data-urlencode "redirect_to=http://localhost:8088/wp-admin/" \
  --data-urlencode "testcookie=1" \
  "http://localhost:8088/wp-login.php"
curl -b cookies.txt "http://localhost:8088/wp-admin/" | grep -o "<title>[^<]*</title>"

注意事项

  • 每个开发模式请求都会代理到 devProxyTarget,不仅仅是 .php 页面 —— /src、/@、/node_modules 以及任何带有 by-pass-proxy 标头的请求是唯一的绕过方式。如果 WordPress 未运行,整个站点都会返回 502,包括 public/ 下的图片/字体。在调试 Astro 方面之前,请先检查 WP 是否实际启动。
  • Astro 7 的开发服务器是一个持久化的后台守护进程。 第二次运行 npm run dev 不会重新绑定或报错 —— 它只会报告已经运行的实例(使用 npx astro dev status / stop / logs 直接管理)。
  • export const partial = true; —— 请参阅步骤 4。这是整个适配器中唯一最不明显的要求;失败模式(PHP 文件顶部出现无关的 <!DOCTYPE html>)看起来与 Astro 完全没有关系。
  • publicDirPath 必须设置为 WP 容器 所看到的路径,而不是主机路径。 适配器使用你提供的任何 publicDirPath,将每个 public/ 文件符号链接到主题目录中,这是字面上的操作。使用 Docker 时,请提供容器路径(/var/www/html/public)—— 通过双向检查生成的符号链接来确认:readlink wp-content/themes/<theme>/style.css → /var/www/html/public/style.css,这个链接在容器内通过 docker exec <container> cat wp-content/themes/<theme>/style.css 可以正常解析,但从主机 shell 访问会显示“No such file or directory”。如果你省略 publicDirPath,适配器会回退到主机路径,这在容器内是无效的。
  • astro dev 的守护进程锁是针对每个项目目录的,而不是针对每个配置/端口的。 当同一个目录中已经有一个普通的 astro dev 在运行时,使用 astro dev --config other.config.mjs --port 4325 只会报告开发服务器已在原始的端口上运行 —— 它不会启动第二个实例。如果你需要在同一个项目中切换配置(例如,裸机 vs. Docker),请先使用 astro dev stop 停止现有的实例。
  • 此转换本身不会将 wp_head() / wp_footer() 连接到布局中,因此由插件注入的 <script> / <style> 标签、SEO 插件元标签等不会出现。astro-wordpress 的 README 展示了如何在布局中添加 <Fragment set:html={} /> / <?php wp_footer(); ?> 来实现这一点。如果布局独立构建自己的 <head>(在移植现有的静态模板时很常见),请将有线连接这些作为后续的刻意操作,而不是本技能步骤已经为你提供的功能。
  • astro check(npm run build 的一部分)可能会 OOM 崩溃,因为它试图对容器启动后落入 ./wp 中的 WordPress 核心文件(wp-includes/js/dist/ 等目录下的数千个 .js / .php 文件)进行类型检查。在 Docker WP 已启动的情况下运行构建之前,请将 "exclude": ["dist", "wp"] 添加到 tsconfig.json —— 否则,失败现象看起来像一个与 WordPress 适配器无关的 Node 堆崩溃。
  • Biome(或任何对整个仓库运行的 linter/formatter)也会尝试处理 ./wp,原因相同 —— 在运行项目的 lint/format 命令之前,添加一个忽略条目(例如,在 biome.json 中添加 "files": { "ignore": ["wp/**"] })。
  • 动态 rest 参数路由无法转换。 src/pages/blog/[...slug].astro(或任何 [...x].astro)不能变成 [...x].php.astro —— Astro 的路由器会直接拒绝它(“rest parameter must be a standalone segment”),因为 .php 后缀破坏了段边界。将这些路由保留为普通的 .astro 并使其不包含在 WP 主题中,除非内容本身正在迁移到 WP。

下一步

一旦适配器连接好:

  • astro-wordpress-acf —— 通过 ACF 使页面的特定部分在 WP 管理后台中可编辑。
  • astro-wordpress-menus —— 将页眉/页脚/导航组件连接到 WordPress 管理的菜单(外观 → 菜单),而不是硬编码的链接。
  • astro-wordpress-global-settings —— 为不限特定页面的值添加一个站点范围的“主题选项”页面(需要 ACF Pro 或 Secure Custom Fields)。

相关技能