/*--------------------------------------------------------------------------------------------- * Licensed under the MIT License. * * @Author: fantasticsoul *--------------------------------------------------------------------------------------------*/ import { buildLimuApis, FINISH_HANDLER_MAP } from './core/build-limu-apis'; import { deepCopy as deepCopyFn } from './core/copy'; import { deepFreeze as deepFreezeFn } from './core/freeze'; import { getDraftMeta, getDraftProxyMeta, getMetaVer, isDiff as isDiffFn, isDraft as isDraftFn, shallowCompare as shallowCompareFn, } from './core/meta'; import { current as currentFn, markRaw as markRawFn, original as originalFn } from './core/user-util'; import type { ICreateDraftOptions, IImutOptions, IInnerCreateDraftOptions, IOperateParams, ObjectLike, Op } from './inner-types'; import { IMMUT_BASE, VER as v } from './support/consts'; import { conf } from './support/inner-data'; import { canBeNum, getDataType, has, isFn, isMap, isMardedRaw, isObject, isPrimitive, isPromiseFn, isPromiseResult, isSet, isSymbol, noop, } from './support/util'; // 避免降到测试覆盖率 // export { getDraftMeta, isDraft, isDiff, shallowCompare }; export type { ICreateDraftOptions, IOperateParams, ObjectLike, Op }; // isDraft isDiff shallowCompare 高频使用的从顶层暴露,其他随 limuUtils 里暴露 /** * 判断是否是草稿 * @see https://tnfe.github.io/limu/docs/api/basic/limu-utils#isdraft */ export const isDraft = isDraftFn; /** * 判断两个值是否相同 * @see https://tnfe.github.io/limu/docs/api/basic/limu-utils#isdiff */ export const isDiff = isDiffFn; /** * 浅比较两个对象是否相同 * @see https://tnfe.github.io/limu/docs/api/basic/limu-utils#shallowcompare */ export const shallowCompare = shallowCompareFn; /** * 内部工具函数集合,写为如下格式会降低覆盖率,故导入后再导出 * ```txt * import * as limuUtils; from './support/util'; * const { isFn, isPromiseFn, isPromiseResult } = limuUtils;; * export { limuUtils; }; * ``` */ export const limuUtils = { has, noop, isObject, isMap, isSet, isFn, isPrimitive, isPromiseFn, isPromiseResult, isSymbol, isMardedRaw, canBeNum, isDraft, isDiff, shallowCompare, getDraftMeta, getDataType, }; export type LimuUtils = typeof limuUtils; type LimuApis = ReturnType; export type Draft = T; export type CreateDraft = (base: T, options?: ICreateDraftOptions) => Draft; export type FinishDraft = (draft: T) => T; export type ProduceCb = (draft: Draft) => void; export type GenNewStateCb = (state: T) => T; // export type GenNewPatchesCb = (state: T) => any[]; /** * 同步完成生成草稿、修改草稿、结束草稿3个步骤的函数 */ export interface IProduce { (baseState: T, recipe: ProduceCb, options?: ICreateDraftOptions): T; /** * use in react: * setState(produce(draft=>{ * draft.name = 2; * })); */ (recipe: ProduceCb, options?: ICreateDraftOptions): GenNewStateCb; } // export interface IProduceWithPatches { // (baseState: T, cb: ProduceCb, options?: ICreateDraftOptions): any[]; // } function checkCbFn(cb: any) { if (!isFn(cb)) { throw new Error('produce callback is not a function'); } } const notRootDraftTip = 'Not a Limu root draft'; const finishedTip = 'Draft has been finished!'; function getFinishHandler(draft: Draft): LimuApis['finishDraft'] { const finishHandler = FINISH_HANDLER_MAP.get(draft); if (!finishHandler) { if (!getMetaVer(draft)) { throw new Error(notRootDraftTip); } const meta = getDraftProxyMeta(draft); if (meta?.level !== 0) { throw new Error(notRootDraftTip); } throw new Error(finishedTip); } return finishHandler; } /** limu 的版本号 */ export const VER = v; /** * 创建草稿函数,可对返回的草稿对象直接修改,此修改不会影响原始对象 * @see https://tnfe.github.io/limu/docs/api/basic/create-draft */ export function createDraft(base: T, options?: ICreateDraftOptions): Draft { const apis = buildLimuApis(options as IInnerCreateDraftOptions); const rootDraft = apis.createDraft(base); return rootDraft; } /** * 结束草稿的函数,生成的新对象和原始对象会结构共享 * @see https://tnfe.github.io/limu/docs/api/basic/create-draft */ export function finishDraft(draft: Draft): T { const finishHandler: LimuApis['finishDraft'] = getFinishHandler(draft); return finishHandler(draft); } /** * 生成一个不可修改的对象im,但原始对象的修改将同步会影响到im * immut 采用了读时浅代理的机制,相比deepFreeze会拥有更好性能,适用于不暴露原始对象出去,只暴露生成的不可变对象出去的场景 * @see: https://tnfe.github.io/limu/docs/api/basic/immut */ export function immut(base: T, options?: IImutOptions): T { const limuApis = buildLimuApis({ ...(options || {}), readOnly: true, [IMMUT_BASE]: true }); const immutData = limuApis.createDraft(base); return immutData; } /** * 结束由 immut 创建的草稿对象 */ export function finishImmut(immutDraft: Draft): T { const finishHandler: LimuApis['finishDraft'] = getFinishHandler(immutDraft); return finishHandler(immutDraft, true); } // see issue https://github.com/tnfe/limu/issues/5 function checkCbPromise(cb: any, result: any) { if (isPromiseFn(cb) || isPromiseResult(result)) { throw new Error('produce callback can not be a promise function or result'); } } function innerProduce(baseState: any, cb: any, options?: ICreateDraftOptions) { checkCbFn(cb); const draft = createDraft(baseState, options); const result = cb(draft); checkCbPromise(cb, result); return finishDraft(draft); } function produceFn(baseState: any, cb: any, options?: ICreateDraftOptions) { if (!cb || !isFn(cb)) { // expect baseState to be a callback, support curried invocation // expect cb to be options const mayCb = baseState; const mayOptions = cb; checkCbFn(baseState); return (state: any) => { return innerProduce(state, mayCb, mayOptions); }; } return innerProduce(baseState, cb, options) as any; } /** * ```ts * // normal use: * const next = produce(draft=>draft.a=1); * * // use in react: * setState(produce(draft=>{ * draft.name = 2; * })); * ``` */ export const produce = produceFn as unknown as IProduce; // to be implemented in the future // export const produceWithPatches = producePatchesFn as unknown as IProduceWithPatches; /** * 深冻结传入的值,返回的是冻结后的新值,如传入原始值则不做任何操作,原样返回 */ export const deepFreeze = deepFreezeFn; /** * 对传入值做深克隆,返回的是克隆后的新值,如传入原始值则不做任何操作,原样返回 */ export function deepCopy(obj: T) { return deepCopyFn(obj); } /** * 设置全局冻结配置,可在 createDraft, produce 时二次覆盖此全局配置 */ export function setAutoFreeze(autoFreeze: boolean) { conf.autoFreeze = autoFreeze; } /** * 获取全局设置的 autoFreeze 值 */ export function getAutoFreeze() { return conf.autoFreeze; } /** * 查看草稿对应的原始值 */ export const original = originalFn; /** * 标记对象为 raw,此对象再次被复用时不会被代理,需注意标为 raw 后此对象会失去结构共享特性 */ export const markRaw = markRawFn; /** * 导出草稿的副本数据( 即深克隆当前草稿 ) */ export const current = currentFn; // disable [export default], let esModuleInterop=true, allowSyntheticDefaultImports=true works in tsconfig.json // export default produce;