diff --git a/.eslintrc.js b/.eslintrc.js index bb262a70cf7c6b..729ce7d8574d66 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -81,6 +81,7 @@ const restrictedImports = [ 'flowRight', 'forEach', 'fromPairs', + 'get', 'groupBy', 'has', 'identity', diff --git a/package-lock.json b/package-lock.json index f552635bec7cc4..2e2a1596987cf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17299,7 +17299,6 @@ "dom-scroll-into-view": "^1.2.1", "fast-deep-equal": "^3.1.3", "inherits": "^2.0.3", - "lodash": "^4.17.21", "react-autosize-textarea": "^7.1.0", "react-easy-crop": "^4.5.1", "rememo": "^4.0.2", diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 5e4b8f466b3f9e..0ce0679b83ca1b 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -72,7 +72,6 @@ "dom-scroll-into-view": "^1.2.1", "fast-deep-equal": "^3.1.3", "inherits": "^2.0.3", - "lodash": "^4.17.21", "react-autosize-textarea": "^7.1.0", "react-easy-crop": "^4.5.1", "rememo": "^4.0.2", diff --git a/packages/block-editor/src/components/global-styles/get-block-css-selector.js b/packages/block-editor/src/components/global-styles/get-block-css-selector.js index 3b2b277c422a48..6cb7cfaa3f47d3 100644 --- a/packages/block-editor/src/components/global-styles/get-block-css-selector.js +++ b/packages/block-editor/src/components/global-styles/get-block-css-selector.js @@ -1,12 +1,8 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * Internal dependencies */ import { scopeSelector } from './utils'; +import { getValueFromObjectPath } from '../../utils/object'; /** * Determine the CSS selector for the block type and target provided, returning @@ -69,15 +65,15 @@ export function getBlockCSSSelector( if ( hasSelectors ) { // Get selector from either `feature.root` or shorthand path. const featureSelector = - get( selectors, `${ path }.root`, null ) || - get( selectors, path, null ); + getValueFromObjectPath( selectors, `${ path }.root`, null ) || + getValueFromObjectPath( selectors, path, null ); // Return feature selector if found or any available fallback. return featureSelector || fallbackSelector; } // Try getting old experimental supports selector value. - const featureSelector = get( + const featureSelector = getValueFromObjectPath( supports, `${ path }.__experimentalSelector`, null @@ -98,7 +94,7 @@ export function getBlockCSSSelector( // Use selectors API if available. if ( hasSelectors ) { - subfeatureSelector = get( selectors, path, null ); + subfeatureSelector = getValueFromObjectPath( selectors, path, null ); } // Only return if we have a subfeature selector. diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js index 75cd084428293c..3c8b0167279804 100644 --- a/packages/block-editor/src/components/global-styles/hooks.js +++ b/packages/block-editor/src/components/global-styles/hooks.js @@ -2,7 +2,6 @@ * External dependencies */ import fastDeepEqual from 'fast-deep-equal/es6'; -import { get } from 'lodash'; /** * WordPress dependencies @@ -16,7 +15,7 @@ import { _x } from '@wordpress/i18n'; * Internal dependencies */ import { getValueFromVariable, getPresetVariableFromValue } from './utils'; -import { setImmutably } from '../../utils/object'; +import { getValueFromObjectPath, setImmutably } from '../../utils/object'; import { GlobalStylesContext } from './context'; import { unlock } from '../../lock-unlock'; @@ -104,18 +103,19 @@ export function useGlobalSetting( propertyPath, blockName, source = 'all' ) { if ( propertyPath ) { return ( - get( configToUse, contextualPath ) ?? - get( configToUse, globalPath ) + getValueFromObjectPath( configToUse, contextualPath ) ?? + getValueFromObjectPath( configToUse, globalPath ) ); } let result = {}; VALID_SETTINGS.forEach( ( setting ) => { const value = - get( + getValueFromObjectPath( configToUse, `settings${ appendedBlockPath }.${ setting }` - ) ?? get( configToUse, `settings.${ setting }` ); + ) ?? + getValueFromObjectPath( configToUse, `settings.${ setting }` ); if ( value ) { result = setImmutably( result, setting.split( '.' ), value ); } @@ -176,19 +176,19 @@ export function useGlobalStyle( let rawResult, result; switch ( source ) { case 'all': - rawResult = get( mergedConfig, finalPath ); + rawResult = getValueFromObjectPath( mergedConfig, finalPath ); result = shouldDecodeEncode ? getValueFromVariable( mergedConfig, blockName, rawResult ) : rawResult; break; case 'user': - rawResult = get( userConfig, finalPath ); + rawResult = getValueFromObjectPath( userConfig, finalPath ); result = shouldDecodeEncode ? getValueFromVariable( mergedConfig, blockName, rawResult ) : rawResult; break; case 'base': - rawResult = get( baseConfig, finalPath ); + rawResult = getValueFromObjectPath( baseConfig, finalPath ); result = shouldDecodeEncode ? getValueFromVariable( baseConfig, blockName, rawResult ) : rawResult; diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js index ea118b55809d47..ec1a6270917f0a 100644 --- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * WordPress dependencies */ @@ -32,7 +27,11 @@ import { PresetDuotoneFilter } from '../duotone/components'; import { getGapCSSValue } from '../../hooks/gap'; import { store as blockEditorStore } from '../../store'; import { LAYOUT_DEFINITIONS } from '../../layouts/definitions'; -import { kebabCase, setImmutably } from '../../utils/object'; +import { + getValueFromObjectPath, + kebabCase, + setImmutably, +} from '../../utils/object'; // List of block support features that can have their related styles // generated under their own feature level selector rather than the block's. @@ -69,7 +68,11 @@ function compileStyleValue( uncompiledValue ) { function getPresetsDeclarations( blockPresets = {}, mergedSettings ) { return PRESET_METADATA.reduce( ( declarations, { path, valueKey, valueFunc, cssVarInfix } ) => { - const presetByOrigin = get( blockPresets, path, [] ); + const presetByOrigin = getValueFromObjectPath( + blockPresets, + path, + [] + ); [ 'default', 'theme', 'custom' ].forEach( ( origin ) => { if ( presetByOrigin[ origin ] ) { presetByOrigin[ origin ].forEach( ( value ) => { @@ -113,7 +116,11 @@ function getPresetsClasses( blockSelector = '*', blockPresets = {} ) { return declarations; } - const presetByOrigin = get( blockPresets, path, [] ); + const presetByOrigin = getValueFromObjectPath( + blockPresets, + path, + [] + ); [ 'default', 'theme', 'custom' ].forEach( ( origin ) => { if ( presetByOrigin[ origin ] ) { presetByOrigin[ origin ].forEach( ( { slug } ) => { @@ -147,7 +154,11 @@ function getPresetsSvgFilters( blockPresets = {} ) { // Duotone are the only type of filters for now. ( metadata ) => metadata.path.at( -1 ) === 'duotone' ).flatMap( ( metadata ) => { - const presetByOrigin = get( blockPresets, metadata.path, {} ); + const presetByOrigin = getValueFromObjectPath( + blockPresets, + metadata.path, + {} + ); return [ 'default', 'theme' ] .filter( ( origin ) => presetByOrigin[ origin ] ) .flatMap( ( origin ) => @@ -319,7 +330,10 @@ export function getStylesDeclarations( return declarations; } - const styleValue = get( blockStyles, pathToValue ); + const styleValue = getValueFromObjectPath( + blockStyles, + pathToValue + ); // Root-level padding styles don't currently support strings with CSS shorthand values. // This may change: https://github.com/WordPress/gutenberg/issues/40132. @@ -334,7 +348,9 @@ export function getStylesDeclarations( Object.entries( properties ).forEach( ( entry ) => { const [ name, prop ] = entry; - if ( ! get( styleValue, [ prop ], false ) ) { + if ( + ! getValueFromObjectPath( styleValue, [ prop ], false ) + ) { // Do not create a declaration // for sub-properties that don't have any value. return; @@ -345,17 +361,19 @@ export function getStylesDeclarations( : kebabCase( name ); declarations.push( `${ cssProperty }: ${ compileStyleValue( - get( styleValue, [ prop ] ) + getValueFromObjectPath( styleValue, [ prop ] ) ) }` ); } ); - } else if ( get( blockStyles, pathToValue, false ) ) { + } else if ( + getValueFromObjectPath( blockStyles, pathToValue, false ) + ) { const cssProperty = key.startsWith( '--' ) ? key : kebabCase( key ); declarations.push( `${ cssProperty }: ${ compileStyleValue( - get( blockStyles, pathToValue ) + getValueFromObjectPath( blockStyles, pathToValue ) ) }` ); } @@ -384,7 +402,7 @@ export function getStylesDeclarations( let ruleValue = rule.value; if ( typeof ruleValue !== 'string' && ruleValue?.ref ) { const refPath = ruleValue.ref.split( '.' ); - ruleValue = get( tree, refPath ); + ruleValue = getValueFromObjectPath( tree, refPath ); // Presence of another ref indicates a reference to another dynamic value. // Pointing to another dynamic value is not supported. if ( ! ruleValue || ruleValue?.ref ) { @@ -680,7 +698,7 @@ export const getNodesWithSettings = ( tree, blockSelectors ) => { const pickPresets = ( treeToPickFrom ) => { let presets = {}; PRESET_METADATA.forEach( ( { path } ) => { - const value = get( treeToPickFrom, path, false ); + const value = getValueFromObjectPath( treeToPickFrom, path, false ); if ( value !== false ) { presets = setImmutably( presets, path, value ); } diff --git a/packages/block-editor/src/components/global-styles/utils.js b/packages/block-editor/src/components/global-styles/utils.js index a12ea2af9f79ea..d4f2d959a33659 100644 --- a/packages/block-editor/src/components/global-styles/utils.js +++ b/packages/block-editor/src/components/global-styles/utils.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import { get } from 'lodash'; import fastDeepEqual from 'fast-deep-equal/es6'; /** @@ -11,6 +10,7 @@ import { getTypographyFontSizeValue, getFluidTypographyOptionsFromSettings, } from './typography-utils'; +import { getValueFromObjectPath } from '../../utils/object'; /* Supporting data. */ export const ROOT_BLOCK_NAME = 'root'; @@ -168,8 +168,12 @@ function findInPresetsBy( ) { // Block presets take priority above root level presets. const orderedPresetsByOrigin = [ - get( features, [ 'blocks', blockName, ...presetPath ] ), - get( features, presetPath ), + getValueFromObjectPath( features, [ + 'blocks', + blockName, + ...presetPath, + ] ), + getValueFromObjectPath( features, presetPath ), ]; for ( const presetByOrigin of orderedPresetsByOrigin ) { @@ -282,8 +286,13 @@ function getValueFromPresetVariable( function getValueFromCustomVariable( features, blockName, variable, path ) { const result = - get( features.settings, [ 'blocks', blockName, 'custom', ...path ] ) ?? - get( features.settings, [ 'custom', ...path ] ); + getValueFromObjectPath( features.settings, [ + 'blocks', + blockName, + 'custom', + ...path, + ] ) ?? + getValueFromObjectPath( features.settings, [ 'custom', ...path ] ); if ( ! result ) { return variable; } @@ -303,7 +312,7 @@ export function getValueFromVariable( features, blockName, variable ) { if ( ! variable || typeof variable !== 'string' ) { if ( variable?.ref && typeof variable?.ref === 'string' ) { const refPath = variable.ref.split( '.' ); - variable = get( features, refPath ); + variable = getValueFromObjectPath( features, refPath ); // Presence of another ref indicates a reference to another dynamic value. // Pointing to another dynamic value is not supported. if ( ! variable || !! variable?.ref ) { diff --git a/packages/block-editor/src/components/use-setting/index.js b/packages/block-editor/src/components/use-setting/index.js index add0b7ff6e08db..c1222c9116ae67 100644 --- a/packages/block-editor/src/components/use-setting/index.js +++ b/packages/block-editor/src/components/use-setting/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * WordPress dependencies */ @@ -18,6 +13,7 @@ import { applyFilters } from '@wordpress/hooks'; */ import { useBlockEditContext } from '../block-edit'; import { store as blockEditorStore } from '../../store'; +import { getValueFromObjectPath } from '../../utils/object'; const blockedPaths = [ 'color', @@ -165,11 +161,14 @@ export default function useSetting( path ) { candidateClientId ); result = - get( + getValueFromObjectPath( candidateAtts, `settings.blocks.${ blockName }.${ normalizedPath }` ) ?? - get( candidateAtts, `settings.${ normalizedPath }` ); + getValueFromObjectPath( + candidateAtts, + `settings.${ normalizedPath }` + ); if ( result !== undefined ) { // Stop the search for more distant ancestors and move on. break; @@ -183,7 +182,8 @@ export default function useSetting( path ) { const defaultsPath = `__experimentalFeatures.${ normalizedPath }`; const blockPath = `__experimentalFeatures.blocks.${ blockName }.${ normalizedPath }`; result = - get( settings, blockPath ) ?? get( settings, defaultsPath ); + getValueFromObjectPath( settings, blockPath ) ?? + getValueFromObjectPath( settings, defaultsPath ); } // Return if the setting was found in either the block instance or the store. diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index ccc5dcd9ff2e2a..9c6bf957d61c51 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * WordPress dependencies */ @@ -14,7 +9,7 @@ import { useMemo } from '@wordpress/element'; */ import { useSetting } from '../components'; import { useSettingsForBlockElement } from '../components/global-styles/hooks'; -import { setImmutably } from '../utils/object'; +import { getValueFromObjectPath, setImmutably } from '../utils/object'; /** * Removed falsy values from nested object. @@ -79,7 +74,10 @@ export function transformStyles( Object.entries( activeSupports ).forEach( ( [ support, isActive ] ) => { if ( isActive ) { migrationPaths[ support ].forEach( ( path ) => { - const styleValue = get( referenceBlockAttributes, path ); + const styleValue = getValueFromObjectPath( + referenceBlockAttributes, + path + ); if ( styleValue ) { returnBlock = { ...returnBlock, diff --git a/packages/block-editor/src/utils/object.js b/packages/block-editor/src/utils/object.js index a144f1b1bf53a9..ed81450e49ab93 100644 --- a/packages/block-editor/src/utils/object.js +++ b/packages/block-editor/src/utils/object.js @@ -111,3 +111,24 @@ export function setImmutably( object, path, value ) { return newObject; } + +/** + * Helper util to return a value from a certain path of the object. + * Path is specified as either: + * - a string of properties, separated by dots, for example: "x.y". + * - an array of properties, for example `[ 'x', 'y' ]`. + * You can also specify a default value in case the result is nullish. + * + * @param {Object} object Input object. + * @param {string|Array} path Path to the object property. + * @param {*} defaultValue Default value if the value at the specified path is nullish. + * @return {*} Value of the object property at the specified path. + */ +export const getValueFromObjectPath = ( object, path, defaultValue ) => { + const normalizedPath = Array.isArray( path ) ? path : path.split( '.' ); + let value = object; + normalizedPath.forEach( ( fieldName ) => { + value = value?.[ fieldName ]; + } ); + return value ?? defaultValue; +};