UNPKG

@ai-growth/nextjs

Version:

Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering

244 lines (243 loc) 8.28 kB
/** * Enhanced template resolver with advanced features */ export class TemplateResolver { constructor(templateRegistry) { this.partialOverrides = []; this.resolutionCache = new Map(); this.templateRegistry = templateRegistry; } /** * Register a partial template override */ registerPartialOverride(override) { this.partialOverrides.push(override); this.clearCache(); } /** * Remove all partial overrides */ clearPartialOverrides() { this.partialOverrides = []; this.clearCache(); } /** * Update the template registry */ updateRegistry(newRegistry) { this.templateRegistry = newRegistry; this.clearCache(); } /** * Clear resolution cache */ clearCache() { this.resolutionCache.clear(); } /** * Resolve template for a content type with advanced logic */ resolveTemplate(contentType, options = {}) { const cacheKey = this.getCacheKey(contentType, options); // Check cache first if (this.resolutionCache.has(cacheKey)) { return this.resolutionCache.get(cacheKey); } const result = this.performResolution(contentType, options); // Cache the result this.resolutionCache.set(cacheKey, result); if (options.debug) { console.log('Template Resolution:', { contentType, result, resolutionPath: result.resolutionPath, }); } return result; } /** * Get partial overrides for a content type */ getPartialOverrides(contentType) { return this.partialOverrides.filter(override => override.contentTypes.some(ct => this.matchesContentType(contentType, ct))); } /** * Create a template component with partial overrides applied */ createTemplateWithOverrides(baseTemplate, contentType) { const overrides = this.getPartialOverrides(contentType); if (overrides.length === 0) { return baseTemplate; } // For now, return the base template // TODO: Implement proper template composition with React.createElement // This would require more complex React component handling return baseTemplate; } /** * Perform the actual template resolution */ performResolution(contentType, options) { const resolutionPath = []; // 1. Try exact match resolutionPath.push('checking exact match'); if (this.templateRegistry[contentType]) { const template = this.createTemplateWithOverrides(this.templateRegistry[contentType], contentType); return { template, matchType: 'exact', matchedContentType: contentType, resolutionPath, }; } // 2. Try wildcard patterns resolutionPath.push('checking wildcard patterns'); const wildcardMatch = this.findWildcardMatch(contentType); if (wildcardMatch) { const template = this.createTemplateWithOverrides(this.templateRegistry[wildcardMatch], contentType); return { template, matchType: 'wildcard', matchedContentType: wildcardMatch, resolutionPath, }; } // 3. Try parent type matching (e.g., "blog" for "blog.post") resolutionPath.push('checking parent types'); const parentMatch = this.findParentMatch(contentType); if (parentMatch) { const template = this.createTemplateWithOverrides(this.templateRegistry[parentMatch], contentType); return { template, matchType: 'parent', matchedContentType: parentMatch, resolutionPath, }; } // 4. Try content type hierarchy if (options.contentTypeHierarchy) { resolutionPath.push('checking content type hierarchy'); for (const hierarchyType of options.contentTypeHierarchy) { if (this.templateRegistry[hierarchyType]) { const template = this.createTemplateWithOverrides(this.templateRegistry[hierarchyType], contentType); return { template, matchType: 'hierarchy', matchedContentType: hierarchyType, resolutionPath, }; } } } // 5. Try fallback template if (options.fallbackTemplate) { resolutionPath.push('using provided fallback'); const template = this.createTemplateWithOverrides(options.fallbackTemplate, contentType); return { template, matchType: 'fallback', resolutionPath, }; } // 6. Try default template from registry if (this.templateRegistry.default) { resolutionPath.push('using registry default'); const template = this.createTemplateWithOverrides(this.templateRegistry.default, contentType); return { template, matchType: 'fallback', matchedContentType: 'default', resolutionPath, }; } // 7. No template found resolutionPath.push('no template found'); return { template: null, matchType: 'none', resolutionPath, }; } /** * Find wildcard pattern match */ findWildcardMatch(contentType) { const wildcardKeys = Object.keys(this.templateRegistry).filter(key => key.includes('*')); for (const wildcardKey of wildcardKeys) { if (this.matchesWildcardPattern(contentType, wildcardKey)) { return wildcardKey; } } return null; } /** * Find parent type match */ findParentMatch(contentType) { const parts = contentType.split('.'); // Try progressively shorter parent types for (let i = parts.length - 1; i > 0; i--) { const parentType = parts.slice(0, i).join('.'); if (this.templateRegistry[parentType]) { return parentType; } } return null; } /** * Check if content type matches a wildcard pattern */ matchesWildcardPattern(contentType, pattern) { const regexPattern = pattern.replace(/\*/g, '.*'); const regex = new RegExp(`^${regexPattern}$`); return regex.test(contentType); } /** * Check if content type matches another content type (including wildcards) */ matchesContentType(contentType, targetType) { if (contentType === targetType) { return true; } if (targetType.includes('*')) { return this.matchesWildcardPattern(contentType, targetType); } return false; } /** * Generate cache key for resolution result */ getCacheKey(contentType, options) { return JSON.stringify({ contentType, fallbackTemplate: options.fallbackTemplate?.name || 'none', hierarchy: options.contentTypeHierarchy || [], overrides: this.partialOverrides.length, }); } /** * Get resolution statistics */ getStatistics() { return { cacheSize: this.resolutionCache.size, registrySize: Object.keys(this.templateRegistry).length, overridesCount: this.partialOverrides.length, }; } } /** * Create a template resolver instance */ export function createTemplateResolver(templateRegistry) { return new TemplateResolver(templateRegistry); } /** * Utility function to resolve template with simple interface */ export function resolveTemplate(contentType, templateRegistry, options) { const resolver = new TemplateResolver(templateRegistry); const result = resolver.resolveTemplate(contentType, options); return result.template; } export default TemplateResolver;