diff --git a/packages/devtools-kit/__tests__/component/replacer.test.ts b/packages/devtools-kit/__tests__/component/replacer.test.ts new file mode 100644 index 000000000..a3e62ab61 --- /dev/null +++ b/packages/devtools-kit/__tests__/component/replacer.test.ts @@ -0,0 +1,38 @@ +import { stringifyReplacer } from '../../src/core/component/state/replacer' + +// `stringifyReplacer` reads the value from `this[key]`, mirroring how it is +// called during `JSON.stringify`. +function replace(value: unknown) { + return stringifyReplacer.call({ value }, 'value') as { _custom?: { type?: string, displayText?: string } } +} + +describe('stringifyReplacer: component definition detection', () => { + // #1100: Pinia store state can hold plain class instances that happen to + // expose a `render` method. Those must not be reported as Vue components. + it('does not treat a plain class instance with a render method as a component', () => { + class Chart { + data = [1, 2, 3] + render() { + return 'draw the chart' + } + } + + const result = replace(new Chart()) + + expect(result?._custom?.type).not.toBe('component-definition') + }) + + it('still detects a real Vue component definition', () => { + const Button = defineComponent({ + name: 'MyButton', + render() { + return h('button', 'Click me') + }, + }) + + const result = replace(Button) + + expect(result?._custom?.type).toBe('component-definition') + expect(result?._custom?.displayText).toBe('MyButton') + }) +}) diff --git a/packages/devtools-kit/src/core/component/state/replacer.ts b/packages/devtools-kit/src/core/component/state/replacer.ts index f711abf9d..a165d54db 100644 --- a/packages/devtools-kit/src/core/component/state/replacer.ts +++ b/packages/devtools-kit/src/core/component/state/replacer.ts @@ -82,7 +82,7 @@ export function stringifyReplacer(key: string | number, _value: any, depth?: num seenInstance?.set(val, depth!) return componentVal } - else if (ensurePropertyExists(val, 'render', true) && typeof val.render === 'function') { + else if (Object.prototype.hasOwnProperty.call(val, 'render') && typeof (val as Record).render === 'function') { return getComponentDefinitionDetails(val) } else if (val.constructor && val.constructor.name === 'VNode') {