Skip to content

fix: Add is404 flag for 404ed routes #151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
603c1f1
feat : add import/no-default-export eslint rule.
Ta5r Apr 4, 2025
bd598d5
chore : updated files for no-default-exports.
Ta5r Apr 4, 2025
5e2df69
chore : add changeset.
Ta5r Apr 4, 2025
33c10fc
Merge branch 'develop' into feat/import-no-default-export
Ta5r Apr 4, 2025
0b88cc1
chore : reorder-import
Ta5r Apr 4, 2025
29025e1
temp : fix failing gh actions
Ta5r Apr 4, 2025
89eaa67
fix : export default for codegen config and baseConfig
Ta5r Apr 4, 2025
123a436
Merge branch 'develop' into feat/import-no-default-export
justlevine Apr 4, 2025
fd0f6da
Merge pull request #147 from Ta5r/feat/import-no-default-export
Ta5r Apr 8, 2025
76dcef2
feat: add connectedNode field to template query
Swanand01 Apr 9, 2025
5a80814
feat: add `is404` flag to parseQueryResult return values
Swanand01 Apr 9, 2025
6542789
chore: add changeset
Swanand01 Apr 9, 2025
5a13a33
chore: add is404 test
Swanand01 Apr 9, 2025
119c625
Merge branch 'develop' into fix/status-code-for-404ed-routes
Swanand01 Apr 9, 2025
0d8f1d5
feat: expose `is404` to the frontend
Swanand01 Apr 10, 2025
63c4f79
chore: use named import in next config
Swanand01 Apr 10, 2025
5ee8cca
Merge remote-tracking branch 'upstream/develop' into fix/status-code-…
Swanand01 Apr 10, 2025
8202409
feat: add 404 handling to current path middleware
Swanand01 Apr 10, 2025
74120c2
Merge branch 'develop' into fix/status-code-for-404ed-routes
justlevine Apr 11, 2025
5b3bc6b
refactor: remove custom header
Swanand01 Apr 11, 2025
e607e12
refactor: update is404 parsing
Swanand01 Apr 14, 2025
5088f62
fix: update tests
Swanand01 Apr 14, 2025
3193579
revert: current path middleware
Swanand01 Apr 15, 2025
a6bb2cd
feat: redirect to not-found
Swanand01 Apr 15, 2025
8d4a0ce
revert: connectedNode from query
Swanand01 Apr 15, 2025
680b91d
Merge branch 'develop' into fix/status-code-for-404ed-routes
Swanand01 Apr 15, 2025
9240dfe
revert: not-found.tsx
Swanand01 Apr 16, 2025
5ff9667
Merge branch 'fix/status-code-for-404ed-routes' of https://github.com…
Swanand01 Apr 16, 2025
73f7b8c
Merge branch 'develop' into fix/status-code-for-404ed-routes
Swanand01 Apr 16, 2025
02877c7
revert: passing is404 to `TeemplateRenderer` children
Swanand01 Apr 17, 2025
f9570ed
feat: add docs on handling 404 pages
Swanand01 Apr 17, 2025
200e7b0
chore: update changeset
Swanand01 Apr 17, 2025
9884e17
chore: sort query a to z
Swanand01 Apr 17, 2025
f01314f
chore: format
Swanand01 Apr 17, 2025
ef20325
chore: cleanup
justlevine Apr 17, 2025
74b88cd
docs: working example
justlevine Apr 17, 2025
c97d860
chore: format
justlevine Apr 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tame-beans-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@snapwp/query": patch
---

