Skip to content

Commit f9c9439

Browse files
committed
ComponentBase, extends
1 parent c290096 commit f9c9439

File tree

5 files changed

+128
-48
lines changed

5 files changed

+128
-48
lines changed

src/component.ts

Lines changed: 86 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineComponent } from 'vue';
2-
import { obtainSlot, toBaseReverse } from './utils'
2+
import { obtainSlot, extendSlotPath } from './utils'
33
import { build as optionComputed } from './option/computed'
44
import { build as optionData } from './option/data'
55
import { build as optionMethodsAndLifecycle } from './option/methodsAndLifecycle'
@@ -20,7 +20,7 @@ export interface OptionBuilder {
2020

2121
}
2222
export interface Cons { new(): any, prototype: any }
23-
function ComponentOption(cons: Cons) {
23+
function ComponentOption(cons: Cons, extend?: any) {
2424
const optionBuilder: OptionBuilder = {}
2525
optionComputed(cons, optionBuilder)
2626
optionWatch(cons, optionBuilder)
@@ -41,12 +41,13 @@ function ComponentOption(cons: Cons) {
4141
watch: optionBuilder.watch,
4242
props: optionBuilder.props,
4343
inject: optionBuilder.inject,
44-
...optionBuilder.lifecycle
44+
...optionBuilder.lifecycle,
45+
extends: extend
4546
}
4647
return raw as any
4748
}
4849

49-
export function Component(arg: Cons | {
50+
type ComponentOption = {
5051
name?: string
5152
emits?: string[]
5253
provide?: Record<string, any> | Function
@@ -55,43 +56,92 @@ export function Component(arg: Cons | {
5556
inheritAttrs?: boolean;
5657
expose?: string[];
5758
modifier?: (raw: any) => any
58-
}): any {
59-
if (typeof arg === 'function') {
60-
return defineComponent(ComponentOption(arg))
59+
}
60+
type ComponentConsOption = Cons | ComponentOption
61+
function ComponentStep(cons: Cons, extend?: any) {
62+
return defineComponent(ComponentOption(cons, extend))
63+
}
64+
65+
function ComponentStepWithOption(cons: Cons, arg: ComponentOption, extend?: any): any {
66+
let option = ComponentOption(cons, extend)
67+
const slot = obtainSlot(cons.prototype)
68+
if (typeof arg.name !== 'undefined') {
69+
option.name = arg.name
6170
}
62-
return function (cons: Cons) {
63-
let option = ComponentOption(cons)
64-
const slot = obtainSlot(cons.prototype)
65-
if (typeof arg.name !== 'undefined') {
66-
option.name = arg.name
67-
}
6871

69-
let emits = Array.from(slot.obtainMap('emits').keys())
70-
if (Array.isArray(arg.emits)) {
71-
emits = Array.from(new Set([...emits,...arg.emits]))
72-
}
73-
option.emits = emits
72+
let emits = Array.from(slot.obtainMap('emits').keys())
73+
if (Array.isArray(arg.emits)) {
74+
emits = Array.from(new Set([...emits, ...arg.emits]))
75+
}
76+
option.emits = emits
7477

7578

76-
if (arg.components) {
77-
option.components = arg.components
78-
}
79-
if (arg.provide) {
80-
option.provide = arg.provide
81-
}
82-
if (arg.directives) {
83-
option.directives = arg.directives
84-
}
85-
if (arg.inheritAttrs) {
86-
option.inheritAttrs = arg.inheritAttrs
87-
}
88-
if (arg.expose) {
89-
option.expose = arg.expose
90-
}
91-
if (arg.modifier) {
92-
option = arg.modifier(option)
79+
if (arg.components) {
80+
option.components = arg.components
81+
}
82+
if (arg.provide) {
83+
option.provide = arg.provide
84+
}
85+
if (arg.directives) {
86+
option.directives = arg.directives
87+
}
88+
if (arg.inheritAttrs) {
89+
option.inheritAttrs = arg.inheritAttrs
90+
}
91+
if (arg.expose) {
92+
option.expose = arg.expose
93+
}
94+
95+
if (arg.modifier) {
96+
option = arg.modifier(option)
97+
if (!option) {
98+
throw 'Component modifier should return vue component option'
9399
}
100+
}
101+
return defineComponent(option)
102+
}
103+
104+
export function ComponentBase(cons: Cons) {
105+
const slot = obtainSlot(cons.prototype)
106+
slot.inComponent = true
107+
return cons
108+
}
109+
110+
export function Component(arg: Cons|ComponentOption) {
111+
112+
function extend(cons: Cons) {
113+
ComponentBase(cons)
114+
const slotPath = extendSlotPath(cons.prototype)
115+
116+
slotPath.forEach(proto => {
117+
const slot = obtainSlot(proto)
118+
if (!slot.inComponent) {
119+
throw 'Class should be decorated by Component or ComponentBase: ' + proto.constructor
120+
}
121+
})
122+
123+
return slotPath.reduceRight<any>((pv, cv, ci) => {
124+
if (ci > 0) {
125+
return ComponentStep(cv.constructor, pv === null ? undefined : pv)
126+
} else {
127+
128+
if (typeof arg === 'function') {
129+
return ComponentStepWithOption(cv.constructor, {}, pv === null ? undefined : pv)
130+
} else {
131+
return ComponentStepWithOption(cv.constructor, arg, pv === null ? undefined : pv)
132+
}
133+
}
134+
}, null)
135+
}
136+
if (typeof arg === 'function') {
137+
138+
const finalComp = extend(arg)
139+
return finalComp
140+
}
141+
return function (cons: Cons) {
142+
143+
const finalComp = extend(cons)
94144

95-
return defineComponent(option)
145+
return finalComp
96146
}
97147
}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
export { Component } from './component'
2+
export { Component, ComponentBase } from './component'
33
export { decorator as Ref } from './option/ref'
44
export { decorator as Watch } from './option/watch'
55
export { decorator as Prop } from './option/props'

src/option/computed.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { makeObject } from '../utils'
22
import { Cons, OptionBuilder } from '../component'
3-
import { obtainSlot, excludeNames, toBaseReverse, getValidNames } from '../utils'
3+
import { obtainSlot, toComponentReverse, getValidNames } from '../utils'
44
export function build(cons: Cons, optionBuilder: OptionBuilder) {
55
optionBuilder.computed ??= {}
66
const slot = obtainSlot(cons.prototype)
77
let map = slot.obtainMap<Map<string, any>>('computed')
8-
const protoArr = toBaseReverse(cons.prototype)
8+
const protoArr = toComponentReverse(cons.prototype)
99
protoArr.forEach(proto => {
1010
getValidNames(proto, (des) => {
1111
return typeof des.get === 'function'

src/option/methodsAndLifecycle.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { makeObject, obtainSlot } from '../utils'
22
import { Cons, OptionBuilder } from '../component'
3-
import { toBaseReverse, excludeNames, getValidNames } from '../utils'
3+
import { toComponentReverse, excludeNames, getValidNames } from '../utils'
44
const LifecycleNames = [
55
"beforeCreate",
66
"created",
@@ -21,7 +21,8 @@ const LifecycleNames = [
2121

2222
export function build(cons: Cons, optionBuilder: OptionBuilder) {
2323
const slot = obtainSlot(cons.prototype)
24-
const protoArr = toBaseReverse(cons.prototype).reverse()
24+
const protoArr = toComponentReverse(cons.prototype)
25+
2526
optionBuilder.lifecycle ??= {}
2627
optionBuilder.methods ??= {}
2728
const LifecycleFunctions: Record<string, Function> = {}

src/utils.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@ class Slot {
55
obtainMap<T extends Map<string, any>>(name: string): T {
66
let map = this.names.get(name)
77
if (!map) {
8-
8+
99
map = new Map
10-
this.names.set(name,map)
11-
}else{
10+
this.names.set(name, map)
11+
} else {
1212

1313
}
1414
return map as any
1515
}
16+
inComponent = false
1617
}
1718

1819
export function makeSlot(obj: any): Slot {
19-
if (obj[SlotSymbol]) {
20+
if (getSlot(obj)) {
2021
throw ''
2122
}
2223
const slot = new Slot
@@ -27,7 +28,8 @@ export function makeSlot(obj: any): Slot {
2728
return slot
2829
}
2930
export function getSlot(obj: any): Slot | undefined {
30-
return obj[SlotSymbol]
31+
32+
return Object.getOwnPropertyDescriptor(obj, SlotSymbol)?.value
3133
}
3234

3335
export function obtainSlot(obj: any): Slot {
@@ -47,11 +49,38 @@ export function makeObject(names: string[], obj: any) {
4749
}, {})
4850
}
4951

50-
export function toBaseReverse(obj: any) {
52+
// export function toBaseReverse(obj: any) {
53+
// const arr: any[] = []
54+
// let curr = obj
55+
// while (curr.constructor !== Base) {
56+
// arr.unshift(curr)
57+
// curr = Object.getPrototypeOf(curr)
58+
// }
59+
// return arr
60+
// }
61+
62+
export function toComponentReverse(obj: any) {
5163
const arr: any[] = []
5264
let curr = obj
65+
66+
do {
67+
68+
arr.unshift(curr)
69+
curr = Object.getPrototypeOf(curr)
70+
} while (curr.constructor !== Base && !getSlot(curr))
71+
return arr
72+
}
73+
74+
export function extendSlotPath(obj: any): {
75+
constructor: any
76+
}[] {
77+
const arr: any[] = []
78+
let curr = obj
79+
5380
while (curr.constructor !== Base) {
54-
arr.push(curr)
81+
if (getSlot(curr)) {
82+
arr.push(curr)
83+
}
5584
curr = Object.getPrototypeOf(curr)
5685
}
5786
return arr

0 commit comments

Comments
 (0)