diff --git a/examples/astro/kitchen-sink/.gitignore b/examples/astro/kitchen-sink/.gitignore new file mode 100644 index 000000000..f26f0594a --- /dev/null +++ b/examples/astro/kitchen-sink/.gitignore @@ -0,0 +1,22 @@ +# Local environment variables +.env +.env.local +.env.production + +# Build output +dist/ +.astro/ + +# Dependencies +node_modules/ + +# Logs +*.log + +# Editor +.vscode/ +.idea/ + +# OS +.DS_Store +Thumbs.db diff --git a/examples/astro/kitchen-sink/README.md b/examples/astro/kitchen-sink/README.md new file mode 100644 index 000000000..433c933ee --- /dev/null +++ b/examples/astro/kitchen-sink/README.md @@ -0,0 +1,138 @@ +# @faustjs/astro Template Hierarchy Example + +This example demonstrates how to use the `@faustjs/astro` package to automatically discover WordPress templates in an Astro project using the WordPress template hierarchy. + +## What This Example Shows + +- **Template Discovery**: Automatically finds WordPress template files in your `wp-templates` directory +- **Content Collections**: Uses Astro's content collections to organize and query templates +- **Template Hierarchy**: Follows WordPress template hierarchy rules for organizing templates +- **Template Resolution**: SSR catch-all route that dynamically resolves WordPress URLs to templates + +## Project Structure + +``` +src/ +├── layouts/ +│ └── WordPressLayout.astro # Shared layout for all templates +├── content.config.js # Content collection configuration +└── pages/ + ├── index.astro # Demo page showing discovered templates + ├── 404.astro # Custom 404 page + ├── [...uri].astro # Catch-all route implementing template resolution + └── wp-templates/ # WordPress template files (protected from direct access) + ├── index.astro # Home/blog template + ├── page.astro # Page template + ├── single-post.astro # Single post template + └── archive.astro # Archive template +``` + +## How It Works + +1. **Template Files**: Place your WordPress templates in `src/pages/wp-templates/` +2. **Auto-Discovery**: The `createTemplateCollection()` function finds all template files +3. **Content Collections**: Templates are available as an Astro content collection with `id` and `path` +4. **SSR Resolution**: The `[...uri].astro` route dynamically fetches content and resolves templates +5. **Shared Layout**: All templates use `WordPressLayout.astro` for consistent styling +6. **Template Protection**: Templates can't be accessed directly (e.g., `/wp-templates/page` returns 404) + +## Running the Example + +```bash +# Install dependencies (includes GraphQL requirements) +pnpm install + +# Copy environment configuration +cp .env.example .env + +# Edit .env to set your WordPress URL +# WORDPRESS_URL=https://your-wordpress-site.com + +# Start development server (SSR mode) +pnpm dev + +# Build for production (outputs server bundle) +pnpm build +``` + +## Dependencies + +This example requires the following dependencies (automatically installed): + +- `@faustjs/astro` - Astro integration with WordPress template hierarchy +- `@faustjs/template-hierarchy` - Core template hierarchy logic +- `astro` - Astro framework (peer dependency) +- `graphql` - GraphQL core library (peer dependency) +- `graphql-tag` - GraphQL query parsing (peer dependency) + +## WordPress Setup + +This example requires a WordPress site with WPGraphQL plugin installed: + +1. **Install WPGraphQL**: Install the [WPGraphQL plugin](https://www.wpgraphql.com/) on your WordPress site +2. **Set Environment Variable**: Copy `.env.example` to `.env` and set your WordPress URL +3. **GraphQL Endpoint**: The endpoint should be `https://your-site.com/graphql` + +### Supported WordPress Content + +The example automatically discovers and creates routes for: + +- **Posts**: Individual blog posts with categories and tags +- **Pages**: Static pages like About, Contact, etc. +- **Category Archives**: Archive pages for each category +- **Tag Archives**: Archive pages for each tag +- **Home Page**: Main blog index + +## Template Discovery + +The `@faustjs/astro` package automatically discovers templates in your `wp-templates` directory: + +- **index.astro** → Home template +- **page.astro** → Page template +- **single-post.astro** → Single post template +- **archive.astro** → Archive template + +Each template is automatically added to the content collection with an `id` and `path`. + +## Template Resolution Demo + +The catch-all route (`[...uri].astro`) fetches real WordPress content and demonstrates URL resolution: + +- `/` → `index.astro` (home page) +- `/about` → `page.astro` (WordPress page) +- `/hello-world` → `single-post.astro` (WordPress post) +- `/category/uncategorized` → `archive.astro` (category archive) +- `/tag/fun` → `archive.astro` (tag archive) + +_Note: Actual URLs depend on your WordPress content_ + +## Content Collection Usage + +```js +// Get all templates +import { getCollection } from 'astro:content'; +const templates = await getCollection('templates'); + +// Find a specific template by id +const pageTemplate = templates.find((t) => t.id === 'page'); + +// Each template has: { id: string, path: string } +console.log(pageTemplate); // { id: 'page', path: 'wp-templates/page' } +``` + +## Real-World Usage + +This example shows how a real WordPress headless site works with SSR: + +1. **WordPress GraphQL API**: Dynamically fetches content from WordPress on each request +2. **Server-Side Rendering**: Renders pages on-demand with fresh WordPress content +3. **Template Resolution**: Uses WordPress template hierarchy to render content +4. **Content Types**: Handles posts, pages, categories, and tags dynamically +5. **SEO Ready**: Generates proper HTML with fresh WordPress content +6. **404 Handling**: Automatically redirects to 404 for non-existent content + +## Learn More + +- [WordPress Template Hierarchy](https://developer.wordpress.org/themes/basics/template-hierarchy/) +- [Astro Content Collections](https://docs.astro.build/en/guides/content-collections/) +- [@faustjs/template-hierarchy](../packages/template-hierarchy/) diff --git a/examples/astro/kitchen-sink/astro.config.mjs b/examples/astro/kitchen-sink/astro.config.mjs new file mode 100644 index 000000000..2ec71dc14 --- /dev/null +++ b/examples/astro/kitchen-sink/astro.config.mjs @@ -0,0 +1,33 @@ +import { defineConfig } from 'astro/config'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + output: 'server', + // Enable SSR for dynamic WordPress content + vite: { + resolve: { + alias: { + '@': '/src', + }, + }, + server: { + watch: { + // Explicitly watch the packages directories + ignored: [ + '**/node_modules/**', + '!**/node_modules/@faustjs/**', // Watch @faustjs packages + ], + followSymlinks: true, + }, + }, + optimizeDeps: { + // Don't pre-bundle workspace packages so changes are picked up immediately + exclude: [ + '@faustjs/astro', + '@faustjs/template-hierarchy', + '@faustjs/graphql', + ], + }, + plugins: [tailwindcss()], + }, +}); diff --git a/examples/astro/kitchen-sink/package.json b/examples/astro/kitchen-sink/package.json new file mode 100644 index 000000000..664a4cec2 --- /dev/null +++ b/examples/astro/kitchen-sink/package.json @@ -0,0 +1,30 @@ +{ + "name": "@faustjs/astro-kitchen-sink-example", + "private": true, + "version": "0.1.0", + "license": "0BSD", + "type": "module", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro check && astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@faustjs/astro": "workspace:*", + "@faustjs/data-fetching": "workspace:*", + "@faustjs/template-hierarchy": "workspace:*", + "@tailwindcss/vite": "^4.1.12", + "astro": "^5.1.1", + "graphql": "^16.9.0", + "graphql-tag": "^2.12.6", + "tailwindcss": "^4.1.11" + }, + "devDependencies": { + "typescript": "^5.6.3" + }, + "engines": { + "node": ">=24" + } +} diff --git a/examples/astro/kitchen-sink/src/components/BlogPostItem.astro b/examples/astro/kitchen-sink/src/components/BlogPostItem.astro new file mode 100644 index 000000000..420595e43 --- /dev/null +++ b/examples/astro/kitchen-sink/src/components/BlogPostItem.astro @@ -0,0 +1,35 @@ +--- +const { title, date, excerpt, uri, featuredImage } = Astro.props?.post ?? {}; +--- + +
+ + + { + featuredImage && ( + + ) + } + +

