diff --git a/packages/block-editor/src/utils/transform-styles/index.js b/packages/block-editor/src/utils/transform-styles/index.js index 742e3a9becaef..808566776f6ea 100644 --- a/packages/block-editor/src/utils/transform-styles/index.js +++ b/packages/block-editor/src/utils/transform-styles/index.js @@ -5,6 +5,52 @@ import postcss, { CssSyntaxError } from 'postcss'; import wrap from 'postcss-prefixwrap'; import rebaseUrl from 'postcss-urlrebase'; +const transformStylesCache = new WeakMap(); + +function transformStyle( + { css, ignoredSelectors = [], baseURL }, + wrapperSelector = '' +) { + // When there is no wrapper selector or base URL, there is no need + // to transform the CSS. This is most cases because in the default + // iframed editor, no wrapping is needed, and not many styles + // provide a base URL. + if ( ! wrapperSelector && ! baseURL ) { + return css; + } + + try { + return postcss( + [ + wrapperSelector && + wrap( wrapperSelector, { + ignoredSelectors: [ + ...ignoredSelectors, + wrapperSelector, + ], + } ), + baseURL && rebaseUrl( { rootUrl: baseURL } ), + ].filter( Boolean ) + ).process( css, {} ).css; // use sync PostCSS API + } catch ( error ) { + if ( error instanceof CssSyntaxError ) { + // eslint-disable-next-line no-console + console.warn( + 'wp.blockEditor.transformStyles Failed to transform CSS.', + error.message + '\n' + error.showSourceCode( false ) + ); + } else { + // eslint-disable-next-line no-console + console.warn( + 'wp.blockEditor.transformStyles Failed to transform CSS.', + error + ); + } + + return null; + } +} + /** * Applies a series of CSS rule transforms to wrap selectors inside a given class and/or rewrite URLs depending on the parameters passed. * @@ -18,45 +64,14 @@ import rebaseUrl from 'postcss-urlrebase'; * @return {Array} converted rules. */ const transformStyles = ( styles, wrapperSelector = '' ) => { - return styles.map( ( { css, ignoredSelectors = [], baseURL } ) => { - // When there is no wrapper selector or base URL, there is no need - // to transform the CSS. This is most cases because in the default - // iframed editor, no wrapping is needed, and not many styles - // provide a base URL. - if ( ! wrapperSelector && ! baseURL ) { - return css; + return styles.map( ( style ) => { + if ( transformStylesCache.has( style ) ) { + return transformStylesCache.get( style ); } - try { - return postcss( - [ - wrapperSelector && - wrap( wrapperSelector, { - ignoredSelectors: [ - ...ignoredSelectors, - wrapperSelector, - ], - } ), - baseURL && rebaseUrl( { rootUrl: baseURL } ), - ].filter( Boolean ) - ).process( css, {} ).css; // use sync PostCSS API - } catch ( error ) { - if ( error instanceof CssSyntaxError ) { - // eslint-disable-next-line no-console - console.warn( - 'wp.blockEditor.transformStyles Failed to transform CSS.', - error.message + '\n' + error.showSourceCode( false ) - ); - } else { - // eslint-disable-next-line no-console - console.warn( - 'wp.blockEditor.transformStyles Failed to transform CSS.', - error - ); - } - - return null; - } + const transformedStyle = transformStyle( style, wrapperSelector ); + transformStylesCache.set( style, transformedStyle ); + return transformedStyle; } ); };