Skip to content

Commit 5e60d64

Browse files
refactor(kit): refactor property checks (#554)
1 parent b217a03 commit 5e60d64

File tree

6 files changed

+42
-22
lines changed

6 files changed

+42
-22
lines changed

packages/devtools-kit/src/core/component/state/custom.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { InspectorState, customTypeEnums } from '../types'
2-
import { getComponentName, getInstanceName } from '../utils'
2+
import { ensurePropertyExists, getComponentName, getInstanceName } from '../utils'
33
import { processInstanceState } from './process'
44
import { escape, getSetupStateType, toRaw } from './util'
55

@@ -225,7 +225,11 @@ export function getObjectDetails(object: Record<string, any>) {
225225
if (isState) {
226226
const stateTypeName = info.computed ? 'Computed' : info.ref ? 'Ref' : info.reactive ? 'Reactive' : null
227227
const value = toRaw(info.reactive ? object : object._value)
228-
const raw = object.effect?.raw?.toString() || object.effect?.fn?.toString()
228+
229+
const raw = ensurePropertyExists(object, 'effect')
230+
? object.effect?.raw?.toString() || object.effect?.fn?.toString()
231+
: null
232+
229233
return {
230234
_custom: {
231235
type: stateTypeName?.toLowerCase(),
@@ -236,7 +240,7 @@ export function getObjectDetails(object: Record<string, any>) {
236240
}
237241
}
238242

239-
if (typeof object.__asyncLoader === 'function') {
243+
if (ensurePropertyExists(object, '__asyncLoader') && typeof object.__asyncLoader === 'function') {
240244
return {
241245
_custom: {
242246
type: 'component-definition' satisfies customTypeEnums,

packages/devtools-kit/src/core/component/state/is.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
export function isVueInstance(value: Record<string, unknown>) {
2-
return value._ && Object.keys(value._).includes('vnode')
1+
import { ensurePropertyExists } from '../utils'
2+
3+
export function isVueInstance(value: any) {
4+
if (!ensurePropertyExists(value, '_')) {
5+
return false
6+
}
7+
if (!isPlainObject(value._)) {
8+
return false
9+
}
10+
return Object.keys(value._).includes('vnode')
311
}
412

5-
export function isPlainObject(obj: unknown) {
13+
export function isPlainObject(obj: unknown): obj is object {
614
return Object.prototype.toString.call(obj) === '[object Object]'
715
}
816

packages/devtools-kit/src/core/component/state/process.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { camelize } from '@vue/devtools-shared'
22
import type { VueAppInstance } from '../../../types'
33
import type { InspectorState } from '../types'
4-
import { returnError } from '../utils'
4+
import { ensurePropertyExists, returnError } from '../utils'
55
import { vueBuiltins } from './constants'
66
import { getPropType, getSetupStateType, toRaw } from './util'
77

@@ -151,8 +151,8 @@ function processSetupState(instance: VueAppInstance) {
151151
let result: Partial<InspectorState>
152152

153153
let isOtherType = typeof value === 'function'
154-
|| typeof value?.render === 'function' // Components
155-
|| typeof value?.__asyncLoader === 'function' // Components
154+
|| (ensurePropertyExists(value, 'render') && typeof value.render === 'function') // Components
155+
|| (ensurePropertyExists(value, '__asyncLoader') && typeof value.__asyncLoader === 'function') // Components
156156
|| (typeof value === 'object' && value && ('setup' in value || 'props' in value)) // Components
157157
|| /^v[A-Z]/.test(key) // Directives
158158

@@ -161,7 +161,9 @@ function processSetupState(instance: VueAppInstance) {
161161

162162
const { stateType, stateTypeName } = getStateTypeAndName(info)
163163
const isState = info.ref || info.computed || info.reactive
164-
const raw = rawData.effect?.raw?.toString() || rawData.effect?.fn?.toString()
164+
const raw = ensurePropertyExists(rawData, 'effect')
165+
? rawData.effect?.raw?.toString() || rawData.effect?.fn?.toString()
166+
: null
165167

166168
if (stateType)
167169
isOtherType = false

packages/devtools-kit/src/core/component/state/replacer.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ensurePropertyExists } from '../utils'
12
import { INFINITY, MAX_ARRAY_SIZE, MAX_STRING_SIZE, NAN, NEGATIVE_INFINITY, UNDEFINED } from './constants'
23
import { getBigIntDetails, getComponentDefinitionDetails, getDateDetails, getFunctionDetails, getHTMLElementDetails, getInstanceDetails, getMapDetails, getObjectDetails, getRouterDetails, getSetDetails, getStoreDetails } from './custom'
34
import { isVueInstance } from './is'
@@ -69,8 +70,7 @@ export function stringifyReplacer(key: string | number, _value: any, depth?: num
6970
else if (proto === '[object Error]') {
7071
return `[native Error ${(val as Error).message}<>${(val as Error).stack}]`
7172
}
72-
// @ts-expect-error skip type check
73-
else if (val.state && val._vm) {
73+
else if (ensurePropertyExists(val, 'state', true) && ensurePropertyExists(val, '_vm', true)) {
7474
return getStoreDetails(val)
7575
}
7676
else if (val.constructor && val.constructor.name === 'VueRouter') {
@@ -85,8 +85,7 @@ export function stringifyReplacer(key: string | number, _value: any, depth?: num
8585
seenInstance?.set(val, depth!)
8686
return componentVal
8787
}
88-
// @ts-expect-error skip type check
89-
else if (typeof val.render === 'function') {
88+
else if (ensurePropertyExists(val, 'render', true) && typeof val.render === 'function') {
9089
return getComponentDefinitionDetails(val)
9190
}
9291
else if (val.constructor && val.constructor.name === 'VNode') {
@@ -96,12 +95,10 @@ export function stringifyReplacer(key: string | number, _value: any, depth?: num
9695
else if (typeof HTMLElement !== 'undefined' && val instanceof HTMLElement) {
9796
return getHTMLElementDetails(val)
9897
}
99-
// @ts-expect-error skip type check
100-
else if (val.constructor?.name === 'Store' && val._wrappedGetters) {
98+
else if (val.constructor?.name === 'Store' && '_wrappedGetters' in val) {
10199
return '[object Store]'
102100
}
103-
// @ts-expect-error skip type check
104-
else if (val.currentRoute) {
101+
else if (ensurePropertyExists(val, 'currentRoute', true)) {
105102
return '[object Router]'
106103
}
107104
const customDetails = getObjectDetails(val)

packages/devtools-kit/src/core/component/utils/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,12 @@ export function getComponentInstance(appRecord: AppRecord, instanceId: string |
138138
// @TODO: find a better way to handle it
139139
return instance || appRecord.instanceMap.get(':root')
140140
}
141+
142+
// #542, should use 'in' operator to check if the key exists in the object
143+
export function ensurePropertyExists<R = Record<string, unknown>>(obj: unknown, key: string, skipObjCheck = false): obj is R {
144+
return skipObjCheck
145+
? key in (obj as object)
146+
: typeof obj === 'object' && obj !== null
147+
? key in obj
148+
: false
149+
}

packages/devtools-kit/src/shared/transfer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ function encode(data: unknown, replacer: Replacer | null, list: unknown[], seen:
4747
const keys = Object.keys(data)
4848
for (i = 0, l = keys.length; i < l; i++) {
4949
key = keys[i]
50+
// fix vue warn for compilerOptions passing-options-to-vuecompiler-sfc
51+
// @TODO: need to check if it will cause any other issues
52+
if (key === 'compilerOptions')
53+
return index
5054
value = data[key]
5155
const isVm = value != null && isObject(value, Object.prototype.toString.call(data)) && isVueInstance(value)
5256
try {
53-
// fix vue warn for compilerOptions passing-options-to-vuecompiler-sfc
54-
// @TODO: need to check if it will cause any other issues
55-
if (key === 'compilerOptions')
56-
return index
5757
if (replacer) {
5858
value = replacer.call(data, key, value, depth, seenVueInstance)
5959
}

0 commit comments

Comments
 (0)