mobx
Version:
Simple, scalable state management.
114 lines (107 loc) • 3.74 kB
text/typescript
import {
ObservableObjectAdministration,
observable,
Annotation,
defineProperty,
createAction,
globalState,
flow,
computed,
autoAction,
isGenerator,
MakeResult,
die
} from "../internal"
const AUTO = "true"
export const autoAnnotation: Annotation = createAutoAnnotation()
export function createAutoAnnotation(options?: object): Annotation {
return {
annotationType_: AUTO,
options_: options,
make_,
extend_,
decorate_20223_
}
}
function make_(
adm: ObservableObjectAdministration,
key: PropertyKey,
descriptor: PropertyDescriptor,
source: object
): MakeResult {
// getter -> computed
if (descriptor.get) {
return computed.make_(adm, key, descriptor, source)
}
// lone setter -> action setter
if (descriptor.set) {
// TODO make action applicable to setter and delegate to action.make_
const set = createAction(key.toString(), descriptor.set) as (v: any) => void
// own
if (source === adm.target_) {
return adm.defineProperty_(key, {
configurable: globalState.safeDescriptors ? adm.isPlainObject_ : true,
set
}) === null
? MakeResult.Cancel
: MakeResult.Continue
}
// proto
defineProperty(source, key, {
configurable: true,
set
})
return MakeResult.Continue
}
// function on proto -> autoAction/flow
if (source !== adm.target_ && typeof descriptor.value === "function") {
if (isGenerator(descriptor.value)) {
const flowAnnotation = this.options_?.autoBind ? flow.bound : flow
return flowAnnotation.make_(adm, key, descriptor, source)
}
const actionAnnotation = this.options_?.autoBind ? autoAction.bound : autoAction
return actionAnnotation.make_(adm, key, descriptor, source)
}
// other -> observable
// Copy props from proto as well, see test:
// "decorate should work with Object.create"
let observableAnnotation = this.options_?.deep === false ? observable.ref : observable
// if function respect autoBind option
if (typeof descriptor.value === "function" && this.options_?.autoBind) {
descriptor.value = descriptor.value.bind(adm.proxy_ ?? adm.target_)
}
return observableAnnotation.make_(adm, key, descriptor, source)
}
function extend_(
adm: ObservableObjectAdministration,
key: PropertyKey,
descriptor: PropertyDescriptor,
proxyTrap: boolean
): boolean | null {
// getter -> computed
if (descriptor.get) {
return computed.extend_(adm, key, descriptor, proxyTrap)
}
// lone setter -> action setter
if (descriptor.set) {
// TODO make action applicable to setter and delegate to action.extend_
return adm.defineProperty_(
key,
{
configurable: globalState.safeDescriptors ? adm.isPlainObject_ : true,
set: createAction(key.toString(), descriptor.set) as (v: any) => void
},
proxyTrap
)
}
// other -> observable
// if function respect autoBind option
if (typeof descriptor.value === "function" && this.options_?.autoBind) {
descriptor.value = descriptor.value.bind(adm.proxy_ ?? adm.target_)
}
let observableAnnotation = this.options_?.deep === false ? observable.ref : observable
return observableAnnotation.extend_(adm, key, descriptor, proxyTrap)
}
function decorate_20223_(this: Annotation, desc, context: ClassGetterDecoratorContext) {
die(`'${this.annotationType_}' cannot be used as a decorator`)
}