Skip to content

Commit

Permalink
fix: prevent duplicative array observation patch (FE2) (#5656)
Browse files Browse the repository at this point in the history
* fix: prevent duplicative array observation patch

* Change files

* refactor: re-organize array observer code

Co-authored-by: EisenbergEffect <[email protected]>
  • Loading branch information
2 people authored and nicholasrice committed May 5, 2022
1 parent ca5d1d2 commit d7c903e
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 129 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: prevent duplicative array observation patch",
"packageName": "@microsoft/fast-element",
"email": "[email protected]",
"dependentChangeType": "patch"
}
272 changes: 143 additions & 129 deletions packages/web-components/fast-element/src/observation/array-observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,6 @@ import { SubscriberSet } from "./notifier.js";
import type { Notifier } from "./notifier.js";
import { Observable } from "./observable.js";

function adjustIndex(changeRecord: Splice, array: any[]): Splice {
let index = changeRecord.index;
const arrayLength = array.length;

if (index > arrayLength) {
index = arrayLength - changeRecord.addedCount;
} else if (index < 0) {
index =
arrayLength + changeRecord.removed.length + index - changeRecord.addedCount;
}

changeRecord.index = index < 0 ? 0 : index;
return changeRecord;
}

class ArrayObserver extends SubscriberSet {
private oldCollection: any[] | undefined = void 0;
private splices: Splice[] | undefined = void 0;
Expand Down Expand Up @@ -68,115 +53,7 @@ class ArrayObserver extends SubscriberSet {
}
}

const proto = Array.prototype;
const pop = proto.pop;
const push = proto.push;
const reverse = proto.reverse;
const shift = proto.shift;
const sort = proto.sort;
const splice = proto.splice;
const unshift = proto.unshift;
const arrayOverrides = {
pop(...args) {
const notEmpty = this.length > 0;
const result = pop.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0 && notEmpty) {
o.addSplice(new Splice(this.length, [result], 0));
}

return result;
},

push(...args) {
const result = push.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.addSplice(
adjustIndex(new Splice(this.length - args.length, [], args.length), this)
);
}

return result;
},

reverse(...args) {
let oldArray;
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.flush();
oldArray = this.slice();
}

const result = reverse.apply(this, args);

if (o !== void 0) {
o.reset(oldArray);
}

return result;
},

shift(...args) {
const notEmpty = this.length > 0;
const result = shift.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0 && notEmpty) {
o.addSplice(new Splice(0, [result], 0));
}

return result;
},

sort(...args) {
let oldArray;
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.flush();
oldArray = this.slice();
}

const result = sort.apply(this, args);

if (o !== void 0) {
o.reset(oldArray);
}

return result;
},

splice(...args) {
const result = splice.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.addSplice(
adjustIndex(
new Splice(+args[0], result, args.length > 2 ? args.length - 2 : 0),
this
)
);
}

return result;
},

unshift(...args) {
const result = unshift.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.addSplice(adjustIndex(new Splice(0, [], args.length), this));
}

return result;
},
};
let enabled = false;

/* eslint-disable prefer-rest-params */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
Expand All @@ -189,19 +66,156 @@ const arrayOverrides = {
* @public
*/
export function enableArrayObservation(): void {
if ((proto as any).$fastObservation) {
if (enabled) {
return;
}

(proto as any).$fastObservation = true;
enabled = true;

Observable.setArrayObserverFactory(
(collection: any[]): Notifier => {
return new ArrayObserver(collection);
}
);

Object.assign(proto, arrayOverrides);
const proto = Array.prototype;

if (!(proto as any).$fastPatch) {
(proto as any).$fastPatch = true;

const pop = proto.pop;
const push = proto.push;
const reverse = proto.reverse;
const shift = proto.shift;
const sort = proto.sort;
const splice = proto.splice;
const unshift = proto.unshift;

function adjustIndex(changeRecord: Splice, array: any[]): Splice {
let index = changeRecord.index;
const arrayLength = array.length;

if (index > arrayLength) {
index = arrayLength - changeRecord.addedCount;
} else if (index < 0) {
index =
arrayLength +
changeRecord.removed.length +
index -
changeRecord.addedCount;
}

changeRecord.index = index < 0 ? 0 : index;
return changeRecord;
}

Object.assign(proto, {
pop(...args) {
const notEmpty = this.length > 0;
const result = pop.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0 && notEmpty) {
o.addSplice(new Splice(this.length, [result], 0));
}

return result;
},

push(...args) {
const result = push.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.addSplice(
adjustIndex(
new Splice(this.length - args.length, [], args.length),
this
)
);
}

return result;
},

reverse(...args) {
let oldArray;
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.flush();
oldArray = this.slice();
}

const result = reverse.apply(this, args);

if (o !== void 0) {
o.reset(oldArray);
}

return result;
},

shift(...args) {
const notEmpty = this.length > 0;
const result = shift.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0 && notEmpty) {
o.addSplice(new Splice(0, [result], 0));
}

return result;
},

sort(...args) {
let oldArray;
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.flush();
oldArray = this.slice();
}

const result = sort.apply(this, args);

if (o !== void 0) {
o.reset(oldArray);
}

return result;
},

splice(...args) {
const result = splice.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.addSplice(
adjustIndex(
new Splice(
+args[0],
result,
args.length > 2 ? args.length - 2 : 0
),
this
)
);
}

return result;
},

unshift(...args) {
const result = unshift.apply(this, args);
const o = this.$fastController as ArrayObserver;

if (o !== void 0) {
o.addSplice(adjustIndex(new Splice(0, [], args.length), this));
}

return result;
},
});
}
}
/* eslint-enable prefer-rest-params */
/* eslint-enable @typescript-eslint/explicit-function-return-type */

0 comments on commit d7c903e

Please sign in to comment.