Skip to content

Commit 8ae09da

Browse files
brrichardsanthony-murphy-agent
authored andcommitted
Enabled unicorn/no-array-reduce in packages/tree (microsoft#26143)
Removes the unicorn/no-array-reduce: off override from the @fluidframework/tree package and fixes the resulting errors. Converts all Array.reduce() calls to explicit for loops. This is part of an ongoing effort to incrementally remove global ESLint rule overrides from eslint.config.mts and .eslintrc.cjs. Changes: - Production code (2 files) - Test code (6 files) No inline overrides were needed - all reduce usages could be converted to for loops.
1 parent 589b4db commit 8ae09da

File tree

10 files changed

+95
-86
lines changed

10 files changed

+95
-86
lines changed

packages/dds/tree/.eslintrc.cjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ module.exports = {
5252

5353
"unicorn/consistent-function-scoping": "off",
5454
"unicorn/no-array-method-this-argument": "off",
55-
"unicorn/no-array-reduce": "off",
5655
"unicorn/no-await-expression-member": "off",
5756
"unicorn/no-new-array": "off",
5857
"unicorn/no-null": "off",

packages/dds/tree/eslint.config.mts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ const config: Linter.Config[] = [
3232
"jsdoc/require-description": "warn",
3333
"unicorn/consistent-function-scoping": "off",
3434
"unicorn/no-array-method-this-argument": "off",
35-
"unicorn/no-array-reduce": "off",
3635
"unicorn/no-await-expression-member": "off",
3736
"unicorn/no-new-array": "off",
3837
"unicorn/no-null": "off",

packages/dds/tree/src/core/rebase/utils.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,10 +403,14 @@ export function rebaseChangeOverChanges<TChange>(
403403
getRevInfoFromTaggedChanges([...changesToRebaseOver, changeToRebase]),
404404
);
405405

406-
return changesToRebaseOver.reduce(
407-
(a, b) => mapTaggedChange(changeToRebase, changeRebaser.rebase(a, b, revisionMetadata)),
408-
changeToRebase,
409-
).change;
406+
let result = changeToRebase;
407+
for (const b of changesToRebaseOver) {
408+
result = mapTaggedChange(
409+
changeToRebase,
410+
changeRebaser.rebase(result, b, revisionMetadata),
411+
);
412+
}
413+
return result.change;
410414
}
411415

412416
// TODO: Deduplicate

packages/dds/tree/src/shared-tree-core/editManager.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,27 +1090,27 @@ class SharedBranch<TEditor extends ChangeFamilyEditor, TChangeset> {
10901090
const parentTrunkBase =
10911091
trunkRevisionCache.get(data.base ?? rootRevision) ??
10921092
fail(0xc61 /* Expected base revision to be in trunk cache */);
1093-
this.trunk.setHead(
1094-
data.trunk.reduce((base, c) => {
1095-
const sequenceId: SequenceId =
1096-
c.indexInBatch === undefined
1097-
? {
1098-
sequenceNumber: c.sequenceNumber,
1099-
}
1100-
: {
1101-
sequenceNumber: c.sequenceNumber,
1102-
indexInBatch: c.indexInBatch,
1103-
};
1104-
const commit = mintCommit(base, c);
1105-
this.sequenceIdToCommit.set(sequenceId, commit);
1106-
this.commitMetadata.set(c.revision, {
1107-
sequenceId,
1108-
sessionId: c.sessionId,
1109-
});
1110-
trunkRevisionCache.set(c.revision, commit);
1111-
return commit;
1112-
}, parentTrunkBase),
1113-
);
1093+
let trunkHead = parentTrunkBase;
1094+
for (const c of data.trunk) {
1095+
const sequenceId: SequenceId =
1096+
c.indexInBatch === undefined
1097+
? {
1098+
sequenceNumber: c.sequenceNumber,
1099+
}
1100+
: {
1101+
sequenceNumber: c.sequenceNumber,
1102+
indexInBatch: c.indexInBatch,
1103+
};
1104+
const commit = mintCommit(trunkHead, c);
1105+
this.sequenceIdToCommit.set(sequenceId, commit);
1106+
this.commitMetadata.set(c.revision, {
1107+
sequenceId,
1108+
sessionId: c.sessionId,
1109+
});
1110+
trunkRevisionCache.set(c.revision, commit);
1111+
trunkHead = commit;
1112+
}
1113+
this.trunk.setHead(trunkHead);
11141114

11151115
this.localBranch.setHead(this.trunk.getHead());
11161116

@@ -1119,13 +1119,13 @@ class SharedBranch<TEditor extends ChangeFamilyEditor, TChangeset> {
11191119
trunkRevisionCache.get(branch.base) ??
11201120
fail(0xad7 /* Expected summary branch to be based off of a revision in the trunk */);
11211121

1122+
let branchHead = commit;
1123+
for (const c of branch.commits) {
1124+
branchHead = mintCommit(branchHead, c);
1125+
}
11221126
this.peerLocalBranches.set(
11231127
sessionId,
1124-
new SharedTreeBranch(
1125-
branch.commits.reduce(mintCommit, commit),
1126-
this.changeFamily,
1127-
this.mintRevisionTag,
1128-
),
1128+
new SharedTreeBranch(branchHead, this.changeFamily, this.mintRevisionTag),
11291129
);
11301130
}
11311131
}

packages/dds/tree/src/test/core/tree/anchorSet.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -616,14 +616,15 @@ type PathStep = [FieldKey, number];
616616

617617
function makePath(...steps: [PathStep, ...PathStep[]]): UpPath {
618618
assert(steps.length > 0, "Path cannot be empty");
619-
return steps.reduce(
620-
(path: UpPath | undefined, step: PathStep) => ({
619+
let path: UpPath | undefined;
620+
for (const step of steps) {
621+
path = {
621622
parent: path,
622623
parentField: step[0],
623624
parentIndex: step[1],
624-
}),
625-
undefined,
626-
) as UpPath;
625+
};
626+
}
627+
return path as UpPath;
627628
}
628629

629630
function makeFieldPath(field: FieldKey, ...stepsToFieldParent: PathStep[]): FieldUpPath {

packages/dds/tree/src/test/feature-libraries/modular-schema/modularChangesetUtil.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -160,22 +160,20 @@ function fieldChangeMapFromDescription(
160160
field: field.fieldKey,
161161
};
162162

163-
const fieldChangeset = field.children.reduce(
164-
(change: unknown, nodeDescription: NodeChangesetDescription) =>
165-
addNodeToField(
166-
family,
167-
change,
168-
nodeDescription,
169-
fieldId,
170-
changeHandler,
171-
nodes,
172-
nodeToParent,
173-
crossFieldKeys,
174-
idAllocator,
175-
),
176-
177-
field.changeset,
178-
);
163+
let fieldChangeset: unknown = field.changeset;
164+
for (const nodeDescription of field.children) {
165+
fieldChangeset = addNodeToField(
166+
family,
167+
fieldChangeset,
168+
nodeDescription,
169+
fieldId,
170+
changeHandler,
171+
nodes,
172+
nodeToParent,
173+
crossFieldKeys,
174+
idAllocator,
175+
);
176+
}
179177

180178
for (const { key, count } of changeHandler.getCrossFieldKeys(fieldChangeset)) {
181179
crossFieldKeys.set(key, count, fieldId);

packages/dds/tree/src/test/feature-libraries/optional-field/optionalChangeRebaser.test.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,15 @@ function rebaseComposedWrapped(
227227
change: TaggedChange<WrappedChangeset>,
228228
...baseChanges: TaggedChange<WrappedChangeset>[]
229229
): WrappedChangeset {
230-
const composed =
231-
baseChanges.length === 0
232-
? makeAnonChange(ChangesetWrapper.create(Change.empty()))
233-
: baseChanges.reduce((change1, change2) =>
234-
makeAnonChange(composeWrapped(change1, change2)),
235-
);
230+
let composed: TaggedChange<WrappedChangeset>;
231+
if (baseChanges.length === 0) {
232+
composed = makeAnonChange(ChangesetWrapper.create(Change.empty()));
233+
} else {
234+
composed = baseChanges[0];
235+
for (let i = 1; i < baseChanges.length; i++) {
236+
composed = makeAnonChange(composeWrapped(composed, baseChanges[i]));
237+
}
238+
}
236239

237240
return rebaseWrapped(change, composed, metadata);
238241
}

packages/dds/tree/src/test/feature-libraries/sequence-field/utils.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,18 @@ export function composeDeep(
242242
): WrappedChange {
243243
const metadata = revisionMetadata ?? defaultRevisionMetadataFromChanges(changes);
244244

245-
return changes.length === 0
246-
? ChangesetWrapper.create([])
247-
: changes.reduce((change1, change2) =>
248-
makeAnonChange(
249-
ChangesetWrapper.compose(change1, change2, (c1, c2, composeChild) =>
250-
composePair(c1.change, c2.change, composeChild, metadata, idAllocatorFromMaxId()),
251-
),
252-
),
253-
).change;
245+
if (changes.length === 0) {
246+
return ChangesetWrapper.create([]);
247+
}
248+
let result = changes[0];
249+
for (let i = 1; i < changes.length; i++) {
250+
result = makeAnonChange(
251+
ChangesetWrapper.compose(result, changes[i], (c1, c2, composeChild) =>
252+
composePair(c1.change, c2.change, composeChild, metadata, idAllocatorFromMaxId()),
253+
),
254+
);
255+
}
256+
return result.change;
254257
}
255258

256259
export function composeNoVerify(

packages/dds/tree/src/test/shared-tree-core/edit-manager/editManagerScenario.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -245,17 +245,17 @@ export function runUnitTestScenario(
245245

246246
// For each peer, find its next step and extract the ref number.
247247
// The min of all these ref numbers for all peers is the highest possible min sequence number across those peers.
248-
const minPeerRef = activePeers
249-
.map(
250-
(peer) =>
251-
steps
252-
.filter(
253-
(s): s is UnitTestPullStepWithIntention =>
254-
s.type === "Pull" && s.from === peer,
255-
)
256-
.find((s) => s.seq > sequenceNumber)?.ref ?? Number.POSITIVE_INFINITY,
257-
)
258-
.reduce((p, c) => Math.min(p, c), Number.POSITIVE_INFINITY);
248+
const peerRefs = activePeers.map(
249+
(peer) =>
250+
steps
251+
.filter(
252+
(s): s is UnitTestPullStepWithIntention =>
253+
s.type === "Pull" && s.from === peer,
254+
)
255+
.find((s) => s.seq > sequenceNumber)?.ref ?? Number.POSITIVE_INFINITY,
256+
);
257+
const minPeerRef =
258+
peerRefs.length > 0 ? Math.min(...peerRefs) : Number.POSITIVE_INFINITY;
259259

260260
// Compute the true min sequence number by including our local session's last seen sequence number as well.
261261
return Math.min(sequenceNumber, minPeerRef);

packages/dds/tree/src/test/simple-tree/list.spec.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,21 @@ const schemaFactory = new SchemaFactory("test");
1414
describe("List", () => {
1515
/** Formats 'args' array, inserting commas and eliding trailing `undefine`s. */
1616
function prettyArgs(...args: any[]) {
17-
return args.reduce((prev: string, arg, index) => {
17+
let result = "";
18+
for (let index = 0; index < args.length; index++) {
1819
// If all remaining arguments are 'undefined' elide them.
1920
if (!args.slice(index).some((value) => value !== undefined)) {
20-
return prev;
21+
break;
2122
}
2223

2324
// If not the first argument add a comma separator.
24-
let next = index > 0 ? `${prev}, ` : prev;
25-
26-
next += pretty(arg);
25+
if (index > 0) {
26+
result += ", ";
27+
}
2728

28-
return next;
29-
}, "");
29+
result += pretty(args[index]);
30+
}
31+
return result;
3032
}
3133

3234
// eslint-disable-next-line @fluid-internal/fluid/no-markdown-links-in-jsdoc -- false positive AB#51719

0 commit comments

Comments
 (0)