Skip to content

Commit fc939aa

Browse files
committed
Make static properties/methods accessible through the runtime object built from the class
1 parent 92bf9ee commit fc939aa

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

src/component.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { defineComponent, type ComponentCustomOptions, type MethodOptions } from 'vue';
22
import { obtainSlot } from './slot'
3-
import { getSuperSlot, getProviderFunction, optionNullableClassDecorator } from './utils'
3+
import { getSuperSlot, getProviderFunction, optionNullableClassDecorator, assignStaticClassProperties } from './utils'
44
import { build as optionSetup } from './option/setup'
55
import { build as optionComputed } from './option/computed'
66
import { build as optionData } from './option/data'
@@ -31,7 +31,7 @@ function componentOptionFactory(cons: VueCons, extend?: any) {
3131
optionRef(cons, optionBuilder)//after Computed
3232
optionAccessor(cons, optionBuilder)
3333
optionMethodsAndHooks(cons, optionBuilder)//the last one
34-
const raw = {
34+
const raw: any = {
3535
name: cons.name,
3636
setup: optionBuilder.setup,
3737
data() {
@@ -51,7 +51,8 @@ function componentOptionFactory(cons: VueCons, extend?: any) {
5151
...optionBuilder.hooks,
5252
extends: extend
5353
}
54-
return raw as any
54+
assignStaticClassProperties(cons, raw);
55+
return raw
5556
}
5657

5758
type ComponentOption = {

src/utils.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,32 @@ export function optionNullableClassDecorator<T>(handler: { (cons: VueCons, optio
128128
return decorator
129129
}
130130

131+
export function assignStaticClassProperties<T extends VueCons = any>(source: T, target: any) {
132+
// keep track of things we've assigned (e.g. overridden variables in child class)
133+
const previouslyAssigned: Record<string, true> = {};
134+
135+
while (source !== Base) {
136+
const classObject = source;
137+
for (const property of Object.getOwnPropertyNames(classObject)) {
138+
if (property === 'prototype' || property === 'name' || property === 'length') {
139+
continue;
140+
}
141+
if ((property in target) && !(property in previouslyAssigned)) {
142+
console.warn(`Property/method ${property} of ${classObject.name} is not supported for static access, as it conflicts with property names in the underlying Vue object.`);
143+
continue;
144+
}
145+
previouslyAssigned[property] = true;
146+
if (typeof (classObject as any)[property] === 'function') {
147+
target[property] = (...args: unknown[]): unknown => {
148+
return (classObject as any)[property].apply(classObject, args);
149+
}
150+
} else {
151+
target[property] = new Proxy(classObject, {
152+
get(target: any, prop) { return target[prop] },
153+
set(target: any, prop, value) { target[prop] = value; return true; },
154+
});
155+
}
156+
}
157+
source = Object.getPrototypeOf(classObject);
158+
}
159+
}

0 commit comments

Comments
 (0)