|
| 1 | +import {getPropertyDescriptor} from './get-property-descriptor.js' |
| 2 | + |
1 | 3 | type PropertyType = 'field' | 'getter' | 'setter' | 'method' |
2 | | -interface PropertyDecorator { |
| 4 | +export interface PropertyDecorator { |
3 | 5 | (proto: object, key: PropertyKey, descriptor?: PropertyDescriptor): void |
4 | 6 | readonly static: unique symbol |
5 | 7 | } |
6 | | -type GetMarks = (instance: object) => Set<PropertyKey> |
7 | | -export function createMark(validate: (key: PropertyKey, type: PropertyType) => void): [PropertyDecorator, GetMarks] { |
| 8 | +type GetMarks<T> = (instance: T) => Set<PropertyKey> |
| 9 | +type InitializeMarks<T> = (instance: T) => void |
| 10 | + |
| 11 | +type Context = { |
| 12 | + kind: PropertyType |
| 13 | + name: PropertyKey |
| 14 | + access: PropertyDescriptor |
| 15 | +} |
| 16 | + |
| 17 | +const getType = (descriptor?: PropertyDescriptor): PropertyType => { |
| 18 | + if (descriptor) { |
| 19 | + if (typeof descriptor.value === 'function') return 'method' |
| 20 | + if (typeof descriptor.get === 'function') return 'getter' |
| 21 | + if (typeof descriptor.set === 'function') return 'setter' |
| 22 | + } |
| 23 | + return 'field' |
| 24 | +} |
| 25 | + |
| 26 | +export function createMark<T extends object>( |
| 27 | + validate: (context: {name: PropertyKey; kind: PropertyType}) => void, |
| 28 | + initialize: (instance: T, context: Context) => PropertyDescriptor | void |
| 29 | +): [PropertyDecorator, GetMarks<T>, InitializeMarks<T>] { |
8 | 30 | const marks = new WeakMap<object, Set<PropertyKey>>() |
9 | | - function get(proto: object): Set<PropertyKey> { |
| 31 | + const get = (proto: object): Set<PropertyKey> => { |
10 | 32 | if (!marks.has(proto)) { |
11 | 33 | const parent = Object.getPrototypeOf(proto) |
12 | | - marks.set(proto, new Set(marks.get(parent) || [])) |
| 34 | + marks.set(proto, new Set(parent ? get(parent) || [] : [])) |
13 | 35 | } |
14 | 36 | return marks.get(proto)! |
15 | 37 | } |
16 | | - const marker = (proto: object, key: PropertyKey, descriptor?: PropertyDescriptor): void => { |
17 | | - if (get(proto).has(key)) return |
18 | | - let type: PropertyType = 'field' |
19 | | - if (descriptor) { |
20 | | - if (typeof descriptor.value === 'function') type = 'method' |
21 | | - if (typeof descriptor.get === 'function') type = 'getter' |
22 | | - if (typeof descriptor.set === 'function') type = 'setter' |
23 | | - } |
24 | | - validate(key, type) |
25 | | - get(proto).add(key) |
| 38 | + const marker = (proto: object, name: PropertyKey, descriptor?: PropertyDescriptor): void => { |
| 39 | + if (get(proto).has(name)) return |
| 40 | + validate({name, kind: getType(descriptor)}) |
| 41 | + get(proto).add(name) |
26 | 42 | } |
27 | 43 | marker.static = Symbol() |
28 | | - |
| 44 | + const getMarks = (instance: T): Set<PropertyKey> => { |
| 45 | + const proto = Object.getPrototypeOf(instance) |
| 46 | + for (const key of proto.constructor[marker.static] || []) { |
| 47 | + marker(proto, key, Object.getOwnPropertyDescriptor(proto, key)) |
| 48 | + } |
| 49 | + return new Set(get(proto)) |
| 50 | + } |
29 | 51 | return [ |
30 | 52 | marker as PropertyDecorator, |
31 | | - (instance: object): Set<PropertyKey> => { |
32 | | - const proto = Object.getPrototypeOf(instance) |
33 | | - for (const key of proto.constructor[marker.static] || []) { |
34 | | - marker(proto, key, Object.getOwnPropertyDescriptor(proto, key)) |
| 53 | + getMarks, |
| 54 | + (instance: T): void => { |
| 55 | + for (const name of getMarks(instance)) { |
| 56 | + const access: PropertyDescriptor = getPropertyDescriptor(instance, name) || { |
| 57 | + value: void 0, |
| 58 | + configurable: true, |
| 59 | + writable: true, |
| 60 | + enumerable: true |
| 61 | + } |
| 62 | + const newDescriptor = initialize(instance, {name, kind: getType(access), access}) || access |
| 63 | + Object.defineProperty(instance, name, newDescriptor) |
35 | 64 | } |
36 | | - return new Set(get(proto)) |
37 | 65 | } |
38 | 66 | ] |
39 | 67 | } |
0 commit comments