From 8a82517a58f70cc3b6bad8dd380055520c9034dd Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Mon, 5 Jan 2026 09:18:44 -0800 Subject: [PATCH 01/14] initial implementation of handling for dynamic imports --- .changeset/funny-ravens-run.md | 47 +++++++++++++++++++ docs/how-to/basic-setup/index.md | 10 ++-- .../wp-templates/index.js | 36 ++++++++++---- packages/faustwp-core/src/getTemplate.ts | 15 +++++- .../faustwp-core/src/getWordPressProps.tsx | 15 +++++- 5 files changed, 105 insertions(+), 18 deletions(-) create mode 100644 .changeset/funny-ravens-run.md diff --git a/.changeset/funny-ravens-run.md b/.changeset/funny-ravens-run.md new file mode 100644 index 000000000..d79104d79 --- /dev/null +++ b/.changeset/funny-ravens-run.md @@ -0,0 +1,47 @@ +--- +'@faustwp/core': minor +--- + +Feat: Added support `next/dynamic` imports for templates to reduce initial bundle size in a way that's backwards compatible with static imports. + +This solves a known issue in Faust where all defined templates are bundled together and loaded on every WordPress page. by enabling the use of dynamic importing of templates this issue is resolved. Now templates are only loaded as needed per route. + +It's recommended you migrate to dynamic imports by updating your template file. Here's an example: + +```js title=src/wp-templates/index.js +// Old Static Templates +import category from './category'; +import tag from './tag'; +import frontPage from './front-page'; +import page from './page'; +import single from './single'; + +export default { + category, + tag, + 'front-page': frontPage, + page, + single, +}; + +// New Dynamic Templates +import dynamic from 'next/dynamic'; + +const category = dynamic(() => import('./category.js')); +const tag = dynamic(() => import('./tag.js')); +const frontPage = dynamic(() => import('./front-page.js')); +const page = dynamic(() => import('./page.js')); + +// The above examples assume use of default exports. If you are using named exports you'll need to handle that: +const single = dynamic(() => import('./single.js').then(mod => mod.Single)); + +export default { + category, + tag, + 'front-page': frontPage, + page, + single, +}; +``` + +For further info see the Next.js docs on the use of [`next/dynamic`](https://nextjs.org/docs/pages/guides/lazy-loading#nextdynamic-1). diff --git a/docs/how-to/basic-setup/index.md b/docs/how-to/basic-setup/index.md index 611a174be..81449ce44 100644 --- a/docs/how-to/basic-setup/index.md +++ b/docs/how-to/basic-setup/index.md @@ -175,13 +175,13 @@ In the `SingleTemplate` component, we receive the props, destructure the `title` Finally, we have to make Faust.js aware that this template exists. To do that, create an `index.js` file inside the `wp-templates` folder with this code inside: ```js title="wp-templates/index.js" -import SingleTemplate from "./single"; +import dynamic from 'next/dynamic'; -const templates = { - single: SingleTemplate, -}; +const category = dynamic(() => import('./category.js')); -export default templates; +export default { + single, +}; ``` ### C. Create a catch-all route diff --git a/examples/next/faustwp-getting-started/wp-templates/index.js b/examples/next/faustwp-getting-started/wp-templates/index.js index 3cabd3e14..0e8e6b51e 100644 --- a/examples/next/faustwp-getting-started/wp-templates/index.js +++ b/examples/next/faustwp-getting-started/wp-templates/index.js @@ -1,13 +1,29 @@ -import category from './category'; -import tag from './tag'; -import frontPage from './front-page'; -import page from './page'; -import single from './single'; +import dynamic from 'next/dynamic'; + +const category = dynamic(() => import('./category.js'), { + loading: () =>

Loading Category Template...

, +}); + +const tag = dynamic(() => import('./tag.js'), { + loading: () =>

Loading Tag Template...

, +}); + +const frontPage = dynamic(() => import('./front-page.js'), { + loading: () =>

Loading Front Page Template...

, +}); + +const page = dynamic(() => import('./page.js'), { + loading: () =>

Loading Page Template...

, +}); + +const single = dynamic(() => import('./single.js'), { + loading: () =>

Loading Single Post Template...

, +}); export default { - category, - tag, - 'front-page': frontPage, - page, - single, + category, + tag, + 'front-page': frontPage, + page, + single, }; diff --git a/packages/faustwp-core/src/getTemplate.ts b/packages/faustwp-core/src/getTemplate.ts index d08ec3ca7..1c5850445 100644 --- a/packages/faustwp-core/src/getTemplate.ts +++ b/packages/faustwp-core/src/getTemplate.ts @@ -146,10 +146,23 @@ export function getPossibleTemplates(node: SeedNode) { return possibleTemplates; } +type DynamicComponent = { + render: { + preload: () => Promise<{ default: C }>; + displayName?: string; + }; +}; + +export function isDynamicComponent( + component: C | DynamicComponent, +): component is DynamicComponent { + return (component as DynamicComponent).render?.preload !== undefined; +} + export function getTemplate( seedNode: SeedNode | null | undefined, templates: { [key: string]: WordPressTemplate }, -): WordPressTemplate | null { +): WordPressTemplate | DynamicComponent | null { if (!seedNode) { return null; } diff --git a/packages/faustwp-core/src/getWordPressProps.tsx b/packages/faustwp-core/src/getWordPressProps.tsx index 6128ccaf6..de81b8fcf 100644 --- a/packages/faustwp-core/src/getWordPressProps.tsx +++ b/packages/faustwp-core/src/getWordPressProps.tsx @@ -6,7 +6,11 @@ import { GetServerSidePropsContext, GetStaticPropsContext } from 'next'; import { addApolloState, getApolloClient } from './client.js'; import { FaustTemplateProps } from './components/WordPressTemplate.js'; import { getConfig } from './config/index.js'; -import { getPossibleTemplates, getTemplate } from './getTemplate.js'; +import { + getPossibleTemplates, + getTemplate, + isDynamicComponent, +} from './getTemplate.js'; import { SEED_QUERY, SeedNode } from './queries/seedQuery.js'; import { debugLog, infoLog } from './utils/log.js'; import { hooks } from './wpHooks/index.js'; @@ -142,12 +146,19 @@ export async function getWordPressProps( getPossibleTemplates(seedNode), ); - const template = getTemplate(seedNode, templates); + let template = getTemplate(seedNode, templates); if (!template) { return createNotFound(ctx, revalidate); } + if (isDynamicComponent(template)) { + + const dynamicTemplate = await template.render.preload(); + + template = dynamicTemplate.default ?? dynamicTemplate; + } + if (template.query && template.queries) { throw new Error( '`Only either `Component.query` or `Component.queries` can be provided, but not both.', From 7d2e095cf5d137b45e61f49e92b91e504c14c123 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 21 Jan 2026 15:21:27 -0800 Subject: [PATCH 02/14] fix: example image issue --- .../components/FeaturedImage/FeaturedImage.js | 105 +++++++++--------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js b/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js index 40a4a78cb..55721c734 100644 --- a/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js +++ b/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js @@ -1,58 +1,61 @@ import { gql } from '@apollo/client'; -import Image from 'next/image'; +import Image from 'next/legacy/image'; export default function FeaturedImage({ - image, - width, - height, - className, - priority, - layout, - ...props + image, + width, + height, + className, + priority, + layout, + ...props }) { - const src = image?.sourceUrl; - - if (!src) return null; - - const { altText = '', mediaDetails = {} } = image ?? {}; - - layout = layout ?? 'fill'; - - const dimensions = { - width: layout === 'fill' ? undefined : width ?? mediaDetails?.width, - height: layout === 'fill' ? undefined : height ?? mediaDetails?.height - }; - - if (layout !== 'fill' && (!dimensions.width || !dimensions.height)) return null; - - return ( -
- {altText} -
- ); + const src = image?.sourceUrl; + + if (!src) return null; + + const { altText = '', mediaDetails = {} } = image ?? {}; + + layout = layout ?? 'fill'; + + const dimensions = { + width: layout === 'fill' ? undefined : width ?? mediaDetails?.width, + height: layout === 'fill' ? undefined : height ?? mediaDetails?.height, + }; + + if (layout !== 'fill' && (!dimensions.width || !dimensions.height)) + return null; + + console.log(layout, mediaDetails, dimensions); + + return ( +
+ {altText} +
+ ); } FeaturedImage.fragments = { - entry: gql` - fragment FeaturedImageFragment on NodeWithFeaturedImage { - featuredImage { - node { - id - sourceUrl - altText - mediaDetails { - width - height - } - } - } - } - `, + entry: gql` + fragment FeaturedImageFragment on NodeWithFeaturedImage { + featuredImage { + node { + id + sourceUrl + altText + mediaDetails { + width + height + } + } + } + } + `, }; From e0a9c00741210540852e57428b1ccd2294ad35ee Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 21 Jan 2026 15:24:58 -0800 Subject: [PATCH 03/14] fix: add missing dynamic handling for queries and resolve ts errors. --- .../src/components/WordPressTemplate.tsx | 46 +++++++++++++++---- packages/faustwp-core/src/getTemplate.ts | 6 +++ .../faustwp-core/src/getWordPressProps.tsx | 6 +-- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/packages/faustwp-core/src/components/WordPressTemplate.tsx b/packages/faustwp-core/src/components/WordPressTemplate.tsx index 28fc7421d..6d5ed5c12 100644 --- a/packages/faustwp-core/src/components/WordPressTemplate.tsx +++ b/packages/faustwp-core/src/components/WordPressTemplate.tsx @@ -10,12 +10,17 @@ import React, { } from 'react'; import { getApolloAuthClient, getApolloClient } from '../client.js'; import { getConfig } from '../config/index.js'; -import { getTemplate } from '../getTemplate.js'; +import { + getTemplate, + isDynamicComponent, + loadDynamicComponent, +} from '../getTemplate.js'; import { useAuth } from '../hooks/useAuth.js'; import { SEED_QUERY, SeedNode } from '../queries/seedQuery.js'; import { FaustContext, FaustQueries } from '../store/FaustContext.js'; import { getQueryParam } from '../utils/convert.js'; import { isWordPressPreview } from '../utils/isWordPressPreview.js'; +import type { WordPressTemplate } from '../getWordPressProps.js'; export type FaustProps = { __SEED_NODE__?: SeedNode | null; @@ -38,6 +43,14 @@ export type FaustTemplateProps> = Props & { __TEMPLATE_VARIABLES__?: { [key: string]: any }; }; +function checkDuplicateQueryQueries(template: WordPressTemplate): void { + if (template.query && template.queries) { + throw new Error( + '`Only either `Component.query` or `Component.queries` can be provided, but not both.', + ); + } +} + export function WordPressTemplateInternal( props: WordPressTemplateProps & { seedNode: SeedNode; @@ -62,27 +75,29 @@ export function WordPressTemplateInternal( setLoading, ...wordpressTemplateProps } = props; - const template = getTemplate(seedNode, templates); + let template = getTemplate(seedNode, templates); const [data, setData] = useState(templateQueryDataProp); const { setQueries } = useContext(FaustContext) || {}; - if (template && template.queries && template.query) { - throw new Error( - '`Only either `Component.query` or `Component.queries` can be provided, but not both.', - ); - } - /** * Fetch the template's queries if defined. */ useEffect(() => { void (async () => { - const client = isPreview ? getApolloAuthClient() : getApolloClient(); if (!template) { return; } + if (isDynamicComponent(template)) { + template = await loadDynamicComponent(template); + } + + checkDuplicateQueryQueries(template); + + const client = isPreview ? getApolloAuthClient() : getApolloClient(); + + if (template.query) { return; } @@ -128,9 +143,20 @@ export function WordPressTemplateInternal( */ useEffect(() => { void (async () => { + + if(!template) { + return; + } + + if (isDynamicComponent(template)) { + template = await loadDynamicComponent(template); + } + + checkDuplicateQueryQueries(template); + const client = isPreview ? getApolloAuthClient() : getApolloClient(); - if (!template || !template?.query || template?.queries || !seedNode) { + if (!template.query || template.queries || !seedNode) { return; } diff --git a/packages/faustwp-core/src/getTemplate.ts b/packages/faustwp-core/src/getTemplate.ts index 1c5850445..ab70cf733 100644 --- a/packages/faustwp-core/src/getTemplate.ts +++ b/packages/faustwp-core/src/getTemplate.ts @@ -159,6 +159,12 @@ export function isDynamicComponent( return (component as DynamicComponent).render?.preload !== undefined; } +export function loadDynamicComponent( + component: DynamicComponent, +): Promise { + return component.render.preload().then((mod) => mod.default); +} + export function getTemplate( seedNode: SeedNode | null | undefined, templates: { [key: string]: WordPressTemplate }, diff --git a/packages/faustwp-core/src/getWordPressProps.tsx b/packages/faustwp-core/src/getWordPressProps.tsx index de81b8fcf..962a72969 100644 --- a/packages/faustwp-core/src/getWordPressProps.tsx +++ b/packages/faustwp-core/src/getWordPressProps.tsx @@ -10,6 +10,7 @@ import { getPossibleTemplates, getTemplate, isDynamicComponent, + loadDynamicComponent, } from './getTemplate.js'; import { SEED_QUERY, SeedNode } from './queries/seedQuery.js'; import { debugLog, infoLog } from './utils/log.js'; @@ -153,10 +154,7 @@ export async function getWordPressProps( } if (isDynamicComponent(template)) { - - const dynamicTemplate = await template.render.preload(); - - template = dynamicTemplate.default ?? dynamicTemplate; + template = await loadDynamicComponent(template); } if (template.query && template.queries) { From fc9b159499a1c1aa3b449a0f54718b1798137cdd Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 21 Jan 2026 15:32:43 -0800 Subject: [PATCH 04/14] fix: type errors in wordpress template --- .../src/components/WordPressTemplate.tsx | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/faustwp-core/src/components/WordPressTemplate.tsx b/packages/faustwp-core/src/components/WordPressTemplate.tsx index 6d5ed5c12..7d5a292c4 100644 --- a/packages/faustwp-core/src/components/WordPressTemplate.tsx +++ b/packages/faustwp-core/src/components/WordPressTemplate.tsx @@ -75,7 +75,7 @@ export function WordPressTemplateInternal( setLoading, ...wordpressTemplateProps } = props; - let template = getTemplate(seedNode, templates); + const unknownTemplate = getTemplate(seedNode, templates); const [data, setData] = useState(templateQueryDataProp); const { setQueries } = useContext(FaustContext) || {}; @@ -85,13 +85,13 @@ export function WordPressTemplateInternal( useEffect(() => { void (async () => { - if (!template) { + if (!unknownTemplate) { return; } - if (isDynamicComponent(template)) { - template = await loadDynamicComponent(template); - } + const template = isDynamicComponent(unknownTemplate) + ? await loadDynamicComponent(unknownTemplate) + : unknownTemplate; checkDuplicateQueryQueries(template); @@ -136,7 +136,7 @@ export function WordPressTemplateInternal( setLoading(false); })(); - }, [isAuthenticated, isPreview, seedNode, template, setQueries, setLoading]); + }, [isAuthenticated, isPreview, seedNode, unknownTemplate, setQueries, setLoading]); /** * Fetch the template's query if defined. @@ -144,13 +144,11 @@ export function WordPressTemplateInternal( useEffect(() => { void (async () => { - if(!template) { + if(!unknownTemplate) { return; } - if (isDynamicComponent(template)) { - template = await loadDynamicComponent(template); - } + const template = isDynamicComponent(unknownTemplate) ? await loadDynamicComponent(unknownTemplate) : unknownTemplate; checkDuplicateQueryQueries(template); @@ -179,14 +177,13 @@ export function WordPressTemplateInternal( setLoading(false); })(); - }, [data, template, seedNode, isPreview, isAuthenticated, setLoading]); + }, [data, unknownTemplate, seedNode, isPreview, isAuthenticated, setLoading]); - if (!template) { + if (!unknownTemplate) { return null; } - const Component = template as React.FC<{ [key: string]: any }>; - + const Component = unknownTemplate as React.FC<{ [key: string]: any }>; const newProps = { ...wordpressTemplateProps, __TEMPLATE_QUERY_DATA__: templateQueryDataProp, @@ -212,8 +209,8 @@ export function WordPressTemplate(props: WordPressTemplateProps) { const [seedNode, setSeedNode] = useState( seedNodeProp ?? null, ); - const template = getTemplate(seedNode, templates); - const [loading, setLoading] = useState(template === null); + const unknownTemplate = getTemplate(seedNode, templates); + const [loading, setLoading] = useState(unknownTemplate === null); const [isPreview, setIsPreview] = useState( templateQueryDataProp ? false : null, ); From fce978c6ad06c58ac94ab5545a72ebce31403cb9 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 21 Jan 2026 15:34:40 -0800 Subject: [PATCH 05/14] fix: type errors in geWPProps --- packages/faustwp-core/src/getWordPressProps.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/faustwp-core/src/getWordPressProps.tsx b/packages/faustwp-core/src/getWordPressProps.tsx index 962a72969..153a2ff47 100644 --- a/packages/faustwp-core/src/getWordPressProps.tsx +++ b/packages/faustwp-core/src/getWordPressProps.tsx @@ -147,15 +147,15 @@ export async function getWordPressProps( getPossibleTemplates(seedNode), ); - let template = getTemplate(seedNode, templates); + const unknownTemplate = getTemplate(seedNode, templates); - if (!template) { + if (!unknownTemplate) { return createNotFound(ctx, revalidate); } - if (isDynamicComponent(template)) { - template = await loadDynamicComponent(template); - } + const template = isDynamicComponent(unknownTemplate) + ? await loadDynamicComponent(unknownTemplate) + : unknownTemplate; if (template.query && template.queries) { throw new Error( From 2e4356a64f5e913f0390bdd7ae88b09e8117301d Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 21 Jan 2026 15:36:43 -0800 Subject: [PATCH 06/14] formatting --- .../src/components/WordPressTemplate.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/faustwp-core/src/components/WordPressTemplate.tsx b/packages/faustwp-core/src/components/WordPressTemplate.tsx index 7d5a292c4..d52988b23 100644 --- a/packages/faustwp-core/src/components/WordPressTemplate.tsx +++ b/packages/faustwp-core/src/components/WordPressTemplate.tsx @@ -84,7 +84,6 @@ export function WordPressTemplateInternal( */ useEffect(() => { void (async () => { - if (!unknownTemplate) { return; } @@ -97,7 +96,6 @@ export function WordPressTemplateInternal( const client = isPreview ? getApolloAuthClient() : getApolloClient(); - if (template.query) { return; } @@ -136,19 +134,27 @@ export function WordPressTemplateInternal( setLoading(false); })(); - }, [isAuthenticated, isPreview, seedNode, unknownTemplate, setQueries, setLoading]); + }, [ + isAuthenticated, + isPreview, + seedNode, + unknownTemplate, + setQueries, + setLoading, + ]); /** * Fetch the template's query if defined. */ useEffect(() => { void (async () => { - - if(!unknownTemplate) { + if (!unknownTemplate) { return; } - const template = isDynamicComponent(unknownTemplate) ? await loadDynamicComponent(unknownTemplate) : unknownTemplate; + const template = isDynamicComponent(unknownTemplate) + ? await loadDynamicComponent(unknownTemplate) + : unknownTemplate; checkDuplicateQueryQueries(template); From d2a0ce03b2040c7e53eb914ae7e12aafe905f5a8 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 21 Jan 2026 15:38:59 -0800 Subject: [PATCH 07/14] fix: name overlap --- packages/faustwp-core/src/components/WordPressTemplate.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/faustwp-core/src/components/WordPressTemplate.tsx b/packages/faustwp-core/src/components/WordPressTemplate.tsx index d52988b23..12b83f2c3 100644 --- a/packages/faustwp-core/src/components/WordPressTemplate.tsx +++ b/packages/faustwp-core/src/components/WordPressTemplate.tsx @@ -20,7 +20,7 @@ import { SEED_QUERY, SeedNode } from '../queries/seedQuery.js'; import { FaustContext, FaustQueries } from '../store/FaustContext.js'; import { getQueryParam } from '../utils/convert.js'; import { isWordPressPreview } from '../utils/isWordPressPreview.js'; -import type { WordPressTemplate } from '../getWordPressProps.js'; +import type { WordPressTemplate as WordPressTemplateType } from '../getWordPressProps.js'; export type FaustProps = { __SEED_NODE__?: SeedNode | null; @@ -43,7 +43,7 @@ export type FaustTemplateProps> = Props & { __TEMPLATE_VARIABLES__?: { [key: string]: any }; }; -function checkDuplicateQueryQueries(template: WordPressTemplate): void { +function checkDuplicateQueryQueries(template: WordPressTemplateType): void { if (template.query && template.queries) { throw new Error( '`Only either `Component.query` or `Component.queries` can be provided, but not both.', From 5dfbb2e8c963e1bee09bbcc49b435bfa1a151ee3 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Tue, 17 Feb 2026 20:51:32 -0500 Subject: [PATCH 08/14] Update .changeset/funny-ravens-run.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .changeset/funny-ravens-run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/funny-ravens-run.md b/.changeset/funny-ravens-run.md index d79104d79..b0600bc5d 100644 --- a/.changeset/funny-ravens-run.md +++ b/.changeset/funny-ravens-run.md @@ -4,7 +4,7 @@ Feat: Added support `next/dynamic` imports for templates to reduce initial bundle size in a way that's backwards compatible with static imports. -This solves a known issue in Faust where all defined templates are bundled together and loaded on every WordPress page. by enabling the use of dynamic importing of templates this issue is resolved. Now templates are only loaded as needed per route. +This solves a known issue in Faust where all defined templates are bundled together and loaded on every WordPress page. By enabling the use of dynamic importing of templates this issue is resolved. Now templates are only loaded as needed per route. It's recommended you migrate to dynamic imports by updating your template file. Here's an example: From b2c17a7e59067a5ebefe642c8e9543d3cb77cb1a Mon Sep 17 00:00:00 2001 From: Huseyn Aghayev Date: Wed, 18 Feb 2026 11:24:01 +0100 Subject: [PATCH 09/14] Update packages/faustwp-core/src/getWordPressProps.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/faustwp-core/src/getWordPressProps.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/faustwp-core/src/getWordPressProps.tsx b/packages/faustwp-core/src/getWordPressProps.tsx index 153a2ff47..6b43bd8a2 100644 --- a/packages/faustwp-core/src/getWordPressProps.tsx +++ b/packages/faustwp-core/src/getWordPressProps.tsx @@ -159,7 +159,7 @@ export async function getWordPressProps( if (template.query && template.queries) { throw new Error( - '`Only either `Component.query` or `Component.queries` can be provided, but not both.', + 'Only either `Component.query` or `Component.queries` can be provided, but not both.', ); } From 0683dd9ea7498da785ecc5ebfa20d99063c72a74 Mon Sep 17 00:00:00 2001 From: Huseyn Aghayev Date: Wed, 18 Feb 2026 11:24:39 +0100 Subject: [PATCH 10/14] Update packages/faustwp-core/src/components/WordPressTemplate.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/faustwp-core/src/components/WordPressTemplate.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/faustwp-core/src/components/WordPressTemplate.tsx b/packages/faustwp-core/src/components/WordPressTemplate.tsx index 12b83f2c3..84115a6b2 100644 --- a/packages/faustwp-core/src/components/WordPressTemplate.tsx +++ b/packages/faustwp-core/src/components/WordPressTemplate.tsx @@ -46,7 +46,7 @@ export type FaustTemplateProps> = Props & { function checkDuplicateQueryQueries(template: WordPressTemplateType): void { if (template.query && template.queries) { throw new Error( - '`Only either `Component.query` or `Component.queries` can be provided, but not both.', + 'Only either `Component.query` or `Component.queries` can be provided, but not both.', ); } } From 92f9028c84eedb57c8bd4e1a369069d4dc1f4fde Mon Sep 17 00:00:00 2001 From: Huseyn Aghayev Date: Wed, 18 Feb 2026 11:26:37 +0100 Subject: [PATCH 11/14] Update docs/how-to/basic-setup/index.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/how-to/basic-setup/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to/basic-setup/index.md b/docs/how-to/basic-setup/index.md index 81449ce44..d125bf250 100644 --- a/docs/how-to/basic-setup/index.md +++ b/docs/how-to/basic-setup/index.md @@ -177,7 +177,7 @@ Finally, we have to make Faust.js aware that this template exists. To do that, c ```js title="wp-templates/index.js" import dynamic from 'next/dynamic'; -const category = dynamic(() => import('./category.js')); +const single = dynamic(() => import('./single.js')); export default { single, From 0e1ed3a45826bb92e5653a89b716d786a439e3e0 Mon Sep 17 00:00:00 2001 From: Huseyn Aghayev Date: Wed, 18 Feb 2026 11:27:50 +0100 Subject: [PATCH 12/14] Update examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../components/FeaturedImage/FeaturedImage.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js b/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js index 55721c734..8fe535986 100644 --- a/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js +++ b/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js @@ -25,8 +25,6 @@ export default function FeaturedImage({ if (layout !== 'fill' && (!dimensions.width || !dimensions.height)) return null; - console.log(layout, mediaDetails, dimensions); - return (
Date: Wed, 18 Feb 2026 11:59:58 +0100 Subject: [PATCH 13/14] revert legacy Image usage in FeaturedImage --- .../components/FeaturedImage/FeaturedImage.js | 3 ++- package-lock.json | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js b/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js index 8fe535986..232242b6c 100644 --- a/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js +++ b/examples/next/faustwp-getting-started/components/FeaturedImage/FeaturedImage.js @@ -1,5 +1,6 @@ import { gql } from '@apollo/client'; -import Image from 'next/legacy/image'; +import Image from 'next/image'; + export default function FeaturedImage({ image, width, diff --git a/package-lock.json b/package-lock.json index 90bd47d91..c0e13e690 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24959,7 +24959,7 @@ }, "packages/block-editor-utils": { "name": "@faustwp/block-editor-utils", - "version": "0.3.6", + "version": "0.3.7", "license": "MIT", "dependencies": { "@wordpress/block-editor": "^12.11.1", @@ -25125,7 +25125,7 @@ }, "packages/blocks": { "name": "@faustwp/blocks", - "version": "6.1.6", + "version": "6.1.7", "license": "MIT", "devDependencies": { "@testing-library/jest-dom": "^5.16.5", @@ -25257,7 +25257,7 @@ }, "packages/faustwp-cli": { "name": "@faustwp/cli", - "version": "3.3.4", + "version": "3.3.5", "license": "MIT", "dependencies": { "archiver": "^6.0.1", @@ -25492,7 +25492,7 @@ }, "packages/faustwp-core": { "name": "@faustwp/core", - "version": "3.3.4", + "version": "3.3.5", "license": "MIT", "dependencies": { "@wordpress/hooks": "^3.14.0", From 14a144ba94bd31cc7512b6508d4793399d10faed Mon Sep 17 00:00:00 2001 From: ahuseyn Date: Wed, 18 Feb 2026 12:31:08 +0100 Subject: [PATCH 14/14] add test coverage for isDynamicComponent and loadDynamicComponent --- .../faustwp-core/tests/getTemplate.test.ts | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/packages/faustwp-core/tests/getTemplate.test.ts b/packages/faustwp-core/tests/getTemplate.test.ts index 1ad2ab7db..81e9520a1 100644 --- a/packages/faustwp-core/tests/getTemplate.test.ts +++ b/packages/faustwp-core/tests/getTemplate.test.ts @@ -105,3 +105,90 @@ describe('getPossibleTemplates', () => { ]); }); }); + +describe('isDynamicComponent', () => { + test('returns true for valid DynamicComponent', () => { + const dynamicComponent = { + render: { + preload: async () => ({ default: () => null }), + }, + }; + + expect(getTemplate.isDynamicComponent(dynamicComponent)).toBe(true); + }); + + test('returns false for regular component', () => { + const regularComponent = () => null; + + expect(getTemplate.isDynamicComponent(regularComponent)).toBe(false); + }); + + test('returns false for object without render.preload', () => { + const invalidComponent = { + render: { + displayName: 'TestComponent', + }, + }; + + expect(getTemplate.isDynamicComponent(invalidComponent)).toBe(false); + }); + + test('returns false for object with render but no preload', () => { + const invalidComponent = { + render: {}, + }; + + expect(getTemplate.isDynamicComponent(invalidComponent)).toBe(false); + }); +}); + +describe('loadDynamicComponent', () => { + test('loads and resolves DynamicComponent correctly', async () => { + const mockComponent = () => 'test component'; + const dynamicComponent = { + render: { + preload: jest.fn().mockResolvedValue({ default: mockComponent }), + }, + }; + + const result = await getTemplate.loadDynamicComponent(dynamicComponent); + + expect(dynamicComponent.render.preload).toHaveBeenCalledTimes(1); + expect(result).toBe(mockComponent); + }); + + test('extracts default export from preload result', async () => { + const mockComponent = { name: 'MyComponent' }; + const dynamicComponent = { + render: { + preload: async () => ({ + default: mockComponent, + otherExport: 'should not be returned', + }), + }, + }; + + const result = await getTemplate.loadDynamicComponent(dynamicComponent); + + expect(result).toBe(mockComponent); + expect(result).not.toBe('should not be returned'); + }); + + test('handles async preload resolution', async () => { + const mockComponent = () => 'async component'; + const dynamicComponent = { + render: { + preload: () => + new Promise<{ default: typeof mockComponent }>((resolve) => { + setTimeout(() => { + resolve({ default: mockComponent }); + }, 10); + }), + }, + }; + + const result = await getTemplate.loadDynamicComponent(dynamicComponent); + + expect(result).toBe(mockComponent); + }); +});