From 71d381530dc576fd5d4f9f9e95da6a0086f7ba62 Mon Sep 17 00:00:00 2001 From: Uyarn Date: Tue, 27 Oct 2026 16:11:24 +0800 Subject: [PATCH] fix: fix blank design guide pages caused by stale attributes --- packages/site-components/src/utils/hybrids.ts | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/site-components/src/utils/hybrids.ts b/packages/site-components/src/utils/hybrids.ts index 1b5cfc8bf..0380cdac0 100644 --- a/packages/site-components/src/utils/hybrids.ts +++ b/packages/site-components/src/utils/hybrids.ts @@ -3,6 +3,7 @@ import { define as baseDefine, type Component } from 'hybrids'; export * from 'hybrids'; const ATTRIBUTE_VERSION = '__attributeVersion'; +const explicitlySetProperties = new WeakMap>(); interface AttributeSyncHost extends HTMLElement { [ATTRIBUTE_VERSION]: number; @@ -13,6 +14,15 @@ interface PropertyConfig { type: string; } +function markPropertyAsExplicitlySet(host: AttributeSyncHost, key: string): void { + let properties = explicitlySetProperties.get(host); + if (!properties) { + properties = new Set(); + explicitlySetProperties.set(host, properties); + } + properties.add(key); +} + function getWritableProperties(component: Component): Map { const properties = new Map(); @@ -34,6 +44,25 @@ function getWritableProperties(component: Component): Map): void { + const constructor = customElements.get(tag); + if (!constructor) return; + + properties.forEach((_property, key) => { + const descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, key); + if (!descriptor?.set) return; + + const setter = descriptor.set; + Object.defineProperty(constructor.prototype, key, { + ...descriptor, + set(this: AttributeSyncHost, value: unknown) { + markPropertyAsExplicitlySet(this, key); + setter.call(this, value); + }, + }); + }); +} + function dashToCamelCase(value: string): string { return value.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase()); } @@ -77,8 +106,11 @@ function withAttributeSync(component: Component): Component { value: 0, connect: (host: AttributeSyncHost) => { Array.from(host.attributes).forEach(({ name }) => { - const property = properties.get(dashToCamelCase(name)); - if (property) syncAttribute(host, name, property); + const key = dashToCamelCase(name); + const property = properties.get(key); + if (property && !explicitlySetProperties.get(host)?.has(key)) { + syncAttribute(host, name, property); + } }); const observer = new MutationObserver((mutations) => { @@ -103,7 +135,12 @@ function withAttributeSync(component: Component): Component { } export const define = Object.assign( - (component: Component): Component => baseDefine(withAttributeSync(component)), + (component: Component): Component => { + const properties = getWritableProperties(component as Component); + const definition = baseDefine(withAttributeSync(component)); + trackPropertySetters(component.tag, properties); + return definition; + }, { compile: baseDefine.compile, from: baseDefine.from,