fix: Add is404 flag for 404ed routes
2 changes: 1 addition & 1 deletion examples/nextjs/starter/src/app/[[...path]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { EditorBlocksRenderer } from '@snapwp/blocks';
export default function Page() {
return (
<TemplateRenderer>
{ ( editorBlocks ) => {
{ ( { editorBlocks, is404 } ) => {
return <EditorBlocksRenderer editorBlocks={ editorBlocks } />;
} }
</TemplateRenderer>
Expand Down
44 changes: 41 additions & 3 deletions packages/next/src/middleware/current-path.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,60 @@
import { NextResponse } from 'next/server';
import { addTrailingSlash } from '@snapwp/core';
import { getConfig } from '@snapwp/core/config';
import { QueryEngine } from '@snapwp/query';
import type { MiddlewareFactory } from './utils';
import type { NextFetchEvent, NextMiddleware, NextRequest } from 'next/server';

/**
* Middleware function for Next.js
*
* This middleware adds a custom header 'x-current-path' to the response,
* which contains the current pathname of the request.
* This middleware adds custom headers 'x-current-path' and 'x-snapwp-is404' to the response,
* which contains the current pathname of the request, and the 404 status.
*
* @param {NextMiddleware} next Next middleware.
*
* @return The response object with modified headers
*/
export const currentPath: MiddlewareFactory = ( next: NextMiddleware ) => {
return async ( request: NextRequest, _next: NextFetchEvent ) => {
const pathname = request.nextUrl.pathname;

const { uploadsDirectory, restUrlPrefix } = getConfig();

const shouldSkip =
pathname.startsWith( '/_next/' ) ||
pathname.startsWith( '/api/' ) ||
pathname.startsWith( addTrailingSlash( uploadsDirectory ) ) || // handled by `proxies` middleware
pathname.startsWith( restUrlPrefix ) || // handled by `proxies` middleware
pathname === '/wp-admin/admin-ajax.php' || // handled by `proxies` middleware
pathname.match(
/\.(js|css|png|jpg|jpeg|gif|svg|webp|mp4|webm|woff|woff2|ttf|eot|ico)$/
);

if ( shouldSkip ) {
return next( request, _next );
}

let is404 = false;
try {
const { is404: fetchedIs404 } = await QueryEngine.getTemplateData(
pathname || '/'
);
is404 = fetchedIs404;
} catch ( error ) {}

const response = await next( request, _next );

if ( response ) {
response.headers.set( 'x-current-path', request.nextUrl.pathname );
response.headers.set( 'x-current-path', pathname );
response.headers.set( 'x-snapwp-is404', String( is404 ) );

if ( is404 ) {
return new NextResponse( response.body, {
status: 404,
headers: response.headers,
} );
}
}

return response;
Expand Down
17 changes: 13 additions & 4 deletions packages/next/src/template-renderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import type { ReactNode } from 'react';

export type TemplateRendererProps = {
getTemplateData?: ( typeof QueryEngine )[ 'getTemplateData' ];
children: ( editorBlocks: BlockData[] ) => ReactNode;
children: ( args: {
editorBlocks: BlockData[];
is404: boolean;
} ) => ReactNode;
};

/**
Expand All @@ -29,8 +32,14 @@ export async function TemplateRenderer( {
const headerList = await headers(); // headers() returns a Promise from NextJS 19.
const pathname = headerList.get( 'x-current-path' );

const { editorBlocks, bodyClasses, stylesheets, scripts, scriptModules } =
await getTemplateData( pathname || '/' );
const {
editorBlocks,
bodyClasses,
stylesheets,
scripts,
scriptModules,
is404,
} = await getTemplateData( pathname || '/' );

if ( ! editorBlocks?.length ) {
throw new Error(
Expand All @@ -48,7 +57,7 @@ export async function TemplateRenderer( {
>
<main>
<div className="wp-site-blocks">
{ children( editorBlocks ) }
{ children( { editorBlocks, is404 } ) }
</div>
</main>
</TemplateScripts>
Expand Down
3 changes: 3 additions & 0 deletions packages/query/src/queries/get-current-template.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,8 @@ query GetCurrentTemplate($uri: String!) {
...CoreVideoFrag
}
}
connectedNode {
id
}
}
}
1 change: 1 addition & 0 deletions packages/query/src/query-engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export class QueryEngine {
scripts: EnqueuedScriptProps[] | undefined;
scriptModules: ScriptModuleProps[] | undefined;
bodyClasses: string[] | undefined;
is404: boolean;
} > => {
if ( ! QueryEngine.isClientInitialized ) {
QueryEngine.initialize();
Expand Down
3 changes: 3 additions & 0 deletions packages/query/src/utils/parse-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function parseQueryResult(
scripts: EnqueuedScriptProps[] | undefined;
scriptModules: ScriptModuleProps[] | undefined;
bodyClasses: string[] | undefined;
is404: boolean;
} {
if ( queryData.errors?.length ) {
queryData.errors?.forEach( ( error ) => {
Expand All @@ -51,13 +52,15 @@ export function parseQueryResult(
}

const templateByUri = queryData.data?.templateByUri;
const is404 = templateByUri?.connectedNode === null;

return {
stylesheets: parseEnqueuedStylesheets( wordpressUrl, templateByUri ),
editorBlocks: parseEditorBlocks( templateByUri ),
scripts: parseEnqueuedScripts( templateByUri, wordpressUrl ),
scriptModules: parseScriptModules( templateByUri, wordpressUrl ),
bodyClasses: parseBodyClasses( templateByUri ),
is404,
};
}

Expand Down
71 changes: 71 additions & 0 deletions packages/query/src/utils/tests/parse-template.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe( 'parseQueryResult', () => {
{ src: 'https://cdn.com/script.js', handle: 'cdn-script' },
],
bodyClasses: [ 'class1', 'class2' ],
is404: false,
} );
} );

Expand All @@ -106,4 +107,74 @@ describe( 'parseQueryResult', () => {
{ message: 'Critical error occurred' }
);
} );

it( 'should return is404 true if `templateByUri.connectedNode` is null', () => {
const queryData: ApolloQueryResult< GetCurrentTemplateQuery > = {
data: {
templateByUri: {
bodyClasses: [ 'class1', 'class2' ],
enqueuedScripts: {
nodes: [
{
id: '122',
src: '/script.js',
handle: 'test-script',
},
{
id: '123',
src: 'https://cdn.com/script.js',
handle: 'cdn-script',
},
],
},
enqueuedStylesheets: {
nodes: [
{
src: '/style.css',
handle: 'test-style',
before: [ 'before-content' ],
after: [ 'after-content' ],
},
],
},
editorBlocks: [
{
type: 'core/paragraph',
renderedHtml: '<p>Text</p>',
},
],
connectedNode: null,
},
},
errors: [],
loading: false,
networkStatus: 7,
};

const result = parseQueryResult( queryData, wordpressUrl, uri );

expect( result ).toEqual( {
stylesheets: [
{
src: 'https://test.com/style.css',
handle: 'test-style',
before: 'before-content',
after: 'after-content',
},
],
editorBlocks: [
{
type: 'core/paragraph',
renderedHtml: '<p>Text</p>',
},
],
scriptModules: undefined,
scripts: [
{ src: 'https://test.com/script.js', handle: 'test-script' },
{ src: 'https://cdn.com/script.js', handle: 'cdn-script' },
],
bodyClasses: [ 'class1', 'class2' ],
is404: true,
} );
} );
} );