Transforma um template Astro estático em um tema WordPress
via astro-wordpress: páginas em
src/pages/*.php.astro compilam para arquivos de template WordPress reais
(index.php, page-template-*.php, …), enquanto npm run dev proxy para
uma instância WordPress local real, permitindo pré-visualização ao vivo do tema.
Esta é a receita verificada, não uma paráfrase do README do adaptador:
os passos abaixo foram aplicados a um template Astro estático real e confirmados
de ponta a ponta — servidor de desenvolvimento fazendo proxy para WordPress,
npm run build produzindo um tema ativável, wp theme activate executado com sucesso.
Pré-requisitos
- Uma instalação WordPress local acessível via HTTP. Duas opções verificadas:
- Docker Compose (recomendado — não requer PHP/MySQL/wp-cli local, e
wp-clipode não estar instalado no host). Veja o passo 0. - Uma instalação WP local já em execução (qualquer servidor PHP + MySQL/MariaDB
apontado por
devProxyTarget). Este skill converte apenas o lado Astro de qualquer forma.
- Docker Compose (recomendado — não requer PHP/MySQL/wp-cli local, e
- Node/npm para o projeto Astro.
wp-clié opcional em todo este skill — todo comando wp-cli abaixo tem uma alternativa sem wp-cli (envio de formulário via navegador/curl, ou um sidecar wp-cli dockerizado). Não o adicione como um requisito obrigatório.
0. (Opção Docker) Iniciar WordPress com Docker Compose
Este compose.yaml exato (do starter astro-wordpress) funciona
sem modificações — verificado executando-o:
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
Não há wp-cli na imagem wordpress por padrão — conclua a instalação e
todo o resto pelo navegador (http://localhost:8088), ou
com um contêiner wp-cli dockerizado descartável compartilhando a mesma rede +
volumes (veja Verificar/Armadilhas — este é o padrão a ser usado sempre que um
passo abaixo disser wp ... e não houver wp-cli local instalado):
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>
(As variáveis -e devem corresponder às env do serviço wordpress — o
contêiner CLI lê o mesmo wp-config.php, que as resolve via
getenv(), mas não herda o ambiente do outro contêiner.)
Passos
1. Instalar o adaptador
npm install astro-wordpress
(passthroughImageService usado abaixo já vem com o Astro core — nenhum
pacote extra.)
2. Configurar astro.config.mjs
WP bare-metal (Astro e WP no mesmo host, sem contêiner):
// @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", // sua URL WP local
outDir: "./wp/wp-content/themes/<theme-name>", // tema construído vai para cá
}),
image: {
service: passthroughImageService(), // obrigatório — WP serve/otimiza imagens por conta própria
},
});
WP no Docker Compose (usando o compose.yaml acima) precisa de mais três
opções — cada uma verificada como necessária ao quebrar sem elas (veja
Armadilhas):
export default defineConfig({
server: {
host: "0.0.0.0", // escuta em todas as interfaces para o contêiner conseguir acessar
allowedHosts: ["host.docker.internal"],
},
adapter: wordpress({
devProxyTarget: "http://localhost:8088",
outDir: "./wp/wp-content/themes/<theme-name>",
// a visão do próprio contêiner WP do diretório public/ montado por bind —
// NÃO o caminho do host (veja Armadilhas)
publicDirPath: "/var/www/html/public",
// permite que o stub do template de desenvolvimento PHP, executando *dentro*
// do contêiner, alcance o servidor de desenvolvimento Astro rodando no host
devServerTarget: "http://host.docker.internal:4321",
}),
image: { service: passthroughImageService() },
});
devProxyTarget deve corresponder a onde o WordPress está realmente ouvindo.
outDir é onde npm run build escreve o tema finalizado; não precisa
estar dentro do projeto Astro. Se estiver (um checkout WP local completo
aninhado em, ex., ./wp/), adicione esse diretório a .gitignore —
WordPress core e seus uploads não pertencem ao repositório do projeto Astro.
3. Adicionar a folha de estilo do tema
WordPress só reconhece um diretório de tema se ele tiver um style.css com
este cabeçalho — crie public/style.css (ele é automaticamente linkado/copiado
para outDir pelo adaptador):
/*!
Theme Name: <Nome do Tema>
Version: 0.0.1
*/
4. Converter páginas para .php.astro
Renomeie cada rota src/pages/*.astro que você quer que seja servida pelo WordPress para
src/pages/*.php.astro — o export default do arquivo ainda renderiza através
dos seus layouts e componentes Astro existentes, sem alterações; apenas a extensão
e o formato de saída final mudam (index.astro → index.php). Nenhuma outra
mudança de código é necessária para que uma página seja construída e servida corretamente.
Arquivos de template WP que não são index seguem o mesmo padrão: o prefixo
page-template- é detectado automaticamente e transformado em um comentário /* Template Name: ... */
que o WordPress reconhece para templates de página personalizados, ex.
src/pages/page-template-landing.php.astro → um template "Landing" selecionável.
(Documentado no README do adaptador; não testado por esta receita.)
Para arquivos que devem gerar PHP puro sem um documento HTML envolvente
(ex.: um functions.php.astro usado para registrar hooks/campos — veja o
skill astro-wordpress-acf), adicione export const partial = true; no
frontmatter. Sem isso, o Astro automaticamente prefixa <!DOCTYPE html> na saída
e corrompe o arquivo PHP.
5. Ativar o tema
Universalmente: Admin WP → Aparência → Temas → Ativar. Com wp-cli (local ou dockerizado, veja passo 0):
npm run build
wp theme activate <theme-name> --path=<wp-dir>
Verificar
# 1. Servidor de desenvolvimento + WP ambos funcionando? toda requisição faz proxy para devProxyTarget,
# então até assets estáticos retornam 502 se WP não estiver acessível
npm run dev &
curl -o /dev/null -s -w "%{http_code}\n" http://127.0.0.1:4321/
# 2. Ignorar o proxy WP para ver a saída Astro renderizada em PHP diretamente
# do servidor de desenvolvimento — útil para isolar "é um problema do Astro
# ou um problema do WordPress"
curl -s -H "by-pass-proxy: 1" http://127.0.0.1:4321/index.php | head -c 300
# 3. A build de produção coloca PHP real e simples em outDir
npm run build
grep -c '<?php' wp/wp-content/themes/<theme-name>/index.php
# 4. Tema ativo — via wp-cli, ou apenas carregue a página inicial e verifique
# a marcação específica do tema se wp-cli não estiver disponível
wp theme list --path=<wp-dir> | grep <theme-name>
Não tem uma instalação WP local para testar e quer confirmar que a instalação WP funciona sem head (sem wp-cli, sem navegador)? O assistente de instalação é um simples POST de formulário — exatamente assim que uma instalação WP nova foi verificada para este skill:
curl "http://localhost:8088/wp-admin/install.php?step=2" \
--data-urlencode "weblog_title=Meu Site" \
--data-urlencode "user_name=admin" \
--data-urlencode "admin_password=<senha>" \
--data-urlencode "admin_password2=<senha>" \
--data-urlencode "pw_weak=1" \
--data-urlencode "[email protected]" \
--data-urlencode "Submit=Instalar WordPress"
# então faça login da mesma forma (cookie jar) para quaisquer verificações de tela de admin posteriores:
curl -c cookies.txt \
--data-urlencode "log=admin" --data-urlencode "pwd=<senha>" \
--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>"
Armadilhas
- Toda requisição em modo de desenvolvimento faz proxy para
devProxyTarget, não apenas páginas.php—/src,/@,/node_modulese qualquer coisa com cabeçalhoby-pass-proxysão as únicas exceções. Se o WordPress não estiver rodando, o site inteiro retorna 502, incluindo imagens/fontes empublic/. Verifique se o WP está realmente ativo antes de depurar o lado Astro. - O servidor de desenvolvimento do Astro 7 é um daemon persistente em segundo plano.
npm run devuma segunda vez não reata nem gera erro — apenas informa a instância já em execução (npx astro dev status/stop/logspara gerenciá-la diretamente). export const partial = true;— veja passo 4. Este é o requisito mais não óbvio de todo o adaptador; o modo de falha (<!DOCTYPE html>solto no topo de um arquivo PHP) não parece relacionado ao Astro de forma alguma.publicDirPathdeve ser o caminho como o contêiner WP o vê, não o caminho do host. O adaptador faz symlink de cada arquivopublic/para o diretório do tema usando exatamente opublicDirPathque você fornecer. Com Docker, forneça o caminho do contêiner (/var/www/html/public) — confirmado verificando o symlink resultante nos dois sentidos:readlink wp-content/themes/<theme>/style.css→/var/www/html/public/style.css, que resolve corretamente viadocker exec <container> cat wp-content/themes/<theme>/style.cssmas resulta em "Arquivo ou diretório não encontrado" a partir do shell do host. Se você omitirpublicDirPath, o adaptador usa o caminho do host, que é inválido dentro do contêiner.- O bloqueio do daemon
astro devé por diretório de projeto, não por configuração/porta. Executarastro dev --config other.config.mjs --port 4325enquanto umastro devcomum já está rodando no mesmo diretório apenas informa "Servidor de desenvolvimento já em execução" na porta original — não inicia uma segunda instância. Useastro dev stoppara parar a existente primeiro se precisar trocar de configuração (ex.: bare-metal vs. Docker) no mesmo projeto. - Esta conversão sozinha não conecta
wp_head()/wp_footer()no layout, então tags<script>/<style>injetadas por plugins, meta tags de plugins SEO, etc., não aparecerão. O README do astro-wordpress mostra como adicionar<Fragment set:html={} />/<?php wp_footer(); ?>ao layout para isso. Se o layout constrói seu próprio<head>de forma independente (comum ao portar um template estático existente), trate a conexão desses como um acompanhamento deliberado, não algo que os passos deste skill já fornecem. astro check(parte donpm run build) pode travar por OOM ao tentar verificar tipos dos arquivos core do WordPress que caem em./wpassim que o contêiner inicia (milhares de arquivos.js/.phpemwp-includes/js/dist/, etc.). Adicione"exclude": ["dist", "wp"]aotsconfig.jsonantes de executar uma build com Docker WP já ativo — caso contrário a falha parece uma quebra de heap do Node não relacionada, sem conexão óbvia com o adaptador WordPress.- Biome (ou qualquer linter/formatter executado em todo o repositório) também tentará
processar
./wpassim que ele existir, pelo mesmo motivo — adicione uma entrada de ignorar (ex.:"files": { "ignore": ["wp/**"] }embiome.json) antes de executar o comando de lint/format do projeto. - Rotas com parâmetros rest dinâmicos não convertem.
src/pages/blog/[...slug].astro(ou qualquer[...x].astro) não pode se tornar[...x].php.astro— o roteador do Astro rejeita diretamente ("rest parameter must be a standalone segment") porque o sufixo.phpquebra o limite do segmento. Deixe essas rotas como.astrosimples e fora do tema WP, a menos que o conteúdo em si esteja sendo migrado para o WP.
Próximo passo
Uma vez que o adaptador esteja configurado:
astro-wordpress-acf— torne seções específicas de uma página editáveis a partir do backoffice WP via ACF.astro-wordpress-menus— conecte um componente de cabeçalho/rodapé/navegação a um menu gerenciado pelo WordPress (Aparência → Menus) em vez de links fixos.astro-wordpress-global-settings— adicione uma página de "Opções do Tema" para todo o site com valores que não são específicos de nenhuma página (requer ACF Pro ou Secure Custom Fields).