Skip to content

Commit 58cb0a0

Browse files
authored
[next]: Add Astro kitchen-sink example, which includes data fetching and preview (#2159)
* init preview example * update gql client, add headers param * update template-hierarchy, add id and asPreview * add preview utility to @faustjs/nextjs package * initial * new templates * add homepage * style fixes * update uri expression * rename example name, add readme * init project * update templates, add data fetching * remove logs, add comments, minor fixes
1 parent c39b4ac commit 58cb0a0

28 files changed

Lines changed: 1076 additions & 9 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Local environment variables
2+
.env
3+
.env.local
4+
.env.production
5+
6+
# Build output
7+
dist/
8+
.astro/
9+
10+
# Dependencies
11+
node_modules/
12+
13+
# Logs
14+
*.log
15+
16+
# Editor
17+
.vscode/
18+
.idea/
19+
20+
# OS
21+
.DS_Store
22+
Thumbs.db
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# @faustjs/astro Template Hierarchy Example
2+
3+
This example demonstrates how to use the `@faustjs/astro` package to automatically discover WordPress templates in an Astro project using the WordPress template hierarchy.
4+
5+
## What This Example Shows
6+
7+
- **Template Discovery**: Automatically finds WordPress template files in your `wp-templates` directory
8+
- **Content Collections**: Uses Astro's content collections to organize and query templates
9+
- **Template Hierarchy**: Follows WordPress template hierarchy rules for organizing templates
10+
- **Template Resolution**: SSR catch-all route that dynamically resolves WordPress URLs to templates
11+
12+
## Project Structure
13+
14+
```
15+
src/
16+
├── layouts/
17+
│ └── WordPressLayout.astro # Shared layout for all templates
18+
├── content.config.js # Content collection configuration
19+
└── pages/
20+
├── index.astro # Demo page showing discovered templates
21+
├── 404.astro # Custom 404 page
22+
├── [...uri].astro # Catch-all route implementing template resolution
23+
└── wp-templates/ # WordPress template files (protected from direct access)
24+
├── index.astro # Home/blog template
25+
├── page.astro # Page template
26+
├── single-post.astro # Single post template
27+
└── archive.astro # Archive template
28+
```
29+
30+
## How It Works
31+
32+
1. **Template Files**: Place your WordPress templates in `src/pages/wp-templates/`
33+
2. **Auto-Discovery**: The `createTemplateCollection()` function finds all template files
34+
3. **Content Collections**: Templates are available as an Astro content collection with `id` and `path`
35+
4. **SSR Resolution**: The `[...uri].astro` route dynamically fetches content and resolves templates
36+
5. **Shared Layout**: All templates use `WordPressLayout.astro` for consistent styling
37+
6. **Template Protection**: Templates can't be accessed directly (e.g., `/wp-templates/page` returns 404)
38+
39+
## Running the Example
40+
41+
```bash
42+
# Install dependencies (includes GraphQL requirements)
43+
pnpm install
44+
45+
# Copy environment configuration
46+
cp .env.example .env
47+
48+
# Edit .env to set your WordPress URL
49+
# WORDPRESS_URL=https://your-wordpress-site.com
50+
51+
# Start development server (SSR mode)
52+
pnpm dev
53+
54+
# Build for production (outputs server bundle)
55+
pnpm build
56+
```
57+
58+
## Dependencies
59+
60+
This example requires the following dependencies (automatically installed):
61+
62+
- `@faustjs/astro` - Astro integration with WordPress template hierarchy
63+
- `@faustjs/template-hierarchy` - Core template hierarchy logic
64+
- `astro` - Astro framework (peer dependency)
65+
- `graphql` - GraphQL core library (peer dependency)
66+
- `graphql-tag` - GraphQL query parsing (peer dependency)
67+
68+
## WordPress Setup
69+
70+
This example requires a WordPress site with WPGraphQL plugin installed:
71+
72+
1. **Install WPGraphQL**: Install the [WPGraphQL plugin](https://www.wpgraphql.com/) on your WordPress site
73+
2. **Set Environment Variable**: Copy `.env.example` to `.env` and set your WordPress URL
74+
3. **GraphQL Endpoint**: The endpoint should be `https://your-site.com/graphql`
75+
76+
### Supported WordPress Content
77+
78+
The example automatically discovers and creates routes for:
79+
80+
- **Posts**: Individual blog posts with categories and tags
81+
- **Pages**: Static pages like About, Contact, etc.
82+
- **Category Archives**: Archive pages for each category
83+
- **Tag Archives**: Archive pages for each tag
84+
- **Home Page**: Main blog index
85+
86+
## Template Discovery
87+
88+
The `@faustjs/astro` package automatically discovers templates in your `wp-templates` directory:
89+
90+
- **index.astro** → Home template
91+
- **page.astro** → Page template
92+
- **single-post.astro** → Single post template
93+
- **archive.astro** → Archive template
94+
95+
Each template is automatically added to the content collection with an `id` and `path`.
96+
97+
## Template Resolution Demo
98+
99+
The catch-all route (`[...uri].astro`) fetches real WordPress content and demonstrates URL resolution:
100+
101+
- `/``index.astro` (home page)
102+
- `/about``page.astro` (WordPress page)
103+
- `/hello-world``single-post.astro` (WordPress post)
104+
- `/category/uncategorized``archive.astro` (category archive)
105+
- `/tag/fun``archive.astro` (tag archive)
106+
107+
_Note: Actual URLs depend on your WordPress content_
108+
109+
## Content Collection Usage
110+
111+
```js
112+
// Get all templates
113+
import { getCollection } from 'astro:content';
114+
const templates = await getCollection('templates');
115+
116+
// Find a specific template by id
117+
const pageTemplate = templates.find((t) => t.id === 'page');
118+
119+
// Each template has: { id: string, path: string }
120+
console.log(pageTemplate); // { id: 'page', path: 'wp-templates/page' }
121+
```
122+
123+
## Real-World Usage
124+
125+
This example shows how a real WordPress headless site works with SSR:
126+
127+
1. **WordPress GraphQL API**: Dynamically fetches content from WordPress on each request
128+
2. **Server-Side Rendering**: Renders pages on-demand with fresh WordPress content
129+
3. **Template Resolution**: Uses WordPress template hierarchy to render content
130+
4. **Content Types**: Handles posts, pages, categories, and tags dynamically
131+
5. **SEO Ready**: Generates proper HTML with fresh WordPress content
132+
6. **404 Handling**: Automatically redirects to 404 for non-existent content
133+
134+
## Learn More
135+
136+
- [WordPress Template Hierarchy](https://developer.wordpress.org/themes/basics/template-hierarchy/)
137+
- [Astro Content Collections](https://docs.astro.build/en/guides/content-collections/)
138+
- [@faustjs/template-hierarchy](../packages/template-hierarchy/)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { defineConfig } from 'astro/config';
2+
import tailwindcss from '@tailwindcss/vite';
3+
4+
export default defineConfig({
5+
output: 'server',
6+
// Enable SSR for dynamic WordPress content
7+
vite: {
8+
resolve: {
9+
alias: {
10+
'@': '/src',
11+
},
12+
},
13+
server: {
14+
watch: {
15+
// Explicitly watch the packages directories
16+
ignored: [
17+
'**/node_modules/**',
18+
'!**/node_modules/@faustjs/**', // Watch @faustjs packages
19+
],
20+
followSymlinks: true,
21+
},
22+
},
23+
optimizeDeps: {
24+
// Don't pre-bundle workspace packages so changes are picked up immediately
25+
exclude: [
26+
'@faustjs/astro',
27+
'@faustjs/template-hierarchy',
28+
'@faustjs/graphql',
29+
],
30+
},
31+
plugins: [tailwindcss()],
32+
},
33+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "@faustjs/astro-kitchen-sink-example",
3+
"private": true,
4+
"version": "0.1.0",
5+
"license": "0BSD",
6+
"type": "module",
7+
"scripts": {
8+
"dev": "astro dev",
9+
"start": "astro dev",
10+
"build": "astro check && astro build",
11+
"preview": "astro preview",
12+
"astro": "astro"
13+
},
14+
"dependencies": {
15+
"@faustjs/astro": "workspace:*",
16+
"@faustjs/data-fetching": "workspace:*",
17+
"@faustjs/template-hierarchy": "workspace:*",
18+
"@tailwindcss/vite": "^4.1.12",
19+
"astro": "^5.1.1",
20+
"graphql": "^16.9.0",
21+
"graphql-tag": "^2.12.6",
22+
"tailwindcss": "^4.1.11"
23+
},
24+
"devDependencies": {
25+
"typescript": "^5.6.3"
26+
},
27+
"engines": {
28+
"node": ">=24"
29+
}
30+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
const { title, date, excerpt, uri, featuredImage } = Astro.props?.post ?? {};
3+
---
4+
5+
<article
6+
class='container max-w-4xl px-10 py-6 mx-auto rounded-lg shadow-sm bg-gray-50 mb-4'>
7+
<time datetime={date} class='text-sm text-gray-600'>
8+
{
9+
new Date(date).toLocaleDateString('en-US', {
10+
year: 'numeric',
11+
month: 'long',
12+
})
13+
}
14+
</time>
15+
16+
{
17+
featuredImage && (
18+
<img
19+
src={featuredImage?.node?.sourceUrl}
20+
alt=''
21+
class='w-full h-48 object-cover rounded-t-lg mt-2 mb-4'
22+
/>
23+
)
24+
}
25+
26+
<h2 class='mt-3'>
27+
<a href={uri} class='text-2xl font-bold hover:underline'>
28+
{title}
29+
</a>
30+
</h2>
31+
32+
<div class='mt-2 mb-4' set:html={excerpt} />
33+
34+
<a href={uri} class='hover:underline text-orange-600 mt-4'> Read more </a>
35+
</article>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
import { GET_LAYOUT } from '@/queries/getLayout';
3+
import { print } from 'graphql';
4+
5+
const { data } = await Astro.locals.client?.request(print(GET_LAYOUT));
6+
---
7+
8+
<header class='bg-gray-800 text-white py-4 px-8'>
9+
<div class='flex justify-between items-center max-w-4xl mx-auto'>
10+
<div class='text-3xl font-semibold'>
11+
<a href='/'>{data?.generalSettings?.title}</a>
12+
</div>
13+
14+
<nav class='space-x-6'>
15+
<a href='/' class='text-lg hover:underline'> Home</a>
16+
</nav>
17+
</div>
18+
</header>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
import Header from './Header.astro';
3+
import '../styles/globals.css';
4+
5+
const pageTitle = Astro.props.pageTitle;
6+
---
7+
8+
<!doctype html>
9+
<html lang='en'>
10+
<head>
11+
<meta charset='UTF-8' />
12+
<meta name='viewport' content='width=device-width' />
13+
<link rel='icon' type='image/svg+xml' href='/favicon.svg' />
14+
<meta name='generator' content={Astro.generator} />
15+
<title>{pageTitle}</title>
16+
</head>
17+
<body>
18+
<Header />
19+
<main class='bg-stone-100 text-gray-800 pb-16 pt-8 min-h-screen'>
20+
<slot />
21+
</main>
22+
</body>
23+
</html>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineCollection } from "astro:content";
2+
import { createTemplateCollection } from "@faustjs/astro";
3+
4+
// Set up WordPress template collection
5+
const templates = defineCollection(createTemplateCollection());
6+
7+
export const collections = { templates };
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare namespace App {
2+
interface Locals {
3+
templateData?: import('./lib/templateHierarchy').TemplateData;
4+
isPreview?: boolean;
5+
client?: import('./lib/types').GraphQLClient;
6+
}
7+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
import Layout from '@/components/Layout.astro';
3+
4+
// Set the response status to 404
5+
Astro.response.status = 404;
6+
---
7+
8+
<Layout title='Page Not Found'>
9+
<div class='max-w-4xl mx-auto px-8 py-16'>
10+
<div class='text-center mb-12'>
11+
<h1 class='text-6xl font-bold text-gray-900 mb-4'>404</h1>
12+
<h2 class='text-2xl font-semibold text-gray-700 mb-6'>Page Not Found</h2>
13+
<p class='text-lg text-gray-600 mb-8'>
14+
Sorry, the page you're looking for doesn't exist.
15+
</p>
16+
</div>
17+
18+
<div class='text-center'>
19+
<a
20+
href='/'
21+
class='inline-flex items-center px-6 py-3 bg-gray-800 text-white font-medium rounded-lg hover:bg-gray-700 transition-colors duration-200'>
22+
← Go back to the homepage
23+
</a>
24+
</div>
25+
</div>
26+
</Layout>

0 commit comments

Comments
 (0)