+ + {title} + +

+ +
+ + Read more +
diff --git a/examples/astro/kitchen-sink/src/components/Header.astro b/examples/astro/kitchen-sink/src/components/Header.astro new file mode 100644 index 000000000..a22290c8b --- /dev/null +++ b/examples/astro/kitchen-sink/src/components/Header.astro @@ -0,0 +1,18 @@ +--- +import { GET_LAYOUT } from '@/queries/getLayout'; +import { print } from 'graphql'; + +const { data } = await Astro.locals.client?.request(print(GET_LAYOUT)); +--- + +
+
+ + + +
+
diff --git a/examples/astro/kitchen-sink/src/components/Layout.astro b/examples/astro/kitchen-sink/src/components/Layout.astro new file mode 100644 index 000000000..01f18537a --- /dev/null +++ b/examples/astro/kitchen-sink/src/components/Layout.astro @@ -0,0 +1,23 @@ +--- +import Header from './Header.astro'; +import '../styles/globals.css'; + +const pageTitle = Astro.props.pageTitle; +--- + + + + + + + + + {pageTitle} + + +
+
+ +
+ + diff --git a/examples/astro/kitchen-sink/src/content.config.js b/examples/astro/kitchen-sink/src/content.config.js new file mode 100644 index 000000000..e8c52b83b --- /dev/null +++ b/examples/astro/kitchen-sink/src/content.config.js @@ -0,0 +1,7 @@ +import { defineCollection } from "astro:content"; +import { createTemplateCollection } from "@faustjs/astro"; + +// Set up WordPress template collection +const templates = defineCollection(createTemplateCollection()); + +export const collections = { templates }; diff --git a/examples/astro/kitchen-sink/src/env.d.ts b/examples/astro/kitchen-sink/src/env.d.ts new file mode 100644 index 000000000..7b59111d0 --- /dev/null +++ b/examples/astro/kitchen-sink/src/env.d.ts @@ -0,0 +1,7 @@ +declare namespace App { + interface Locals { + templateData?: import('./lib/templateHierarchy').TemplateData; + isPreview?: boolean; + client?: import('./lib/types').GraphQLClient; + } +} diff --git a/examples/astro/kitchen-sink/src/pages/404.astro b/examples/astro/kitchen-sink/src/pages/404.astro new file mode 100644 index 000000000..048321584 --- /dev/null +++ b/examples/astro/kitchen-sink/src/pages/404.astro @@ -0,0 +1,26 @@ +--- +import Layout from '@/components/Layout.astro'; + +// Set the response status to 404 +Astro.response.status = 404; +--- + + +
+
+

