Skip to content

Commit

Permalink
feat: more progress on data fetching on app router
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasio committed Jun 22, 2024
1 parent fb075b2 commit 41f73e3
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 35 deletions.
13 changes: 13 additions & 0 deletions packages/core/src/data/strategies/AbstractFetchStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ export interface FetchResponse<T> {
queriedObject: QueriedObject;
}

type NextJSHeaders = {
next?: {
revalidate?: false | 0 | number;
tags?: string[];
};
cache?: 'no-store' | 'force-cache';
};

/**
* The options supported by the default fetcher method
*/
Expand Down Expand Up @@ -85,6 +93,11 @@ export interface FetchOptions {
* Whether to burst cache by appending a timestamp to the query
*/
burstCache?: boolean;

/**
* Headers to sent to fetch
*/
headers?: Record<string, unknown> & NextJSHeaders;
}

export interface FilterDataOptions<T> {
Expand Down
39 changes: 33 additions & 6 deletions packages/core/src/data/strategies/executeFetchStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ import { AbstractFetchStrategy, EndpointParams, FetchOptions } from './AbstractF
import { HeadlessConfig } from '../../types';

/**
* Executes a fetch strategy
* Prepares a fetch strategy for execution
*
* @param fetchStrategy
* @param config
* @param params
* @param fetchStrategy
* @param options
* @param path
* @returns
*/
export async function executeFetchStrategy<E, Params extends EndpointParams, R = E>(
export function prepareFetchStrategy<E, Params extends EndpointParams, R = E>(
fetchStrategy: AbstractFetchStrategy<E, Params, R>,
config: HeadlessConfig,
params: Partial<Params>,
options?: Partial<FetchOptions>,
path = '',
) {
const { sourceUrl, debug } = config;
const { sourceUrl } = config;

fetchStrategy.setBaseURL(sourceUrl);

Expand All @@ -32,6 +31,34 @@ export async function executeFetchStrategy<E, Params extends EndpointParams, R =
const endpointUrl = fetchStrategy.buildEndpointURL(finalParams);
const key = fetchStrategy.getCacheKey(finalParams);

return { isMainQuery, params: finalParams, endpointUrl, key };
}

/**
* Executes a fetch strategy
*
* @param config
* @param params
* @param fetchStrategy
* @param options
* @param path
*/
export async function executeFetchStrategy<E, Params extends EndpointParams, R = E>(
fetchStrategy: AbstractFetchStrategy<E, Params, R>,
config: HeadlessConfig,
params: Partial<Params>,
options?: Partial<FetchOptions>,
path = '',
) {
const { debug } = config;

const {
key,
endpointUrl,
params: finalParams,
isMainQuery,
} = prepareFetchStrategy(fetchStrategy, config, params, path);

if (debug?.devMode) {
log(LOGTYPE.INFO, `[useFetch] key for ${key.url}`, key);
}
Expand Down
23 changes: 10 additions & 13 deletions packages/core/src/react/hooks/useFetch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import useSWR, { useSWRConfig } from 'swr';
import deepmerge from 'deepmerge';

import type { EndpointParams, FetchResponse } from '../../data';
import { AbstractFetchStrategy } from '../../data';
import { AbstractFetchStrategy, prepareFetchStrategy } from '../../data';

import { useSettings } from '../provider';
import { FetchHookOptions } from './types';
Expand Down Expand Up @@ -33,16 +33,16 @@ export function useFetch<E, Params extends EndpointParams, R = E>(
options: FetchHookOptions<FetchResponse<R>> = {},
path = '',
) {
const { sourceUrl, debug } = useSettings();
const settings = useSettings();
const { mutate } = useSWRConfig();
const { debug } = settings;

fetchStrategy.setBaseURL(sourceUrl);

const defaultParams = fetchStrategy.getDefaultParams();
const urlParams = fetchStrategy.getParamsFromURL(path, params);
const isMainQuery = fetchStrategy.isMainQuery(path, params);

const finalParams = deepmerge.all([defaultParams, urlParams, params]) as Partial<Params>;
const {
params: finalParams,
isMainQuery,
endpointUrl,
key,
} = prepareFetchStrategy(fetchStrategy, settings, params, path);

const { fetchStrategyOptions, shouldFetch = true, ...validSWROptions } = options;

Expand All @@ -59,9 +59,6 @@ export function useFetch<E, Params extends EndpointParams, R = E>(
options.swr = { ...validSWROptions };
}

const endpointUrl = fetchStrategy.buildEndpointURL(finalParams);
const key = fetchStrategy.getCacheKey(finalParams);

if (debug?.devMode) {
log(LOGTYPE.INFO, `[useFetch] key for ${key.url}`, key);
}
Expand Down
4 changes: 4 additions & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"typedocMain": "src/docs-entry-point.ts",
"exports": {
".": {
"react-server": {
"import": "./dist/mjs/rsc/index.js",
"types": "./dist/mjs/rsc/index.d.ts"
},
"require": "./dist/cjs/index.js",
"import": "./dist/mjs/index.js",
"types": "./dist/mjs/index.d.ts"
Expand Down
10 changes: 8 additions & 2 deletions packages/next/src/config/withHeadstartWPConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const isPackageInstalled = (packageName: string): boolean => {

return false;
};

function traverse(rules) {
for (const rule of rules) {
if (typeof rule.loader === 'string' && rule.loader.includes('css-loader')) {
Expand Down Expand Up @@ -59,6 +60,9 @@ export function withHeadstartWPConfig(
headlessConfig: HeadlessConfig = {},
withHeadstarWPConfigOptions: { injectConfig: boolean } = { injectConfig: true },
): NextConfig {
const isUsingAppRouter =
fs.existsSync(`${process.cwd()}/src/app`) || fs.existsSync(`${process.cwd()}/app`);

const headlessConfigPath = `${process.cwd()}/headless.config.js`;
const headstartWpConfigPath = `${process.cwd()}/headstartwp.config.js`;
const headstartWpConfigClientPath = `${process.cwd()}/headstartwp.config.client.js`;
Expand Down Expand Up @@ -229,7 +233,8 @@ export function withHeadstartWPConfig(
const matched =
/_app.(tsx|ts|js|mjs|jsx)$/.test(moduleRequest) ||
/middleware.(ts|js|mjs)$/.test(moduleRequest) ||
/pages\/api\/.*.(ts|js|mjs)/.test(moduleRequest);
/pages\/api\/.*.(ts|js|mjs)/.test(moduleRequest) ||
/app\/.*layout.(tsx|ts|js|mjs|jsx)$/.test(moduleRequest);

return matched;
},
Expand All @@ -246,7 +251,8 @@ export function withHeadstartWPConfig(
}),
);

if (isPackageInstalled('@linaria/webpack-loader')) {
// only load linaria with the pages router configuration if not using app router
if (isPackageInstalled('@linaria/webpack-loader') && !isUsingAppRouter) {
traverse(config.module.rules);
config.module.rules.push({
test: /\.(tsx|ts|js|mjs|jsx)$/,
Expand Down
26 changes: 26 additions & 0 deletions packages/next/src/rsc/data/handleFetchError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { LOGTYPE, fetchRedirect, getHeadstartWPConfig, log } from '@headstartwp/core';
import { permanentRedirect, redirect } from 'next/navigation';

export async function handleFetchError(error: Error, path = '') {
const { redirectStrategy, sourceUrl, debug } = getHeadstartWPConfig();

if (debug?.devMode) {
log(LOGTYPE.INFO, '[handleError] error', error.name, error.message);
}

if (error.name === 'NotFoundError') {
if (redirectStrategy === '404' && path) {
const redirectObject = await fetchRedirect(path, sourceUrl || '');

if (redirectObject.location) {
if ([301, 308].includes(redirectObject.status)) {
permanentRedirect(redirectObject.location);
} else {
redirect(redirectObject.location);
}
}
}
}

throw error;
}
2 changes: 2 additions & 0 deletions packages/next/src/rsc/data/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from '../../data/convertToPath';
export * from './queries/fetchPostByPath';
26 changes: 26 additions & 0 deletions packages/next/src/rsc/data/queries/fetchPostByPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { FetchOptions, HeadlessConfig, PostEntity, PostParams, fetchPost } from '@headstartwp/core';
import { handleFetchError } from '../handleFetchError';
import { convertToPath } from '../../../data/convertToPath';

export async function fetchPostByPath<
T extends PostEntity = PostEntity,
P extends PostParams = PostParams,
>(
path: string | string[],
params: P | {} = {},
options: Partial<FetchOptions> = {},
_config: HeadlessConfig | undefined = undefined,
) {
const pathname = Array.isArray(path) ? convertToPath(path) : path;

try {
const result = await fetchPost<T, P>(params, options, pathname, _config);

return result;
} catch (error) {
if (error instanceof Error) {
handleFetchError(error, pathname);
}
throw error;
}
}
2 changes: 2 additions & 0 deletions packages/next/src/rsc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './data';
export * from './type';
3 changes: 3 additions & 0 deletions packages/next/src/rsc/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type HeadstartWPRoute<Params extends { [k: string]: unknown } = {}> = {
params: { path: string[] };
} & Params;
6 changes: 3 additions & 3 deletions projects/wp-nextjs-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
"dependencies": {
"react": "^18",
"react-dom": "^18",
"next": "14.2.3"
"next": "14.2.3",
"@headstartwp/core": "^1.4.3",
"@headstartwp/next": "^1.4.2"
},
"devDependencies": {
"@headstartwp/core": "^1.4.3",
"@headstartwp/next": "^1.4.2",
"@10up/eslint-config": "^4.0.0",
"typescript": "^5",
"@types/node": "^20",
Expand Down
23 changes: 23 additions & 0 deletions projects/wp-nextjs-app/src/app/(single)/[...path]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { fetchPostByPath, HeadstartWPRoute } from '@headstartwp/next';

const Single = async ({ params }: HeadstartWPRoute) => {
const { data } = await fetchPostByPath(
params.path,
{
params: {},
},
{
headers: {
cache: 'no-store',
},
},
);

return (
<article>
<h1>{data.post.title.rendered}</h1>
</article>
);
};

export default Single;
11 changes: 1 addition & 10 deletions projects/wp-nextjs-app/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { BlocksRenderer } from '@headstartwp/core/react';
import { fetchPost, fetchPosts, setHeadstartWPConfig } from '@headstartwp/core';
import { fetchPost } from '@headstartwp/core';
import styles from './page.module.css';
import config from '../../headstartwp.config';

setHeadstartWPConfig(config);

const Home = async () => {
const {
data: { posts },
} = await fetchPosts();

const { data } = await fetchPost(
{
matchCurrentPath: false,
Expand All @@ -18,8 +11,6 @@ const Home = async () => {
'/distinctio-rerum-ratione-maxime-repudiandae-laboriosam-quam',
);

console.log(posts);

return (
<main className={styles.main}>
<div className={styles.description}>
Expand Down
3 changes: 2 additions & 1 deletion projects/wp-nextjs-app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"strictNullChecks": true
"strictNullChecks": true,
"customConditions": ["react-server"]
},
"include": [
"next-env.d.ts",
Expand Down

0 comments on commit 41f73e3

Please sign in to comment.