Skip to content

Instantly share code, notes, and snippets.

@jasonbahl
Created May 19, 2025 17:08
Show Gist options
  • Save jasonbahl/7da8e27c5fb5dce4a1f11715ead79a3f to your computer and use it in GitHub Desktop.
Save jasonbahl/7da8e27c5fb5dce4a1f11715ead79a3f to your computer and use it in GitHub Desktop.
import { GraphQLClient, gql } from 'graphql-request';
import { SEED_QUERY } from '@/components/WPTemplateRouter/SEED_QUERY';
import templates from '@/wp-templates/wp-templates.js';
/**
* Fetches the seed node data from WordPress using the SEED_QUERY.
*
* @param {object} context - The context object.
* @param {string} context.uri - The URI of the node to fetch.
* @param {boolean} [context.isPreview=false] - Whether this is a preview request.
* @param {object} [context.previewData={}] - Preview data containing token/id.
* @returns {Promise<object|null>} The seed node data or null if not found/error.
*/
export async function fetchSeedNodeData({ uri, isPreview = false, previewData = {} }) {
const endpoint = process.env.NEXT_PUBLIC_WORDPRESS_URL;
if (!endpoint) {
console.error('NEXT_PUBLIC_WORDPRESS_URL environment variable is not set.');
// Returning null or throwing an error are options here
return null;
}
// TODO: Refine how preview ID and token are obtained if needed
const id = isPreview ? previewData?.id : 0;
const headers = isPreview ? { 'authorization': `Bearer ${previewData?.token}` } : {};
const graphQLClient = new GraphQLClient(endpoint, { headers });
try {
const data = await graphQLClient.request(SEED_QUERY, { uri, id, asPreview: isPreview });
const seedNodeData = isPreview ? data?.contentNode : data?.nodeByUri;
if (!seedNodeData) {
console.log(`Seed node not found via fetchSeedNodeData for URI: ${uri}, Preview: ${isPreview}`);
return null;
}
return seedNodeData;
} catch (error) {
console.error(`Error in fetchSeedNodeData for URI: ${uri}, Preview: ${isPreview}`, error);
// Returning null signifies an error or not found state to getStaticProps
return null;
}
}
// Function to determine the template hierarchy based on seed node data
// Moved from WPTemplateRouter - now runs server-side in getStaticProps
export const getPossibleTemplates = (seedNode) => {
const templatesList = [];
if (!seedNode) {
return ['index']; // Fallback
}
// Note: Ensure these fields are available in your SEED_QUERY results
const { __typename, databaseId, slug, contentType, template, hPageTemplates, isFrontPage, isPostsPage } = seedNode;
// TODO: Add logic for CPT Archives, Taxonomies, Author pages, Search, 404 if needed
if (isFrontPage) {
templatesList.push('front-page');
} else if (isPostsPage) {
templatesList.push('home');
}
// --- Custom Condition for hPageTemplates --- START
const customHPagesTemplateSlug = hPageTemplates?.nodes?.[0]?.slug;
if (customHPagesTemplateSlug) {
templatesList.push(`h-page-template-${customHPagesTemplateSlug}`);
console.log(`[getPossibleTemplates] Added custom hPageTemplate: h-page-template-${customHPagesTemplateSlug}`);
}
// --- Custom Condition for hPageTemplates --- END
// Handle specific WP Templates assigned in the editor (Less priority than hPageTemplates)
const wpTemplateName = template?.templateName?.toLowerCase().replace(' ', '-');
if (wpTemplateName && wpTemplateName !== 'default') {
templatesList.push(wpTemplateName);
}
const customTemplateSlug = hPageTemplates?.nodes?.[0]?.slug;
if (customTemplateSlug) {
templatesList.push(`page-${customTemplateSlug}`);
}
if (seedNode.isContentNode) {
const contentTypeSlug = contentType?.node?.name?.toLowerCase();
if (slug) {
templatesList.push(`${contentTypeSlug}-${slug}`);
}
if (databaseId) {
templatesList.push(`${contentTypeSlug}-${databaseId}`);
}
if (contentTypeSlug) {
templatesList.push(`single-${contentTypeSlug}`);
}
templatesList.push('singular');
}
// Handle Content Type Archives
if (__typename === 'ContentType') {
const contentTypeName = seedNode.name?.toLowerCase();
if (contentTypeName) {
templatesList.push(`archive-${contentTypeName}`);
}
templatesList.push('archive');
}
if (seedNode.isTermNode) {
const taxonomySlug = seedNode.taxonomyName?.toLowerCase();
if (slug) {
templatesList.push(`taxonomy-${taxonomySlug}-${slug}`);
}
if (databaseId) {
templatesList.push(`taxonomy-${taxonomySlug}-${databaseId}`);
}
if (taxonomySlug) {
templatesList.push(`taxonomy-${taxonomySlug}`);
}
templatesList.push('taxonomy');
templatesList.push('archive');
}
templatesList.push('index');
return [...new Set(templatesList)];
};
/**
* Fetches seed data, resolves the template key, and executes template-specific queries.
*
* @param {object} context - The context object from getStaticProps.
* @returns {Promise<{seedNodeData: object|null, templateKey: string, queryResults: object, isPreview: boolean}>}
*/
export async function resolveTemplateData(context) {
const { previewData } = context;
const uri = '/' + (context.params?.wordpressNode || []).join('/');
const isPreview = !!previewData?.token;
const endpoint = process.env.NEXT_PUBLIC_WORDPRESS_URL;
const headers = isPreview ? { 'authorization': `Bearer ${previewData?.token}` } : {};
const graphQLClient = new GraphQLClient(endpoint, { headers });
console.log(`[resolveTemplateData] Calculated URI: ${uri}`);
// Fetch initial seed data
const seedNodeData = await fetchSeedNodeData({ uri, isPreview, previewData });
if (!seedNodeData) {
// Return null seed data; template resolution will fallback to 'index'
// No footerData to return here anymore
return { seedNodeData: null, templateKey: 'index', queryResults: {}, isPreview };
}
// Determine the prioritized list of template keys
const possibleTemplateKeys = getPossibleTemplates(seedNodeData);
console.log(`[resolveTemplateData] Possible keys for URI ${context.params?.wordpressNode?.join('/') || '/'}:`, possibleTemplateKeys);
// Find the first *component* that exists for the possible keys
let TemplateComponent = null;
let templateKey = 'index'; // Default fallback key
for (const key of possibleTemplateKeys) {
if (templates[key]) {
TemplateComponent = templates[key];
templateKey = key;
console.log(`[resolveTemplateData] Matched component for key: ${templateKey}`);
break;
}
}
// If no specific component found, fallback to index
if (!TemplateComponent) {
TemplateComponent = templates.index;
templateKey = 'index';
console.log('[resolveTemplateData] No specific template matched, falling back to index.');
}
// --- Execute template-specific queries --- START
let queryResults = {};
if (TemplateComponent && typeof TemplateComponent.queries === 'object') {
console.log(`[resolveTemplateData] Executing queries for template: ${templateKey}`);
// graphQLClient already initialized above
const queryPromises = Object.entries(TemplateComponent.queries).map(async ([queryKey, queryConfig]) => {
if (!queryConfig || !queryConfig.query || typeof queryConfig.variables !== 'function') {
console.warn(`[resolveTemplateData] Invalid query config for key: ${queryKey} in template: ${templateKey}`);
return [queryKey, null]; // Store null for invalid configs
}
try {
const variables = queryConfig.variables(seedNodeData);
console.log(`[resolveTemplateData] Executing query: ${queryKey} with variables:`, variables);
const result = await graphQLClient.request(queryConfig.query, variables);
return [queryKey, result];
} catch (error) {
console.error(`[resolveTemplateData] Error executing query ${queryKey} for template ${templateKey}:`, error);
return [queryKey, null]; // Store null on error
}
});
const results = await Promise.all(queryPromises);
queryResults = Object.fromEntries(results.filter(([, result]) => result !== undefined));
console.log(`[resolveTemplateData] Query results for ${templateKey}:`, queryResults);
} else {
console.log(`[resolveTemplateData] No queries defined for template: ${templateKey}`);
}
// --- Execute template-specific queries --- END
// Return seed node, template key, and results from template queries
return { seedNodeData, templateKey, queryResults, isPreview };
}
/**
* Fetches only the data required for the main layout (Header/Footer).
*
* @returns {Promise<object>} Object containing keys like 'header', 'footer' with their query results.
*/
export async function fetchLayoutData() {
const endpoint = process.env.NEXT_PUBLIC_WORDPRESS_URL;
if (!endpoint) {
console.error('NEXT_PUBLIC_WORDPRESS_URL environment variable is not set.');
return {}; // Return empty object on error
}
const graphQLClient = new GraphQLClient(endpoint);
// Import queries directly here or pass them in if preferred
const { HEADER_QUERY } = await import('@/components/Header/Header');
const { FOOTER_QUERY } = await import('@/components/Footer/Footer');
let layoutQueryResults = {};
try {
const headerResult = await graphQLClient.request(HEADER_QUERY);
layoutQueryResults.header = headerResult;
} catch (error) {
console.error('[fetchLayoutData] Error fetching Header data:', error);
layoutQueryResults.header = null; // Indicate error
}
try {
const footerResult = await graphQLClient.request(FOOTER_QUERY);
layoutQueryResults.footer = footerResult;
} catch (error) {
console.error('[fetchLayoutData] Error fetching Footer data:', error);
layoutQueryResults.footer = null; // Indicate error
}
return layoutQueryResults;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment