@turbox3d/reactivity
Version:
Large-scale reactive state management library
140 lines (131 loc) • 5.03 kB
text/typescript
import { bind, convert2UniqueString, isPromise, invariant, fail, quacksLikeADecorator } from '@turbox3d/shared';
import { store } from '../core/store';
import { EMPTY_ACTION_NAME, MATERIAL_TYPE } from '../const/symbol';
import { Mutation, BabelDescriptor } from '../interfaces';
import { Domain } from '../core/domain';
import { EMaterialType } from '../const/enums';
export interface MutationConfig {
immediately: boolean;
displayName?: string;
forceSaveHistory?: boolean;
isNeedRecord?: boolean;
}
function createMutation(target: object | undefined, name: string | symbol | number, original: Mutation, config: MutationConfig) {
const stringMethodName = convert2UniqueString(name);
const mutationWp = function (...payload: any[]) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let _this = this;
if (!target) {
_this = new Domain();
}
if (!store) {
fail('store is not ready, please init first.');
}
const result = store.dispatch({
name: stringMethodName,
displayName: config.displayName || EMPTY_ACTION_NAME,
payload,
type: EMaterialType.MUTATION,
domain: _this,
original: bind(original, _this) as Mutation,
immediately: !!config.immediately,
forceSaveHistory: !!config.forceSaveHistory,
isNeedRecord: !!config.isNeedRecord,
});
if (isPromise(result)) {
return new Promise((resolve, reject) => {
(result as Promise<any>).then((res) => {
resolve(res);
}, (err) => {
reject(err);
});
});
}
if (result && result instanceof Error) {
throw result;
}
return result;
};
mutationWp[MATERIAL_TYPE] = EMaterialType.MUTATION;
return mutationWp;
}
export function mutation(target: object, name: string | symbol | number, descriptor?: BabelDescriptor<Mutation>): any;
export function mutation(displayName?: string, immediately?: boolean, forceSaveHistory?: boolean, isNeedRecord?: boolean): (target: object, name: string | symbol | number, descriptor?: BabelDescriptor<Mutation>) => any;
export function mutation(name: string | symbol | number, original: Mutation, config?: MutationConfig): (...payload: any[]) => any;
/**
* decorator @mutation, update state by mutation styling.
*/
export function mutation(...args: any[]) {
const config: MutationConfig = {
immediately: false,
displayName: '',
forceSaveHistory: false,
isNeedRecord: true,
};
if (typeof args[0] === 'string' && typeof args[1] === 'function') {
if (args[2] !== void 0) {
config.displayName = args[2].displayName;
config.immediately = args[2].immediately;
config.forceSaveHistory = !!args[2].forceSaveHistory;
config.isNeedRecord = !!args[2].isNeedRecord;
}
const name = args[0];
const original = args[1];
if (original[MATERIAL_TYPE] === EMaterialType.MUTATION) {
return original;
}
return createMutation(undefined, name, original, config);
}
const decorator = (target: object, name: string | symbol | number, descriptor?: BabelDescriptor<Mutation>): BabelDescriptor<Mutation> => {
// typescript only: @mutation method = () => {}
if (descriptor === void 0) {
let mutationFunc: Function;
return Object.defineProperty(target, name, {
enumerable: true,
configurable: true,
get() {
return mutationFunc;
},
set(original: Mutation) {
if (original[MATERIAL_TYPE] === EMaterialType.MUTATION) {
mutationFunc = original;
} else {
mutationFunc = createMutation(target, name, original, config);
}
},
});
}
// babel/typescript: @mutation method() {}
if (descriptor.value !== void 0) {
const original: Mutation = descriptor.value;
if (original[MATERIAL_TYPE] === EMaterialType.MUTATION) {
descriptor.value = original;
} else {
descriptor.value = createMutation(target, name, original, config);
}
return descriptor;
}
// babel only: @mutation method = () => {}
const { initializer } = descriptor;
descriptor.initializer = function () {
invariant(!!initializer, 'The initializer of the descriptor doesn\'t exist, please compile it by using babel and correspond decorator plugin.');
const original = (initializer && initializer.call(this)) as Mutation;
if (original[MATERIAL_TYPE] === EMaterialType.MUTATION) {
return original;
}
return createMutation(target, name, original, config);
};
return descriptor;
};
if (quacksLikeADecorator(args)) {
// @mutation
// eslint-disable-next-line prefer-spread
return decorator.apply(null, args as any);
}
// @mutation(args)
config.displayName = args[0] || '';
config.immediately = args[1] !== void 0 ? args[1] : false;
config.forceSaveHistory = args[2] !== void 0 ? args[2] : false;
config.isNeedRecord = args[3] !== void 0 ? args[3] : true;
return decorator;
}