Skip to content

Commit

Permalink
fix(array): update
Browse files Browse the repository at this point in the history
  • Loading branch information
unadlib committed Jan 4, 2025
1 parent aa048b2 commit a712cee
Show file tree
Hide file tree
Showing 8 changed files with 4,415 additions and 65 deletions.
57 changes: 39 additions & 18 deletions src/draft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
Finalities,
Patches,
ProxyDraft,
Options,
Operation,
DraftOptions,
} from './interface';
import { dataTypes, PROXY_DRAFT } from './constant';
import { mapHandler, mapHandlerKeys } from './map';
Expand Down Expand Up @@ -38,7 +38,7 @@ const draftsCache = new WeakSet<object>();

let arrayHandling = false;

const proxyArrayMethods = ['splice', 'shift', 'unshift', 'reverse'];
const proxyArrayMethods = ['splice', 'shift', 'unshift'];

const proxyHandler: ProxyHandler<ProxyDraft> = {
get(target: ProxyDraft, key: string | number | symbol, receiver: any) {
Expand Down Expand Up @@ -94,19 +94,39 @@ const proxyHandler: ProxyHandler<ProxyDraft> = {
if (!has(source, key)) {
const desc = getDescriptor(source, key);
const value = desc?.value;
if (target.type === DraftType.Array) {
if (proxyArrayMethods.includes(key as string)) {
return function (this: any, ...args: any[]) {
let returnValue: any;
arrayHandling = true;
try {
returnValue = value.apply(this, args);
} finally {
arrayHandling = false;
if (
target.type === DraftType.Array &&
proxyArrayMethods.includes(key as string)
) {
const handleItem = (item: any) => {
if (!isDraftable(item)) return item;
ensureShallowCopy(target);
const draft = createDraft({
original: item,
parentDraft: target,
key: undefined,
finalities: target.finalities,
options: target.options,
});
// TODO: support for custom shallow copy function
return draft;
};
return function (this: any, ...args: any[]) {
let returnValue: any;
arrayHandling = true;
try {
returnValue = value.apply(this, args);
if (key === 'splice' && returnValue.length > 0) {
returnValue = returnValue.map(handleItem);
}
if (key === 'shift' && typeof returnValue === 'object') {
returnValue = handleItem(returnValue);
}
return returnValue;
};
}
} finally {
arrayHandling = false;
}
};
}
return desc
? `value` in desc
Expand All @@ -126,11 +146,11 @@ const proxyHandler: ProxyHandler<ProxyDraft> = {
if (
!arrayHandling &&
(value === peek(target.original, key) ||
target.options.skipFinalization!.has(value))
target.options.skipFinalization.has(value))
) {
const shouldSkip = target.options.skipFinalization!.has(value);
const shouldSkip = target.options.skipFinalization.has(value);
if (shouldSkip) {
target.options.skipFinalization!.delete(value);
target.options.skipFinalization.delete(value);
}
ensureShallowCopy(target);
target.copy![key] = createDraft({
Expand All @@ -151,7 +171,8 @@ const proxyHandler: ProxyHandler<ProxyDraft> = {
return target.copy![key];
}
if (arrayHandling && !isDraft(value) && isDraftable(value)) {
target.options.skipFinalization!.add(value);
// !case: handle the case of assigning the original array item via array methods(`splice`, `shift``, `unshift`, `reverse`)
target.options.skipFinalization.add(value);
}
return value;
},
Expand Down Expand Up @@ -258,7 +279,7 @@ export function createDraft<T extends object>(createDraftOptions: {
parentDraft?: ProxyDraft | null;
key?: string | number | symbol;
finalities: Finalities;
options: Options<any, any>;
options: DraftOptions;
}): T {
const { original, parentDraft, key, finalities, options } =
createDraftOptions;
Expand Down
4 changes: 2 additions & 2 deletions src/draftify.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
DraftOptions,
Finalities,
Options,
Patches,
PatchesOptions,
Result,
Expand All @@ -15,7 +15,7 @@ export function draftify<
F extends boolean = false,
>(
baseState: T,
options: Options<O, F>
options: DraftOptions
): [T, (returnedValue: [T] | []) => Result<T, O, F>] {
const finalities: Finalities = {
draft: [],
Expand Down
16 changes: 12 additions & 4 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ export interface ProxyDraft<T = any> {
copy: T | null;
proxy: T | null;
finalities: Finalities;
options: Options<any, any> & {
updatedValues?: WeakMap<any, any>;
skipFinalization?: WeakSet<any>;
};
options: DraftOptions;
parent?: ProxyDraft | null;
key?: string | number | symbol;
setMap?: Map<any, ProxyDraft>;
Expand Down Expand Up @@ -125,6 +122,17 @@ export interface Options<O extends PatchesOptions, F extends boolean> {
mark?: Mark<O, F>;
}

export type DraftOptions = Options<any, any> & {
/**
* a collection for circular reference check
*/
updatedValues?: WeakMap<any, any>;
/**
* a collection for array item skip deep check
*/
skipFinalization: WeakSet<any>;
};

export interface ExternalOptions<O extends PatchesOptions, F extends boolean> {
/**
* In strict mode, Forbid accessing non-draftable values and forbid returning a non-draft value.
Expand Down
5 changes: 2 additions & 3 deletions src/makeCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ExternalOptions,
PatchesOptions,
Result,
DraftOptions,
} from './interface';
import { draftify } from './draftify';
import {
Expand Down Expand Up @@ -143,9 +144,7 @@ export const makeCreator: MakeCreator = (arg) => {
const enablePatches = options.enablePatches ?? false;
const strict = options.strict ?? false;
const enableAutoFreeze = options.enableAutoFreeze ?? false;
const _options: Options<any, any> & {
skipFinalization: WeakSet<any>;
} = {
const _options: DraftOptions = {
enableAutoFreeze,
mark,
strict,
Expand Down
4 changes: 3 additions & 1 deletion src/utils/finalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export function handleValue(
!isDraftable(target, options) ||
handledSet.has(target) ||
Object.isFrozen(target) ||
(options!.skipFinalization!.has(target))
options!.skipFinalization!.has(target)
// It should skip the finalization process
// This can avoid unnecessary deep traversal, as these objects are non-draft and do not contain draft in their deep object.
)
return;
const isSet = target instanceof Set;
Expand Down
Loading

0 comments on commit a712cee

Please sign in to comment.