@wordpress/server-side-render 6.24.0 → 6.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- ## 6.24.0 (2026-06-04)
5
+ ## 6.24.1 (2026-06-16)
6
+
7
+ ## 6.24.0 (2026-06-10)
8
+
9
+ ### Code Quality
10
+
11
+ - Add missing `@types/react` dependency. [#78882](https://github.com/WordPress/gutenberg/pull/78882).
6
12
 
7
13
  ## 6.23.0 (2026-05-27)
8
14
 
package/README.md CHANGED
@@ -63,20 +63,12 @@ function Example() {
63
63
 
64
64
  _Parameters_
65
65
 
66
- - _props_ `Object`: Component props.
67
- - _props.block_ `string`: The identifier of the block to be serverside rendered.
68
- - _props.attributes_ `Object`: The block attributes to be sent to the server for rendering.
69
- - _props.className_ `[string]`: Additional classes to apply to the wrapper element.
70
- - _props.httpMethod_ `[string]`: The HTTP method to use ('GET' or 'POST'). Default is 'GET'
71
- - _props.urlQueryArgs_ `[Object]`: Additional query arguments to append to the request URL.
72
- - _props.skipBlockSupportAttributes_ `[boolean]`: Whether to remove block support attributes before sending.
73
- - _props.EmptyResponsePlaceholder_ `[Function]`: Component rendered when the API response is empty.
74
- - _props.ErrorResponsePlaceholder_ `[Function]`: Component rendered when the API response is an error.
75
- - _props.LoadingResponsePlaceholder_ `[Function]`: Component rendered while the API request is loading.
66
+ - _props_ `ServerSideRenderWithPostIdProps`: Component props.
67
+ - _props.urlQueryArgs_ `ServerSideRenderWithPostIdProps[ 'urlQueryArgs' ]`: Additional query arguments to append to the request URL.
76
68
 
77
69
  _Returns_
78
70
 
79
- - `React.JSX.Element`: The rendered server-side content.
71
+ - The rendered server-side content.
80
72
 
81
73
  ### useServerSideRender
82
74
 
@@ -112,12 +104,7 @@ function MyServerSideRender( { attributes, block } ) {
112
104
 
113
105
  _Parameters_
114
106
 
115
- - _args_ `Object`: The hook configuration object.
116
- - _args.attributes_ `Object`: The block attributes to be sent to the server for rendering.
117
- - _args.block_ `string`: The identifier of the block to be serverside rendered. Example: 'core/archives'.
118
- - _args.skipBlockSupportAttributes_ `[boolean]`: Whether to remove block support attributes before sending.
119
- - _args.httpMethod_ `[string]`: The HTTP method to use ('GET' or 'POST'). Default is 'GET'.
120
- - _args.urlQueryArgs_ `[Object]`: Additional query arguments to append to the request URL.
107
+ - _args_ `UseServerSideRenderArgs`: The hook configuration object.
121
108
 
122
109
  _Returns_
123
110
 
package/build/hook.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -26,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
27
  ));
27
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
29
 
29
- // packages/server-side-render/src/hook.js
30
+ // packages/server-side-render/src/hook.ts
30
31
  var hook_exports = {};
31
32
  __export(hook_exports, {
32
33
  removeBlockSupportAttributes: () => removeBlockSupportAttributes,
@@ -72,7 +73,9 @@ function removeBlockSupportAttributes(attributes) {
72
73
  };
73
74
  }
74
75
  function useServerSideRender(args) {
75
- const [response, setResponse] = (0, import_element.useState)({ status: "idle" });
76
+ const [response, setResponse] = (0, import_element.useState)({
77
+ status: "idle"
78
+ });
76
79
  const shouldDebounceRef = (0, import_element.useRef)(false);
77
80
  const {
78
81
  attributes,
@@ -82,7 +85,7 @@ function useServerSideRender(args) {
82
85
  urlQueryArgs
83
86
  } = args;
84
87
  let sanitizedAttributes = attributes && (0, import_blocks.__experimentalSanitizeBlockAttributes)(block, attributes);
85
- if (skipBlockSupportAttributes) {
88
+ if (skipBlockSupportAttributes && sanitizedAttributes) {
86
89
  sanitizedAttributes = removeBlockSupportAttributes(sanitizedAttributes);
87
90
  }
88
91
  const isPostRequest = "POST" === httpMethod;
@@ -109,12 +112,12 @@ function useServerSideRender(args) {
109
112
  content: res ? res.rendered : ""
110
113
  });
111
114
  }).catch((error) => {
112
- if (error.name === "AbortError") {
115
+ if (error instanceof Error && error.name === "AbortError") {
113
116
  return;
114
117
  }
115
118
  setResponse({
116
119
  status: "error",
117
- error: error.message
120
+ error: error instanceof Error ? error.message : String(error)
118
121
  });
119
122
  }).finally(() => {
120
123
  shouldDebounceRef.current = true;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/hook.js"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { debounce } from '@wordpress/compose';\nimport { useEffect, useState, useRef } from '@wordpress/element';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\nimport { __experimentalSanitizeBlockAttributes } from '@wordpress/blocks';\n\nexport function rendererPath( block, attributes = null, urlQueryArgs = {} ) {\n\treturn addQueryArgs( `/wp/v2/block-renderer/${ block }`, {\n\t\tcontext: 'edit',\n\t\t...( null !== attributes ? { attributes } : {} ),\n\t\t...urlQueryArgs,\n\t} );\n}\n\nexport function removeBlockSupportAttributes( attributes ) {\n\tconst {\n\t\tbackgroundColor,\n\t\tborderColor,\n\t\tfontFamily,\n\t\tfontSize,\n\t\tgradient,\n\t\ttextColor,\n\t\tclassName,\n\t\t...restAttributes\n\t} = attributes;\n\n\tconst {\n\t\tborder,\n\t\tcolor,\n\t\telements,\n\t\tshadow,\n\t\tspacing,\n\t\ttypography,\n\t\t...restStyles\n\t} = attributes?.style || {};\n\n\treturn {\n\t\t...restAttributes,\n\t\tstyle: restStyles,\n\t};\n}\n\n/**\n * @typedef {Object} ServerSideRenderResponse\n * @property {string} status - The current request status: 'idle', 'loading', 'success', or 'error'.\n * @property {string} [content] - The rendered block content (available when status is 'success').\n * @property {string} [error] - The error message (available when status is 'error').\n */\n\n/**\n * A hook for server-side rendering a preview of dynamic blocks to display in the editor.\n *\n * Handles fetching server-rendered previews for blocks, managing loading states,\n * and automatically debouncing requests to prevent excessive API calls. It supports both\n * GET and POST requests, with POST requests used for larger attribute payloads.\n *\n * @example\n * Basic usage:\n *\n * ```jsx\n * import { RawHTML } from '@wordpress/element';\n * import { useServerSideRender } from '@wordpress/server-side-render';\n *\n * function MyServerSideRender( { attributes, block } ) {\n * const { content, status, error } = useServerSideRender( {\n * attributes,\n * block,\n * } );\n *\n * if ( status === 'loading' ) {\n * return <div>Loading...</div>;\n * }\n *\n * if ( status === 'error' ) {\n * return <div>Error: { error }</div>;\n * }\n *\n * return <RawHTML>{ content }</RawHTML>;\n * }\n * ```\n *\n * @param {Object} args The hook configuration object.\n * @param {Object} args.attributes The block attributes to be sent to the server for rendering.\n * @param {string} args.block The identifier of the block to be serverside rendered. Example: 'core/archives'.\n * @param {boolean} [args.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.\n * @param {string} [args.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'.\n * @param {Object} [args.urlQueryArgs] Additional query arguments to append to the request URL.\n *\n * @return {ServerSideRenderResponse} The server-side render response object.\n */\nexport function useServerSideRender( args ) {\n\tconst [ response, setResponse ] = useState( { status: 'idle' } );\n\tconst shouldDebounceRef = useRef( false );\n\n\tconst {\n\t\tattributes,\n\t\tblock,\n\t\tskipBlockSupportAttributes = false,\n\t\thttpMethod = 'GET',\n\t\turlQueryArgs,\n\t} = args;\n\n\tlet sanitizedAttributes =\n\t\tattributes &&\n\t\t__experimentalSanitizeBlockAttributes( block, attributes );\n\n\tif ( skipBlockSupportAttributes ) {\n\t\tsanitizedAttributes =\n\t\t\tremoveBlockSupportAttributes( sanitizedAttributes );\n\t}\n\n\t// If httpMethod is 'POST', send the attributes in the request body instead of the URL.\n\t// This allows sending a larger attributes object than in a GET request, where the attributes are in the URL.\n\tconst isPostRequest = 'POST' === httpMethod;\n\tconst urlAttributes = isPostRequest ? null : sanitizedAttributes;\n\tconst path = rendererPath( block, urlAttributes, urlQueryArgs );\n\tconst body = isPostRequest\n\t\t? JSON.stringify( { attributes: sanitizedAttributes ?? null } )\n\t\t: undefined;\n\n\tuseEffect( () => {\n\t\tconst controller = new AbortController();\n\t\tconst debouncedFetch = debounce(\n\t\t\tfunction () {\n\t\t\t\t{\n\t\t\t\t\tsetResponse( { status: 'loading' } );\n\n\t\t\t\t\tapiFetch( {\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tmethod: isPostRequest ? 'POST' : 'GET',\n\t\t\t\t\t\tbody,\n\t\t\t\t\t\theaders: isPostRequest\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t: {},\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t} )\n\t\t\t\t\t\t.then( ( res ) => {\n\t\t\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\t\t\tcontent: res ? res.rendered : '',\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.catch( ( error ) => {\n\t\t\t\t\t\t\t// The request was aborted, do not update the response.\n\t\t\t\t\t\t\tif ( error.name === 'AbortError' ) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\t\terror: error.message,\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.finally( () => {\n\t\t\t\t\t\t\t// Debounce requests after first fetch.\n\t\t\t\t\t\t\tshouldDebounceRef.current = true;\n\t\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t},\n\t\t\tshouldDebounceRef.current ? 500 : 0\n\t\t);\n\n\t\tdebouncedFetch();\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t\tdebouncedFetch.cancel();\n\t\t};\n\t}, [ path, isPostRequest, body ] );\n\n\treturn response;\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,qBAAyB;AACzB,qBAA4C;AAC5C,uBAAqB;AACrB,iBAA6B;AAC7B,oBAAsD;AAE/C,SAAS,aAAc,OAAO,aAAa,MAAM,eAAe,CAAC,GAAI;AAC3E,aAAO,yBAAc,yBAA0B,KAAM,IAAI;AAAA,IACxD,SAAS;AAAA,IACT,GAAK,SAAS,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IAC7C,GAAG;AAAA,EACJ,CAAE;AACH;AAEO,SAAS,6BAA8B,YAAa;AAC1D,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ,IAAI;AAEJ,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ,IAAI,YAAY,SAAS,CAAC;AAE1B,SAAO;AAAA,IACN,GAAG;AAAA,IACH,OAAO;AAAA,EACR;AACD;AAkDO,SAAS,oBAAqB,MAAO;AAC3C,QAAM,CAAE,UAAU,WAAY,QAAI,yBAAU,EAAE,QAAQ,OAAO,CAAE;AAC/D,QAAM,wBAAoB,uBAAQ,KAAM;AAExC,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA,6BAA6B;AAAA,IAC7B,aAAa;AAAA,IACb;AAAA,EACD,IAAI;AAEJ,MAAI,sBACH,kBACA,qDAAuC,OAAO,UAAW;AAE1D,MAAK,4BAA6B;AACjC,0BACC,6BAA8B,mBAAoB;AAAA,EACpD;AAIA,QAAM,gBAAgB,WAAW;AACjC,QAAM,gBAAgB,gBAAgB,OAAO;AAC7C,QAAM,OAAO,aAAc,OAAO,eAAe,YAAa;AAC9D,QAAM,OAAO,gBACV,KAAK,UAAW,EAAE,YAAY,uBAAuB,KAAK,CAAE,IAC5D;AAEH,gCAAW,MAAM;AAChB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,qBAAiB;AAAA,MACtB,WAAY;AACX;AACC,sBAAa,EAAE,QAAQ,UAAU,CAAE;AAEnC,+BAAAA,SAAU;AAAA,YACT;AAAA,YACA,QAAQ,gBAAgB,SAAS;AAAA,YACjC;AAAA,YACA,SAAS,gBACN;AAAA,cACA,gBAAgB;AAAA,YAChB,IACA,CAAC;AAAA,YACJ,QAAQ,WAAW;AAAA,UACpB,CAAE,EACA,KAAM,CAAE,QAAS;AACjB,wBAAa;AAAA,cACZ,QAAQ;AAAA,cACR,SAAS,MAAM,IAAI,WAAW;AAAA,YAC/B,CAAE;AAAA,UACH,CAAE,EACD,MAAO,CAAE,UAAW;AAEpB,gBAAK,MAAM,SAAS,cAAe;AAClC;AAAA,YACD;AAEA,wBAAa;AAAA,cACZ,QAAQ;AAAA,cACR,OAAO,MAAM;AAAA,YACd,CAAE;AAAA,UACH,CAAE,EACD,QAAS,MAAM;AAEf,8BAAkB,UAAU;AAAA,UAC7B,CAAE;AAAA,QACJ;AAAA,MACD;AAAA,MACA,kBAAkB,UAAU,MAAM;AAAA,IACnC;AAEA,mBAAe;AAEf,WAAO,MAAM;AACZ,iBAAW,MAAM;AACjB,qBAAe,OAAO;AAAA,IACvB;AAAA,EACD,GAAG,CAAE,MAAM,eAAe,IAAK,CAAE;AAEjC,SAAO;AACR;",
3
+ "sources": ["../src/hook.ts"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { debounce } from '@wordpress/compose';\nimport { useEffect, useState, useRef } from '@wordpress/element';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\n\nimport { __experimentalSanitizeBlockAttributes } from '@wordpress/blocks';\n\nexport function rendererPath(\n\tblock: string,\n\tattributes: Record< string, unknown > | null = null,\n\turlQueryArgs: Record< string, unknown > = {}\n): string {\n\treturn addQueryArgs( `/wp/v2/block-renderer/${ block }`, {\n\t\tcontext: 'edit',\n\t\t...( null !== attributes ? { attributes } : {} ),\n\t\t...urlQueryArgs,\n\t} );\n}\n\nexport function removeBlockSupportAttributes(\n\tattributes: Record< string, unknown > & {\n\t\tstyle?: Record< string, unknown >;\n\t}\n): Record< string, unknown > {\n\tconst {\n\t\tbackgroundColor,\n\t\tborderColor,\n\t\tfontFamily,\n\t\tfontSize,\n\t\tgradient,\n\t\ttextColor,\n\t\tclassName,\n\t\t...restAttributes\n\t} = attributes;\n\n\tconst {\n\t\tborder,\n\t\tcolor,\n\t\telements,\n\t\tshadow,\n\t\tspacing,\n\t\ttypography,\n\t\t...restStyles\n\t} = attributes?.style || {};\n\n\treturn {\n\t\t...restAttributes,\n\t\tstyle: restStyles,\n\t};\n}\n\n/**\n * Server-side render response object.\n */\nexport interface ServerSideRenderResponse {\n\t/** The current request status: 'idle', 'loading', 'success', or 'error'. */\n\tstatus: 'idle' | 'loading' | 'success' | 'error';\n\t/** The rendered block content (available when status is 'success'). */\n\tcontent?: string;\n\t/** The error message (available when status is 'error'). */\n\terror?: string;\n}\n\n/**\n * Configuration object for the useServerSideRender hook.\n */\nexport interface UseServerSideRenderArgs {\n\t/** The block attributes to be sent to the server for rendering. */\n\tattributes: Record< string, unknown >;\n\t/** The identifier of the block to be serverside rendered. Example: 'core/archives'. */\n\tblock: string;\n\t/** Whether to remove block support attributes before sending. */\n\tskipBlockSupportAttributes?: boolean;\n\t/** The HTTP method to use ('GET' or 'POST'). Default is 'GET'. */\n\thttpMethod?: 'GET' | 'POST';\n\t/** Additional query arguments to append to the request URL. */\n\turlQueryArgs?: Record< string, unknown >;\n}\n\n/**\n * A hook for server-side rendering a preview of dynamic blocks to display in the editor.\n *\n * Handles fetching server-rendered previews for blocks, managing loading states,\n * and automatically debouncing requests to prevent excessive API calls. It supports both\n * GET and POST requests, with POST requests used for larger attribute payloads.\n *\n * @example\n * Basic usage:\n *\n * ```jsx\n * import { RawHTML } from '@wordpress/element';\n * import { useServerSideRender } from '@wordpress/server-side-render';\n *\n * function MyServerSideRender( { attributes, block } ) {\n * const { content, status, error } = useServerSideRender( {\n * attributes,\n * block,\n * } );\n *\n * if ( status === 'loading' ) {\n * return <div>Loading...</div>;\n * }\n *\n * if ( status === 'error' ) {\n * return <div>Error: { error }</div>;\n * }\n *\n * return <RawHTML>{ content }</RawHTML>;\n * }\n * ```\n *\n * @param args The hook configuration object.\n *\n * @return The server-side render response object.\n */\nexport function useServerSideRender(\n\targs: UseServerSideRenderArgs\n): ServerSideRenderResponse {\n\tconst [ response, setResponse ] = useState< ServerSideRenderResponse >( {\n\t\tstatus: 'idle',\n\t} );\n\tconst shouldDebounceRef = useRef< boolean >( false );\n\n\tconst {\n\t\tattributes,\n\t\tblock,\n\t\tskipBlockSupportAttributes = false,\n\t\thttpMethod = 'GET',\n\t\turlQueryArgs,\n\t} = args;\n\n\tlet sanitizedAttributes: Record< string, unknown > | null =\n\t\tattributes &&\n\t\t__experimentalSanitizeBlockAttributes( block, attributes );\n\n\tif ( skipBlockSupportAttributes && sanitizedAttributes ) {\n\t\tsanitizedAttributes =\n\t\t\tremoveBlockSupportAttributes( sanitizedAttributes );\n\t}\n\n\t// If httpMethod is 'POST', send the attributes in the request body instead of the URL.\n\t// This allows sending a larger attributes object than in a GET request, where the attributes are in the URL.\n\tconst isPostRequest = 'POST' === httpMethod;\n\tconst urlAttributes = isPostRequest ? null : sanitizedAttributes;\n\tconst path = rendererPath( block, urlAttributes, urlQueryArgs );\n\tconst body = isPostRequest\n\t\t? JSON.stringify( { attributes: sanitizedAttributes ?? null } )\n\t\t: undefined;\n\n\tuseEffect( () => {\n\t\tconst controller = new AbortController();\n\t\tconst debouncedFetch = debounce(\n\t\t\tfunction () {\n\t\t\t\t{\n\t\t\t\t\tsetResponse( { status: 'loading' } );\n\n\t\t\t\t\tapiFetch< { rendered: string } >( {\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tmethod: isPostRequest ? 'POST' : 'GET',\n\t\t\t\t\t\tbody,\n\t\t\t\t\t\theaders: isPostRequest\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t: {},\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t} )\n\t\t\t\t\t\t.then( ( res ) => {\n\t\t\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\t\t\tcontent: res ? res.rendered : '',\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.catch( ( error: unknown ) => {\n\t\t\t\t\t\t\t// The request was aborted, do not update the response.\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\t\t\terror.name === 'AbortError'\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t\t\t\t\t: String( error ),\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.finally( () => {\n\t\t\t\t\t\t\t// Debounce requests after first fetch.\n\t\t\t\t\t\t\tshouldDebounceRef.current = true;\n\t\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t},\n\t\t\tshouldDebounceRef.current ? 500 : 0\n\t\t);\n\n\t\tdebouncedFetch();\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t\tdebouncedFetch.cancel();\n\t\t};\n\t}, [ path, isPostRequest, body ] );\n\n\treturn response;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,qBAAyB;AACzB,qBAA4C;AAC5C,uBAAqB;AACrB,iBAA6B;AAE7B,oBAAsD;AAE/C,SAAS,aACf,OACA,aAA+C,MAC/C,eAA0C,CAAC,GAClC;AACT,aAAO,yBAAc,yBAA0B,KAAM,IAAI;AAAA,IACxD,SAAS;AAAA,IACT,GAAK,SAAS,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IAC7C,GAAG;AAAA,EACJ,CAAE;AACH;AAEO,SAAS,6BACf,YAG4B;AAC5B,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ,IAAI;AAEJ,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ,IAAI,YAAY,SAAS,CAAC;AAE1B,SAAO;AAAA,IACN,GAAG;AAAA,IACH,OAAO;AAAA,EACR;AACD;AAkEO,SAAS,oBACf,MAC2B;AAC3B,QAAM,CAAE,UAAU,WAAY,QAAI,yBAAsC;AAAA,IACvE,QAAQ;AAAA,EACT,CAAE;AACF,QAAM,wBAAoB,uBAAmB,KAAM;AAEnD,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA,6BAA6B;AAAA,IAC7B,aAAa;AAAA,IACb;AAAA,EACD,IAAI;AAEJ,MAAI,sBACH,kBACA,qDAAuC,OAAO,UAAW;AAE1D,MAAK,8BAA8B,qBAAsB;AACxD,0BACC,6BAA8B,mBAAoB;AAAA,EACpD;AAIA,QAAM,gBAAgB,WAAW;AACjC,QAAM,gBAAgB,gBAAgB,OAAO;AAC7C,QAAM,OAAO,aAAc,OAAO,eAAe,YAAa;AAC9D,QAAM,OAAO,gBACV,KAAK,UAAW,EAAE,YAAY,uBAAuB,KAAK,CAAE,IAC5D;AAEH,gCAAW,MAAM;AAChB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,qBAAiB;AAAA,MACtB,WAAY;AACX;AACC,sBAAa,EAAE,QAAQ,UAAU,CAAE;AAEnC,+BAAAA,SAAkC;AAAA,YACjC;AAAA,YACA,QAAQ,gBAAgB,SAAS;AAAA,YACjC;AAAA,YACA,SAAS,gBACN;AAAA,cACA,gBAAgB;AAAA,YAChB,IACA,CAAC;AAAA,YACJ,QAAQ,WAAW;AAAA,UACpB,CAAE,EACA,KAAM,CAAE,QAAS;AACjB,wBAAa;AAAA,cACZ,QAAQ;AAAA,cACR,SAAS,MAAM,IAAI,WAAW;AAAA,YAC/B,CAAE;AAAA,UACH,CAAE,EACD,MAAO,CAAE,UAAoB;AAE7B,gBACC,iBAAiB,SACjB,MAAM,SAAS,cACd;AACD;AAAA,YACD;AAEA,wBAAa;AAAA,cACZ,QAAQ;AAAA,cACR,OACC,iBAAiB,QACd,MAAM,UACN,OAAQ,KAAM;AAAA,YACnB,CAAE;AAAA,UACH,CAAE,EACD,QAAS,MAAM;AAEf,8BAAkB,UAAU;AAAA,UAC7B,CAAE;AAAA,QACJ;AAAA,MACD;AAAA,MACA,kBAAkB,UAAU,MAAM;AAAA,IACnC;AAEA,mBAAe;AAEf,WAAO,MAAM;AACZ,iBAAW,MAAM;AACjB,qBAAe,OAAO;AAAA,IACvB;AAAA,EACD,GAAG,CAAE,MAAM,eAAe,IAAK,CAAE;AAEjC,SAAO;AACR;",
6
6
  "names": ["apiFetch"]
7
7
  }
package/build/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -16,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
16
17
  };
17
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
19
 
19
- // packages/server-side-render/src/index.js
20
+ // packages/server-side-render/src/index.ts
20
21
  var index_exports = {};
21
22
  __export(index_exports, {
22
23
  ServerSideRender: () => import_server_side_render.ServerSideRenderWithPostId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/index.js"],
4
- "sourcesContent": ["/**\n * Internal dependencies\n */\nimport { ServerSideRenderWithPostId } from './server-side-render';\nimport { useServerSideRender } from './hook';\n\n/**\n * A compatibility layer for the `ServerSideRender` component when used with `wp` global namespace.\n *\n * @deprecated Use `ServerSideRender` non-default export instead.\n *\n * @example\n * ```js\n * import ServerSideRender from '@wordpress/server-side-render';\n * ```\n */\nconst ServerSideRenderCompat = ServerSideRenderWithPostId;\nServerSideRenderCompat.ServerSideRender = ServerSideRenderWithPostId;\nServerSideRenderCompat.useServerSideRender = useServerSideRender;\n\nexport { ServerSideRenderWithPostId as ServerSideRender };\nexport { useServerSideRender };\nexport default ServerSideRenderCompat;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,gCAA2C;AAC3C,kBAAoC;AAYpC,IAAM,yBAAyB;AAC/B,uBAAuB,mBAAmB;AAC1C,uBAAuB,sBAAsB;AAI7C,IAAO,gBAAQ;",
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["/**\n * Internal dependencies\n */\nimport { ServerSideRenderWithPostId } from './server-side-render';\nimport { useServerSideRender } from './hook';\n\n/**\n * A compatibility layer for the `ServerSideRender` component when used with `wp` global namespace.\n *\n * @deprecated Use `ServerSideRender` non-default export instead.\n *\n * @example\n * ```js\n * import ServerSideRender from '@wordpress/server-side-render';\n * ```\n */\nconst ServerSideRenderCompat =\n\tServerSideRenderWithPostId as typeof ServerSideRenderWithPostId & {\n\t\tServerSideRender: typeof ServerSideRenderWithPostId;\n\t\tuseServerSideRender: typeof useServerSideRender;\n\t};\n\nServerSideRenderCompat.ServerSideRender = ServerSideRenderWithPostId;\nServerSideRenderCompat.useServerSideRender = useServerSideRender;\n\nexport { ServerSideRenderWithPostId as ServerSideRender };\nexport { useServerSideRender };\nexport default ServerSideRenderCompat;\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,gCAA2C;AAC3C,kBAAoC;AAYpC,IAAM,yBACL;AAKD,uBAAuB,mBAAmB;AAC1C,uBAAuB,sBAAsB;AAI7C,IAAO,gBAAQ;",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -16,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
16
17
  };
17
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
19
 
19
- // packages/server-side-render/src/server-side-render.js
20
+ // packages/server-side-render/src/server-side-render.tsx
20
21
  var server_side_render_exports = {};
21
22
  __export(server_side_render_exports, {
22
23
  ServerSideRender: () => ServerSideRender,
@@ -33,15 +34,20 @@ var EMPTY_OBJECT = {};
33
34
  function DefaultEmptyResponsePlaceholder({ className }) {
34
35
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Placeholder, { className, children: (0, import_i18n.__)("Block rendered as empty.") });
35
36
  }
36
- function DefaultErrorResponsePlaceholder({ message, className }) {
37
+ function DefaultErrorResponsePlaceholder({
38
+ message,
39
+ className
40
+ }) {
37
41
  const errorMessage = (0, import_i18n.sprintf)(
38
42
  // translators: %s: error message describing the problem
39
43
  (0, import_i18n.__)("Error loading block: %s"),
40
- message
44
+ message || (0, import_i18n.__)("Unknown error")
41
45
  );
42
46
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Placeholder, { className, children: errorMessage });
43
47
  }
44
- function DefaultLoadingResponsePlaceholder({ children }) {
48
+ function DefaultLoadingResponsePlaceholder({
49
+ children
50
+ }) {
45
51
  const [showLoader, setShowLoader] = (0, import_element.useState)(false);
46
52
  (0, import_element.useEffect)(() => {
47
53
  const timeout = setTimeout(() => {
@@ -82,7 +88,11 @@ function ServerSideRender(props) {
82
88
  }
83
89
  }, [content]);
84
90
  if (status === "loading") {
85
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingResponsePlaceholder, { ...props, children: !!prevContentRef.current && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_element.RawHTML, { className, children: prevContentRef.current }) });
91
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingResponsePlaceholder, { ...props, children: !!prevContentRef.current && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_element.RawHTML, {
92
+ className,
93
+ // eslint-disable-next-line react-hooks/refs
94
+ children: prevContentRef.current
95
+ }) });
86
96
  }
87
97
  if (status === "success" && !content) {
88
98
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyResponsePlaceholder, { ...props });
@@ -90,14 +100,15 @@ function ServerSideRender(props) {
90
100
  if (status === "error") {
91
101
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorResponsePlaceholder, { message: error, ...props });
92
102
  }
93
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_element.RawHTML, { className, children: content });
103
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_element.RawHTML, { className, children: content || "" });
94
104
  }
95
105
  function ServerSideRenderWithPostId({
96
106
  urlQueryArgs = EMPTY_OBJECT,
97
107
  ...props
98
108
  }) {
99
109
  const currentPostId = (0, import_data.useSelect)((select) => {
100
- const postId = select("core/editor")?.getCurrentPostId();
110
+ const editorStore = select("core/editor");
111
+ const postId = editorStore?.getCurrentPostId?.();
101
112
  return postId && typeof postId === "number" ? postId : null;
102
113
  }, []);
103
114
  const newUrlQueryArgs = (0, import_element.useMemo)(() => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/server-side-render.js"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport {\n\tRawHTML,\n\tuseEffect,\n\tuseState,\n\tuseRef,\n\tuseMemo,\n} from '@wordpress/element';\nimport { __, sprintf } from '@wordpress/i18n';\nimport { Placeholder, Spinner } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport { useServerSideRender } from './hook';\n\nconst EMPTY_OBJECT = {};\n\nfunction DefaultEmptyResponsePlaceholder( { className } ) {\n\treturn (\n\t\t<Placeholder className={ className }>\n\t\t\t{ __( 'Block rendered as empty.' ) }\n\t\t</Placeholder>\n\t);\n}\n\nfunction DefaultErrorResponsePlaceholder( { message, className } ) {\n\tconst errorMessage = sprintf(\n\t\t// translators: %s: error message describing the problem\n\t\t__( 'Error loading block: %s' ),\n\t\tmessage\n\t);\n\treturn <Placeholder className={ className }>{ errorMessage }</Placeholder>;\n}\n\nfunction DefaultLoadingResponsePlaceholder( { children } ) {\n\tconst [ showLoader, setShowLoader ] = useState( false );\n\n\tuseEffect( () => {\n\t\t// Schedule showing the Spinner after 1 second.\n\t\tconst timeout = setTimeout( () => {\n\t\t\tsetShowLoader( true );\n\t\t}, 1000 );\n\t\treturn () => clearTimeout( timeout );\n\t}, [] );\n\n\treturn (\n\t\t<div style={ { position: 'relative' } }>\n\t\t\t{ showLoader && (\n\t\t\t\t<div\n\t\t\t\t\tstyle={ {\n\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\ttop: '50%',\n\t\t\t\t\t\tleft: '50%',\n\t\t\t\t\t\tmarginTop: '-9px',\n\t\t\t\t\t\tmarginLeft: '-9px',\n\t\t\t\t\t} }\n\t\t\t\t>\n\t\t\t\t\t<Spinner />\n\t\t\t\t</div>\n\t\t\t) }\n\t\t\t<div style={ { opacity: showLoader ? '0.3' : 1 } }>\n\t\t\t\t{ children }\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport function ServerSideRender( props ) {\n\tconst prevContentRef = useRef( '' );\n\tconst {\n\t\tclassName,\n\t\tEmptyResponsePlaceholder = DefaultEmptyResponsePlaceholder,\n\t\tErrorResponsePlaceholder = DefaultErrorResponsePlaceholder,\n\t\tLoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder,\n\t\t...restProps\n\t} = props;\n\n\tconst { content, status, error } = useServerSideRender( restProps );\n\n\t// Store the previous successful HTML response to show while loading.\n\tuseEffect( () => {\n\t\tif ( content ) {\n\t\t\tprevContentRef.current = content;\n\t\t}\n\t}, [ content ] );\n\n\tif ( status === 'loading' ) {\n\t\treturn (\n\t\t\t<LoadingResponsePlaceholder { ...props }>\n\t\t\t\t{ !! prevContentRef.current && (\n\t\t\t\t\t<RawHTML className={ className }>\n\t\t\t\t\t\t{ prevContentRef.current }\n\t\t\t\t\t</RawHTML>\n\t\t\t\t) }\n\t\t\t</LoadingResponsePlaceholder>\n\t\t);\n\t}\n\n\tif ( status === 'success' && ! content ) {\n\t\treturn <EmptyResponsePlaceholder { ...props } />;\n\t}\n\n\tif ( status === 'error' ) {\n\t\treturn <ErrorResponsePlaceholder message={ error } { ...props } />;\n\t}\n\n\treturn <RawHTML className={ className }>{ content }</RawHTML>;\n}\n\n/**\n * A component that renders server-side content for blocks.\n *\n * Note: URL query will include the current post ID when applicable.\n * This is useful for blocks that depend on the context of the current post for rendering.\n *\n * @example\n * ```jsx\n * import { ServerSideRender } from '@wordpress/server-side-render';\n * // Legacy import for WordPress 6.8 and earlier\n * // import { default as ServerSideRender } from '@wordpress/server-side-render';\n *\n * function Example() {\n * return (\n * <ServerSideRender\n * block=\"core/archives\"\n * attributes={ { showPostCounts: true } }\n * urlQueryArgs={ { customArg: 'value' } }\n * className=\"custom-class\"\n * />\n * );\n * }\n * ```\n *\n * @param {Object} props Component props.\n * @param {string} props.block The identifier of the block to be serverside rendered.\n * @param {Object} props.attributes The block attributes to be sent to the server for rendering.\n * @param {string} [props.className] Additional classes to apply to the wrapper element.\n * @param {string} [props.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'\n * @param {Object} [props.urlQueryArgs] Additional query arguments to append to the request URL.\n * @param {boolean} [props.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.\n * @param {Function} [props.EmptyResponsePlaceholder] Component rendered when the API response is empty.\n * @param {Function} [props.ErrorResponsePlaceholder] Component rendered when the API response is an error.\n * @param {Function} [props.LoadingResponsePlaceholder] Component rendered while the API request is loading.\n *\n * @return {React.JSX.Element} The rendered server-side content.\n */\nexport function ServerSideRenderWithPostId( {\n\turlQueryArgs = EMPTY_OBJECT,\n\t...props\n} ) {\n\tconst currentPostId = useSelect( ( select ) => {\n\t\t// FIXME: @wordpress/server-side-render should not depend on @wordpress/editor.\n\t\t// It is used by blocks that can be loaded into a *non-post* block editor.\n\t\t// eslint-disable-next-line @wordpress/data-no-store-string-literals\n\t\tconst postId = select( 'core/editor' )?.getCurrentPostId();\n\n\t\t// For templates and template parts we use a custom ID format.\n\t\t// Since they aren't real posts, we don't want to use their ID\n\t\t// for server-side rendering. Since they use a string based ID,\n\t\t// we can assume real post IDs are numbers.\n\t\treturn postId && typeof postId === 'number' ? postId : null;\n\t}, [] );\n\n\tconst newUrlQueryArgs = useMemo( () => {\n\t\tif ( ! currentPostId ) {\n\t\t\treturn urlQueryArgs;\n\t\t}\n\t\treturn {\n\t\t\tpost_id: currentPostId,\n\t\t\t...urlQueryArgs,\n\t\t};\n\t}, [ currentPostId, urlQueryArgs ] );\n\n\treturn <ServerSideRender urlQueryArgs={ newUrlQueryArgs } { ...props } />;\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,qBAMO;AACP,kBAA4B;AAC5B,wBAAqC;AACrC,kBAA0B;AAK1B,kBAAoC;AAMlC;AAJF,IAAM,eAAe,CAAC;AAEtB,SAAS,gCAAiC,EAAE,UAAU,GAAI;AACzD,SACC,4CAAC,iCAAY,WACV,8BAAI,0BAA2B,GAClC;AAEF;AAEA,SAAS,gCAAiC,EAAE,SAAS,UAAU,GAAI;AAClE,QAAM,mBAAe;AAAA;AAAA,QAEpB,gBAAI,yBAA0B;AAAA,IAC9B;AAAA,EACD;AACA,SAAO,4CAAC,iCAAY,WAA0B,wBAAc;AAC7D;AAEA,SAAS,kCAAmC,EAAE,SAAS,GAAI;AAC1D,QAAM,CAAE,YAAY,aAAc,QAAI,yBAAU,KAAM;AAEtD,gCAAW,MAAM;AAEhB,UAAM,UAAU,WAAY,MAAM;AACjC,oBAAe,IAAK;AAAA,IACrB,GAAG,GAAK;AACR,WAAO,MAAM,aAAc,OAAQ;AAAA,EACpC,GAAG,CAAC,CAAE;AAEN,SACC,6CAAC,SAAI,OAAQ,EAAE,UAAU,WAAW,GACjC;AAAA,kBACD;AAAA,MAAC;AAAA;AAAA,QACA,OAAQ;AAAA,UACP,UAAU;AAAA,UACV,KAAK;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,YAAY;AAAA,QACb;AAAA,QAEA,sDAAC,6BAAQ;AAAA;AAAA,IACV;AAAA,IAED,4CAAC,SAAI,OAAQ,EAAE,SAAS,aAAa,QAAQ,EAAE,GAC5C,UACH;AAAA,KACD;AAEF;AAEO,SAAS,iBAAkB,OAAQ;AACzC,QAAM,qBAAiB,uBAAQ,EAAG;AAClC,QAAM;AAAA,IACL;AAAA,IACA,2BAA2B;AAAA,IAC3B,2BAA2B;AAAA,IAC3B,6BAA6B;AAAA,IAC7B,GAAG;AAAA,EACJ,IAAI;AAEJ,QAAM,EAAE,SAAS,QAAQ,MAAM,QAAI,iCAAqB,SAAU;AAGlE,gCAAW,MAAM;AAChB,QAAK,SAAU;AACd,qBAAe,UAAU;AAAA,IAC1B;AAAA,EACD,GAAG,CAAE,OAAQ,CAAE;AAEf,MAAK,WAAW,WAAY;AAC3B,WACC,4CAAC,8BAA6B,GAAG,OAC9B,WAAC,CAAE,eAAe,WACnB,4CAAC,0BAAQ,WACN,yBAAe,SAClB,GAEF;AAAA,EAEF;AAEA,MAAK,WAAW,aAAa,CAAE,SAAU;AACxC,WAAO,4CAAC,4BAA2B,GAAG,OAAQ;AAAA,EAC/C;AAEA,MAAK,WAAW,SAAU;AACzB,WAAO,4CAAC,4BAAyB,SAAU,OAAU,GAAG,OAAQ;AAAA,EACjE;AAEA,SAAO,4CAAC,0BAAQ,WAA0B,mBAAS;AACpD;AAuCO,SAAS,2BAA4B;AAAA,EAC3C,eAAe;AAAA,EACf,GAAG;AACJ,GAAI;AACH,QAAM,oBAAgB,uBAAW,CAAE,WAAY;AAI9C,UAAM,SAAS,OAAQ,aAAc,GAAG,iBAAiB;AAMzD,WAAO,UAAU,OAAO,WAAW,WAAW,SAAS;AAAA,EACxD,GAAG,CAAC,CAAE;AAEN,QAAM,sBAAkB,wBAAS,MAAM;AACtC,QAAK,CAAE,eAAgB;AACtB,aAAO;AAAA,IACR;AACA,WAAO;AAAA,MACN,SAAS;AAAA,MACT,GAAG;AAAA,IACJ;AAAA,EACD,GAAG,CAAE,eAAe,YAAa,CAAE;AAEnC,SAAO,4CAAC,oBAAiB,cAAe,iBAAoB,GAAG,OAAQ;AACxE;",
3
+ "sources": ["../src/server-side-render.tsx"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport {\n\tRawHTML,\n\tuseEffect,\n\tuseState,\n\tuseMemo,\n\tuseRef,\n} from '@wordpress/element';\nimport { __, sprintf } from '@wordpress/i18n';\nimport { Placeholder, Spinner } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport { useServerSideRender } from './hook';\nimport type {\n\tPlaceholderProps,\n\tErrorPlaceholderProps,\n\tLoadingPlaceholderProps,\n\tServerSideRenderProps,\n\tServerSideRenderWithPostIdProps,\n} from './types';\n\nconst EMPTY_OBJECT = {};\n\nfunction DefaultEmptyResponsePlaceholder( { className }: PlaceholderProps ) {\n\treturn (\n\t\t<Placeholder className={ className }>\n\t\t\t{ __( 'Block rendered as empty.' ) }\n\t\t</Placeholder>\n\t);\n}\n\nfunction DefaultErrorResponsePlaceholder( {\n\tmessage,\n\tclassName,\n}: ErrorPlaceholderProps ) {\n\tconst errorMessage = sprintf(\n\t\t// translators: %s: error message describing the problem\n\t\t__( 'Error loading block: %s' ),\n\t\tmessage || __( 'Unknown error' )\n\t);\n\treturn <Placeholder className={ className }>{ errorMessage }</Placeholder>;\n}\n\nfunction DefaultLoadingResponsePlaceholder( {\n\tchildren,\n}: LoadingPlaceholderProps ) {\n\tconst [ showLoader, setShowLoader ] = useState( false );\n\n\tuseEffect( () => {\n\t\t// Schedule showing the Spinner after 1 second.\n\t\tconst timeout = setTimeout( () => {\n\t\t\tsetShowLoader( true );\n\t\t}, 1000 );\n\t\treturn () => clearTimeout( timeout );\n\t}, [] );\n\n\treturn (\n\t\t<div style={ { position: 'relative' } }>\n\t\t\t{ showLoader && (\n\t\t\t\t<div\n\t\t\t\t\tstyle={ {\n\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\ttop: '50%',\n\t\t\t\t\t\tleft: '50%',\n\t\t\t\t\t\tmarginTop: '-9px',\n\t\t\t\t\t\tmarginLeft: '-9px',\n\t\t\t\t\t} }\n\t\t\t\t>\n\t\t\t\t\t<Spinner />\n\t\t\t\t</div>\n\t\t\t) }\n\t\t\t<div style={ { opacity: showLoader ? '0.3' : 1 } }>\n\t\t\t\t{ children }\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport function ServerSideRender( props: ServerSideRenderProps ) {\n\tconst prevContentRef = useRef( '' );\n\tconst {\n\t\tclassName,\n\t\tEmptyResponsePlaceholder = DefaultEmptyResponsePlaceholder,\n\t\tErrorResponsePlaceholder = DefaultErrorResponsePlaceholder,\n\t\tLoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder,\n\t\t...restProps\n\t} = props;\n\n\tconst { content, status, error } = useServerSideRender( restProps );\n\n\t// Store the previous successful HTML response to show while loading.\n\tuseEffect( () => {\n\t\tif ( content ) {\n\t\t\tprevContentRef.current = content;\n\t\t}\n\t}, [ content ] );\n\n\tif ( status === 'loading' ) {\n\t\treturn (\n\t\t\t<LoadingResponsePlaceholder { ...props }>\n\t\t\t\t{ /* eslint-disable-next-line react-hooks/refs */ }\n\t\t\t\t{ !! prevContentRef.current && (\n\t\t\t\t\t<RawHTML className={ className }>\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// eslint-disable-next-line react-hooks/refs\n\t\t\t\t\t\t\tprevContentRef.current\n\t\t\t\t\t\t}\n\t\t\t\t\t</RawHTML>\n\t\t\t\t) }\n\t\t\t</LoadingResponsePlaceholder>\n\t\t);\n\t}\n\n\tif ( status === 'success' && ! content ) {\n\t\treturn <EmptyResponsePlaceholder { ...props } />;\n\t}\n\n\tif ( status === 'error' ) {\n\t\treturn <ErrorResponsePlaceholder message={ error } { ...props } />;\n\t}\n\n\treturn <RawHTML className={ className }>{ content || '' }</RawHTML>;\n}\n\n/**\n * A component that renders server-side content for blocks.\n *\n * Note: URL query will include the current post ID when applicable.\n * This is useful for blocks that depend on the context of the current post for rendering.\n *\n * @example\n * ```jsx\n * import { ServerSideRender } from '@wordpress/server-side-render';\n * // Legacy import for WordPress 6.8 and earlier\n * // import { default as ServerSideRender } from '@wordpress/server-side-render';\n *\n * function Example() {\n * return (\n * <ServerSideRender\n * block=\"core/archives\"\n * attributes={ { showPostCounts: true } }\n * urlQueryArgs={ { customArg: 'value' } }\n * className=\"custom-class\"\n * />\n * );\n * }\n * ```\n *\n * @param props Component props.\n * @param props.urlQueryArgs Additional query arguments to append to the request URL.\n * @return The rendered server-side content.\n */\nexport function ServerSideRenderWithPostId( {\n\turlQueryArgs = EMPTY_OBJECT,\n\t...props\n}: ServerSideRenderWithPostIdProps ) {\n\tconst currentPostId = useSelect( ( select ) => {\n\t\t// FIXME: @wordpress/server-side-render should not depend on @wordpress/editor.\n\t\t// It is used by blocks that can be loaded into a *non-post* block editor.\n\t\t// eslint-disable-next-line @wordpress/data-no-store-string-literals\n\t\tconst editorStore = select( 'core/editor' );\n\t\t// @ts-expect-error - editorStore is not typed in @wordpress/data\n\t\tconst postId = editorStore?.getCurrentPostId?.();\n\n\t\t// For templates and template parts we use a custom ID format.\n\t\t// Since they aren't real posts, we don't want to use their ID\n\t\t// for server-side rendering. Since they use a string based ID,\n\t\t// we can assume real post IDs are numbers.\n\t\treturn postId && typeof postId === 'number' ? postId : null;\n\t}, [] );\n\n\tconst newUrlQueryArgs = useMemo( () => {\n\t\tif ( ! currentPostId ) {\n\t\t\treturn urlQueryArgs;\n\t\t}\n\t\treturn {\n\t\t\tpost_id: currentPostId,\n\t\t\t...urlQueryArgs,\n\t\t};\n\t}, [ currentPostId, urlQueryArgs ] );\n\n\treturn <ServerSideRender urlQueryArgs={ newUrlQueryArgs } { ...props } />;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,qBAMO;AACP,kBAA4B;AAC5B,wBAAqC;AACrC,kBAA0B;AAK1B,kBAAoC;AAalC;AAJF,IAAM,eAAe,CAAC;AAEtB,SAAS,gCAAiC,EAAE,UAAU,GAAsB;AAC3E,SACC,4CAAC,iCAAY,WACV,8BAAI,0BAA2B,GAClC;AAEF;AAEA,SAAS,gCAAiC;AAAA,EACzC;AAAA,EACA;AACD,GAA2B;AAC1B,QAAM,mBAAe;AAAA;AAAA,QAEpB,gBAAI,yBAA0B;AAAA,IAC9B,eAAW,gBAAI,eAAgB;AAAA,EAChC;AACA,SAAO,4CAAC,iCAAY,WAA0B,wBAAc;AAC7D;AAEA,SAAS,kCAAmC;AAAA,EAC3C;AACD,GAA6B;AAC5B,QAAM,CAAE,YAAY,aAAc,QAAI,yBAAU,KAAM;AAEtD,gCAAW,MAAM;AAEhB,UAAM,UAAU,WAAY,MAAM;AACjC,oBAAe,IAAK;AAAA,IACrB,GAAG,GAAK;AACR,WAAO,MAAM,aAAc,OAAQ;AAAA,EACpC,GAAG,CAAC,CAAE;AAEN,SACC,6CAAC,SAAI,OAAQ,EAAE,UAAU,WAAW,GACjC;AAAA,kBACD;AAAA,MAAC;AAAA;AAAA,QACA,OAAQ;AAAA,UACP,UAAU;AAAA,UACV,KAAK;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,YAAY;AAAA,QACb;AAAA,QAEA,sDAAC,6BAAQ;AAAA;AAAA,IACV;AAAA,IAED,4CAAC,SAAI,OAAQ,EAAE,SAAS,aAAa,QAAQ,EAAE,GAC5C,UACH;AAAA,KACD;AAEF;AAEO,SAAS,iBAAkB,OAA+B;AAChE,QAAM,qBAAiB,uBAAQ,EAAG;AAClC,QAAM;AAAA,IACL;AAAA,IACA,2BAA2B;AAAA,IAC3B,2BAA2B;AAAA,IAC3B,6BAA6B;AAAA,IAC7B,GAAG;AAAA,EACJ,IAAI;AAEJ,QAAM,EAAE,SAAS,QAAQ,MAAM,QAAI,iCAAqB,SAAU;AAGlE,gCAAW,MAAM;AAChB,QAAK,SAAU;AACd,qBAAe,UAAU;AAAA,IAC1B;AAAA,EACD,GAAG,CAAE,OAAQ,CAAE;AAEf,MAAK,WAAW,WAAY;AAC3B,WACC,4CAAC,8BAA6B,GAAG,OAE9B,WAAC,CAAE,eAAe,WACnB,4CAAC;AAAA,MAAQ;AAAA;AAAA,MAGP,yBAAe;AAAA,KAEjB,GAEF;AAAA,EAEF;AAEA,MAAK,WAAW,aAAa,CAAE,SAAU;AACxC,WAAO,4CAAC,4BAA2B,GAAG,OAAQ;AAAA,EAC/C;AAEA,MAAK,WAAW,SAAU;AACzB,WAAO,4CAAC,4BAAyB,SAAU,OAAU,GAAG,OAAQ;AAAA,EACjE;AAEA,SAAO,4CAAC,0BAAQ,WAA0B,qBAAW,IAAI;AAC1D;AA8BO,SAAS,2BAA4B;AAAA,EAC3C,eAAe;AAAA,EACf,GAAG;AACJ,GAAqC;AACpC,QAAM,oBAAgB,uBAAW,CAAE,WAAY;AAI9C,UAAM,cAAc,OAAQ,aAAc;AAE1C,UAAM,SAAS,aAAa,mBAAmB;AAM/C,WAAO,UAAU,OAAO,WAAW,WAAW,SAAS;AAAA,EACxD,GAAG,CAAC,CAAE;AAEN,QAAM,sBAAkB,wBAAS,MAAM;AACtC,QAAK,CAAE,eAAgB;AACtB,aAAO;AAAA,IACR;AACA,WAAO;AAAA,MACN,SAAS;AAAA,MACT,GAAG;AAAA,IACJ;AAAA,EACD,GAAG,CAAE,eAAe,YAAa,CAAE;AAEnC,SAAO,4CAAC,oBAAiB,cAAe,iBAAoB,GAAG,OAAQ;AACxE;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // packages/server-side-render/src/types.ts
17
+ var types_exports = {};
18
+ module.exports = __toCommonJS(types_exports);
19
+ //# sourceMappingURL=types.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/types.ts"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type React from 'react';\n\n/**\n * Internal dependencies\n */\nimport type { UseServerSideRenderArgs } from './hook';\n\nexport interface PlaceholderProps {\n\t/** Additional classes to apply to the wrapper element. */\n\tclassName?: string;\n}\n\nexport interface ErrorPlaceholderProps extends PlaceholderProps {\n\t/** Error message describing the problem. */\n\tmessage?: string;\n}\n\nexport interface LoadingPlaceholderProps {\n\tchildren?: React.ReactNode;\n}\n\nexport interface ServerSideRenderProps extends UseServerSideRenderArgs {\n\t/** Additional classes to apply to the wrapper element. */\n\tclassName?: string;\n\t/** Component rendered when the API response is empty. */\n\tEmptyResponsePlaceholder?: React.ComponentType< PlaceholderProps >;\n\t/** Component rendered when the API response is an error. */\n\tErrorResponsePlaceholder?: React.ComponentType< ErrorPlaceholderProps >;\n\t/** Component rendered while the API request is loading. */\n\tLoadingResponsePlaceholder?: React.ComponentType< LoadingPlaceholderProps >;\n}\n\nexport interface ServerSideRenderWithPostIdProps\n\textends Omit< ServerSideRenderProps, 'urlQueryArgs' > {\n\t/** Additional query arguments to append to the request URL. */\n\turlQueryArgs?: Record< string, unknown >;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
6
+ "names": []
7
+ }
@@ -1,4 +1,4 @@
1
- // packages/server-side-render/src/hook.js
1
+ // packages/server-side-render/src/hook.ts
2
2
  import { debounce } from "@wordpress/compose";
3
3
  import { useEffect, useState, useRef } from "@wordpress/element";
4
4
  import apiFetch from "@wordpress/api-fetch";
@@ -37,7 +37,9 @@ function removeBlockSupportAttributes(attributes) {
37
37
  };
38
38
  }
39
39
  function useServerSideRender(args) {
40
- const [response, setResponse] = useState({ status: "idle" });
40
+ const [response, setResponse] = useState({
41
+ status: "idle"
42
+ });
41
43
  const shouldDebounceRef = useRef(false);
42
44
  const {
43
45
  attributes,
@@ -47,7 +49,7 @@ function useServerSideRender(args) {
47
49
  urlQueryArgs
48
50
  } = args;
49
51
  let sanitizedAttributes = attributes && __experimentalSanitizeBlockAttributes(block, attributes);
50
- if (skipBlockSupportAttributes) {
52
+ if (skipBlockSupportAttributes && sanitizedAttributes) {
51
53
  sanitizedAttributes = removeBlockSupportAttributes(sanitizedAttributes);
52
54
  }
53
55
  const isPostRequest = "POST" === httpMethod;
@@ -74,12 +76,12 @@ function useServerSideRender(args) {
74
76
  content: res ? res.rendered : ""
75
77
  });
76
78
  }).catch((error) => {
77
- if (error.name === "AbortError") {
79
+ if (error instanceof Error && error.name === "AbortError") {
78
80
  return;
79
81
  }
80
82
  setResponse({
81
83
  status: "error",
82
- error: error.message
84
+ error: error instanceof Error ? error.message : String(error)
83
85
  });
84
86
  }).finally(() => {
85
87
  shouldDebounceRef.current = true;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/hook.js"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { debounce } from '@wordpress/compose';\nimport { useEffect, useState, useRef } from '@wordpress/element';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\nimport { __experimentalSanitizeBlockAttributes } from '@wordpress/blocks';\n\nexport function rendererPath( block, attributes = null, urlQueryArgs = {} ) {\n\treturn addQueryArgs( `/wp/v2/block-renderer/${ block }`, {\n\t\tcontext: 'edit',\n\t\t...( null !== attributes ? { attributes } : {} ),\n\t\t...urlQueryArgs,\n\t} );\n}\n\nexport function removeBlockSupportAttributes( attributes ) {\n\tconst {\n\t\tbackgroundColor,\n\t\tborderColor,\n\t\tfontFamily,\n\t\tfontSize,\n\t\tgradient,\n\t\ttextColor,\n\t\tclassName,\n\t\t...restAttributes\n\t} = attributes;\n\n\tconst {\n\t\tborder,\n\t\tcolor,\n\t\telements,\n\t\tshadow,\n\t\tspacing,\n\t\ttypography,\n\t\t...restStyles\n\t} = attributes?.style || {};\n\n\treturn {\n\t\t...restAttributes,\n\t\tstyle: restStyles,\n\t};\n}\n\n/**\n * @typedef {Object} ServerSideRenderResponse\n * @property {string} status - The current request status: 'idle', 'loading', 'success', or 'error'.\n * @property {string} [content] - The rendered block content (available when status is 'success').\n * @property {string} [error] - The error message (available when status is 'error').\n */\n\n/**\n * A hook for server-side rendering a preview of dynamic blocks to display in the editor.\n *\n * Handles fetching server-rendered previews for blocks, managing loading states,\n * and automatically debouncing requests to prevent excessive API calls. It supports both\n * GET and POST requests, with POST requests used for larger attribute payloads.\n *\n * @example\n * Basic usage:\n *\n * ```jsx\n * import { RawHTML } from '@wordpress/element';\n * import { useServerSideRender } from '@wordpress/server-side-render';\n *\n * function MyServerSideRender( { attributes, block } ) {\n * const { content, status, error } = useServerSideRender( {\n * attributes,\n * block,\n * } );\n *\n * if ( status === 'loading' ) {\n * return <div>Loading...</div>;\n * }\n *\n * if ( status === 'error' ) {\n * return <div>Error: { error }</div>;\n * }\n *\n * return <RawHTML>{ content }</RawHTML>;\n * }\n * ```\n *\n * @param {Object} args The hook configuration object.\n * @param {Object} args.attributes The block attributes to be sent to the server for rendering.\n * @param {string} args.block The identifier of the block to be serverside rendered. Example: 'core/archives'.\n * @param {boolean} [args.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.\n * @param {string} [args.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'.\n * @param {Object} [args.urlQueryArgs] Additional query arguments to append to the request URL.\n *\n * @return {ServerSideRenderResponse} The server-side render response object.\n */\nexport function useServerSideRender( args ) {\n\tconst [ response, setResponse ] = useState( { status: 'idle' } );\n\tconst shouldDebounceRef = useRef( false );\n\n\tconst {\n\t\tattributes,\n\t\tblock,\n\t\tskipBlockSupportAttributes = false,\n\t\thttpMethod = 'GET',\n\t\turlQueryArgs,\n\t} = args;\n\n\tlet sanitizedAttributes =\n\t\tattributes &&\n\t\t__experimentalSanitizeBlockAttributes( block, attributes );\n\n\tif ( skipBlockSupportAttributes ) {\n\t\tsanitizedAttributes =\n\t\t\tremoveBlockSupportAttributes( sanitizedAttributes );\n\t}\n\n\t// If httpMethod is 'POST', send the attributes in the request body instead of the URL.\n\t// This allows sending a larger attributes object than in a GET request, where the attributes are in the URL.\n\tconst isPostRequest = 'POST' === httpMethod;\n\tconst urlAttributes = isPostRequest ? null : sanitizedAttributes;\n\tconst path = rendererPath( block, urlAttributes, urlQueryArgs );\n\tconst body = isPostRequest\n\t\t? JSON.stringify( { attributes: sanitizedAttributes ?? null } )\n\t\t: undefined;\n\n\tuseEffect( () => {\n\t\tconst controller = new AbortController();\n\t\tconst debouncedFetch = debounce(\n\t\t\tfunction () {\n\t\t\t\t{\n\t\t\t\t\tsetResponse( { status: 'loading' } );\n\n\t\t\t\t\tapiFetch( {\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tmethod: isPostRequest ? 'POST' : 'GET',\n\t\t\t\t\t\tbody,\n\t\t\t\t\t\theaders: isPostRequest\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t: {},\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t} )\n\t\t\t\t\t\t.then( ( res ) => {\n\t\t\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\t\t\tcontent: res ? res.rendered : '',\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.catch( ( error ) => {\n\t\t\t\t\t\t\t// The request was aborted, do not update the response.\n\t\t\t\t\t\t\tif ( error.name === 'AbortError' ) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\t\terror: error.message,\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.finally( () => {\n\t\t\t\t\t\t\t// Debounce requests after first fetch.\n\t\t\t\t\t\t\tshouldDebounceRef.current = true;\n\t\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t},\n\t\t\tshouldDebounceRef.current ? 500 : 0\n\t\t);\n\n\t\tdebouncedFetch();\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t\tdebouncedFetch.cancel();\n\t\t};\n\t}, [ path, isPostRequest, body ] );\n\n\treturn response;\n}\n"],
5
- "mappings": ";AAGA,SAAS,gBAAgB;AACzB,SAAS,WAAW,UAAU,cAAc;AAC5C,OAAO,cAAc;AACrB,SAAS,oBAAoB;AAC7B,SAAS,6CAA6C;AAE/C,SAAS,aAAc,OAAO,aAAa,MAAM,eAAe,CAAC,GAAI;AAC3E,SAAO,aAAc,yBAA0B,KAAM,IAAI;AAAA,IACxD,SAAS;AAAA,IACT,GAAK,SAAS,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IAC7C,GAAG;AAAA,EACJ,CAAE;AACH;AAEO,SAAS,6BAA8B,YAAa;AAC1D,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ,IAAI;AAEJ,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ,IAAI,YAAY,SAAS,CAAC;AAE1B,SAAO;AAAA,IACN,GAAG;AAAA,IACH,OAAO;AAAA,EACR;AACD;AAkDO,SAAS,oBAAqB,MAAO;AAC3C,QAAM,CAAE,UAAU,WAAY,IAAI,SAAU,EAAE,QAAQ,OAAO,CAAE;AAC/D,QAAM,oBAAoB,OAAQ,KAAM;AAExC,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA,6BAA6B;AAAA,IAC7B,aAAa;AAAA,IACb;AAAA,EACD,IAAI;AAEJ,MAAI,sBACH,cACA,sCAAuC,OAAO,UAAW;AAE1D,MAAK,4BAA6B;AACjC,0BACC,6BAA8B,mBAAoB;AAAA,EACpD;AAIA,QAAM,gBAAgB,WAAW;AACjC,QAAM,gBAAgB,gBAAgB,OAAO;AAC7C,QAAM,OAAO,aAAc,OAAO,eAAe,YAAa;AAC9D,QAAM,OAAO,gBACV,KAAK,UAAW,EAAE,YAAY,uBAAuB,KAAK,CAAE,IAC5D;AAEH,YAAW,MAAM;AAChB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,iBAAiB;AAAA,MACtB,WAAY;AACX;AACC,sBAAa,EAAE,QAAQ,UAAU,CAAE;AAEnC,mBAAU;AAAA,YACT;AAAA,YACA,QAAQ,gBAAgB,SAAS;AAAA,YACjC;AAAA,YACA,SAAS,gBACN;AAAA,cACA,gBAAgB;AAAA,YAChB,IACA,CAAC;AAAA,YACJ,QAAQ,WAAW;AAAA,UACpB,CAAE,EACA,KAAM,CAAE,QAAS;AACjB,wBAAa;AAAA,cACZ,QAAQ;AAAA,cACR,SAAS,MAAM,IAAI,WAAW;AAAA,YAC/B,CAAE;AAAA,UACH,CAAE,EACD,MAAO,CAAE,UAAW;AAEpB,gBAAK,MAAM,SAAS,cAAe;AAClC;AAAA,YACD;AAEA,wBAAa;AAAA,cACZ,QAAQ;AAAA,cACR,OAAO,MAAM;AAAA,YACd,CAAE;AAAA,UACH,CAAE,EACD,QAAS,MAAM;AAEf,8BAAkB,UAAU;AAAA,UAC7B,CAAE;AAAA,QACJ;AAAA,MACD;AAAA,MACA,kBAAkB,UAAU,MAAM;AAAA,IACnC;AAEA,mBAAe;AAEf,WAAO,MAAM;AACZ,iBAAW,MAAM;AACjB,qBAAe,OAAO;AAAA,IACvB;AAAA,EACD,GAAG,CAAE,MAAM,eAAe,IAAK,CAAE;AAEjC,SAAO;AACR;",
3
+ "sources": ["../src/hook.ts"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { debounce } from '@wordpress/compose';\nimport { useEffect, useState, useRef } from '@wordpress/element';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\n\nimport { __experimentalSanitizeBlockAttributes } from '@wordpress/blocks';\n\nexport function rendererPath(\n\tblock: string,\n\tattributes: Record< string, unknown > | null = null,\n\turlQueryArgs: Record< string, unknown > = {}\n): string {\n\treturn addQueryArgs( `/wp/v2/block-renderer/${ block }`, {\n\t\tcontext: 'edit',\n\t\t...( null !== attributes ? { attributes } : {} ),\n\t\t...urlQueryArgs,\n\t} );\n}\n\nexport function removeBlockSupportAttributes(\n\tattributes: Record< string, unknown > & {\n\t\tstyle?: Record< string, unknown >;\n\t}\n): Record< string, unknown > {\n\tconst {\n\t\tbackgroundColor,\n\t\tborderColor,\n\t\tfontFamily,\n\t\tfontSize,\n\t\tgradient,\n\t\ttextColor,\n\t\tclassName,\n\t\t...restAttributes\n\t} = attributes;\n\n\tconst {\n\t\tborder,\n\t\tcolor,\n\t\telements,\n\t\tshadow,\n\t\tspacing,\n\t\ttypography,\n\t\t...restStyles\n\t} = attributes?.style || {};\n\n\treturn {\n\t\t...restAttributes,\n\t\tstyle: restStyles,\n\t};\n}\n\n/**\n * Server-side render response object.\n */\nexport interface ServerSideRenderResponse {\n\t/** The current request status: 'idle', 'loading', 'success', or 'error'. */\n\tstatus: 'idle' | 'loading' | 'success' | 'error';\n\t/** The rendered block content (available when status is 'success'). */\n\tcontent?: string;\n\t/** The error message (available when status is 'error'). */\n\terror?: string;\n}\n\n/**\n * Configuration object for the useServerSideRender hook.\n */\nexport interface UseServerSideRenderArgs {\n\t/** The block attributes to be sent to the server for rendering. */\n\tattributes: Record< string, unknown >;\n\t/** The identifier of the block to be serverside rendered. Example: 'core/archives'. */\n\tblock: string;\n\t/** Whether to remove block support attributes before sending. */\n\tskipBlockSupportAttributes?: boolean;\n\t/** The HTTP method to use ('GET' or 'POST'). Default is 'GET'. */\n\thttpMethod?: 'GET' | 'POST';\n\t/** Additional query arguments to append to the request URL. */\n\turlQueryArgs?: Record< string, unknown >;\n}\n\n/**\n * A hook for server-side rendering a preview of dynamic blocks to display in the editor.\n *\n * Handles fetching server-rendered previews for blocks, managing loading states,\n * and automatically debouncing requests to prevent excessive API calls. It supports both\n * GET and POST requests, with POST requests used for larger attribute payloads.\n *\n * @example\n * Basic usage:\n *\n * ```jsx\n * import { RawHTML } from '@wordpress/element';\n * import { useServerSideRender } from '@wordpress/server-side-render';\n *\n * function MyServerSideRender( { attributes, block } ) {\n * const { content, status, error } = useServerSideRender( {\n * attributes,\n * block,\n * } );\n *\n * if ( status === 'loading' ) {\n * return <div>Loading...</div>;\n * }\n *\n * if ( status === 'error' ) {\n * return <div>Error: { error }</div>;\n * }\n *\n * return <RawHTML>{ content }</RawHTML>;\n * }\n * ```\n *\n * @param args The hook configuration object.\n *\n * @return The server-side render response object.\n */\nexport function useServerSideRender(\n\targs: UseServerSideRenderArgs\n): ServerSideRenderResponse {\n\tconst [ response, setResponse ] = useState< ServerSideRenderResponse >( {\n\t\tstatus: 'idle',\n\t} );\n\tconst shouldDebounceRef = useRef< boolean >( false );\n\n\tconst {\n\t\tattributes,\n\t\tblock,\n\t\tskipBlockSupportAttributes = false,\n\t\thttpMethod = 'GET',\n\t\turlQueryArgs,\n\t} = args;\n\n\tlet sanitizedAttributes: Record< string, unknown > | null =\n\t\tattributes &&\n\t\t__experimentalSanitizeBlockAttributes( block, attributes );\n\n\tif ( skipBlockSupportAttributes && sanitizedAttributes ) {\n\t\tsanitizedAttributes =\n\t\t\tremoveBlockSupportAttributes( sanitizedAttributes );\n\t}\n\n\t// If httpMethod is 'POST', send the attributes in the request body instead of the URL.\n\t// This allows sending a larger attributes object than in a GET request, where the attributes are in the URL.\n\tconst isPostRequest = 'POST' === httpMethod;\n\tconst urlAttributes = isPostRequest ? null : sanitizedAttributes;\n\tconst path = rendererPath( block, urlAttributes, urlQueryArgs );\n\tconst body = isPostRequest\n\t\t? JSON.stringify( { attributes: sanitizedAttributes ?? null } )\n\t\t: undefined;\n\n\tuseEffect( () => {\n\t\tconst controller = new AbortController();\n\t\tconst debouncedFetch = debounce(\n\t\t\tfunction () {\n\t\t\t\t{\n\t\t\t\t\tsetResponse( { status: 'loading' } );\n\n\t\t\t\t\tapiFetch< { rendered: string } >( {\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tmethod: isPostRequest ? 'POST' : 'GET',\n\t\t\t\t\t\tbody,\n\t\t\t\t\t\theaders: isPostRequest\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t: {},\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t} )\n\t\t\t\t\t\t.then( ( res ) => {\n\t\t\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\t\t\tcontent: res ? res.rendered : '',\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.catch( ( error: unknown ) => {\n\t\t\t\t\t\t\t// The request was aborted, do not update the response.\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\t\t\terror.name === 'AbortError'\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsetResponse( {\n\t\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t\t\t\t\t: String( error ),\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.finally( () => {\n\t\t\t\t\t\t\t// Debounce requests after first fetch.\n\t\t\t\t\t\t\tshouldDebounceRef.current = true;\n\t\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t},\n\t\t\tshouldDebounceRef.current ? 500 : 0\n\t\t);\n\n\t\tdebouncedFetch();\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t\tdebouncedFetch.cancel();\n\t\t};\n\t}, [ path, isPostRequest, body ] );\n\n\treturn response;\n}\n"],
5
+ "mappings": ";AAGA,SAAS,gBAAgB;AACzB,SAAS,WAAW,UAAU,cAAc;AAC5C,OAAO,cAAc;AACrB,SAAS,oBAAoB;AAE7B,SAAS,6CAA6C;AAE/C,SAAS,aACf,OACA,aAA+C,MAC/C,eAA0C,CAAC,GAClC;AACT,SAAO,aAAc,yBAA0B,KAAM,IAAI;AAAA,IACxD,SAAS;AAAA,IACT,GAAK,SAAS,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IAC7C,GAAG;AAAA,EACJ,CAAE;AACH;AAEO,SAAS,6BACf,YAG4B;AAC5B,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ,IAAI;AAEJ,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ,IAAI,YAAY,SAAS,CAAC;AAE1B,SAAO;AAAA,IACN,GAAG;AAAA,IACH,OAAO;AAAA,EACR;AACD;AAkEO,SAAS,oBACf,MAC2B;AAC3B,QAAM,CAAE,UAAU,WAAY,IAAI,SAAsC;AAAA,IACvE,QAAQ;AAAA,EACT,CAAE;AACF,QAAM,oBAAoB,OAAmB,KAAM;AAEnD,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA,6BAA6B;AAAA,IAC7B,aAAa;AAAA,IACb;AAAA,EACD,IAAI;AAEJ,MAAI,sBACH,cACA,sCAAuC,OAAO,UAAW;AAE1D,MAAK,8BAA8B,qBAAsB;AACxD,0BACC,6BAA8B,mBAAoB;AAAA,EACpD;AAIA,QAAM,gBAAgB,WAAW;AACjC,QAAM,gBAAgB,gBAAgB,OAAO;AAC7C,QAAM,OAAO,aAAc,OAAO,eAAe,YAAa;AAC9D,QAAM,OAAO,gBACV,KAAK,UAAW,EAAE,YAAY,uBAAuB,KAAK,CAAE,IAC5D;AAEH,YAAW,MAAM;AAChB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,iBAAiB;AAAA,MACtB,WAAY;AACX;AACC,sBAAa,EAAE,QAAQ,UAAU,CAAE;AAEnC,mBAAkC;AAAA,YACjC;AAAA,YACA,QAAQ,gBAAgB,SAAS;AAAA,YACjC;AAAA,YACA,SAAS,gBACN;AAAA,cACA,gBAAgB;AAAA,YAChB,IACA,CAAC;AAAA,YACJ,QAAQ,WAAW;AAAA,UACpB,CAAE,EACA,KAAM,CAAE,QAAS;AACjB,wBAAa;AAAA,cACZ,QAAQ;AAAA,cACR,SAAS,MAAM,IAAI,WAAW;AAAA,YAC/B,CAAE;AAAA,UACH,CAAE,EACD,MAAO,CAAE,UAAoB;AAE7B,gBACC,iBAAiB,SACjB,MAAM,SAAS,cACd;AACD;AAAA,YACD;AAEA,wBAAa;AAAA,cACZ,QAAQ;AAAA,cACR,OACC,iBAAiB,QACd,MAAM,UACN,OAAQ,KAAM;AAAA,YACnB,CAAE;AAAA,UACH,CAAE,EACD,QAAS,MAAM;AAEf,8BAAkB,UAAU;AAAA,UAC7B,CAAE;AAAA,QACJ;AAAA,MACD;AAAA,MACA,kBAAkB,UAAU,MAAM;AAAA,IACnC;AAEA,mBAAe;AAEf,WAAO,MAAM;AACZ,iBAAW,MAAM;AACjB,qBAAe,OAAO;AAAA,IACvB;AAAA,EACD,GAAG,CAAE,MAAM,eAAe,IAAK,CAAE;AAEjC,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- // packages/server-side-render/src/index.js
1
+ // packages/server-side-render/src/index.ts
2
2
  import { ServerSideRenderWithPostId } from "./server-side-render.mjs";
3
3
  import { useServerSideRender } from "./hook.mjs";
4
4
  var ServerSideRenderCompat = ServerSideRenderWithPostId;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/index.js"],
4
- "sourcesContent": ["/**\n * Internal dependencies\n */\nimport { ServerSideRenderWithPostId } from './server-side-render';\nimport { useServerSideRender } from './hook';\n\n/**\n * A compatibility layer for the `ServerSideRender` component when used with `wp` global namespace.\n *\n * @deprecated Use `ServerSideRender` non-default export instead.\n *\n * @example\n * ```js\n * import ServerSideRender from '@wordpress/server-side-render';\n * ```\n */\nconst ServerSideRenderCompat = ServerSideRenderWithPostId;\nServerSideRenderCompat.ServerSideRender = ServerSideRenderWithPostId;\nServerSideRenderCompat.useServerSideRender = useServerSideRender;\n\nexport { ServerSideRenderWithPostId as ServerSideRender };\nexport { useServerSideRender };\nexport default ServerSideRenderCompat;\n"],
5
- "mappings": ";AAGA,SAAS,kCAAkC;AAC3C,SAAS,2BAA2B;AAYpC,IAAM,yBAAyB;AAC/B,uBAAuB,mBAAmB;AAC1C,uBAAuB,sBAAsB;AAI7C,IAAO,gBAAQ;",
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["/**\n * Internal dependencies\n */\nimport { ServerSideRenderWithPostId } from './server-side-render';\nimport { useServerSideRender } from './hook';\n\n/**\n * A compatibility layer for the `ServerSideRender` component when used with `wp` global namespace.\n *\n * @deprecated Use `ServerSideRender` non-default export instead.\n *\n * @example\n * ```js\n * import ServerSideRender from '@wordpress/server-side-render';\n * ```\n */\nconst ServerSideRenderCompat =\n\tServerSideRenderWithPostId as typeof ServerSideRenderWithPostId & {\n\t\tServerSideRender: typeof ServerSideRenderWithPostId;\n\t\tuseServerSideRender: typeof useServerSideRender;\n\t};\n\nServerSideRenderCompat.ServerSideRender = ServerSideRenderWithPostId;\nServerSideRenderCompat.useServerSideRender = useServerSideRender;\n\nexport { ServerSideRenderWithPostId as ServerSideRender };\nexport { useServerSideRender };\nexport default ServerSideRenderCompat;\n"],
5
+ "mappings": ";AAGA,SAAS,kCAAkC;AAC3C,SAAS,2BAA2B;AAYpC,IAAM,yBACL;AAKD,uBAAuB,mBAAmB;AAC1C,uBAAuB,sBAAsB;AAI7C,IAAO,gBAAQ;",
6
6
  "names": []
7
7
  }
@@ -1,10 +1,10 @@
1
- // packages/server-side-render/src/server-side-render.js
1
+ // packages/server-side-render/src/server-side-render.tsx
2
2
  import {
3
3
  RawHTML,
4
4
  useEffect,
5
5
  useState,
6
- useRef,
7
- useMemo
6
+ useMemo,
7
+ useRef
8
8
  } from "@wordpress/element";
9
9
  import { __, sprintf } from "@wordpress/i18n";
10
10
  import { Placeholder, Spinner } from "@wordpress/components";
@@ -15,15 +15,20 @@ var EMPTY_OBJECT = {};
15
15
  function DefaultEmptyResponsePlaceholder({ className }) {
16
16
  return /* @__PURE__ */ jsx(Placeholder, { className, children: __("Block rendered as empty.") });
17
17
  }
18
- function DefaultErrorResponsePlaceholder({ message, className }) {
18
+ function DefaultErrorResponsePlaceholder({
19
+ message,
20
+ className
21
+ }) {
19
22
  const errorMessage = sprintf(
20
23
  // translators: %s: error message describing the problem
21
24
  __("Error loading block: %s"),
22
- message
25
+ message || __("Unknown error")
23
26
  );
24
27
  return /* @__PURE__ */ jsx(Placeholder, { className, children: errorMessage });
25
28
  }
26
- function DefaultLoadingResponsePlaceholder({ children }) {
29
+ function DefaultLoadingResponsePlaceholder({
30
+ children
31
+ }) {
27
32
  const [showLoader, setShowLoader] = useState(false);
28
33
  useEffect(() => {
29
34
  const timeout = setTimeout(() => {
@@ -64,7 +69,11 @@ function ServerSideRender(props) {
64
69
  }
65
70
  }, [content]);
66
71
  if (status === "loading") {
67
- return /* @__PURE__ */ jsx(LoadingResponsePlaceholder, { ...props, children: !!prevContentRef.current && /* @__PURE__ */ jsx(RawHTML, { className, children: prevContentRef.current }) });
72
+ return /* @__PURE__ */ jsx(LoadingResponsePlaceholder, { ...props, children: !!prevContentRef.current && /* @__PURE__ */ jsx(RawHTML, {
73
+ className,
74
+ // eslint-disable-next-line react-hooks/refs
75
+ children: prevContentRef.current
76
+ }) });
68
77
  }
69
78
  if (status === "success" && !content) {
70
79
  return /* @__PURE__ */ jsx(EmptyResponsePlaceholder, { ...props });
@@ -72,14 +81,15 @@ function ServerSideRender(props) {
72
81
  if (status === "error") {
73
82
  return /* @__PURE__ */ jsx(ErrorResponsePlaceholder, { message: error, ...props });
74
83
  }
75
- return /* @__PURE__ */ jsx(RawHTML, { className, children: content });
84
+ return /* @__PURE__ */ jsx(RawHTML, { className, children: content || "" });
76
85
  }
77
86
  function ServerSideRenderWithPostId({
78
87
  urlQueryArgs = EMPTY_OBJECT,
79
88
  ...props
80
89
  }) {
81
90
  const currentPostId = useSelect((select) => {
82
- const postId = select("core/editor")?.getCurrentPostId();
91
+ const editorStore = select("core/editor");
92
+ const postId = editorStore?.getCurrentPostId?.();
83
93
  return postId && typeof postId === "number" ? postId : null;
84
94
  }, []);
85
95
  const newUrlQueryArgs = useMemo(() => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/server-side-render.js"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport {\n\tRawHTML,\n\tuseEffect,\n\tuseState,\n\tuseRef,\n\tuseMemo,\n} from '@wordpress/element';\nimport { __, sprintf } from '@wordpress/i18n';\nimport { Placeholder, Spinner } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport { useServerSideRender } from './hook';\n\nconst EMPTY_OBJECT = {};\n\nfunction DefaultEmptyResponsePlaceholder( { className } ) {\n\treturn (\n\t\t<Placeholder className={ className }>\n\t\t\t{ __( 'Block rendered as empty.' ) }\n\t\t</Placeholder>\n\t);\n}\n\nfunction DefaultErrorResponsePlaceholder( { message, className } ) {\n\tconst errorMessage = sprintf(\n\t\t// translators: %s: error message describing the problem\n\t\t__( 'Error loading block: %s' ),\n\t\tmessage\n\t);\n\treturn <Placeholder className={ className }>{ errorMessage }</Placeholder>;\n}\n\nfunction DefaultLoadingResponsePlaceholder( { children } ) {\n\tconst [ showLoader, setShowLoader ] = useState( false );\n\n\tuseEffect( () => {\n\t\t// Schedule showing the Spinner after 1 second.\n\t\tconst timeout = setTimeout( () => {\n\t\t\tsetShowLoader( true );\n\t\t}, 1000 );\n\t\treturn () => clearTimeout( timeout );\n\t}, [] );\n\n\treturn (\n\t\t<div style={ { position: 'relative' } }>\n\t\t\t{ showLoader && (\n\t\t\t\t<div\n\t\t\t\t\tstyle={ {\n\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\ttop: '50%',\n\t\t\t\t\t\tleft: '50%',\n\t\t\t\t\t\tmarginTop: '-9px',\n\t\t\t\t\t\tmarginLeft: '-9px',\n\t\t\t\t\t} }\n\t\t\t\t>\n\t\t\t\t\t<Spinner />\n\t\t\t\t</div>\n\t\t\t) }\n\t\t\t<div style={ { opacity: showLoader ? '0.3' : 1 } }>\n\t\t\t\t{ children }\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport function ServerSideRender( props ) {\n\tconst prevContentRef = useRef( '' );\n\tconst {\n\t\tclassName,\n\t\tEmptyResponsePlaceholder = DefaultEmptyResponsePlaceholder,\n\t\tErrorResponsePlaceholder = DefaultErrorResponsePlaceholder,\n\t\tLoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder,\n\t\t...restProps\n\t} = props;\n\n\tconst { content, status, error } = useServerSideRender( restProps );\n\n\t// Store the previous successful HTML response to show while loading.\n\tuseEffect( () => {\n\t\tif ( content ) {\n\t\t\tprevContentRef.current = content;\n\t\t}\n\t}, [ content ] );\n\n\tif ( status === 'loading' ) {\n\t\treturn (\n\t\t\t<LoadingResponsePlaceholder { ...props }>\n\t\t\t\t{ !! prevContentRef.current && (\n\t\t\t\t\t<RawHTML className={ className }>\n\t\t\t\t\t\t{ prevContentRef.current }\n\t\t\t\t\t</RawHTML>\n\t\t\t\t) }\n\t\t\t</LoadingResponsePlaceholder>\n\t\t);\n\t}\n\n\tif ( status === 'success' && ! content ) {\n\t\treturn <EmptyResponsePlaceholder { ...props } />;\n\t}\n\n\tif ( status === 'error' ) {\n\t\treturn <ErrorResponsePlaceholder message={ error } { ...props } />;\n\t}\n\n\treturn <RawHTML className={ className }>{ content }</RawHTML>;\n}\n\n/**\n * A component that renders server-side content for blocks.\n *\n * Note: URL query will include the current post ID when applicable.\n * This is useful for blocks that depend on the context of the current post for rendering.\n *\n * @example\n * ```jsx\n * import { ServerSideRender } from '@wordpress/server-side-render';\n * // Legacy import for WordPress 6.8 and earlier\n * // import { default as ServerSideRender } from '@wordpress/server-side-render';\n *\n * function Example() {\n * return (\n * <ServerSideRender\n * block=\"core/archives\"\n * attributes={ { showPostCounts: true } }\n * urlQueryArgs={ { customArg: 'value' } }\n * className=\"custom-class\"\n * />\n * );\n * }\n * ```\n *\n * @param {Object} props Component props.\n * @param {string} props.block The identifier of the block to be serverside rendered.\n * @param {Object} props.attributes The block attributes to be sent to the server for rendering.\n * @param {string} [props.className] Additional classes to apply to the wrapper element.\n * @param {string} [props.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'\n * @param {Object} [props.urlQueryArgs] Additional query arguments to append to the request URL.\n * @param {boolean} [props.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.\n * @param {Function} [props.EmptyResponsePlaceholder] Component rendered when the API response is empty.\n * @param {Function} [props.ErrorResponsePlaceholder] Component rendered when the API response is an error.\n * @param {Function} [props.LoadingResponsePlaceholder] Component rendered while the API request is loading.\n *\n * @return {React.JSX.Element} The rendered server-side content.\n */\nexport function ServerSideRenderWithPostId( {\n\turlQueryArgs = EMPTY_OBJECT,\n\t...props\n} ) {\n\tconst currentPostId = useSelect( ( select ) => {\n\t\t// FIXME: @wordpress/server-side-render should not depend on @wordpress/editor.\n\t\t// It is used by blocks that can be loaded into a *non-post* block editor.\n\t\t// eslint-disable-next-line @wordpress/data-no-store-string-literals\n\t\tconst postId = select( 'core/editor' )?.getCurrentPostId();\n\n\t\t// For templates and template parts we use a custom ID format.\n\t\t// Since they aren't real posts, we don't want to use their ID\n\t\t// for server-side rendering. Since they use a string based ID,\n\t\t// we can assume real post IDs are numbers.\n\t\treturn postId && typeof postId === 'number' ? postId : null;\n\t}, [] );\n\n\tconst newUrlQueryArgs = useMemo( () => {\n\t\tif ( ! currentPostId ) {\n\t\t\treturn urlQueryArgs;\n\t\t}\n\t\treturn {\n\t\t\tpost_id: currentPostId,\n\t\t\t...urlQueryArgs,\n\t\t};\n\t}, [ currentPostId, urlQueryArgs ] );\n\n\treturn <ServerSideRender urlQueryArgs={ newUrlQueryArgs } { ...props } />;\n}\n"],
5
- "mappings": ";AAGA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,IAAI,eAAe;AAC5B,SAAS,aAAa,eAAe;AACrC,SAAS,iBAAiB;AAK1B,SAAS,2BAA2B;AAMlC,cA2BA,YA3BA;AAJF,IAAM,eAAe,CAAC;AAEtB,SAAS,gCAAiC,EAAE,UAAU,GAAI;AACzD,SACC,oBAAC,eAAY,WACV,aAAI,0BAA2B,GAClC;AAEF;AAEA,SAAS,gCAAiC,EAAE,SAAS,UAAU,GAAI;AAClE,QAAM,eAAe;AAAA;AAAA,IAEpB,GAAI,yBAA0B;AAAA,IAC9B;AAAA,EACD;AACA,SAAO,oBAAC,eAAY,WAA0B,wBAAc;AAC7D;AAEA,SAAS,kCAAmC,EAAE,SAAS,GAAI;AAC1D,QAAM,CAAE,YAAY,aAAc,IAAI,SAAU,KAAM;AAEtD,YAAW,MAAM;AAEhB,UAAM,UAAU,WAAY,MAAM;AACjC,oBAAe,IAAK;AAAA,IACrB,GAAG,GAAK;AACR,WAAO,MAAM,aAAc,OAAQ;AAAA,EACpC,GAAG,CAAC,CAAE;AAEN,SACC,qBAAC,SAAI,OAAQ,EAAE,UAAU,WAAW,GACjC;AAAA,kBACD;AAAA,MAAC;AAAA;AAAA,QACA,OAAQ;AAAA,UACP,UAAU;AAAA,UACV,KAAK;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,YAAY;AAAA,QACb;AAAA,QAEA,8BAAC,WAAQ;AAAA;AAAA,IACV;AAAA,IAED,oBAAC,SAAI,OAAQ,EAAE,SAAS,aAAa,QAAQ,EAAE,GAC5C,UACH;AAAA,KACD;AAEF;AAEO,SAAS,iBAAkB,OAAQ;AACzC,QAAM,iBAAiB,OAAQ,EAAG;AAClC,QAAM;AAAA,IACL;AAAA,IACA,2BAA2B;AAAA,IAC3B,2BAA2B;AAAA,IAC3B,6BAA6B;AAAA,IAC7B,GAAG;AAAA,EACJ,IAAI;AAEJ,QAAM,EAAE,SAAS,QAAQ,MAAM,IAAI,oBAAqB,SAAU;AAGlE,YAAW,MAAM;AAChB,QAAK,SAAU;AACd,qBAAe,UAAU;AAAA,IAC1B;AAAA,EACD,GAAG,CAAE,OAAQ,CAAE;AAEf,MAAK,WAAW,WAAY;AAC3B,WACC,oBAAC,8BAA6B,GAAG,OAC9B,WAAC,CAAE,eAAe,WACnB,oBAAC,WAAQ,WACN,yBAAe,SAClB,GAEF;AAAA,EAEF;AAEA,MAAK,WAAW,aAAa,CAAE,SAAU;AACxC,WAAO,oBAAC,4BAA2B,GAAG,OAAQ;AAAA,EAC/C;AAEA,MAAK,WAAW,SAAU;AACzB,WAAO,oBAAC,4BAAyB,SAAU,OAAU,GAAG,OAAQ;AAAA,EACjE;AAEA,SAAO,oBAAC,WAAQ,WAA0B,mBAAS;AACpD;AAuCO,SAAS,2BAA4B;AAAA,EAC3C,eAAe;AAAA,EACf,GAAG;AACJ,GAAI;AACH,QAAM,gBAAgB,UAAW,CAAE,WAAY;AAI9C,UAAM,SAAS,OAAQ,aAAc,GAAG,iBAAiB;AAMzD,WAAO,UAAU,OAAO,WAAW,WAAW,SAAS;AAAA,EACxD,GAAG,CAAC,CAAE;AAEN,QAAM,kBAAkB,QAAS,MAAM;AACtC,QAAK,CAAE,eAAgB;AACtB,aAAO;AAAA,IACR;AACA,WAAO;AAAA,MACN,SAAS;AAAA,MACT,GAAG;AAAA,IACJ;AAAA,EACD,GAAG,CAAE,eAAe,YAAa,CAAE;AAEnC,SAAO,oBAAC,oBAAiB,cAAe,iBAAoB,GAAG,OAAQ;AACxE;",
3
+ "sources": ["../src/server-side-render.tsx"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport {\n\tRawHTML,\n\tuseEffect,\n\tuseState,\n\tuseMemo,\n\tuseRef,\n} from '@wordpress/element';\nimport { __, sprintf } from '@wordpress/i18n';\nimport { Placeholder, Spinner } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport { useServerSideRender } from './hook';\nimport type {\n\tPlaceholderProps,\n\tErrorPlaceholderProps,\n\tLoadingPlaceholderProps,\n\tServerSideRenderProps,\n\tServerSideRenderWithPostIdProps,\n} from './types';\n\nconst EMPTY_OBJECT = {};\n\nfunction DefaultEmptyResponsePlaceholder( { className }: PlaceholderProps ) {\n\treturn (\n\t\t<Placeholder className={ className }>\n\t\t\t{ __( 'Block rendered as empty.' ) }\n\t\t</Placeholder>\n\t);\n}\n\nfunction DefaultErrorResponsePlaceholder( {\n\tmessage,\n\tclassName,\n}: ErrorPlaceholderProps ) {\n\tconst errorMessage = sprintf(\n\t\t// translators: %s: error message describing the problem\n\t\t__( 'Error loading block: %s' ),\n\t\tmessage || __( 'Unknown error' )\n\t);\n\treturn <Placeholder className={ className }>{ errorMessage }</Placeholder>;\n}\n\nfunction DefaultLoadingResponsePlaceholder( {\n\tchildren,\n}: LoadingPlaceholderProps ) {\n\tconst [ showLoader, setShowLoader ] = useState( false );\n\n\tuseEffect( () => {\n\t\t// Schedule showing the Spinner after 1 second.\n\t\tconst timeout = setTimeout( () => {\n\t\t\tsetShowLoader( true );\n\t\t}, 1000 );\n\t\treturn () => clearTimeout( timeout );\n\t}, [] );\n\n\treturn (\n\t\t<div style={ { position: 'relative' } }>\n\t\t\t{ showLoader && (\n\t\t\t\t<div\n\t\t\t\t\tstyle={ {\n\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\ttop: '50%',\n\t\t\t\t\t\tleft: '50%',\n\t\t\t\t\t\tmarginTop: '-9px',\n\t\t\t\t\t\tmarginLeft: '-9px',\n\t\t\t\t\t} }\n\t\t\t\t>\n\t\t\t\t\t<Spinner />\n\t\t\t\t</div>\n\t\t\t) }\n\t\t\t<div style={ { opacity: showLoader ? '0.3' : 1 } }>\n\t\t\t\t{ children }\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport function ServerSideRender( props: ServerSideRenderProps ) {\n\tconst prevContentRef = useRef( '' );\n\tconst {\n\t\tclassName,\n\t\tEmptyResponsePlaceholder = DefaultEmptyResponsePlaceholder,\n\t\tErrorResponsePlaceholder = DefaultErrorResponsePlaceholder,\n\t\tLoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder,\n\t\t...restProps\n\t} = props;\n\n\tconst { content, status, error } = useServerSideRender( restProps );\n\n\t// Store the previous successful HTML response to show while loading.\n\tuseEffect( () => {\n\t\tif ( content ) {\n\t\t\tprevContentRef.current = content;\n\t\t}\n\t}, [ content ] );\n\n\tif ( status === 'loading' ) {\n\t\treturn (\n\t\t\t<LoadingResponsePlaceholder { ...props }>\n\t\t\t\t{ /* eslint-disable-next-line react-hooks/refs */ }\n\t\t\t\t{ !! prevContentRef.current && (\n\t\t\t\t\t<RawHTML className={ className }>\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// eslint-disable-next-line react-hooks/refs\n\t\t\t\t\t\t\tprevContentRef.current\n\t\t\t\t\t\t}\n\t\t\t\t\t</RawHTML>\n\t\t\t\t) }\n\t\t\t</LoadingResponsePlaceholder>\n\t\t);\n\t}\n\n\tif ( status === 'success' && ! content ) {\n\t\treturn <EmptyResponsePlaceholder { ...props } />;\n\t}\n\n\tif ( status === 'error' ) {\n\t\treturn <ErrorResponsePlaceholder message={ error } { ...props } />;\n\t}\n\n\treturn <RawHTML className={ className }>{ content || '' }</RawHTML>;\n}\n\n/**\n * A component that renders server-side content for blocks.\n *\n * Note: URL query will include the current post ID when applicable.\n * This is useful for blocks that depend on the context of the current post for rendering.\n *\n * @example\n * ```jsx\n * import { ServerSideRender } from '@wordpress/server-side-render';\n * // Legacy import for WordPress 6.8 and earlier\n * // import { default as ServerSideRender } from '@wordpress/server-side-render';\n *\n * function Example() {\n * return (\n * <ServerSideRender\n * block=\"core/archives\"\n * attributes={ { showPostCounts: true } }\n * urlQueryArgs={ { customArg: 'value' } }\n * className=\"custom-class\"\n * />\n * );\n * }\n * ```\n *\n * @param props Component props.\n * @param props.urlQueryArgs Additional query arguments to append to the request URL.\n * @return The rendered server-side content.\n */\nexport function ServerSideRenderWithPostId( {\n\turlQueryArgs = EMPTY_OBJECT,\n\t...props\n}: ServerSideRenderWithPostIdProps ) {\n\tconst currentPostId = useSelect( ( select ) => {\n\t\t// FIXME: @wordpress/server-side-render should not depend on @wordpress/editor.\n\t\t// It is used by blocks that can be loaded into a *non-post* block editor.\n\t\t// eslint-disable-next-line @wordpress/data-no-store-string-literals\n\t\tconst editorStore = select( 'core/editor' );\n\t\t// @ts-expect-error - editorStore is not typed in @wordpress/data\n\t\tconst postId = editorStore?.getCurrentPostId?.();\n\n\t\t// For templates and template parts we use a custom ID format.\n\t\t// Since they aren't real posts, we don't want to use their ID\n\t\t// for server-side rendering. Since they use a string based ID,\n\t\t// we can assume real post IDs are numbers.\n\t\treturn postId && typeof postId === 'number' ? postId : null;\n\t}, [] );\n\n\tconst newUrlQueryArgs = useMemo( () => {\n\t\tif ( ! currentPostId ) {\n\t\t\treturn urlQueryArgs;\n\t\t}\n\t\treturn {\n\t\t\tpost_id: currentPostId,\n\t\t\t...urlQueryArgs,\n\t\t};\n\t}, [ currentPostId, urlQueryArgs ] );\n\n\treturn <ServerSideRender urlQueryArgs={ newUrlQueryArgs } { ...props } />;\n}\n"],
5
+ "mappings": ";AAGA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,IAAI,eAAe;AAC5B,SAAS,aAAa,eAAe;AACrC,SAAS,iBAAiB;AAK1B,SAAS,2BAA2B;AAalC,cAgCA,YAhCA;AAJF,IAAM,eAAe,CAAC;AAEtB,SAAS,gCAAiC,EAAE,UAAU,GAAsB;AAC3E,SACC,oBAAC,eAAY,WACV,aAAI,0BAA2B,GAClC;AAEF;AAEA,SAAS,gCAAiC;AAAA,EACzC;AAAA,EACA;AACD,GAA2B;AAC1B,QAAM,eAAe;AAAA;AAAA,IAEpB,GAAI,yBAA0B;AAAA,IAC9B,WAAW,GAAI,eAAgB;AAAA,EAChC;AACA,SAAO,oBAAC,eAAY,WAA0B,wBAAc;AAC7D;AAEA,SAAS,kCAAmC;AAAA,EAC3C;AACD,GAA6B;AAC5B,QAAM,CAAE,YAAY,aAAc,IAAI,SAAU,KAAM;AAEtD,YAAW,MAAM;AAEhB,UAAM,UAAU,WAAY,MAAM;AACjC,oBAAe,IAAK;AAAA,IACrB,GAAG,GAAK;AACR,WAAO,MAAM,aAAc,OAAQ;AAAA,EACpC,GAAG,CAAC,CAAE;AAEN,SACC,qBAAC,SAAI,OAAQ,EAAE,UAAU,WAAW,GACjC;AAAA,kBACD;AAAA,MAAC;AAAA;AAAA,QACA,OAAQ;AAAA,UACP,UAAU;AAAA,UACV,KAAK;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,YAAY;AAAA,QACb;AAAA,QAEA,8BAAC,WAAQ;AAAA;AAAA,IACV;AAAA,IAED,oBAAC,SAAI,OAAQ,EAAE,SAAS,aAAa,QAAQ,EAAE,GAC5C,UACH;AAAA,KACD;AAEF;AAEO,SAAS,iBAAkB,OAA+B;AAChE,QAAM,iBAAiB,OAAQ,EAAG;AAClC,QAAM;AAAA,IACL;AAAA,IACA,2BAA2B;AAAA,IAC3B,2BAA2B;AAAA,IAC3B,6BAA6B;AAAA,IAC7B,GAAG;AAAA,EACJ,IAAI;AAEJ,QAAM,EAAE,SAAS,QAAQ,MAAM,IAAI,oBAAqB,SAAU;AAGlE,YAAW,MAAM;AAChB,QAAK,SAAU;AACd,qBAAe,UAAU;AAAA,IAC1B;AAAA,EACD,GAAG,CAAE,OAAQ,CAAE;AAEf,MAAK,WAAW,WAAY;AAC3B,WACC,oBAAC,8BAA6B,GAAG,OAE9B,WAAC,CAAE,eAAe,WACnB,oBAAC;AAAA,MAAQ;AAAA;AAAA,MAGP,yBAAe;AAAA,KAEjB,GAEF;AAAA,EAEF;AAEA,MAAK,WAAW,aAAa,CAAE,SAAU;AACxC,WAAO,oBAAC,4BAA2B,GAAG,OAAQ;AAAA,EAC/C;AAEA,MAAK,WAAW,SAAU;AACzB,WAAO,oBAAC,4BAAyB,SAAU,OAAU,GAAG,OAAQ;AAAA,EACjE;AAEA,SAAO,oBAAC,WAAQ,WAA0B,qBAAW,IAAI;AAC1D;AA8BO,SAAS,2BAA4B;AAAA,EAC3C,eAAe;AAAA,EACf,GAAG;AACJ,GAAqC;AACpC,QAAM,gBAAgB,UAAW,CAAE,WAAY;AAI9C,UAAM,cAAc,OAAQ,aAAc;AAE1C,UAAM,SAAS,aAAa,mBAAmB;AAM/C,WAAO,UAAU,OAAO,WAAW,WAAW,SAAS;AAAA,EACxD,GAAG,CAAC,CAAE;AAEN,QAAM,kBAAkB,QAAS,MAAM;AACtC,QAAK,CAAE,eAAgB;AACtB,aAAO;AAAA,IACR;AACA,WAAO;AAAA,MACN,SAAS;AAAA,MACT,GAAG;AAAA,IACJ;AAAA,EACD,GAAG,CAAE,eAAe,YAAa,CAAE;AAEnC,SAAO,oBAAC,oBAAiB,cAAe,iBAAoB,GAAG,OAAQ;AACxE;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,68 @@
1
+ export declare function rendererPath(block: string, attributes?: Record<string, unknown> | null, urlQueryArgs?: Record<string, unknown>): string;
2
+ export declare function removeBlockSupportAttributes(attributes: Record<string, unknown> & {
3
+ style?: Record<string, unknown>;
4
+ }): Record<string, unknown>;
5
+ /**
6
+ * Server-side render response object.
7
+ */
8
+ export interface ServerSideRenderResponse {
9
+ /** The current request status: 'idle', 'loading', 'success', or 'error'. */
10
+ status: 'idle' | 'loading' | 'success' | 'error';
11
+ /** The rendered block content (available when status is 'success'). */
12
+ content?: string;
13
+ /** The error message (available when status is 'error'). */
14
+ error?: string;
15
+ }
16
+ /**
17
+ * Configuration object for the useServerSideRender hook.
18
+ */
19
+ export interface UseServerSideRenderArgs {
20
+ /** The block attributes to be sent to the server for rendering. */
21
+ attributes: Record<string, unknown>;
22
+ /** The identifier of the block to be serverside rendered. Example: 'core/archives'. */
23
+ block: string;
24
+ /** Whether to remove block support attributes before sending. */
25
+ skipBlockSupportAttributes?: boolean;
26
+ /** The HTTP method to use ('GET' or 'POST'). Default is 'GET'. */
27
+ httpMethod?: 'GET' | 'POST';
28
+ /** Additional query arguments to append to the request URL. */
29
+ urlQueryArgs?: Record<string, unknown>;
30
+ }
31
+ /**
32
+ * A hook for server-side rendering a preview of dynamic blocks to display in the editor.
33
+ *
34
+ * Handles fetching server-rendered previews for blocks, managing loading states,
35
+ * and automatically debouncing requests to prevent excessive API calls. It supports both
36
+ * GET and POST requests, with POST requests used for larger attribute payloads.
37
+ *
38
+ * @example
39
+ * Basic usage:
40
+ *
41
+ * ```jsx
42
+ * import { RawHTML } from '@wordpress/element';
43
+ * import { useServerSideRender } from '@wordpress/server-side-render';
44
+ *
45
+ * function MyServerSideRender( { attributes, block } ) {
46
+ * const { content, status, error } = useServerSideRender( {
47
+ * attributes,
48
+ * block,
49
+ * } );
50
+ *
51
+ * if ( status === 'loading' ) {
52
+ * return <div>Loading...</div>;
53
+ * }
54
+ *
55
+ * if ( status === 'error' ) {
56
+ * return <div>Error: { error }</div>;
57
+ * }
58
+ *
59
+ * return <RawHTML>{ content }</RawHTML>;
60
+ * }
61
+ * ```
62
+ *
63
+ * @param args The hook configuration object.
64
+ *
65
+ * @return The server-side render response object.
66
+ */
67
+ export declare function useServerSideRender(args: UseServerSideRenderArgs): ServerSideRenderResponse;
68
+ //# sourceMappingURL=hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAUA,wBAAgB,YAAY,CAC3B,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,GAAG,IAAW,EACnD,YAAY,GAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAO,GAC1C,MAAM,CAMR;AAED,wBAAgB,4BAA4B,CAC3C,UAAU,EAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,GAAG;IACvC,KAAK,CAAC,EAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,CAAC;CAClC,GACC,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,CA0B3B;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACjD,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,CAAC;IACtC,uFAAuF;IACvF,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,kEAAkE;IAClE,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC5B,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,uBAAuB,GAC3B,wBAAwB,CA2F1B"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { ServerSideRenderWithPostId } from './server-side-render';
5
+ import { useServerSideRender } from './hook';
6
+ /**
7
+ * A compatibility layer for the `ServerSideRender` component when used with `wp` global namespace.
8
+ *
9
+ * @deprecated Use `ServerSideRender` non-default export instead.
10
+ *
11
+ * @example
12
+ * ```js
13
+ * import ServerSideRender from '@wordpress/server-side-render';
14
+ * ```
15
+ */
16
+ declare const ServerSideRenderCompat: typeof ServerSideRenderWithPostId & {
17
+ ServerSideRender: typeof ServerSideRenderWithPostId;
18
+ useServerSideRender: typeof useServerSideRender;
19
+ };
20
+ export { ServerSideRenderWithPostId as ServerSideRender };
21
+ export { useServerSideRender };
22
+ export default ServerSideRenderCompat;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAE7C;;;;;;;;;GASG;AACH,QAAA,MAAM,sBAAsB,EACG,OAAO,0BAA0B,GAAG;IACjE,gBAAgB,EAAE,OAAO,0BAA0B,CAAC;IACpD,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;CAChD,CAAC;AAKH,OAAO,EAAE,0BAA0B,IAAI,gBAAgB,EAAE,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,CAAC;eAChB,sBAAsB"}
@@ -0,0 +1,32 @@
1
+ import type { ServerSideRenderProps, ServerSideRenderWithPostIdProps } from './types';
2
+ export declare function ServerSideRender(props: ServerSideRenderProps): import("react").JSX.Element;
3
+ /**
4
+ * A component that renders server-side content for blocks.
5
+ *
6
+ * Note: URL query will include the current post ID when applicable.
7
+ * This is useful for blocks that depend on the context of the current post for rendering.
8
+ *
9
+ * @example
10
+ * ```jsx
11
+ * import { ServerSideRender } from '@wordpress/server-side-render';
12
+ * // Legacy import for WordPress 6.8 and earlier
13
+ * // import { default as ServerSideRender } from '@wordpress/server-side-render';
14
+ *
15
+ * function Example() {
16
+ * return (
17
+ * <ServerSideRender
18
+ * block="core/archives"
19
+ * attributes={ { showPostCounts: true } }
20
+ * urlQueryArgs={ { customArg: 'value' } }
21
+ * className="custom-class"
22
+ * />
23
+ * );
24
+ * }
25
+ * ```
26
+ *
27
+ * @param props Component props.
28
+ * @param props.urlQueryArgs Additional query arguments to append to the request URL.
29
+ * @return The rendered server-side content.
30
+ */
31
+ export declare function ServerSideRenderWithPostId({ urlQueryArgs, ...props }: ServerSideRenderWithPostIdProps): import("react").JSX.Element;
32
+ //# sourceMappingURL=server-side-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-side-render.d.ts","sourceRoot":"","sources":["../src/server-side-render.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAIX,qBAAqB,EACrB,+BAA+B,EAC/B,MAAM,SAAS,CAAC;AA2DjB,wBAAgB,gBAAgB,CAAE,KAAK,EAAE,qBAAqB,+BA4C7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,0BAA0B,CAAE,EAC3C,YAA2B,EAC3B,GAAG,KAAK,EACR,EAAE,+BAA+B,+BA2BjC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import type React from 'react';
5
+ /**
6
+ * Internal dependencies
7
+ */
8
+ import type { UseServerSideRenderArgs } from './hook';
9
+ export interface PlaceholderProps {
10
+ /** Additional classes to apply to the wrapper element. */
11
+ className?: string;
12
+ }
13
+ export interface ErrorPlaceholderProps extends PlaceholderProps {
14
+ /** Error message describing the problem. */
15
+ message?: string;
16
+ }
17
+ export interface LoadingPlaceholderProps {
18
+ children?: React.ReactNode;
19
+ }
20
+ export interface ServerSideRenderProps extends UseServerSideRenderArgs {
21
+ /** Additional classes to apply to the wrapper element. */
22
+ className?: string;
23
+ /** Component rendered when the API response is empty. */
24
+ EmptyResponsePlaceholder?: React.ComponentType<PlaceholderProps>;
25
+ /** Component rendered when the API response is an error. */
26
+ ErrorResponsePlaceholder?: React.ComponentType<ErrorPlaceholderProps>;
27
+ /** Component rendered while the API request is loading. */
28
+ LoadingResponsePlaceholder?: React.ComponentType<LoadingPlaceholderProps>;
29
+ }
30
+ export interface ServerSideRenderWithPostIdProps extends Omit<ServerSideRenderProps, 'urlQueryArgs'> {
31
+ /** Additional query arguments to append to the request URL. */
32
+ urlQueryArgs?: Record<string, unknown>;
33
+ }
34
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B;;GAEG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAChC,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC9D,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAsB,SAAQ,uBAAuB;IACrE,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,wBAAwB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAE,gBAAgB,CAAE,CAAC;IACnE,4DAA4D;IAC5D,wBAAwB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAE,qBAAqB,CAAE,CAAC;IACxE,2DAA2D;IAC3D,0BAA0B,CAAC,EAAE,KAAK,CAAC,aAAa,CAAE,uBAAuB,CAAE,CAAC;CAC5E;AAED,MAAM,WAAW,+BAChB,SAAQ,IAAI,CAAE,qBAAqB,EAAE,cAAc,CAAE;IACrD,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,CAAC;CACzC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/server-side-render",
3
- "version": "6.24.0",
3
+ "version": "6.24.1",
4
4
  "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -34,24 +34,26 @@
34
34
  "module": "build-module/index.mjs",
35
35
  "exports": {
36
36
  ".": {
37
+ "types": "./build-types/index.d.ts",
37
38
  "import": "./build-module/index.mjs",
38
39
  "require": "./build/index.cjs"
39
40
  },
40
41
  "./package.json": "./package.json"
41
42
  },
42
- "react-native": "src/index",
43
43
  "wpScript": true,
44
44
  "wpScriptDefaultExport": true,
45
+ "types": "build-types/index.d.ts",
45
46
  "dependencies": {
46
- "@wordpress/api-fetch": "^7.48.0",
47
- "@wordpress/blocks": "^15.21.0",
48
- "@wordpress/components": "^35.0.0",
49
- "@wordpress/compose": "^8.1.0",
50
- "@wordpress/data": "^10.48.0",
51
- "@wordpress/deprecated": "^4.48.0",
52
- "@wordpress/element": "^8.0.0",
53
- "@wordpress/i18n": "^6.21.0",
54
- "@wordpress/url": "^4.48.0"
47
+ "@types/react": "^18.3.27",
48
+ "@wordpress/api-fetch": "^7.48.1",
49
+ "@wordpress/blocks": "^15.21.1",
50
+ "@wordpress/components": "^35.0.1",
51
+ "@wordpress/compose": "^8.1.1",
52
+ "@wordpress/data": "^10.48.1",
53
+ "@wordpress/deprecated": "^4.48.1",
54
+ "@wordpress/element": "^8.0.1",
55
+ "@wordpress/i18n": "^6.21.1",
56
+ "@wordpress/url": "^4.48.1"
55
57
  },
56
58
  "peerDependencies": {
57
59
  "react": "^18.0.0",
@@ -60,5 +62,5 @@
60
62
  "publishConfig": {
61
63
  "access": "public"
62
64
  },
63
- "gitHead": "e7856693aeb4e2522d13608cd32c994e4a97cb9c"
65
+ "gitHead": "99df7432c5c7cb83ba41146fd1f57f3c19004305"
64
66
  }
@@ -5,9 +5,14 @@ import { debounce } from '@wordpress/compose';
5
5
  import { useEffect, useState, useRef } from '@wordpress/element';
6
6
  import apiFetch from '@wordpress/api-fetch';
7
7
  import { addQueryArgs } from '@wordpress/url';
8
+
8
9
  import { __experimentalSanitizeBlockAttributes } from '@wordpress/blocks';
9
10
 
10
- export function rendererPath( block, attributes = null, urlQueryArgs = {} ) {
11
+ export function rendererPath(
12
+ block: string,
13
+ attributes: Record< string, unknown > | null = null,
14
+ urlQueryArgs: Record< string, unknown > = {}
15
+ ): string {
11
16
  return addQueryArgs( `/wp/v2/block-renderer/${ block }`, {
12
17
  context: 'edit',
13
18
  ...( null !== attributes ? { attributes } : {} ),
@@ -15,7 +20,11 @@ export function rendererPath( block, attributes = null, urlQueryArgs = {} ) {
15
20
  } );
16
21
  }
17
22
 
18
- export function removeBlockSupportAttributes( attributes ) {
23
+ export function removeBlockSupportAttributes(
24
+ attributes: Record< string, unknown > & {
25
+ style?: Record< string, unknown >;
26
+ }
27
+ ): Record< string, unknown > {
19
28
  const {
20
29
  backgroundColor,
21
30
  borderColor,
@@ -44,11 +53,32 @@ export function removeBlockSupportAttributes( attributes ) {
44
53
  }
45
54
 
46
55
  /**
47
- * @typedef {Object} ServerSideRenderResponse
48
- * @property {string} status - The current request status: 'idle', 'loading', 'success', or 'error'.
49
- * @property {string} [content] - The rendered block content (available when status is 'success').
50
- * @property {string} [error] - The error message (available when status is 'error').
56
+ * Server-side render response object.
57
+ */
58
+ export interface ServerSideRenderResponse {
59
+ /** The current request status: 'idle', 'loading', 'success', or 'error'. */
60
+ status: 'idle' | 'loading' | 'success' | 'error';
61
+ /** The rendered block content (available when status is 'success'). */
62
+ content?: string;
63
+ /** The error message (available when status is 'error'). */
64
+ error?: string;
65
+ }
66
+
67
+ /**
68
+ * Configuration object for the useServerSideRender hook.
51
69
  */
70
+ export interface UseServerSideRenderArgs {
71
+ /** The block attributes to be sent to the server for rendering. */
72
+ attributes: Record< string, unknown >;
73
+ /** The identifier of the block to be serverside rendered. Example: 'core/archives'. */
74
+ block: string;
75
+ /** Whether to remove block support attributes before sending. */
76
+ skipBlockSupportAttributes?: boolean;
77
+ /** The HTTP method to use ('GET' or 'POST'). Default is 'GET'. */
78
+ httpMethod?: 'GET' | 'POST';
79
+ /** Additional query arguments to append to the request URL. */
80
+ urlQueryArgs?: Record< string, unknown >;
81
+ }
52
82
 
53
83
  /**
54
84
  * A hook for server-side rendering a preview of dynamic blocks to display in the editor.
@@ -82,18 +112,17 @@ export function removeBlockSupportAttributes( attributes ) {
82
112
  * }
83
113
  * ```
84
114
  *
85
- * @param {Object} args The hook configuration object.
86
- * @param {Object} args.attributes The block attributes to be sent to the server for rendering.
87
- * @param {string} args.block The identifier of the block to be serverside rendered. Example: 'core/archives'.
88
- * @param {boolean} [args.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.
89
- * @param {string} [args.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'.
90
- * @param {Object} [args.urlQueryArgs] Additional query arguments to append to the request URL.
115
+ * @param args The hook configuration object.
91
116
  *
92
- * @return {ServerSideRenderResponse} The server-side render response object.
117
+ * @return The server-side render response object.
93
118
  */
94
- export function useServerSideRender( args ) {
95
- const [ response, setResponse ] = useState( { status: 'idle' } );
96
- const shouldDebounceRef = useRef( false );
119
+ export function useServerSideRender(
120
+ args: UseServerSideRenderArgs
121
+ ): ServerSideRenderResponse {
122
+ const [ response, setResponse ] = useState< ServerSideRenderResponse >( {
123
+ status: 'idle',
124
+ } );
125
+ const shouldDebounceRef = useRef< boolean >( false );
97
126
 
98
127
  const {
99
128
  attributes,
@@ -103,11 +132,11 @@ export function useServerSideRender( args ) {
103
132
  urlQueryArgs,
104
133
  } = args;
105
134
 
106
- let sanitizedAttributes =
135
+ let sanitizedAttributes: Record< string, unknown > | null =
107
136
  attributes &&
108
137
  __experimentalSanitizeBlockAttributes( block, attributes );
109
138
 
110
- if ( skipBlockSupportAttributes ) {
139
+ if ( skipBlockSupportAttributes && sanitizedAttributes ) {
111
140
  sanitizedAttributes =
112
141
  removeBlockSupportAttributes( sanitizedAttributes );
113
142
  }
@@ -128,7 +157,7 @@ export function useServerSideRender( args ) {
128
157
  {
129
158
  setResponse( { status: 'loading' } );
130
159
 
131
- apiFetch( {
160
+ apiFetch< { rendered: string } >( {
132
161
  path,
133
162
  method: isPostRequest ? 'POST' : 'GET',
134
163
  body,
@@ -145,15 +174,21 @@ export function useServerSideRender( args ) {
145
174
  content: res ? res.rendered : '',
146
175
  } );
147
176
  } )
148
- .catch( ( error ) => {
177
+ .catch( ( error: unknown ) => {
149
178
  // The request was aborted, do not update the response.
150
- if ( error.name === 'AbortError' ) {
179
+ if (
180
+ error instanceof Error &&
181
+ error.name === 'AbortError'
182
+ ) {
151
183
  return;
152
184
  }
153
185
 
154
186
  setResponse( {
155
187
  status: 'error',
156
- error: error.message,
188
+ error:
189
+ error instanceof Error
190
+ ? error.message
191
+ : String( error ),
157
192
  } );
158
193
  } )
159
194
  .finally( () => {
@@ -14,7 +14,12 @@ import { useServerSideRender } from './hook';
14
14
  * import ServerSideRender from '@wordpress/server-side-render';
15
15
  * ```
16
16
  */
17
- const ServerSideRenderCompat = ServerSideRenderWithPostId;
17
+ const ServerSideRenderCompat =
18
+ ServerSideRenderWithPostId as typeof ServerSideRenderWithPostId & {
19
+ ServerSideRender: typeof ServerSideRenderWithPostId;
20
+ useServerSideRender: typeof useServerSideRender;
21
+ };
22
+
18
23
  ServerSideRenderCompat.ServerSideRender = ServerSideRenderWithPostId;
19
24
  ServerSideRenderCompat.useServerSideRender = useServerSideRender;
20
25
 
@@ -5,8 +5,8 @@ import {
5
5
  RawHTML,
6
6
  useEffect,
7
7
  useState,
8
- useRef,
9
8
  useMemo,
9
+ useRef,
10
10
  } from '@wordpress/element';
11
11
  import { __, sprintf } from '@wordpress/i18n';
12
12
  import { Placeholder, Spinner } from '@wordpress/components';
@@ -16,10 +16,17 @@ import { useSelect } from '@wordpress/data';
16
16
  * Internal dependencies
17
17
  */
18
18
  import { useServerSideRender } from './hook';
19
+ import type {
20
+ PlaceholderProps,
21
+ ErrorPlaceholderProps,
22
+ LoadingPlaceholderProps,
23
+ ServerSideRenderProps,
24
+ ServerSideRenderWithPostIdProps,
25
+ } from './types';
19
26
 
20
27
  const EMPTY_OBJECT = {};
21
28
 
22
- function DefaultEmptyResponsePlaceholder( { className } ) {
29
+ function DefaultEmptyResponsePlaceholder( { className }: PlaceholderProps ) {
23
30
  return (
24
31
  <Placeholder className={ className }>
25
32
  { __( 'Block rendered as empty.' ) }
@@ -27,16 +34,21 @@ function DefaultEmptyResponsePlaceholder( { className } ) {
27
34
  );
28
35
  }
29
36
 
30
- function DefaultErrorResponsePlaceholder( { message, className } ) {
37
+ function DefaultErrorResponsePlaceholder( {
38
+ message,
39
+ className,
40
+ }: ErrorPlaceholderProps ) {
31
41
  const errorMessage = sprintf(
32
42
  // translators: %s: error message describing the problem
33
43
  __( 'Error loading block: %s' ),
34
- message
44
+ message || __( 'Unknown error' )
35
45
  );
36
46
  return <Placeholder className={ className }>{ errorMessage }</Placeholder>;
37
47
  }
38
48
 
39
- function DefaultLoadingResponsePlaceholder( { children } ) {
49
+ function DefaultLoadingResponsePlaceholder( {
50
+ children,
51
+ }: LoadingPlaceholderProps ) {
40
52
  const [ showLoader, setShowLoader ] = useState( false );
41
53
 
42
54
  useEffect( () => {
@@ -69,7 +81,7 @@ function DefaultLoadingResponsePlaceholder( { children } ) {
69
81
  );
70
82
  }
71
83
 
72
- export function ServerSideRender( props ) {
84
+ export function ServerSideRender( props: ServerSideRenderProps ) {
73
85
  const prevContentRef = useRef( '' );
74
86
  const {
75
87
  className,
@@ -91,9 +103,13 @@ export function ServerSideRender( props ) {
91
103
  if ( status === 'loading' ) {
92
104
  return (
93
105
  <LoadingResponsePlaceholder { ...props }>
106
+ { /* eslint-disable-next-line react-hooks/refs */ }
94
107
  { !! prevContentRef.current && (
95
108
  <RawHTML className={ className }>
96
- { prevContentRef.current }
109
+ {
110
+ // eslint-disable-next-line react-hooks/refs
111
+ prevContentRef.current
112
+ }
97
113
  </RawHTML>
98
114
  ) }
99
115
  </LoadingResponsePlaceholder>
@@ -108,7 +124,7 @@ export function ServerSideRender( props ) {
108
124
  return <ErrorResponsePlaceholder message={ error } { ...props } />;
109
125
  }
110
126
 
111
- return <RawHTML className={ className }>{ content }</RawHTML>;
127
+ return <RawHTML className={ className }>{ content || '' }</RawHTML>;
112
128
  }
113
129
 
114
130
  /**
@@ -135,28 +151,21 @@ export function ServerSideRender( props ) {
135
151
  * }
136
152
  * ```
137
153
  *
138
- * @param {Object} props Component props.
139
- * @param {string} props.block The identifier of the block to be serverside rendered.
140
- * @param {Object} props.attributes The block attributes to be sent to the server for rendering.
141
- * @param {string} [props.className] Additional classes to apply to the wrapper element.
142
- * @param {string} [props.httpMethod='GET'] The HTTP method to use ('GET' or 'POST'). Default is 'GET'
143
- * @param {Object} [props.urlQueryArgs] Additional query arguments to append to the request URL.
144
- * @param {boolean} [props.skipBlockSupportAttributes=false] Whether to remove block support attributes before sending.
145
- * @param {Function} [props.EmptyResponsePlaceholder] Component rendered when the API response is empty.
146
- * @param {Function} [props.ErrorResponsePlaceholder] Component rendered when the API response is an error.
147
- * @param {Function} [props.LoadingResponsePlaceholder] Component rendered while the API request is loading.
148
- *
149
- * @return {React.JSX.Element} The rendered server-side content.
154
+ * @param props Component props.
155
+ * @param props.urlQueryArgs Additional query arguments to append to the request URL.
156
+ * @return The rendered server-side content.
150
157
  */
151
158
  export function ServerSideRenderWithPostId( {
152
159
  urlQueryArgs = EMPTY_OBJECT,
153
160
  ...props
154
- } ) {
161
+ }: ServerSideRenderWithPostIdProps ) {
155
162
  const currentPostId = useSelect( ( select ) => {
156
163
  // FIXME: @wordpress/server-side-render should not depend on @wordpress/editor.
157
164
  // It is used by blocks that can be loaded into a *non-post* block editor.
158
165
  // eslint-disable-next-line @wordpress/data-no-store-string-literals
159
- const postId = select( 'core/editor' )?.getCurrentPostId();
166
+ const editorStore = select( 'core/editor' );
167
+ // @ts-expect-error - editorStore is not typed in @wordpress/data
168
+ const postId = editorStore?.getCurrentPostId?.();
160
169
 
161
170
  // For templates and template parts we use a custom ID format.
162
171
  // Since they aren't real posts, we don't want to use their ID
package/src/types.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import type React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import type { UseServerSideRenderArgs } from './hook';
10
+
11
+ export interface PlaceholderProps {
12
+ /** Additional classes to apply to the wrapper element. */
13
+ className?: string;
14
+ }
15
+
16
+ export interface ErrorPlaceholderProps extends PlaceholderProps {
17
+ /** Error message describing the problem. */
18
+ message?: string;
19
+ }
20
+
21
+ export interface LoadingPlaceholderProps {
22
+ children?: React.ReactNode;
23
+ }
24
+
25
+ export interface ServerSideRenderProps extends UseServerSideRenderArgs {
26
+ /** Additional classes to apply to the wrapper element. */
27
+ className?: string;
28
+ /** Component rendered when the API response is empty. */
29
+ EmptyResponsePlaceholder?: React.ComponentType< PlaceholderProps >;
30
+ /** Component rendered when the API response is an error. */
31
+ ErrorResponsePlaceholder?: React.ComponentType< ErrorPlaceholderProps >;
32
+ /** Component rendered while the API request is loading. */
33
+ LoadingResponsePlaceholder?: React.ComponentType< LoadingPlaceholderProps >;
34
+ }
35
+
36
+ export interface ServerSideRenderWithPostIdProps
37
+ extends Omit< ServerSideRenderProps, 'urlQueryArgs' > {
38
+ /** Additional query arguments to append to the request URL. */
39
+ urlQueryArgs?: Record< string, unknown >;
40
+ }
File without changes