Skip to content

Commit

Permalink
Restructure dev check middleware logic to enable better minification
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Apr 16, 2023
1 parent d438300 commit 1048f3f
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 157 deletions.
162 changes: 81 additions & 81 deletions packages/toolkit/src/immutableStateInvariantMiddleware.ts
Expand Up @@ -25,42 +25,6 @@ function invariant(condition: any, message?: string) {
throw new Error(`${prefix}: ${message || ''}`)
}

function stringify(
obj: any,
serializer?: EntryProcessor,
indent?: string | number,
decycler?: EntryProcessor
): string {
return JSON.stringify(obj, getSerialize(serializer, decycler), indent)
}

function getSerialize(
serializer?: EntryProcessor,
decycler?: EntryProcessor
): EntryProcessor {
let stack: any[] = [],
keys: any[] = []

if (!decycler)
decycler = function (_: string, value: any) {
if (stack[0] === value) return '[Circular ~]'
return (
'[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']'
)
}

return function (this: any, key: string, value: any) {
if (stack.length > 0) {
var thisPos = stack.indexOf(this)
~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
if (~stack.indexOf(value)) value = decycler!.call(this, key, value)
} else stack.push(value)

return serializer == null ? value : serializer.call(this, key, value)
}
}

/**
* The default `isImmutable` function.
*
Expand Down Expand Up @@ -221,69 +185,105 @@ export function createImmutableStateInvariantMiddleware(
): Middleware {
if (process.env.NODE_ENV === 'production') {
return () => (next) => (action) => next(action)
}
} else {
function stringify(
obj: any,
serializer?: EntryProcessor,
indent?: string | number,
decycler?: EntryProcessor
): string {
return JSON.stringify(obj, getSerialize(serializer, decycler), indent)
}

let {
isImmutable = isImmutableDefault,
ignoredPaths,
warnAfter = 32,
ignore,
} = options
function getSerialize(
serializer?: EntryProcessor,
decycler?: EntryProcessor
): EntryProcessor {
let stack: any[] = [],
keys: any[] = []

if (!decycler)
decycler = function (_: string, value: any) {
if (stack[0] === value) return '[Circular ~]'
return (
'[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']'
)
}

// Alias ignore->ignoredPaths, but prefer ignoredPaths if present
ignoredPaths = ignoredPaths || ignore
return function (this: any, key: string, value: any) {
if (stack.length > 0) {
var thisPos = stack.indexOf(this)
~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
if (~stack.indexOf(value)) value = decycler!.call(this, key, value)
} else stack.push(value)

const track = trackForMutations.bind(null, isImmutable, ignoredPaths)
return serializer == null ? value : serializer.call(this, key, value)
}
}

return ({ getState }) => {
let state = getState()
let tracker = track(state)
let {
isImmutable = isImmutableDefault,
ignoredPaths,
warnAfter = 32,
ignore,
} = options

let result
return (next) => (action) => {
const measureUtils = getTimeMeasureUtils(
warnAfter,
'ImmutableStateInvariantMiddleware'
)
// Alias ignore->ignoredPaths, but prefer ignoredPaths if present
ignoredPaths = ignoredPaths || ignore

measureUtils.measureTime(() => {
state = getState()
const track = trackForMutations.bind(null, isImmutable, ignoredPaths)

result = tracker.detectMutations()
// Track before potentially not meeting the invariant
tracker = track(state)
return ({ getState }) => {
let state = getState()
let tracker = track(state)

invariant(
!result.wasMutated,
`A state mutation was detected between dispatches, in the path '${
result.path || ''
}'. This may cause incorrect behavior. (https://redux.js.org/style-guide/style-guide#do-not-mutate-state)`
let result
return (next) => (action) => {
const measureUtils = getTimeMeasureUtils(
warnAfter,
'ImmutableStateInvariantMiddleware'
)
})

const dispatchedAction = next(action)

measureUtils.measureTime(() => {
state = getState()
measureUtils.measureTime(() => {
state = getState()

result = tracker.detectMutations()
// Track before potentially not meeting the invariant
tracker = track(state)
result = tracker.detectMutations()
// Track before potentially not meeting the invariant
tracker = track(state)

result.wasMutated &&
invariant(
!result.wasMutated,
`A state mutation was detected inside a dispatch, in the path: ${
`A state mutation was detected between dispatches, in the path '${
result.path || ''
}. Take a look at the reducer(s) handling the action ${stringify(
action
)}. (https://redux.js.org/style-guide/style-guide#do-not-mutate-state)`
}'. This may cause incorrect behavior. (https://redux.js.org/style-guide/style-guide#do-not-mutate-state)`
)
})
})

const dispatchedAction = next(action)

measureUtils.measureTime(() => {
state = getState()

measureUtils.warnIfExceeded()
result = tracker.detectMutations()
// Track before potentially not meeting the invariant
tracker = track(state)

return dispatchedAction
result.wasMutated &&
invariant(
!result.wasMutated,
`A state mutation was detected inside a dispatch, in the path: ${
result.path || ''
}. Take a look at the reducer(s) handling the action ${stringify(
action
)}. (https://redux.js.org/style-guide/style-guide#do-not-mutate-state)`
)
})

measureUtils.warnIfExceeded()

return dispatchedAction
}
}
}
}
153 changes: 77 additions & 76 deletions packages/toolkit/src/serializableStateInvariantMiddleware.ts
Expand Up @@ -190,88 +190,89 @@ export function createSerializableStateInvariantMiddleware(
): Middleware {
if (process.env.NODE_ENV === 'production') {
return () => (next) => (action) => next(action)
}
const {
isSerializable = isPlain,
getEntries,
ignoredActions = [],
ignoredActionPaths = ['meta.arg', 'meta.baseQueryMeta'],
ignoredPaths = [],
warnAfter = 32,
ignoreState = false,
ignoreActions = false,
disableCache = false,
} = options

const cache: WeakSet<object> | undefined =
!disableCache && WeakSet ? new WeakSet() : undefined

return (storeAPI) => (next) => (action) => {
const result = next(action)

const measureUtils = getTimeMeasureUtils(
warnAfter,
'SerializableStateInvariantMiddleware'
)

if (
!ignoreActions &&
!(ignoredActions.length && ignoredActions.indexOf(action.type) !== -1)
) {
measureUtils.measureTime(() => {
const foundActionNonSerializableValue = findNonSerializableValue(
action,
'',
isSerializable,
getEntries,
ignoredActionPaths,
cache
)

if (foundActionNonSerializableValue) {
const { keyPath, value } = foundActionNonSerializableValue

console.error(
`A non-serializable value was detected in an action, in the path: \`${keyPath}\`. Value:`,
value,
'\nTake a look at the logic that dispatched this action: ',
} else {
const {
isSerializable = isPlain,
getEntries,
ignoredActions = [],
ignoredActionPaths = ['meta.arg', 'meta.baseQueryMeta'],
ignoredPaths = [],
warnAfter = 32,
ignoreState = false,
ignoreActions = false,
disableCache = false,
} = options

const cache: WeakSet<object> | undefined =
!disableCache && WeakSet ? new WeakSet() : undefined

return (storeAPI) => (next) => (action) => {
const result = next(action)

const measureUtils = getTimeMeasureUtils(
warnAfter,
'SerializableStateInvariantMiddleware'
)

if (
!ignoreActions &&
!(ignoredActions.length && ignoredActions.indexOf(action.type) !== -1)
) {
measureUtils.measureTime(() => {
const foundActionNonSerializableValue = findNonSerializableValue(
action,
'\n(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants)',
'\n(To allow non-serializable values see: https://redux-toolkit.js.org/usage/usage-guide#working-with-non-serializable-data)'
'',
isSerializable,
getEntries,
ignoredActionPaths,
cache
)
}
})
}

if (!ignoreState) {
measureUtils.measureTime(() => {
const state = storeAPI.getState()

const foundStateNonSerializableValue = findNonSerializableValue(
state,
'',
isSerializable,
getEntries,
ignoredPaths,
cache
)

if (foundStateNonSerializableValue) {
const { keyPath, value } = foundStateNonSerializableValue

console.error(
`A non-serializable value was detected in the state, in the path: \`${keyPath}\`. Value:`,
value,
`
if (foundActionNonSerializableValue) {
const { keyPath, value } = foundActionNonSerializableValue

console.error(
`A non-serializable value was detected in an action, in the path: \`${keyPath}\`. Value:`,
value,
'\nTake a look at the logic that dispatched this action: ',
action,
'\n(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants)',
'\n(To allow non-serializable values see: https://redux-toolkit.js.org/usage/usage-guide#working-with-non-serializable-data)'
)
}
})
}

if (!ignoreState) {
measureUtils.measureTime(() => {
const state = storeAPI.getState()

const foundStateNonSerializableValue = findNonSerializableValue(
state,
'',
isSerializable,
getEntries,
ignoredPaths,
cache
)

if (foundStateNonSerializableValue) {
const { keyPath, value } = foundStateNonSerializableValue

console.error(
`A non-serializable value was detected in the state, in the path: \`${keyPath}\`. Value:`,
value,
`
Take a look at the reducer(s) handling this action type: ${action.type}.
(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)`
)
}
})
)
}
})

measureUtils.warnIfExceeded()
}
measureUtils.warnIfExceeded()
}

return result
return result
}
}
}

0 comments on commit 1048f3f

Please sign in to comment.