From 2a3314673e925583f5ded39c75e54ebe065696dc Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 23 Apr 2024 18:19:10 +0200 Subject: [PATCH] overhaul apifetch so it works --- packages/api-fetch/src/core.js | 35 +++++-------------- packages/api-fetch/src/index.ts | 17 ++++----- .../src/middlewares/fetch-all-middleware.js | 8 ++--- packages/api-fetch/src/middlewares/http-v1.js | 4 +-- .../api-fetch/src/middlewares/media-upload.js | 4 +-- .../src/middlewares/namespace-endpoint.js | 4 +-- packages/api-fetch/src/middlewares/nonce.js | 4 +-- .../api-fetch/src/middlewares/preloading.js | 4 +-- .../api-fetch/src/middlewares/root-url.js | 8 ++--- .../api-fetch/src/middlewares/singleton.js | 26 ++++++++++++++ .../src/middlewares/theme-preview.js | 4 +-- .../api-fetch/src/middlewares/user-locale.js | 4 +-- packages/api-fetch/src/wp-module.js | 34 ++++++++++-------- 13 files changed, 76 insertions(+), 80 deletions(-) create mode 100644 packages/api-fetch/src/middlewares/singleton.js diff --git a/packages/api-fetch/src/core.js b/packages/api-fetch/src/core.js index 1fb58fc6b8bd85..4f41de7889e470 100644 --- a/packages/api-fetch/src/core.js +++ b/packages/api-fetch/src/core.js @@ -6,10 +6,6 @@ /** * Internal dependencies */ -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 { parseResponseAndNormalizeError, parseAndThrowError, @@ -39,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 - */ -export 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. @@ -142,12 +116,19 @@ export function setFetchHandler( newFetchHandler ) { fetchHandler = newFetchHandler; } +/** @type { typeof import('./middlewares/singleton').middlewares } */ +let middlewares; + /** * @template T * @param {import('./types').APIFetchOptions} options * @return {Promise} A promise representing the request processed via the registered middlewares. */ -export 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: // ``` diff --git a/packages/api-fetch/src/index.ts b/packages/api-fetch/src/index.ts index b211ec9032889f..78b621a200e808 100644 --- a/packages/api-fetch/src/index.ts +++ b/packages/api-fetch/src/index.ts @@ -1,18 +1,19 @@ /** * 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 { 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 ApiFetchCore.registerMiddleware; + use: typeof registerMiddleware; setFetchHandler: typeof ApiFetchCore.setFetchHandler; createNonceMiddleware: typeof createNonceMiddleware; createPreloadingMiddleware: typeof createPreloadingMiddleware; @@ -25,7 +26,7 @@ export interface ApiFetch { // @ts-expect-error This is an incomplete type we'll add properties to. const apiFetch: ApiFetch = ApiFetchCore.apiFetch; -apiFetch.use = ApiFetchCore.registerMiddleware; +apiFetch.use = registerMiddleware; apiFetch.setFetchHandler = ApiFetchCore.setFetchHandler; apiFetch.createNonceMiddleware = createNonceMiddleware; apiFetch.createPreloadingMiddleware = createPreloadingMiddleware; diff --git a/packages/api-fetch/src/middlewares/fetch-all-middleware.js b/packages/api-fetch/src/middlewares/fetch-all-middleware.js index f51c99ae3d2b40..f2ec447a93e266 100644 --- a/packages/api-fetch/src/middlewares/fetch-all-middleware.js +++ b/packages/api-fetch/src/middlewares/fetch-all-middleware.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import apiFetch from '..'; +import { apiFetch } from '../core'; /** * Apply query arguments to both URL and Path, whichever is present. @@ -11,7 +11,7 @@ import apiFetch from '..'; * @return {import('../types').APIFetchOptions} The request with the modified query args */ const modifyQuery = ( { path, url, ...options }, queryArgs ) => { - /** @type {import('../core').APIFetchOptions} */ + /** @type {import('../types').APIFetchOptions} */ const result = { ...options, }; @@ -98,7 +98,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 ); @@ -148,5 +148,3 @@ const fetchAllMiddleware = async ( options, next ) => { } return mergedResults; }; - -export default fetchAllMiddleware; diff --git a/packages/api-fetch/src/middlewares/http-v1.js b/packages/api-fetch/src/middlewares/http-v1.js index 953fc26ff70c8a..78daf62282ab74 100644 --- a/packages/api-fetch/src/middlewares/http-v1.js +++ b/packages/api-fetch/src/middlewares/http-v1.js @@ -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 = { @@ -39,5 +39,3 @@ const httpV1Middleware = ( options, next ) => { return next( options ); }; - -export default httpV1Middleware; diff --git a/packages/api-fetch/src/middlewares/media-upload.js b/packages/api-fetch/src/middlewares/media-upload.js index 365c20a7439469..7aa4df90a4c0ae 100644 --- a/packages/api-fetch/src/middlewares/media-upload.js +++ b/packages/api-fetch/src/middlewares/media-upload.js @@ -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 ); } @@ -89,5 +89,3 @@ const mediaUploadMiddleware = ( options, next ) => { parseResponseAndNormalizeError( response, options.parse ) ); }; - -export default mediaUploadMiddleware; diff --git a/packages/api-fetch/src/middlewares/namespace-endpoint.js b/packages/api-fetch/src/middlewares/namespace-endpoint.js index bfb0e824187f35..260caf89c7ef79 100644 --- a/packages/api-fetch/src/middlewares/namespace-endpoint.js +++ b/packages/api-fetch/src/middlewares/namespace-endpoint.js @@ -1,7 +1,7 @@ /** * @type {import('../types').APIFetchMiddleware} */ -const namespaceAndEndpointMiddleware = ( options, next ) => { +export const namespaceEndpointMiddleware = ( options, next ) => { let path = options.path; let namespaceTrimmed, endpointTrimmed; @@ -26,5 +26,3 @@ const namespaceAndEndpointMiddleware = ( options, next ) => { path, } ); }; - -export default namespaceAndEndpointMiddleware; diff --git a/packages/api-fetch/src/middlewares/nonce.js b/packages/api-fetch/src/middlewares/nonce.js index 60914823661ed9..21e432bb4aa9aa 100644 --- a/packages/api-fetch/src/middlewares/nonce.js +++ b/packages/api-fetch/src/middlewares/nonce.js @@ -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 }} */ @@ -33,5 +33,3 @@ function createNonceMiddleware( nonce ) { return middleware; } - -export default createNonceMiddleware; diff --git a/packages/api-fetch/src/middlewares/preloading.js b/packages/api-fetch/src/middlewares/preloading.js index dac1228866520d..90bdf07fa5e1bd 100644 --- a/packages/api-fetch/src/middlewares/preloading.js +++ b/packages/api-fetch/src/middlewares/preloading.js @@ -6,7 +6,7 @@ * @param {Record} preloadedData * @return {import('../types').APIFetchMiddleware} Preloading middleware. */ -function createPreloadingMiddleware( preloadedData ) { +export function createPreloadingMiddleware( preloadedData ) { const cache = Object.fromEntries( Object.entries( preloadedData ).map( ( [ path, data ] ) => [ path, @@ -68,5 +68,3 @@ function prepareResponse( responseData, parse ) { } ) ); } - -export default createPreloadingMiddleware; diff --git a/packages/api-fetch/src/middlewares/root-url.js b/packages/api-fetch/src/middlewares/root-url.js index cde855f3f9504f..9b522ee481bcb5 100644 --- a/packages/api-fetch/src/middlewares/root-url.js +++ b/packages/api-fetch/src/middlewares/root-url.js @@ -1,14 +1,14 @@ /** * Internal dependencies */ -import namespaceAndEndpointMiddleware from './namespace-endpoint'; +import { namespaceEndpointMiddleware } from './namespace-endpoint'; /** * @param {string} rootURL * @return {import('../types').APIFetchMiddleware} Root URL middleware. */ -const createRootURLMiddleware = ( rootURL ) => ( options, next ) => { - return namespaceAndEndpointMiddleware( options, ( optionsWithPath ) => { +export const createRootURLMiddleware = ( rootURL ) => ( options, next ) => { + return namespaceEndpointMiddleware( options, ( optionsWithPath ) => { let url = optionsWithPath.url; let path = optionsWithPath.path; let apiRoot; @@ -40,5 +40,3 @@ const createRootURLMiddleware = ( rootURL ) => ( options, next ) => { } ); } ); }; - -export default createRootURLMiddleware; diff --git a/packages/api-fetch/src/middlewares/singleton.js b/packages/api-fetch/src/middlewares/singleton.js new file mode 100644 index 00000000000000..48fc4ff23b6cd3 --- /dev/null +++ b/packages/api-fetch/src/middlewares/singleton.js @@ -0,0 +1,26 @@ +/** + * Internal dependencies + */ +import { fetchAllMiddleware } from './fetch-all-middleware'; +import { namespaceEndpointMiddleware } from './namespace-endpoint'; +import { httpV1Middleware } from './http-v1'; +import { userLocaleMiddleware } from './user-locale'; + +/** + * @type {import('../types').APIFetchMiddleware[]} + */ +export const middlewares = [ + userLocaleMiddleware, + namespaceEndpointMiddleware, + httpV1Middleware, + fetchAllMiddleware, +]; + +/** + * Register a middleware + * + * @param {import('../types').APIFetchMiddleware} middleware + */ +export function registerMiddleware( middleware ) { + middlewares.unshift( middleware ); +} diff --git a/packages/api-fetch/src/middlewares/theme-preview.js b/packages/api-fetch/src/middlewares/theme-preview.js index c0b928eb2a3b8e..7f332120cc2a97 100644 --- a/packages/api-fetch/src/middlewares/theme-preview.js +++ b/packages/api-fetch/src/middlewares/theme-preview.js @@ -11,8 +11,6 @@ * * @return {import('../types').APIFetchMiddleware} Preloading middleware. */ -const createThemePreviewMiddleware = () => ( options, next ) => { +export const createThemePreviewMiddleware = () => ( options, next ) => { return next( options ); }; - -export default createThemePreviewMiddleware; diff --git a/packages/api-fetch/src/middlewares/user-locale.js b/packages/api-fetch/src/middlewares/user-locale.js index 0546e61003fcf8..9b6eae44e46730 100644 --- a/packages/api-fetch/src/middlewares/user-locale.js +++ b/packages/api-fetch/src/middlewares/user-locale.js @@ -5,8 +5,6 @@ /** * @type {import('../types').APIFetchMiddleware} */ -const userLocaleMiddleware = ( options, next ) => { +export const userLocaleMiddleware = ( options, next ) => { return next( options ); }; - -export default userLocaleMiddleware; diff --git a/packages/api-fetch/src/wp-module.js b/packages/api-fetch/src/wp-module.js index da2150b5d64850..7e71670c006e81 100644 --- a/packages/api-fetch/src/wp-module.js +++ b/packages/api-fetch/src/wp-module.js @@ -1,16 +1,19 @@ /** * 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 { apiFetch, registerMiddleware, setFetchHandler } from './core'; +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 { apiFetch, setFetchHandler } from './core'; +import { registerMiddleware } from './middlewares/singleton'; if ( typeof document !== 'undefined' ) { - const el = document.getElementById( 'scriptmoduledata_@wordpress/api-fetch' ); + const el = document.getElementById( + 'wp-scriptmodule-data_@wordpress/api-fetch' + ); if ( el?.textContent ) { console.group( 'api-fetch init' ); try { @@ -34,15 +37,18 @@ if ( typeof document !== 'undefined' ) { apiFetch.nonceEndpoint = config.nonceEndpoint; } if ( config.themePreviewPath ) { - // @ts-expect-error This is wrong, done for testing. - registerMiddleware( createThemePreviewMiddleware( config.themePreviewPath ) ); + registerMiddleware( + // @ts-expect-error This is wrong, done for testing. + createThemePreviewMiddleware( config.themePreviewPath ) + ); } if ( config.preloadData ) { - registerMiddleware( createPreloadingMiddleware( config.preloadData ) ); + registerMiddleware( + createPreloadingMiddleware( config.preloadData ) + ); } - - } catch { - console.error( 'error' ); + } catch ( err ) { + console.error( err ); } finally { console.groupEnd(); }