From 042980b094777f8eff0d3329eccd3c7c25b2668a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcholas=20Andr=C3=A9?= Date: Wed, 16 Oct 2024 17:49:08 -0300 Subject: [PATCH] Feature/multisite per slug (#868) --- .changeset/perfect-cobras-knock.md | 6 + .../{esm-build-test.yml => build-test.yml} | 8 +- .../nextjs_bundle_analysis-app-router.yml | 113 ++++++++++++++++++ .github/workflows/nextjs_bundle_analysis.yml | 6 +- .gitignore | 1 - .vscode/launch.json | 17 +++ .vscode/settings.json | 9 ++ package-lock.json | 16 +-- package.json | 1 + packages/core/src/types.ts | 4 + packages/core/src/utils/__tests__/config.ts | 7 ++ packages/core/src/utils/config.ts | 5 +- packages/next/src/components/HeadlessApp.tsx | 12 +- .../src/data/server/__tests__/addHookData.ts | 5 + ...kDataa-cache.ts => fetchHookData-cache.ts} | 0 .../server/__tests__/getSiteFromContext.ts | 26 ++++ .../data/server/__tests__/withSiteContext.ts | 12 ++ packages/next/src/data/server/addHookData.ts | 2 + .../next/src/data/server/fetchHookData.ts | 13 +- .../src/data/server/getSiteFromContext.ts | 20 ++-- packages/next/src/data/server/index.ts | 1 + .../next/src/data/server/withSiteContext.ts | 15 +++ packages/next/src/middlewares/appMidleware.ts | 9 +- .../headstartwp.config.js | 2 + .../src/app/site1/(single)/[...path]/page.tsx | 24 ++++ ...adless.config.js => headstartwp.config.js} | 2 + projects/wp-multisite-nextjs/next.config.js | 4 +- .../src/pages/_sites/[site]/[...path].js | 4 +- .../src/pages/_sites/[site]/index.js | 12 +- .../src/pages/_sites/site1/index.js | 51 ++++++++ 30 files changed, 360 insertions(+), 47 deletions(-) create mode 100644 .changeset/perfect-cobras-knock.md rename .github/workflows/{esm-build-test.yml => build-test.yml} (81%) create mode 100644 .github/workflows/nextjs_bundle_analysis-app-router.yml create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json rename packages/next/src/data/server/__tests__/{fetchHookDataa-cache.ts => fetchHookData-cache.ts} (100%) create mode 100644 packages/next/src/data/server/__tests__/getSiteFromContext.ts create mode 100644 packages/next/src/data/server/__tests__/withSiteContext.ts create mode 100644 packages/next/src/data/server/withSiteContext.ts create mode 100644 projects/wp-multisite-nextjs-app/src/app/site1/(single)/[...path]/page.tsx rename projects/wp-multisite-nextjs/{headless.config.js => headstartwp.config.js} (94%) create mode 100644 projects/wp-multisite-nextjs/src/pages/_sites/site1/index.js diff --git a/.changeset/perfect-cobras-knock.md b/.changeset/perfect-cobras-knock.md new file mode 100644 index 000000000..a0a2f9724 --- /dev/null +++ b/.changeset/perfect-cobras-knock.md @@ -0,0 +1,6 @@ +--- +"@headstartwp/core": minor +"@headstartwp/next": minor +--- + +Add the ability to add aliases/slug to sites in the multisite setup diff --git a/.github/workflows/esm-build-test.yml b/.github/workflows/build-test.yml similarity index 81% rename from .github/workflows/esm-build-test.yml rename to .github/workflows/build-test.yml index e12ff6bab..0ea486e5b 100644 --- a/.github/workflows/esm-build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,9 +1,9 @@ -name: ESM Build Test +name: Build Test on: [pull_request] jobs: - esm-build-test: + build-test: runs-on: ubuntu-latest strategy: @@ -28,5 +28,5 @@ jobs: ${{ runner.os }}-node- - name: Install dependencies run: npm install - - name: Build Vite Test Project - run: npm run build -- --filter=@headstartwp/vite-react-test \ No newline at end of file + - name: Build Projects + run: npm run build \ No newline at end of file diff --git a/.github/workflows/nextjs_bundle_analysis-app-router.yml b/.github/workflows/nextjs_bundle_analysis-app-router.yml new file mode 100644 index 000000000..7cb0228ad --- /dev/null +++ b/.github/workflows/nextjs_bundle_analysis-app-router.yml @@ -0,0 +1,113 @@ +name: '(App Router) Next.js Bundle Analysis' + +on: + pull_request: + push: + branches: + - develop # change this if your default branch is named differently + workflow_dispatch: + +defaults: + run: + # change this if your nextjs app does not live at the root of the repo + working-directory: ./ + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: '20.x' + + - name: Install dependencies + uses: bahmutov/npm-install@v1 + + - name: Restore next build + uses: actions/cache@v2 + id: restore-build-cache + env: + cache-name: cache-next-build + with: + # if you use a custom build directory, replace all instances of `.next` in this file with your build directory + # ex: if your app builds to `dist`, replace `.next` with `dist` + path: ./projects/wp-nextjs/.next/cache + # change this if you prefer a more strict cache + key: ${{ runner.os }}-build-${{ env.cache-name }} + + - name: Build next.js app + # change this if your site requires a custom build command + run: npm run build -- --filter=@10up/wp-nextjs-app + + # Here's the first place where next-bundle-analysis' own script is used + # This step pulls the raw bundle stats for the current bundle + - name: Analyze bundle + run: npx -p nextjs-bundle-analysis report + + - name: Upload bundle + uses: actions/upload-artifact@v4 + with: + name: bundle + path: ./projects/wp-nextjs/.next/analyze/__bundle_analysis.json + + - name: Download base branch bundle stats + uses: dawidd6/action-download-artifact@v2 + if: success() && github.event.number + with: + workflow: nextjs_bundle_analysis.yml + branch: ${{ github.event.pull_request.base.ref }} + path: ./projects/wp-nextjs/.next/analyze/base + + # And here's the second place - this runs after we have both the current and + # base branch bundle stats, and will compare them to determine what changed. + # There are two configurable arguments that come from package.json: + # + # - budget: optional, set a budget (bytes) against which size changes are measured + # it's set to 350kb here by default, as informed by the following piece: + # https://infrequently.org/2021/03/the-performance-inequality-gap/ + # + # - red-status-percentage: sets the percent size increase where you get a red + # status indicator, defaults to 20% + # + # Either of these arguments can be changed or removed by editing the `nextBundleAnalysis` + # entry in your package.json file. + - name: Compare with base branch bundle + if: success() && github.event.number + run: ls -laR ./projects/wp-nextjs/.next/analyze/base && npx -p nextjs-bundle-analysis compare + + - name: Get comment body + id: get-comment-body + if: success() && github.event.number + run: | + body=$(cat ./projects/wp-nextjs/.next/analyze/__bundle_analysis_comment.txt) + body="${body//'%'/'%25'}" + body="${body//$'\n'/'%0A'}" + body="${body//$'\r'/'%0D'}" + echo ::set-output name=body::$body + + - name: Find Comment + uses: peter-evans/find-comment@v1 + if: success() && github.event.number + id: fc + with: + issue-number: ${{ github.event.number }} + body-includes: '' + + - name: Create Comment + uses: peter-evans/create-or-update-comment@v1.4.4 + if: success() && github.event.number && steps.fc.outputs.comment-id == 0 + with: + issue-number: ${{ github.event.number }} + body: ${{ steps.get-comment-body.outputs.body }} + + - name: Update Comment + uses: peter-evans/create-or-update-comment@v1.4.4 + if: success() && github.event.number && steps.fc.outputs.comment-id != 0 + with: + issue-number: ${{ github.event.number }} + body: ${{ steps.get-comment-body.outputs.body }} + comment-id: ${{ steps.fc.outputs.comment-id }} + edit-mode: replace diff --git a/.github/workflows/nextjs_bundle_analysis.yml b/.github/workflows/nextjs_bundle_analysis.yml index 49f2581d4..e2e3e010d 100644 --- a/.github/workflows/nextjs_bundle_analysis.yml +++ b/.github/workflows/nextjs_bundle_analysis.yml @@ -1,4 +1,4 @@ -name: 'Next.js Bundle Analysis' +name: '(Pages Router) Next.js Bundle Analysis' on: pull_request: @@ -40,7 +40,7 @@ jobs: - name: Build next.js app # change this if your site requires a custom build command - run: npm run build + run: npm run build -- --filter=@10up/wp-nextjs # Here's the first place where next-bundle-analysis' own script is used # This step pulls the raw bundle stats for the current bundle @@ -48,7 +48,7 @@ jobs: run: npx -p nextjs-bundle-analysis report - name: Upload bundle - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: bundle path: ./projects/wp-nextjs/.next/analyze/__bundle_analysis.json diff --git a/.gitignore b/.gitignore index 97d6e87fe..48c8ed31e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ node_modules .turbo .vercel .DS_Store -.vscode coverage \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..a4b04d7d7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for Xdebug", + "type": "php", + "request": "launch", + "port": 9003, + "pathMappings": { + "/var/www/html/wp-content/plugins/headless-wp": "${workspaceFolder}/wp/headless-wp" + } + }, + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..3c107013d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "phpsab.composerJsonPath": "wp/headless-wp/composer.json", + "phpsab.fixerEnable": true, + "phpsab.snifferEnable": true, + "phpsab.snifferShowSources": true, + "[php]": { + "editor.defaultFormatter": "valeryanm.vscode-phpsab" + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2e07b9509..2033cb8db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17903,7 +17903,7 @@ }, "packages/next": { "name": "@headstartwp/next", - "version": "1.5.0-next.5", + "version": "1.5.0-next.6", "license": "MIT", "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", @@ -18018,7 +18018,7 @@ "version": "0.0.0", "dependencies": { "@headstartwp/core": "^1.5.0-next.5", - "@headstartwp/next": "^1.5.0-next.5", + "@headstartwp/next": "^1.5.0-next.6", "react": "^18.3.1", "react-dom": "^18.3.1" }, @@ -18189,7 +18189,7 @@ "license": "GPL-2.0-or-later", "dependencies": { "@headstartwp/core": "^1.5.0-next.5", - "@headstartwp/next": "^1.5.0-next.5", + "@headstartwp/next": "^1.5.0-next.6", "@linaria/core": "^6.2.0", "@linaria/react": "^6.2.1", "clsx": "^1.1.1", @@ -18229,7 +18229,7 @@ "license": "GPL-2.0-or-later", "dependencies": { "@headstartwp/core": "^1.5.0-next.5", - "@headstartwp/next": "^1.5.0-next.5", + "@headstartwp/next": "^1.5.0-next.6", "@linaria/core": "^6.2.0", "@linaria/react": "^6.2.1", "clsx": "^1.1.1", @@ -18257,7 +18257,7 @@ "version": "0.1.0", "dependencies": { "@headstartwp/core": "^1.5.0-next.5", - "@headstartwp/next": "^1.5.0-next.5", + "@headstartwp/next": "^1.5.0-next.6", "next": "^14.2.5", "react": "^18", "react-dom": "^18" @@ -18312,7 +18312,7 @@ "dependencies": { "@10up/next-redis-cache-provider": "^1.0.0", "@headstartwp/core": "^1.5.0-next.5", - "@headstartwp/next": "^1.5.0-next.5", + "@headstartwp/next": "^1.5.0-next.6", "@linaria/core": "^6.2.0", "@linaria/react": "^6.2.1", "clsx": "^1.1.1", @@ -18343,7 +18343,7 @@ "version": "0.1.0", "dependencies": { "@headstartwp/core": "^1.5.0-next.5", - "@headstartwp/next": "^1.5.0-next.5", + "@headstartwp/next": "^1.5.0-next.6", "next": "^14.2.5", "react": "^18", "react-dom": "^18" @@ -18407,7 +18407,7 @@ "version": "0.1.0", "dependencies": { "@headstartwp/core": "^1.5.0-next.5", - "@headstartwp/next": "^1.5.0-next.5", + "@headstartwp/next": "^1.5.0-next.6", "next": "^14.2.5", "react": "^18", "react-dom": "^18" diff --git a/package.json b/package.json index f756336bd..30e455c09 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "build": "turbo run build", "build:packages": "turbo run build --filter=./packages/*", "build:wpnextjs": "turbo run build --filter=./projects/wp-nextjs", + "build:wpnextjs:app": "turbo run build --filter=./projects/wp-nextjs-app", "test": "turbo run test", "test:watch": "turbo run test:watch", "lint": "turbo run lint", diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index fac4a511b..a28123522 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -150,6 +150,10 @@ export type I18NConfig = { }; export type HeadlessConfig = { + /** + * The slug of the site, this is only used for the multisite feature and it acts as an alias for the site host + */ + slug?: string; host?: string; locale?: string; sourceUrl?: string; diff --git a/packages/core/src/utils/__tests__/config.ts b/packages/core/src/utils/__tests__/config.ts index 17bccc3b6..148b4240d 100644 --- a/packages/core/src/utils/__tests__/config.ts +++ b/packages/core/src/utils/__tests__/config.ts @@ -144,6 +144,7 @@ describe('getSiteByHost', () => { { sourceUrl: 'https://sourceurl.com/site1', hostUrl: 'https://site1.com', + slug: 'site1', locale: 'en', }, { @@ -151,6 +152,7 @@ describe('getSiteByHost', () => { host: 'site2.com', hostUrl: 'https://site2.com', locale: 'es', + slug: 'site2', }, ], }; @@ -179,4 +181,9 @@ describe('getSiteByHost', () => { expect(getSiteByHost('site2.com', 'en')).toBeNull(); expect(getSiteByHost('site1.com', 'es')).toBeNull(); }); + + it('find sites by slug', () => { + expect(getSiteByHost('site1', 'en')?.sourceUrl).toBe('https://sourceurl.com/site1'); + expect(getSiteByHost('site2', 'es')?.sourceUrl).toBe('https://sourceurl.com/site2'); + }); }); diff --git a/packages/core/src/utils/config.ts b/packages/core/src/utils/config.ts index 0fa290091..e9ed06dfb 100644 --- a/packages/core/src/utils/config.ts +++ b/packages/core/src/utils/config.ts @@ -34,6 +34,7 @@ export function getHeadstartWPConfig(): HeadlessConfig { cache, locale, i18n, + slug, } = __10up__HEADLESS_CONFIG; const defaultTaxonomies: CustomTaxonomies = [ @@ -75,6 +76,7 @@ export function getHeadstartWPConfig(): HeadlessConfig { : [...(customPostTypes || []), ...defaultPostTypes]; const headlessConfig = { + slug, locale, sourceUrl, hostUrl: hostUrl || '', @@ -119,6 +121,7 @@ export const getHeadlessConfig = getHeadstartWPConfig; export function getSite(site?: HeadlessConfig) { const settings = getHeadstartWPConfig(); const headlessConfig: HeadlessConfig = { + slug: site?.slug, sourceUrl: site?.sourceUrl || settings.sourceUrl, hostUrl: site?.hostUrl, host: site?.host, @@ -158,7 +161,7 @@ export function getSiteByHost(hostOrUrl: string, locale?: string) { const site = settings.sites && settings.sites.find((site) => { - const isHost = site.host === normalizedHost; + const isHost = site.host === normalizedHost || site.slug === normalizedHost; if (typeof locale !== 'undefined' && locale) { return isHost && site.locale === locale; diff --git a/packages/next/src/components/HeadlessApp.tsx b/packages/next/src/components/HeadlessApp.tsx index 05c1431c4..902cdaca0 100644 --- a/packages/next/src/components/HeadlessApp.tsx +++ b/packages/next/src/components/HeadlessApp.tsx @@ -101,7 +101,12 @@ export function HeadlessApp({ useYoastHtml = false, handleYoast = true, }: HeadlessAppProps) { - const { fallback = {}, seo = {}, themeJSON = { settings: {}, styles: {} } } = pageProps; + const { + fallback = {}, + seo = {}, + themeJSON = { settings: {}, styles: {} }, + __headstartwp_site = '', + } = pageProps; const router = useRouter(); // if preview mode disable revalidating @@ -116,12 +121,15 @@ export function HeadlessApp({ } const currentSite = useMemo(() => { + if (__headstartwp_site) { + return getSiteByHost(__headstartwp_site, router.locale); + } if (router.query?.site && !Array.isArray(router.query.site)) { return getSiteByHost(router.query.site, router.locale); } return {}; - }, [router]); + }, [router, __headstartwp_site]); const siteSettings = useMemo(() => ({ ...settings, ...currentSite }), [settings, currentSite]); diff --git a/packages/next/src/data/server/__tests__/addHookData.ts b/packages/next/src/data/server/__tests__/addHookData.ts index e5b49cbe2..fc6e402eb 100644 --- a/packages/next/src/data/server/__tests__/addHookData.ts +++ b/packages/next/src/data/server/__tests__/addHookData.ts @@ -65,6 +65,7 @@ describe('addHookData', () => { pageInfo: { ...samplePageInfo }, }, isMainQuery: true, + hostOrSlug: 'mainsite', }, { key: 'second-key', @@ -74,10 +75,12 @@ describe('addHookData', () => { pageInfo: { ...samplePageInfo }, }, isMainQuery: false, + hostOrSlug: 'site2', }, ]; expect(addHookData(hookStates, {})).toStrictEqual({ props: { + __headstartwp_site: 'mainsite', fallback: { 'first-key': { result: { @@ -120,6 +123,7 @@ describe('addHookData', () => { pageInfo: samplePageInfo, }, isMainQuery: false, + hostOrSlug: 'site2', }, ]; @@ -135,6 +139,7 @@ describe('addHookData', () => { expect(addHookData(hookStates, {})).toStrictEqual({ props: { + __headstartwp_site: 'site2', fallback: { 'first-key': { queriedObject: {}, diff --git a/packages/next/src/data/server/__tests__/fetchHookDataa-cache.ts b/packages/next/src/data/server/__tests__/fetchHookData-cache.ts similarity index 100% rename from packages/next/src/data/server/__tests__/fetchHookDataa-cache.ts rename to packages/next/src/data/server/__tests__/fetchHookData-cache.ts diff --git a/packages/next/src/data/server/__tests__/getSiteFromContext.ts b/packages/next/src/data/server/__tests__/getSiteFromContext.ts new file mode 100644 index 000000000..5464174d6 --- /dev/null +++ b/packages/next/src/data/server/__tests__/getSiteFromContext.ts @@ -0,0 +1,26 @@ +import { setHeadstartWPConfig } from '@headstartwp/core'; +import { getSiteFromContext } from '../getSiteFromContext'; + +describe('getSiteFromContext', () => { + it('returns settings if no site is provided', () => { + const ctx = { params: { site: 'site2' } }; + setHeadstartWPConfig({ + useWordPressPlugin: true, + sites: [ + { + slug: 'site1', + hostUrl: 'http://site1.localhost:3001', + sourceUrl: 'http://sourceUrl1.com', + redirectStrategy: '404', + }, + { + slug: 'site2', + hostUrl: 'http://site2.localhost:3001', + sourceUrl: 'http://sourceUrl2.com', + redirectStrategy: '404', + }, + ], + }); + expect(getSiteFromContext(ctx).slug).toBe('site2'); + }); +}); diff --git a/packages/next/src/data/server/__tests__/withSiteContext.ts b/packages/next/src/data/server/__tests__/withSiteContext.ts new file mode 100644 index 000000000..f0495492d --- /dev/null +++ b/packages/next/src/data/server/__tests__/withSiteContext.ts @@ -0,0 +1,12 @@ +import { withSiteContext } from '../withSiteContext'; + +describe('withSiteContext', () => { + it('adds site to params', () => { + expect(withSiteContext({ params: { id: 1 } }, 'site1')).toEqual({ + params: { id: 1, site: 'site1' }, + }); + expect(withSiteContext({ params: { id: 1, site: 'site2' } }, 'site1')).toEqual({ + params: { id: 1, site: 'site1' }, + }); + }); +}); diff --git a/packages/next/src/data/server/addHookData.ts b/packages/next/src/data/server/addHookData.ts index f4cbe2f4a..118431aaf 100644 --- a/packages/next/src/data/server/addHookData.ts +++ b/packages/next/src/data/server/addHookData.ts @@ -12,6 +12,7 @@ export type HookState = { key: string; data: T; isMainQuery: boolean; + hostOrSlug?: string; additionalCacheObjects?: HookState[]; }; @@ -223,6 +224,7 @@ export function addHookData

( }, themeJSON, fallback, + __headstartwp_site: mainQuery?.hostOrSlug ?? validHookStates?.[0]?.hostOrSlug ?? null, }, } satisfies | GetStaticPropsResult diff --git a/packages/next/src/data/server/fetchHookData.ts b/packages/next/src/data/server/fetchHookData.ts index 1dfdaeea6..84905356b 100644 --- a/packages/next/src/data/server/fetchHookData.ts +++ b/packages/next/src/data/server/fetchHookData.ts @@ -73,7 +73,15 @@ export function prepareFetchHookData | GetStaticPropsContext, options: FetchHookDataOptions = {}, ) { - const { sourceUrl, integrations, cache: globalCacheConfig } = getSiteFromContext(ctx); + const { + sourceUrl, + integrations, + cache: globalCacheConfig, + host, + slug, + } = getSiteFromContext(ctx); + + const hostOrSlug = slug ?? host; const defaultCacheTTL = 5 * 60; const cacheConfig = merge([ @@ -140,6 +148,7 @@ export function prepareFetchHookData { - if (ctx.locale) { - return host === currentSite && locale === ctx.locale; - } + if (currentSite && typeof currentSite === 'string') { + const site = getSiteByHost(currentSite); - return host === currentSite; - }); - - if (site) { - return getSite(site); + if (site) { + return getSite(site); + } } return settings; diff --git a/packages/next/src/data/server/index.ts b/packages/next/src/data/server/index.ts index 520c039ea..d6141707b 100644 --- a/packages/next/src/data/server/index.ts +++ b/packages/next/src/data/server/index.ts @@ -3,3 +3,4 @@ export * from '../convertToPath'; export * from './fetchHookData'; export * from './getSiteFromContext'; export * from './handleError'; +export * from './withSiteContext'; diff --git a/packages/next/src/data/server/withSiteContext.ts b/packages/next/src/data/server/withSiteContext.ts new file mode 100644 index 000000000..70429f2ce --- /dev/null +++ b/packages/next/src/data/server/withSiteContext.ts @@ -0,0 +1,15 @@ +import { GetServerSidePropsContext, GetStaticPropsContext } from 'next'; +import { PreviewData } from '../../handlers/types'; + +export function withSiteContext( + ctx: GetServerSidePropsContext | GetStaticPropsContext, + site: string, +) { + return { + ...ctx, + params: { + ...(ctx.params ?? {}), + site, + }, + }; +} diff --git a/packages/next/src/middlewares/appMidleware.ts b/packages/next/src/middlewares/appMidleware.ts index 6b903f314..86fc3312a 100644 --- a/packages/next/src/middlewares/appMidleware.ts +++ b/packages/next/src/middlewares/appMidleware.ts @@ -190,16 +190,17 @@ export async function AppMiddleware( } if (isMultisiteRequest && !shouldRedirect) { - const pagesRouterRewrite = `/_sites/${hostname}${pathname}`; + const hostNameOrSlug = site.slug || hostname; + const pagesRouterRewrite = `/_sites/${hostNameOrSlug}${pathname}`; const appRouterRewrite = locale - ? `/${locale}/${hostname}${pathname.replace(`/${locale}`, '')}` - : `/${hostname}${pathname}`; + ? `/${locale}/${hostNameOrSlug}${pathname.replace(`/${locale}`, '')}` + : `/${hostNameOrSlug}${pathname}`; response = NextResponse.rewrite( new URL(options.appRouter ? appRouterRewrite : pagesRouterRewrite, req.nextUrl), ); - response.headers.set('x-headstartwp-site', hostname); + response.headers.set('x-headstartwp-site', hostNameOrSlug); } if (locale) { diff --git a/projects/wp-multisite-nextjs-app/headstartwp.config.js b/projects/wp-multisite-nextjs-app/headstartwp.config.js index 790cfa27d..6565f4309 100644 --- a/projects/wp-multisite-nextjs-app/headstartwp.config.js +++ b/projects/wp-multisite-nextjs-app/headstartwp.config.js @@ -12,10 +12,12 @@ module.exports = { sites: [ { + slug: 'site1', sourceUrl: process.env.NEXT_PUBLIC_HEADLESS_WP_URL, hostUrl: 'http://site1.localhost:3000', }, { + slug: 'js1', sourceUrl: 'https://js1.10up.com/', hostUrl: 'http://js1.localhost:3000', }, diff --git a/projects/wp-multisite-nextjs-app/src/app/site1/(single)/[...path]/page.tsx b/projects/wp-multisite-nextjs-app/src/app/site1/(single)/[...path]/page.tsx new file mode 100644 index 000000000..d0d417ba7 --- /dev/null +++ b/projects/wp-multisite-nextjs-app/src/app/site1/(single)/[...path]/page.tsx @@ -0,0 +1,24 @@ +import { BlocksRenderer, HtmlDecoder } from '@headstartwp/core/react'; +import { HeadstartWPRoute, queryPost } from '@headstartwp/next/app'; + +const Single = async ({ params }: HeadstartWPRoute) => { + const { data } = await queryPost({ + routeParams: { ...params, site: 'site1' }, + params: { + postType: ['post', 'page'], + }, + }); + + return ( +

+

This is site 1

+

+ +

+ + +
+ ); +}; + +export default Single; diff --git a/projects/wp-multisite-nextjs/headless.config.js b/projects/wp-multisite-nextjs/headstartwp.config.js similarity index 94% rename from projects/wp-multisite-nextjs/headless.config.js rename to projects/wp-multisite-nextjs/headstartwp.config.js index e431ab405..4b2a6d6ff 100644 --- a/projects/wp-multisite-nextjs/headless.config.js +++ b/projects/wp-multisite-nextjs/headstartwp.config.js @@ -6,6 +6,7 @@ module.exports = { sites: [ { + slug: 'site1', hostUrl: 'http://site1.localhost:3001', /** * The WordPress Source Url @@ -18,6 +19,7 @@ module.exports = { useWordPressPlugin: true, }, { + slug: 'site2', hostUrl: 'http://site2.localhost:3001', /** * The WordPress Source Url diff --git a/projects/wp-multisite-nextjs/next.config.js b/projects/wp-multisite-nextjs/next.config.js index 94750ce17..034a820d1 100644 --- a/projects/wp-multisite-nextjs/next.config.js +++ b/projects/wp-multisite-nextjs/next.config.js @@ -4,8 +4,6 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); -const headlessConfig = require('./headless.config'); - /** * Update whatever you need within the nextConfig object. * @@ -17,4 +15,4 @@ const nextConfig = { }, }; -module.exports = withBundleAnalyzer(withHeadlessConfig(nextConfig, headlessConfig)); +module.exports = withBundleAnalyzer(withHeadlessConfig(nextConfig)); diff --git a/projects/wp-multisite-nextjs/src/pages/_sites/[site]/[...path].js b/projects/wp-multisite-nextjs/src/pages/_sites/[site]/[...path].js index 6ac38f24d..103badf48 100644 --- a/projects/wp-multisite-nextjs/src/pages/_sites/[site]/[...path].js +++ b/projects/wp-multisite-nextjs/src/pages/_sites/[site]/[...path].js @@ -54,7 +54,7 @@ export async function getStaticPaths() { // path is the catch all route, so it must be array with url segments // if you don't want to support date urls just remove the date from the path params: { - site: site.host, + site: site?.slug ?? site.host, path: removeSourceUrl({ link, backendUrl: site.sourceUrl }) .substring(1) .split('/'), @@ -75,7 +75,7 @@ export async function getStaticPaths() { return { // path is the catch all route, so it must be array with url segments params: { - site: site.host, + site: site?.slug ?? site.host, path: normalizedLink.substring(1).split('/'), }, }; diff --git a/projects/wp-multisite-nextjs/src/pages/_sites/[site]/index.js b/projects/wp-multisite-nextjs/src/pages/_sites/[site]/index.js index b8868e4ff..1d6244f3c 100644 --- a/projects/wp-multisite-nextjs/src/pages/_sites/[site]/index.js +++ b/projects/wp-multisite-nextjs/src/pages/_sites/[site]/index.js @@ -61,11 +61,13 @@ export const getStaticPaths = async () => { const { sites } = getHeadlessConfig(); return { - paths: sites.map((site) => ({ - params: { - site: site.host, - }, - })), + paths: sites + .filter((site) => site.slug !== 'site1') + .map((site) => ({ + params: { + site: site?.slug ?? site.host, + }, + })), fallback: true, }; }; diff --git a/projects/wp-multisite-nextjs/src/pages/_sites/site1/index.js b/projects/wp-multisite-nextjs/src/pages/_sites/site1/index.js new file mode 100644 index 000000000..fc1c940d2 --- /dev/null +++ b/projects/wp-multisite-nextjs/src/pages/_sites/site1/index.js @@ -0,0 +1,51 @@ +import { + addHookData, + fetchHookData, + handleError, + useAppSettings, + usePost, + withSiteContext, +} from '@headstartwp/next'; +import { useSettings } from '@headstartwp/core/react'; +import { indexParams } from '../../../params'; + +const Site1Homepage = () => { + const settings = useSettings(); + return
{settings.slug} Homepage
; +}; + +export default Site1Homepage; + +export async function getStaticProps(_context) { + const context = withSiteContext(_context, 'site1'); + + let appSettings; + let slug; + try { + appSettings = await fetchHookData(useAppSettings.fetcher(), context); + /** + * The static front-page can be set in the WP admin. The default one will be 'front-page' + */ + slug = appSettings.data.result?.home?.slug ?? 'front-page'; + } catch (e) { + if (e.name === 'EndpointError') { + slug = 'front-page'; + } + } + + try { + const hookData = await fetchHookData(usePost.fetcher(), context, { + params: { + ...indexParams, + slug, + }, + }); + + return addHookData([hookData, appSettings], { + props: { homePageSlug: slug }, + revalidate: 5 * 60, + }); + } catch (e) { + return handleError(e, context); + } +}