Skip to content
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

ApiFetch: Script module #60952

Draft
wants to merge 8 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions packages/api-fetch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"module": "build-module/index.js",
"react-native": "src/index",
"types": "build-types",
"wpScriptModuleExports": "./build-module/module/index.js",
"dependencies": {
"@babel/runtime": "^7.16.0",
"@wordpress/i18n": "file:../i18n",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
//import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import createNonceMiddleware from './middlewares/nonce';
import createRootURLMiddleware from './middlewares/root-url';
import createPreloadingMiddleware from './middlewares/preloading';
import fetchAllMiddleware from './middlewares/fetch-all-middleware';
import namespaceEndpointMiddleware from './middlewares/namespace-endpoint';
import httpV1Middleware from './middlewares/http-v1';
import userLocaleMiddleware from './middlewares/user-locale';
import mediaUploadMiddleware from './middlewares/media-upload';
import createThemePreviewMiddleware from './middlewares/theme-preview';
import {
parseResponseAndNormalizeError,
parseAndThrowError,
Expand Down Expand Up @@ -44,28 +35,6 @@ const DEFAULT_OPTIONS = {
credentials: 'include',
};

/** @typedef {import('./types').APIFetchMiddleware} APIFetchMiddleware */
/** @typedef {import('./types').APIFetchOptions} APIFetchOptions */

/**
* @type {import('./types').APIFetchMiddleware[]}
*/
const middlewares = [
userLocaleMiddleware,
namespaceEndpointMiddleware,
httpV1Middleware,
fetchAllMiddleware,
];

/**
* Register a middleware
*
* @param {import('./types').APIFetchMiddleware} middleware
*/
function registerMiddleware( middleware ) {
middlewares.unshift( middleware );
}

/**
* Checks the status of a response, throwing the Response as an error if
* it is outside the 200 range.
Expand Down Expand Up @@ -128,7 +97,7 @@ const defaultFetchHandler = ( nextOptions ) => {
// Unfortunately the message might depend on the browser.
throw {
code: 'fetch_error',
message: __( 'You are probably offline.' ),
message: 'You are probably offline.',
};
}
);
Expand All @@ -143,16 +112,23 @@ let fetchHandler = defaultFetchHandler;
*
* @param {FetchHandler} newFetchHandler The new fetch handler
*/
function setFetchHandler( newFetchHandler ) {
export function setFetchHandler( newFetchHandler ) {
fetchHandler = newFetchHandler;
}

/** @type { typeof import('./middlewares/singleton').middlewares } */
let middlewares;

/**
* @template T
* @param {import('./types').APIFetchOptions} options
* @return {Promise<T>} A promise representing the request processed via the registered middlewares.
*/
function apiFetch( options ) {
export async function apiFetch( options ) {
if ( ! middlewares ) {
middlewares = ( await import( './middlewares/singleton' ) ).middlewares;
}

// creates a nested function chain that calls all middlewares and finally the `fetchHandler`,
// converting `middlewares = [ m1, m2, m3 ]` into:
// ```
Expand Down Expand Up @@ -185,15 +161,3 @@ function apiFetch( options ) {
);
} );
}

apiFetch.use = registerMiddleware;
apiFetch.setFetchHandler = setFetchHandler;

apiFetch.createNonceMiddleware = createNonceMiddleware;
apiFetch.createPreloadingMiddleware = createPreloadingMiddleware;
apiFetch.createRootURLMiddleware = createRootURLMiddleware;
apiFetch.fetchAllMiddleware = fetchAllMiddleware;
apiFetch.mediaUploadMiddleware = mediaUploadMiddleware;
apiFetch.createThemePreviewMiddleware = createThemePreviewMiddleware;

export default apiFetch;
39 changes: 39 additions & 0 deletions packages/api-fetch/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Internal dependencies
*/
import { createNonceMiddleware } from './middlewares/nonce';
import { createRootURLMiddleware } from './middlewares/root-url';
import { createPreloadingMiddleware } from './middlewares/preloading';
import { fetchAllMiddleware } from './middlewares/fetch-all-middleware';
import { mediaUploadMiddleware } from './middlewares/media-upload';
import { createThemePreviewMiddleware } from './middlewares/theme-preview';
import * as ApiFetchCore from './core';
import { type APIFetchOptions } from './types';
import { registerMiddleware } from './middlewares/singleton';

export interface ApiFetch {
< T >( options: APIFetchOptions ): Promise< T >;
use: typeof registerMiddleware;
setFetchHandler: typeof ApiFetchCore.setFetchHandler;
createNonceMiddleware: typeof createNonceMiddleware;
createPreloadingMiddleware: typeof createPreloadingMiddleware;
createRootURLMiddleware: typeof createRootURLMiddleware;
fetchAllMiddleware: typeof fetchAllMiddleware;
mediaUploadMiddleware: typeof mediaUploadMiddleware;
createThemePreviewMiddleware: typeof createThemePreviewMiddleware;
}

// @ts-expect-error This is an incomplete type we'll add properties to.
const apiFetch: ApiFetch = ApiFetchCore.apiFetch;

apiFetch.use = registerMiddleware;
apiFetch.setFetchHandler = ApiFetchCore.setFetchHandler;
apiFetch.createNonceMiddleware = createNonceMiddleware;
apiFetch.createPreloadingMiddleware = createPreloadingMiddleware;
apiFetch.createRootURLMiddleware = createRootURLMiddleware;
apiFetch.fetchAllMiddleware = fetchAllMiddleware;
apiFetch.mediaUploadMiddleware = mediaUploadMiddleware;
apiFetch.createThemePreviewMiddleware = createThemePreviewMiddleware;

export { apiFetch as default };
export type { APIFetchOptions };
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const requestContainsUnboundedQuery = ( options ) => {
*
* @type {import('../types').APIFetchMiddleware}
*/
const fetchAllMiddleware = async ( options, next ) => {
export const fetchAllMiddleware = async ( options, next ) => {
if ( options.parse === false ) {
// If a consumer has opted out of parsing, do not apply middleware.
return next( options );
Expand Down Expand Up @@ -124,5 +124,3 @@ const fetchAllMiddleware = async ( options, next ) => {
}
return mergedResults;
};

export default fetchAllMiddleware;
4 changes: 1 addition & 3 deletions packages/api-fetch/src/middlewares/http-v1.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const DEFAULT_METHOD = 'GET';
*
* @type {import('../types').APIFetchMiddleware}
*/
const httpV1Middleware = ( options, next ) => {
export const httpV1Middleware = ( options, next ) => {
const { method = DEFAULT_METHOD } = options;
if ( OVERRIDE_METHODS.has( method.toUpperCase() ) ) {
options = {
Expand All @@ -39,5 +39,3 @@ const httpV1Middleware = ( options, next ) => {

return next( options );
};

export default httpV1Middleware;
4 changes: 1 addition & 3 deletions packages/api-fetch/src/middlewares/media-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function isMediaUploadRequest( options ) {
*
* @type {import('../types').APIFetchMiddleware}
*/
const mediaUploadMiddleware = ( options, next ) => {
export const mediaUploadMiddleware = ( options, next ) => {
if ( ! isMediaUploadRequest( options ) ) {
return next( options );
}
Expand Down Expand Up @@ -95,5 +95,3 @@ const mediaUploadMiddleware = ( options, next ) => {
parseResponseAndNormalizeError( response, options.parse )
);
};

export default mediaUploadMiddleware;
4 changes: 1 addition & 3 deletions packages/api-fetch/src/middlewares/namespace-endpoint.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @type {import('../types').APIFetchMiddleware}
*/
const namespaceAndEndpointMiddleware = ( options, next ) => {
export const namespaceAndEndpointMiddleware = ( options, next ) => {
let path = options.path;
let namespaceTrimmed, endpointTrimmed;

Expand All @@ -26,5 +26,3 @@ const namespaceAndEndpointMiddleware = ( options, next ) => {
path,
} );
};

export default namespaceAndEndpointMiddleware;
4 changes: 1 addition & 3 deletions packages/api-fetch/src/middlewares/nonce.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @param {string} nonce
* @return {import('../types').APIFetchMiddleware & { nonce: string }} A middleware to enhance a request with a nonce.
*/
function createNonceMiddleware( nonce ) {
export function createNonceMiddleware( nonce ) {
/**
* @type {import('../types').APIFetchMiddleware & { nonce: string }}
*/
Expand Down Expand Up @@ -33,5 +33,3 @@ function createNonceMiddleware( nonce ) {

return middleware;
}

export default createNonceMiddleware;
4 changes: 1 addition & 3 deletions packages/api-fetch/src/middlewares/preloading.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { addQueryArgs, getQueryArgs, normalizePath } from '@wordpress/url';
* @param {Record<string, any>} preloadedData
* @return {import('../types').APIFetchMiddleware} Preloading middleware.
*/
function createPreloadingMiddleware( preloadedData ) {
export function createPreloadingMiddleware( preloadedData ) {
const cache = Object.fromEntries(
Object.entries( preloadedData ).map( ( [ path, data ] ) => [
normalizePath( path ),
Expand Down Expand Up @@ -78,5 +78,3 @@ function prepareResponse( responseData, parse ) {
} )
);
}

export default createPreloadingMiddleware;
6 changes: 2 additions & 4 deletions packages/api-fetch/src/middlewares/root-url.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/**
* Internal dependencies
*/
import namespaceAndEndpointMiddleware from './namespace-endpoint';
import { namespaceAndEndpointMiddleware } from './namespace-endpoint';

/**
* @param {string} rootURL
* @return {import('../types').APIFetchMiddleware} Root URL middleware.
*/
const createRootURLMiddleware = ( rootURL ) => ( options, next ) => {
export const createRootURLMiddleware = ( rootURL ) => ( options, next ) => {
return namespaceAndEndpointMiddleware( options, ( optionsWithPath ) => {
let url = optionsWithPath.url;
let path = optionsWithPath.path;
Expand Down Expand Up @@ -40,5 +40,3 @@ const createRootURLMiddleware = ( rootURL ) => ( options, next ) => {
} );
} );
};

export default createRootURLMiddleware;
26 changes: 26 additions & 0 deletions packages/api-fetch/src/middlewares/singleton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Internal dependencies
*/
import { fetchAllMiddleware } from './fetch-all-middleware';
import { namespaceAndEndpointMiddleware } from './namespace-endpoint';
import { httpV1Middleware } from './http-v1';
import { userLocaleMiddleware } from './user-locale';

/**
* @type {import('../types').APIFetchMiddleware[]}
*/
export const middlewares = [
userLocaleMiddleware,
namespaceAndEndpointMiddleware,
httpV1Middleware,
fetchAllMiddleware,
];

/**
* Register a middleware
*
* @param {import('../types').APIFetchMiddleware} middleware
*/
export function registerMiddleware( middleware ) {
middlewares.unshift( middleware );
}
57 changes: 34 additions & 23 deletions packages/api-fetch/src/middlewares/theme-preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,41 @@ import { addQueryArgs, getQueryArg, removeQueryArgs } from '@wordpress/url';
* @param {Record<string, any>} themePath
* @return {import('../types').APIFetchMiddleware} Preloading middleware.
*/
const createThemePreviewMiddleware = ( themePath ) => ( options, next ) => {
if ( typeof options.url === 'string' ) {
const wpThemePreview = getQueryArg( options.url, 'wp_theme_preview' );
if ( wpThemePreview === undefined ) {
options.url = addQueryArgs( options.url, {
wp_theme_preview: themePath,
} );
} else if ( wpThemePreview === '' ) {
options.url = removeQueryArgs( options.url, 'wp_theme_preview' );
export const createThemePreviewMiddleware =
( themePath ) => ( options, next ) => {
if ( typeof options.url === 'string' ) {
const wpThemePreview = getQueryArg(
options.url,
'wp_theme_preview'
);
if ( wpThemePreview === undefined ) {
options.url = addQueryArgs( options.url, {
wp_theme_preview: themePath,
} );
} else if ( wpThemePreview === '' ) {
options.url = removeQueryArgs(
options.url,
'wp_theme_preview'
);
}
}
}

if ( typeof options.path === 'string' ) {
const wpThemePreview = getQueryArg( options.path, 'wp_theme_preview' );
if ( wpThemePreview === undefined ) {
options.path = addQueryArgs( options.path, {
wp_theme_preview: themePath,
} );
} else if ( wpThemePreview === '' ) {
options.path = removeQueryArgs( options.path, 'wp_theme_preview' );
if ( typeof options.path === 'string' ) {
const wpThemePreview = getQueryArg(
options.path,
'wp_theme_preview'
);
if ( wpThemePreview === undefined ) {
options.path = addQueryArgs( options.path, {
wp_theme_preview: themePath,
} );
} else if ( wpThemePreview === '' ) {
options.path = removeQueryArgs(
options.path,
'wp_theme_preview'
);
}
}
}

return next( options );
};

export default createThemePreviewMiddleware;
return next( options );
};
4 changes: 1 addition & 3 deletions packages/api-fetch/src/middlewares/user-locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { addQueryArgs, hasQueryArg } from '@wordpress/url';
/**
* @type {import('../types').APIFetchMiddleware}
*/
const userLocaleMiddleware = ( options, next ) => {
export const userLocaleMiddleware = ( options, next ) => {
if (
typeof options.url === 'string' &&
! hasQueryArg( options.url, '_locale' )
Expand All @@ -23,5 +23,3 @@ const userLocaleMiddleware = ( options, next ) => {

return next( options );
};

export default userLocaleMiddleware;
Loading
Loading