404

+

Page Not Found

+

+ Sorry, the page you're looking for doesn't exist. +

+
+ + +
+
diff --git a/examples/astro/kitchen-sink/src/pages/[...identifier].astro b/examples/astro/kitchen-sink/src/pages/[...identifier].astro new file mode 100644 index 000000000..81882980d --- /dev/null +++ b/examples/astro/kitchen-sink/src/pages/[...identifier].astro @@ -0,0 +1,44 @@ +--- +import { + uriToTemplate, + createDefaultClient, + setGraphQLClient, +} from '@faustjs/astro'; +import { getAuthString } from '@/utils/getAuthString.js'; + +// Determine if we are in preview mode based on the URL parameter and the secret +const isPreview = + Astro.url.searchParams.get('preview') === 'true' && + import.meta.env.WP_PREVIEW_SECRET === Astro.url.searchParams.get('secret'); + +const headers = isPreview ? { Authorization: getAuthString() } : undefined; + +// Set up GraphQL client using environment variable +const client = createDefaultClient(import.meta.env.WORDPRESS_URL, headers); +setGraphQLClient(client); + +const { identifier = '/' } = Astro.params; + +// Fetch template data based on the URI and preview mode +const templateData = await uriToTemplate( + isPreview + ? { + id: identifier, + asPreview: true, + } + : { uri: identifier }, +); + +// Make data available to other templates and components +Astro.locals.templateData = templateData; +Astro.locals.isPreview = isPreview || false; +Astro.locals.client = client; + +// If a template was found, rewrite to that template's path +if (templateData.template) { + return Astro.rewrite(templateData.template.path); +} + +// If no template was found, redirect to 404 +return Astro.rewrite('/404'); +--- diff --git a/examples/astro/kitchen-sink/src/pages/wp-templates/archive.astro b/examples/astro/kitchen-sink/src/pages/wp-templates/archive.astro new file mode 100644 index 000000000..26ee4def3 --- /dev/null +++ b/examples/astro/kitchen-sink/src/pages/wp-templates/archive.astro @@ -0,0 +1,72 @@ +--- +import Layout from '@/components/Layout.astro'; +import { GET_ARCHIVE } from '@/queries/getArchive'; +import { print } from 'graphql'; + +/** + * This is a template for the WordPress template hierarchy. + * It will be used to render the WordPress templates. + * It should never be used directly. + * This check confirms that the template is being used in the correct context. + * If the template is being used directly, it will redirect to the 404 page. + */ +if (Astro.url.pathname === Astro.originPathname) { + return Astro.rewrite('/404'); +} + +const uri = Astro.locals.templateData?.seedNode?.uri; + +const { data, error } = await Astro.locals.client.request(print(GET_ARCHIVE), { + uri, +}); + +if (error) { + console.error('Error fetching data:', error); + return Astro.rewrite('/500'); +} + +if (!data?.nodeByUri) { + console.error('HTTP/404 - Not Found in WordPress:', uri); + return Astro.rewrite('/404'); +} + +const categoryData = data?.nodeByUri; +const { posts, name } = categoryData || {}; +--- + + +
+

