/
githubmirror
/
wp-calypso
Обзор
Документация
Войти
/
githubmirror
/
wp-calypso
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
trunk
client/state/utils/schema-utils.js
113 строк
4 KB
Griffith Chen
Remove lodash `get` in favor of native optional chaining (#112149)
01 июл 2026, 13:04
Не верифицирован
01 июл 2026, 13:04
b455cc9
Код
Авторство
О чём код?
import { isEmpty } from '@automattic/js-utils'; import { getInitialState } from '@automattic/state-utils'; import warn from '@wordpress/warning'; import isEqual from 'fast-deep-equal/es6'; import validator from 'is-my-json-valid'; import { serialize, deserialize } from './serialize'; import { withPersistence } from './with-persistence'; export function isValidStateWithSchema( state, schema, debugInfo ) { const validate = validator( schema, { greedy: process.env.NODE_ENV !== 'production', verbose: process.env.NODE_ENV !== 'production', } ); const valid = validate( state ); if ( ! valid && process.env.NODE_ENV !== 'production' ) { const msgLines = [ 'State validation failed.', 'State: %o', '' ]; const substitutions = [ state ]; ( validate.errors ?? [] ).forEach( ( { field, message, schemaPath, value } ) => { // data.myField is required msgLines.push( '%s %s' ); substitutions.push( field, message ); // Found: { my: 'state' } msgLines.push( 'Found: %o' ); substitutions.push( value ); // Violates rule: { type: 'boolean' } if ( ! isEmpty( schemaPath ) ) { msgLines.push( 'Violates rule: %o' ); substitutions.push( schemaPath.reduce( ( node, key ) => node?.[ key ], schema ) ); } msgLines.push( '' ); } ); if ( ! isEmpty( debugInfo ) ) { msgLines.push( 'Source: %o' ); substitutions.push( debugInfo ); } warn( msgLines.join( '\n' ), ...substitutions ); } return valid; } function isValidSerializedState( schema, reducer, state ) { // The stored state is often equal to initial state of the reducer. Because initial state // is always valid, we can validate much faster by just comparing the two states. The full // JSON schema check is much slower and we do it only on nontrivial states. // Note that we need to serialize the initial state to make a correct check. For reducers // with custom persistence, the initial state can be arbitrary non-serializable object. We // need to compare two serialized objects. const serializedInitialState = serialize( reducer, getInitialState( reducer ) ); if ( isEqual( state, serializedInitialState ) ) { return true; } return isValidStateWithSchema( state, schema ); } /** * Creates a schema-validating reducer * * Use this to wrap simple reducers with a schema-based validation check when loading * the initial state from persistent storage. * * The wrapped reducer implements the `deserialize` method and checks if the persisted state * is a valid state. If yes, it returns the persisted state (and calls the inner reducer's * `deserialize` method if defined). Otherwise it returns the initial state of the inner reducer. * @example * ```js * const ageReducer = ( state = 0, action ) => GROW === action.type ? state + 1 : state * * const schema = { type: 'number', minimum: 0 } * * export const age = withSchemaValidation( schema, ageReducer ); * * expect( deserialize( ageReducer, -5 ) ).toBe( -5 ); // no schema check * expect( deserialize( age, -5 ) ).toBe( 0 ); // schema check failed, return initial state * expect( deserialize( age, 23 ) ).toBe( 23 ); // schema check passed * ``` * @param {Object} schema JSON-schema description of state * @param {Function} reducer normal reducer from ( state, action ) to new state * @returns {import('redux').Reducer} wrapped reducer handling validation on `.deserialize()` */ export const withSchemaValidation = ( schema, reducer ) => { if ( process.env.NODE_ENV !== 'production' && ! schema ) { throw new Error( 'null schema passed to withSchemaValidation' ); } // Add default identity-mapping persistence to the wrapped reducer. Prevent // it to default to no-persistence. const persistingReducer = withPersistence( reducer ); return withPersistence( persistingReducer, { deserialize( persisted ) { if ( persisted === undefined ) { // If the state is not present in the stored data, initialize it with the // initial state. return getInitialState( persistingReducer ); } // If the stored state fails JSON schema validation, treat it as if it was // `undefined`, i.e., ignore it and replace with initial state. if ( ! isValidSerializedState( schema, persistingReducer, persisted ) ) { return getInitialState( persistingReducer ); } // Otherwise, if the state is valid, deserialize it with the inner reducer. return deserialize( persistingReducer, persisted ); }, } ); };