Convierte una plantilla estática de Astro en un tema de WordPress
mediante astro-wordpress: las páginas en
src/pages/*.php.astro se compilan a archivos de plantilla reales de WordPress
(index.php, page-template-*.php, …), mientras que npm run dev redirige a través
de una instancia local real de WordPress para que el tema siempre se previsualice en vivo
contra ella.
Esta es la receta verificada, no una paráfrasis del README del adaptador:
los pasos siguientes se aplicaron a una plantilla estática de Astro real y se confirmaron
de punta a punta: el servidor de desarrollo redirige en vivo a través de WordPress, npm run build produce un tema activable real, wp theme activate funciona correctamente.
Prerrequisitos
- Una instalación local de WordPress accesible por HTTP. Dos opciones verificadas:
- Docker Compose (recomendada: no requiere PHP/MySQL/wp-cli local en absoluto, y es posible que wp-cli no esté instalado en el host). Ver paso 0.
- Una instalación local de WP ya en funcionamiento (cualquier servidor PHP + MySQL/MariaDB
apuntado por
devProxyTarget). Este skill solo convierte el lado de Astro en cualquier caso.
- Node/npm para el proyecto Astro.
wp-clies opcional en todo este skill — cada comando wp-cli que aparece a continuación tiene una alternativa sin wp-cli (envío de formularios por navegador/curl, o un sidecar wp-cli dockerizado). No lo añadas como requisito obligatorio.
0. (Opción Docker) Iniciar WordPress con Docker Compose
Este compose.yaml exacto (del starter astro-wordpress) funciona sin modificaciones
— verificado ejecutándolo realmente:
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
No hay wp-cli en la imagen wordpress por defecto: completa la instalación y todo lo demás
ya sea a través del navegador (http://localhost:8088), o con un contenedor wp-cli dockerizado
de un solo uso que comparta la misma red + volúmenes (ver Verificación/Problemas — este es el patrón
a utilizar cada vez que un paso siguiente diga wp ... y no haya wp-cli local instalado):
docker run --rm --network container:<nombre-contenedor-wordpress> \
--volumes-from <nombre-contenedor-wordpress> -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 <comando-wp-cli>
(Las variables -e deben coincidir con el entorno del servicio wordpress — el contenedor CLI
lee el mismo wp-config.php, que las resuelve mediante getenv(), pero no hereda el entorno
del otro contenedor).
Pasos
1. Instalar el adaptador
npm install astro-wordpress
(passthroughImageService que se usa más abajo viene incluido con Astro core — no se necesita
ningún paquete adicional).
2. Configurar astro.config.mjs
WP directo (Astro y WP en el mismo host, sin límite de contenedor):
// @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", // tu URL local de WP
outDir: "./wp/wp-content/themes/<nombre-del-tema>", // el tema compilado va aquí
}),
image: {
service: passthroughImageService(), // requerido — WP sirve/optimiza las imágenes por sí mismo
},
});
WP con Docker Compose (usando el compose.yaml de arriba) necesita tres opciones
más — cada una verificada como necesaria al fallar realmente sin ellas (ver
Problemas):
export default defineConfig({
server: {
host: "0.0.0.0", // vincular en todas las interfaces para que el contenedor pueda alcanzarlo
allowedHosts: ["host.docker.internal"],
},
adapter: wordpress({
devProxyTarget: "http://localhost:8088",
outDir: "./wp/wp-content/themes/<nombre-del-tema>",
// la propia vista del contenedor WP del directorio public/ montado por bind —
// NO la ruta del host (ver Problemas)
publicDirPath: "/var/www/html/public",
// permite que el stub de la plantilla de desarrollo PHP, ejecutándose *dentro*
// del contenedor, se comunique de vuelta con el servidor de desarrollo de Astro
// que se ejecuta en el host
devServerTarget: "http://host.docker.internal:4321",
}),
image: { service: passthroughImageService() },
});
devProxyTarget debe coincidir con donde sea que WordPress esté realmente escuchando.
outDir es donde npm run build escribe el tema terminado; no es necesario que
esté dentro del proyecto Astro. Si lo está (un checkout local completo de WP anidado bajo,
p.ej., ./wp/), añade ese directorio a .gitignore — el núcleo de WordPress y sus
subidas no pertenecen al repositorio del proyecto Astro.
3. Añadir la hoja de estilo del tema
WordPress solo reconoce un directorio de tema si tiene un style.css con esta
cabecera: crea public/style.css (el adaptador lo enlaza/copia automáticamente
en outDir):
/*!
Theme Name: <Nombre del Tema>
Version: 0.0.1
*/
4. Convertir páginas a .php.astro
Renombra cada ruta src/pages/*.astro que quieras que WordPress sirva a
src/pages/*.php.astro — la exportación por defecto del archivo sigue renderizándose a través
de Astro/tus layouts y componentes existentes sin cambios; solo cambian la extensión
y el formato de salida final (index.astro → index.php). No se requieren otros cambios
de código para que una página se compile y sirva correctamente.
Los archivos de plantilla no index de WP siguen el mismo patrón: el prefijo page-template-
se detecta automáticamente y se convierte en un comentario /* Template Name: ... */
que WordPress reconoce para plantillas de página personalizadas, p.ej.
src/pages/page-template-landing.php.astro → una plantilla "Landing" seleccionable.
(Documentado en el README del adaptador; no se ha probado en esta receta).
Para archivos que deben generar PHP sin procesar sin documento HTML circundante
(p.ej., un functions.php.astro usado para registrar hooks/campos — consulta el
skill astro-wordpress-acf), añade export const partial = true; en el frontmatter.
Sin él, Astro antepone automáticamente <!DOCTYPE html> a la salida y corrompe
el archivo PHP.
5. Activar el tema
Universalmente: Administración de WP → Apariencia → Temas → Activar. Con wp-cli (local o dockerizado, ver paso 0):
npm run build
wp theme activate <nombre-del-tema> --path=<directorio-wp>
Verificar
# 1. ¿Servidor de desarrollo y WP funcionando? cada solicitud se redirige a devProxyTarget,
# por lo que incluso los activos estáticos dan 502 si WP no es accesible
npm run dev &
curl -o /dev/null -s -w "%{http_code}\n" http://127.0.0.1:4321/
# 2. omitir el proxy de WP para ver la salida sin procesar renderizada por Astro con sabor a PHP
# directamente desde el servidor de desarrollo — útil para aislar "¿es un problema de Astro
# o de WordPress?"
curl -s -H "by-pass-proxy: 1" http://127.0.0.1:4321/index.php | head -c 300
# 3. la compilación de producción coloca PHP real y sin procesar en outDir
npm run build
grep -c '<?php' wp/wp-content/themes/<nombre-del-tema>/index.php
# 4. el tema está activo — mediante wp-cli, o simplemente carga la página principal y comprueba
# si hay marcado específico del tema en lugar de wp-cli
wp theme list --path=<directorio-wp> | grep <nombre-del-tema>
¿Aún no tienes una instalación local de WP para probar y quieres confirmar que la instalación de WP funciona sin interfaz gráfica (sin wp-cli, sin navegador)? El asistente de instalación es un formulario POST simple — así es exactamente como se verificó una instalación nueva de WP para este skill:
curl "http://localhost:8088/wp-admin/install.php?step=2" \
--data-urlencode "weblog_title=Mi Sitio" \
--data-urlencode "user_name=admin" \
--data-urlencode "admin_password=<contraseña>" \
--data-urlencode "admin_password2=<contraseña>" \
--data-urlencode "pw_weak=1" \
--data-urlencode "[email protected]" \
--data-urlencode "Submit=Instalar WordPress"
# luego inicia sesión de la misma manera (cookie jar) para cualquier comprobación
# posterior en la pantalla de administración:
curl -c cookies.txt \
--data-urlencode "log=admin" --data-urlencode "pwd=<contraseña>" \
--data-urlencode "wp-submit=Iniciar Sesión" \
--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>"
Problemas comunes
- Cada solicitud en modo desarrollo se redirige a
devProxyTarget, no solo las páginas.php—/src,/@,/node_modulesy cualquier cosa con una cabeceraby-pass-proxyson las únicas excepciones. Si WordPress no está funcionando, todo el sitio da 502, incluyendo imágenes/fuentes enpublic/. Comprueba que WP esté realmente activo antes de depurar el lado de Astro. - El servidor de desarrollo de Astro 7 es un demonio persistente en segundo plano.
Ejecutar
npm run devuna segunda vez no reasigna ni da error — simplemente informa de la instancia ya en ejecución (npx astro dev status/stop/logspara gestionarlo directamente). export const partial = true;— ver paso 4. Este es el requisito menos obvio de todo el adaptador; el modo de fallo (un<!DOCTYPE html>suelto al principio de un archivo PHP) no parece estar relacionado con Astro en absoluto.publicDirPathdebe ser la ruta tal como la ve el contenedor de WP, no la ruta del host. El adaptador enlaza simbólicamente cada archivo depublic/al directorio del tema usando elpublicDirPathque le proporciones, literalmente. Con Docker, asígnale la ruta del contenedor (/var/www/html/public) — confirmado comprobando el enlace simbólico resultante en ambos sentidos:readlink wp-content/themes/<tema>/style.css→/var/www/html/public/style.css, que se resuelve bien mediantedocker exec <contenedor> cat wp-content/themes/<tema>/style.csspero daNo such file or directorydesde el shell del host. Si omitespublicDirPath, el adaptador utiliza la ruta del host, que no es válida dentro del contenedor.- El bloqueo del demonio de
astro deves por directorio de proyecto, no por configuración/puerto. Ejecutarastro dev --config other.config.mjs --port 4325mientras unastro devsimple ya se está ejecutando en el mismo directorio simplemente muestra "El servidor de desarrollo ya está en ejecución" en el puerto original — no inicia una segunda instancia. Ejecutaastro dev stopen la instancia existente primero si necesitas cambiar de configuración (p.ej., directo vs. Docker) en el mismo proyecto. - Esta conversión por sí sola no conecta
wp_head()/wp_footer()en el layout, por lo que las etiquetas<script>/<style>inyectadas por plugins, las metaetiquetas de plugins SEO, etc., no aparecerán. El README de astro-wordpress muestra cómo añadir<Fragment set:html={} />/<?php wp_footer(); ?>al layout para eso. Si el layout construye su propio<head>de forma independiente (común al portar una plantilla estática existente), trata la conexión de esos elementos como una tarea posterior deliberada, no algo que los pasos de este skill ya te proporcionen. astro check(parte denpm run build) puede fallar por falta de memoria al intentar verificar tipos de los archivos centrales de WordPress que llegan a./wpuna vez que el contenedor se inicia (miles de archivos.js/.phpbajowp-includes/js/dist/, etc.). Añade"exclude": ["dist", "wp"]atsconfig.jsonantes de ejecutar una compilación con Docker WP ya activo; de lo contrario, el fallo parece un cuelgue de Node sin relación alguna con el adaptador de WordPress.- Biome (o cualquier linter/formateador que se ejecute sobre todo el repo) también
intentará procesar
./wpuna vez que exista, por la misma razón: añade una entrada de ignorado (p.ej.,"files": { "ignore": ["wp/**"] }enbiome.json) antes de ejecutar el comando de lint/formateo del proyecto. - Las rutas con parámetros rest dinámicos no se convierten.
src/pages/blog/[...slug].astro(o cualquier[...x].astro) no puede convertirse en[...x].php.astro— el enrutador de Astro lo rechaza directamente ("el parámetro rest debe ser un segmento independiente") porque el sufijo.phprompe el límite del segmento. Deja esas rutas como.astrosimples y fuera del tema de WP, a menos que el contenido mismo se esté migrando a WP.
Siguiente paso
Una vez que el adaptador esté conectado:
astro-wordpress-acf— haz que secciones específicas de una página sean editables desde el backoffice de WP mediante ACF.astro-wordpress-menus— conecta un componente de encabezado/pie de página/navegación a un menú gestionado por WordPress (Apariencia → Menús) en lugar de enlaces fijos.astro-wordpress-global-settings— añade una página de "Opciones del tema" para todo el sitio con valores que no estén limitados a una sola página (requiere ACF Pro o Secure Custom Fields).