{name}

+ + { + posts?.edges?.map((item: any) => { + const post = item.node; + + return ( +
+

+ + {post.title} + +

+ + {post.featuredImage && ( + {post.featuredImage.node.altText + )} + +
+ By {post.author.node.name} on{' '} + {new Date(post.date).toLocaleDateString()} +
+ +
+
+ ); + }) + } +
+
diff --git a/examples/astro/kitchen-sink/src/pages/wp-templates/home.astro b/examples/astro/kitchen-sink/src/pages/wp-templates/home.astro new file mode 100644 index 000000000..ea5cdb354 --- /dev/null +++ b/examples/astro/kitchen-sink/src/pages/wp-templates/home.astro @@ -0,0 +1,42 @@ +--- +import Layout from '@/components/Layout.astro'; +import BlogPostItem from '@/components/BlogPostItem.astro'; +import { GET_POSTS } from '@/queries/getPosts'; +import { print } from 'graphql'; + +/** + * This is a template for the WordPress template hierarchy. + * It will be used to render the WordPress templates. + * It should never be used directly. + * This check confirms that the template is being used in the correct context. + * If the template is being used directly, it will redirect to the 404 page. + */ +if (Astro.url.pathname === Astro.originPathname) { + return Astro.rewrite('/404'); +} + +const databaseId = Astro.locals.templateData?.seedNode?.databaseId; + +const { data, error } = await Astro.locals.client.request(print(GET_POSTS)); + +if (error) { + console.error('Error fetching data:', error); + return Astro.rewrite('/500'); +} + +if (!data.posts) { + console.error('HTTP/404 - Not Found in WordPress:', databaseId); + return Astro.rewrite('/404'); +} + +const posts = data?.posts; +--- + + + { + posts?.edges?.map((item: any) => { + const post = item.node; + return ; + }) + } + diff --git a/examples/astro/kitchen-sink/src/pages/wp-templates/index.astro b/examples/astro/kitchen-sink/src/pages/wp-templates/index.astro new file mode 100644 index 000000000..9bd4d8452 --- /dev/null +++ b/examples/astro/kitchen-sink/src/pages/wp-templates/index.astro @@ -0,0 +1,53 @@ +--- +import Layout from '@/components/Layout.astro'; + +/** + * This is the index template for the WordPress template hierarchy. + * It serves as the fallback template for any content that doesn't have + * a more specific template (like single, page, archive, etc.). + * This template should never be accessed directly. + */ +if (Astro.url.pathname === Astro.originPathname) { + return Astro.rewrite('/404'); +} + +const templateData = Astro.locals.templateData; +const seedNode = templateData?.seedNode; +const contentType = seedNode?.__typename || 'content'; +--- + + +
+
+

+ Template Not Found +

+

+ No specific template was found for the {contentType} type. + This content is being displayed using the default fallback template. +

+
+ + + +
+

+ Debug Information +

+
+
{JSON.stringify(
+					templateData,
+					null,
+					2
+				)}
+
+
+
+
diff --git a/examples/astro/kitchen-sink/src/pages/wp-templates/page.astro b/examples/astro/kitchen-sink/src/pages/wp-templates/page.astro new file mode 100644 index 000000000..1f59f8f05 --- /dev/null +++ b/examples/astro/kitchen-sink/src/pages/wp-templates/page.astro @@ -0,0 +1,58 @@ +--- +import Layout from '@/components/Layout.astro'; +import { GET_PAGE } from '@/queries/getPage'; +import { print } from 'graphql'; + +/** + * This is a template for the WordPress template hierarchy. + * It will be used to render the WordPress templates. + * It should never be used directly. + * This check confirms that the template is being used in the correct context. + * If the template is being used directly, it will redirect to the 404 page. + */ +if (Astro.url.pathname === Astro.originPathname) { + return Astro.rewrite('/404'); +} + +const isPreview = Astro.locals.isPreview; +const databaseId = Astro.locals.templateData?.seedNode?.databaseId; + +const { data, error } = await Astro.locals.client.request(print(GET_PAGE), { + databaseId, + asPreview: isPreview, +}); + +if (error) { + console.error('Error fetching data:', error); + return Astro.rewrite('/500'); +} + +if (!data.page) { + console.error('HTTP/404 - Not Found in WordPress:', databaseId); + return Astro.rewrite('/404'); +} + +const { title, content, featuredImage } = data?.page || {}; +--- + + +
+
+

+ {title} +

+
+ + { + featuredImage && ( + + ) + } + +
+
+
diff --git a/examples/astro/kitchen-sink/src/pages/wp-templates/single.astro b/examples/astro/kitchen-sink/src/pages/wp-templates/single.astro new file mode 100644 index 000000000..c00431300 --- /dev/null +++ b/examples/astro/kitchen-sink/src/pages/wp-templates/single.astro @@ -0,0 +1,73 @@ +--- +import Layout from '@/components/Layout.astro'; +import { GET_POST } from '@/queries/getPost.js'; +import { print } from 'graphql'; + +/** + * This is a template for the WordPress template hierarchy. + * It will be used to render the WordPress templates. + * It should never be used directly. + * This check confirms that the template is being used in the correct context. + * If the template is being used directly, it will redirect to the 404 page. + */ +if (Astro.url.pathname === Astro.originPathname) { + return Astro.rewrite('/404'); +} + +const isPreview = Astro.locals.isPreview; +const databaseId = Astro.locals.templateData?.seedNode?.databaseId; + +const { data, error } = await Astro.locals.client.request(print(GET_POST), { + databaseId, + asPreview: isPreview, +}); + +if (error) { + console.error('Error fetching data:', error); + return Astro.rewrite('/500'); +} + +if (!data.post) { + console.error('HTTP/404 - Not Found in WordPress:', databaseId); + return Astro.rewrite('/404'); +} + +const { title, content, featuredImage } = data?.post || {}; +--- + + +
+
+

+ {title} +

+
+ + { + featuredImage && ( + + ) + } + +
+
+
+ + diff --git a/examples/astro/kitchen-sink/src/queries/getArchive.js b/examples/astro/kitchen-sink/src/queries/getArchive.js new file mode 100644 index 000000000..fc59b7f14 --- /dev/null +++ b/examples/astro/kitchen-sink/src/queries/getArchive.js @@ -0,0 +1,68 @@ +import gql from 'graphql-tag'; + +export const GET_ARCHIVE = gql` + query GetArchivePage($uri: String!) { + nodeByUri(uri: $uri) { + ... on Category { + name + posts { + edges { + node { + id + title + content + date + uri + featuredImage { + node { + id + sourceUrl + altText + mediaDetails { + width + height + } + } + } + author { + node { + name + } + } + } + } + } + } + ... on Tag { + name + posts { + edges { + node { + id + title + content + date + uri + featuredImage { + node { + id + sourceUrl + altText + mediaDetails { + width + height + } + } + } + author { + node { + name + } + } + } + } + } + } + } + } +`; diff --git a/examples/astro/kitchen-sink/src/queries/getLayout.js b/examples/astro/kitchen-sink/src/queries/getLayout.js new file mode 100644 index 000000000..f278ee1af --- /dev/null +++ b/examples/astro/kitchen-sink/src/queries/getLayout.js @@ -0,0 +1,10 @@ +import { gql } from 'graphql-tag'; + +export const GET_LAYOUT = gql` + query GetLayout { + generalSettings { + title + description + } + } +`; diff --git a/examples/astro/kitchen-sink/src/queries/getPage.js b/examples/astro/kitchen-sink/src/queries/getPage.js new file mode 100644 index 000000000..76646f1a5 --- /dev/null +++ b/examples/astro/kitchen-sink/src/queries/getPage.js @@ -0,0 +1,27 @@ +import gql from 'graphql-tag'; + +export const GET_PAGE = gql` + query GetPage($databaseId: ID!, $asPreview: Boolean = false) { + page(id: $databaseId, idType: DATABASE_ID, asPreview: $asPreview) { + title + content + date + author { + node { + name + } + } + featuredImage { + node { + id + sourceUrl + altText + mediaDetails { + width + height + } + } + } + } + } +`; diff --git a/examples/astro/kitchen-sink/src/queries/getPost.js b/examples/astro/kitchen-sink/src/queries/getPost.js new file mode 100644 index 000000000..30ba7b8d7 --- /dev/null +++ b/examples/astro/kitchen-sink/src/queries/getPost.js @@ -0,0 +1,27 @@ +import gql from 'graphql-tag'; + +export const GET_POST = gql` + query GetPost($databaseId: ID!, $asPreview: Boolean = false) { + post(id: $databaseId, idType: DATABASE_ID, asPreview: $asPreview) { + title + content + date + author { + node { + name + } + } + featuredImage { + node { + id + sourceUrl + altText + mediaDetails { + width + height + } + } + } + } + } +`; diff --git a/examples/astro/kitchen-sink/src/queries/getPosts.js b/examples/astro/kitchen-sink/src/queries/getPosts.js new file mode 100644 index 000000000..9cfcdd4e1 --- /dev/null +++ b/examples/astro/kitchen-sink/src/queries/getPosts.js @@ -0,0 +1,33 @@ +import gql from 'graphql-tag'; + +export const GET_POSTS = gql` + query GetPosts { + posts { + edges { + node { + id + title + content + date + uri + featuredImage { + node { + id + sourceUrl + altText + mediaDetails { + width + height + } + } + } + author { + node { + name + } + } + } + } + } + } +`; diff --git a/examples/astro/kitchen-sink/src/styles/globals.css b/examples/astro/kitchen-sink/src/styles/globals.css new file mode 100644 index 000000000..d4b507858 --- /dev/null +++ b/examples/astro/kitchen-sink/src/styles/globals.css @@ -0,0 +1 @@ +@import 'tailwindcss'; diff --git a/examples/astro/kitchen-sink/src/utils/getAuthString.js b/examples/astro/kitchen-sink/src/utils/getAuthString.js new file mode 100644 index 000000000..666bc6721 --- /dev/null +++ b/examples/astro/kitchen-sink/src/utils/getAuthString.js @@ -0,0 +1,8 @@ +// Forming the authentication string for WordPress App Password +// More info: https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/ + +export const getAuthString = () => + 'Basic ' + + Buffer.from( + import.meta.env.WP_USERNAME + ':' + import.meta.env.WP_APP_PASSWORD, + ).toString('base64'); diff --git a/examples/astro/kitchen-sink/tsconfig.json b/examples/astro/kitchen-sink/tsconfig.json new file mode 100644 index 000000000..547e4a6ce --- /dev/null +++ b/examples/astro/kitchen-sink/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/packages/astro/templateHierarchy.js b/packages/astro/templateHierarchy.js index a0e471f20..5ef364d02 100644 --- a/packages/astro/templateHierarchy.js +++ b/packages/astro/templateHierarchy.js @@ -20,7 +20,7 @@ console.log( * @param {import('@faustjs/template-hierarchy').UriToTemplateBaseParams & { graphqlClient?: import('./types.js').GraphQLClient }} options - Resolution options * @returns {Promise} The resolved template data */ -export async function uriToTemplate({ uri, graphqlClient }) { +export async function uriToTemplate({ uri, graphqlClient, id, asPreview }) { /** @type {import('@faustjs/template-hierarchy').TemplateData} */ const returnData = { uri, @@ -28,20 +28,30 @@ export async function uriToTemplate({ uri, graphqlClient }) { availableTemplates: undefined, possibleTemplates: undefined, template: undefined, + seedNode: undefined, }; // Get the GraphQL client - use provided one or get configured one const client = getGraphQLClient(graphqlClient); - const { data, error } = await getSeedQuery({ uri, graphqlClient: client }); + const { data, error } = await getSeedQuery({ + uri, + graphqlClient: client, + id, + asPreview, + }); returnData.seedQuery = { data, error }; + const seedNode = data?.nodeByUri || data?.contentNode; + + returnData.seedNode = seedNode ?? error; + if (error) { console.error('Error fetching seedQuery:', error); return returnData; } - if (!data.nodeByUri) { + if (!seedNode) { console.error('HTTP/404 - Not Found in WordPress:', uri); returnData.template = { id: '404 Not Found', path: '/404' }; @@ -58,7 +68,7 @@ export async function uriToTemplate({ uri, graphqlClient }) { return returnData; } - const possibleTemplates = getPossibleTemplates(data.nodeByUri); + const possibleTemplates = getPossibleTemplates(seedNode); returnData.possibleTemplates = possibleTemplates; diff --git a/packages/nextjs/pages/templateHierarchy.js b/packages/nextjs/pages/templateHierarchy.js index 43e365e3f..e9afbc41b 100644 --- a/packages/nextjs/pages/templateHierarchy.js +++ b/packages/nextjs/pages/templateHierarchy.js @@ -70,8 +70,6 @@ export async function uriToTemplate({ const possibleTemplates = getPossibleTemplates(seedNode); - console.log(possibleTemplates); - returnData.possibleTemplates = possibleTemplates; if (!possibleTemplates || possibleTemplates.length === 0) { diff --git a/packages/template-hierarchy/seedQueryExecutor.js b/packages/template-hierarchy/seedQueryExecutor.js index 15a4a4dd5..9eec53d08 100644 --- a/packages/template-hierarchy/seedQueryExecutor.js +++ b/packages/template-hierarchy/seedQueryExecutor.js @@ -22,8 +22,6 @@ export async function getSeedQuery({ uri, id, asPreview, graphqlClient }) { asPreview, }); - console.log('Seed query result:', uri, id, asPreview, result); - return { data: result.data || result, error: result.error || null, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dad6121ba..7e18df114 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,6 +66,37 @@ importers: specifier: ^3.0.2 version: 3.0.2 + examples/astro/kitchen-sink: + dependencies: + '@faustjs/astro': + specifier: workspace:* + version: link:../../../packages/astro + '@faustjs/data-fetching': + specifier: workspace:* + version: link:../../../packages/data-fetching + '@faustjs/template-hierarchy': + specifier: workspace:* + version: link:../../../packages/template-hierarchy + '@tailwindcss/vite': + specifier: ^4.1.12 + version: 4.1.12(vite@6.3.5(jiti@2.5.1)(lightningcss@1.30.1)) + astro: + specifier: ^5.1.1 + version: 5.12.8(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.46.2)(typescript@5.8.3) + graphql: + specifier: ^16.9.0 + version: 16.11.0 + graphql-tag: + specifier: ^2.12.6 + version: 2.12.6(graphql@16.11.0) + tailwindcss: + specifier: ^4.1.11 + version: 4.1.11 + devDependencies: + typescript: + specifier: ^5.6.3 + version: 5.8.3 + examples/astro/template-hierarchy: dependencies: '@faustjs/astro': @@ -88,7 +119,7 @@ importers: specifier: ^5.6.3 version: 5.8.3 - examples/nextjs/previews: + examples/nextjs/kitchen-sink: dependencies: '@faustjs/data-fetching': specifier: workspace:* @@ -1273,60 +1304,117 @@ packages: '@tailwindcss/node@4.1.11': resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==} + '@tailwindcss/node@4.1.12': + resolution: {integrity: sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==} + '@tailwindcss/oxide-android-arm64@4.1.11': resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==} engines: {node: '>= 10'} cpu: [arm64] os: [android] + '@tailwindcss/oxide-android-arm64@4.1.12': + resolution: {integrity: sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + '@tailwindcss/oxide-darwin-arm64@4.1.11': resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] + '@tailwindcss/oxide-darwin-arm64@4.1.12': + resolution: {integrity: sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.1.11': resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.1.12': + resolution: {integrity: sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@tailwindcss/oxide-freebsd-x64@4.1.11': resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] + '@tailwindcss/oxide-freebsd-x64@4.1.12': + resolution: {integrity: sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==} engines: {node: '>= 10'} cpu: [arm] os: [linux] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + resolution: {integrity: sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + resolution: {integrity: sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@tailwindcss/oxide-linux-arm64-musl@4.1.11': resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + resolution: {integrity: sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@tailwindcss/oxide-linux-x64-gnu@4.1.11': resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + resolution: {integrity: sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@tailwindcss/oxide-linux-x64-musl@4.1.11': resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + '@tailwindcss/oxide-linux-x64-musl@4.1.12': + resolution: {integrity: sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@tailwindcss/oxide-wasm32-wasi@4.1.11': resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==} engines: {node: '>=14.0.0'} @@ -1339,25 +1427,58 @@ packages: - '@emnapi/wasi-threads' - tslib + '@tailwindcss/oxide-wasm32-wasi@4.1.12': + resolution: {integrity: sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] + '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + resolution: {integrity: sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.1.11': resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + resolution: {integrity: sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@tailwindcss/oxide@4.1.11': resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==} engines: {node: '>= 10'} + '@tailwindcss/oxide@4.1.12': + resolution: {integrity: sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==} + engines: {node: '>= 10'} + '@tailwindcss/postcss@4.1.11': resolution: {integrity: sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==} + '@tailwindcss/vite@4.1.12': + resolution: {integrity: sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + '@tybys/wasm-util@0.10.0': resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} @@ -3700,6 +3821,9 @@ packages: tailwindcss@4.1.11: resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==} + tailwindcss@4.1.12: + resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==} + tapable@2.2.2: resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} engines: {node: '>=6'} @@ -5029,42 +5153,88 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.1.11 + '@tailwindcss/node@4.1.12': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.12 + '@tailwindcss/oxide-android-arm64@4.1.11': optional: true + '@tailwindcss/oxide-android-arm64@4.1.12': + optional: true + '@tailwindcss/oxide-darwin-arm64@4.1.11': optional: true + '@tailwindcss/oxide-darwin-arm64@4.1.12': + optional: true + '@tailwindcss/oxide-darwin-x64@4.1.11': optional: true + '@tailwindcss/oxide-darwin-x64@4.1.12': + optional: true + '@tailwindcss/oxide-freebsd-x64@4.1.11': optional: true + '@tailwindcss/oxide-freebsd-x64@4.1.12': + optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.1.11': optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.1.11': optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + optional: true + '@tailwindcss/oxide-linux-x64-musl@4.1.11': optional: true + '@tailwindcss/oxide-linux-x64-musl@4.1.12': + optional: true + '@tailwindcss/oxide-wasm32-wasi@4.1.11': optional: true + '@tailwindcss/oxide-wasm32-wasi@4.1.12': + optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.1.11': optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + optional: true + '@tailwindcss/oxide@4.1.11': dependencies: detect-libc: 2.0.4 @@ -5083,6 +5253,24 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11 '@tailwindcss/oxide-win32-x64-msvc': 4.1.11 + '@tailwindcss/oxide@4.1.12': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.12 + '@tailwindcss/oxide-darwin-arm64': 4.1.12 + '@tailwindcss/oxide-darwin-x64': 4.1.12 + '@tailwindcss/oxide-freebsd-x64': 4.1.12 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.12 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.12 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.12 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.12 + '@tailwindcss/oxide-linux-x64-musl': 4.1.12 + '@tailwindcss/oxide-wasm32-wasi': 4.1.12 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 + '@tailwindcss/postcss@4.1.11': dependencies: '@alloc/quick-lru': 5.2.0 @@ -5091,6 +5279,13 @@ snapshots: postcss: 8.5.6 tailwindcss: 4.1.11 + '@tailwindcss/vite@4.1.12(vite@6.3.5(jiti@2.5.1)(lightningcss@1.30.1))': + dependencies: + '@tailwindcss/node': 4.1.12 + '@tailwindcss/oxide': 4.1.12 + tailwindcss: 4.1.12 + vite: 6.3.5(jiti@2.5.1)(lightningcss@1.30.1) + '@tybys/wasm-util@0.10.0': dependencies: tslib: 2.8.1 @@ -8274,6 +8469,8 @@ snapshots: tailwindcss@4.1.11: {} + tailwindcss@4.1.12: {} + tapable@2.2.2: {} tar@7.4.3: