-
Notifications
You must be signed in to change notification settings - Fork 523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improvement(merge-tree): Use recommended eslint config #21684
Conversation
@@ -77,7 +77,7 @@ export abstract class BaseSegment implements ISegment { | |||
// (undocumented) | |||
splitAt(pos: number): ISegment | undefined; | |||
// (undocumented) | |||
abstract toJSONObject(): any; | |||
abstract toJSONObject(): unknown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is better than suppressing the errors for any json stuff...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it affects the API surface in a way that might break consumers, we should suppress the error. This is a case where we would need to put it back to any
.
We should fix other violations where they can be fixed without too much effort, but the primary goal is to prevent new violations of our rules. So feel free to disable rules anywhere it would break the API, or if it would require substantial refactoring to accommodate the changes. We should leave TODO comments in any places where we disable though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to commenting wherever we disable so we don't lose the context and don't normalize suppression.
@@ -3,9 +3,10 @@ | |||
* Licensed under the MIT License. | |||
*/ | |||
|
|||
/* eslint-disable unicorn/no-useless-spread */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This rule has some false positives - we actually do need the spread operator, since the result of the mapping made by the autofix is an Iterable, not an array.
sindresorhus/eslint-plugin-unicorn#2018
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave a comment in the code explaining this next to the disable comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do the same thing without the spread operator.
const pending = [...client.mergeTree.pendingSegments.map((n) => n.data)];
can be changed to
const pending = Array.from(client.mergeTree.pendingSegments.map((n) => n.data));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, since all we do is shift the array, we could replace that with just iterating and skip the array entirely?
const pending = client.mergeTree.pendingSegments.map((n) => n.data);
const pendingIterator: Iterator<SegmentGroup, undefined> = pending[Symbol.iterator]();
opList = oldops.map((op) => ({
op: client.regeneratePendingOp(op.op, pendingIterator.next().value!),
refSeq: client.getCurrentSeq(),
}));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Array.from
with 2 arguments seems good. I think that's a bit different from what I suggested to Jillian while she was looking at it, but a small variant on it was rejected by a 'prefer-spread' type rule.
FWIW, I don't really like having a rule in our recommended config with such a large gap like this. It has an autofixer which will make correct code incorrect (which was exactly what happened to Jillian here), presumably because it assumes the return of .map
is already an array without actually having typing knowledge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In most such cases it probably complains at build time in which case it's not the worst, but I'm not sure that's always the case. IIRC @taylorsw04 or @noencke mentioned at some point getting toasted by linter autofixes in id-compressor that I think was also related to spread operators, but not sure if it was this rule or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I don't really like having a rule in our recommended config with such a large gap like this. It has an autofixer which will make correct code incorrect (which was exactly what happened to Jillian here), presumably because it assumes the return of .map is already an array without actually having typing knowledge.
That seems like a good reason to disable the rule, yeah. Definitely feel free to disable in the config here for this PR, since it's clearly causing problems. I can get a PR up disabling it in our shared config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
Array.from
with 2 arguments seems good. I think that's a bit different from what I suggested to Jillian while she was looking at it, but a small variant on it was rejected by a 'prefer-spread' type rule.
@Abe27342 @titrindl The Array.from is still getting rejected, so I'll leave the spread operator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR disabling the rule in the base config: #21804
@@ -262,9 +271,13 @@ export class LocalReferenceCollection { | |||
} | |||
|
|||
/** | |||
* Returns an iterator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we be more specific here? What is the returned iterator intended to iterate?
@@ -1170,17 +1205,19 @@ export class MergeTree { | |||
const locallyMovedSegments = this.locallyMovedSegments.get(localMovedSeq); | |||
|
|||
if (locallyMovedSegments) { | |||
locallyMovedSegments.segments.forEach((segment: ISegmentLeaf) => { | |||
segment.localMovedSeq = undefined; | |||
for (const segment of locallyMovedSegments.segments) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The typing difference might be a good reason to revert this particular change and disable the lint rule (with a comment explaining why).
if (this.properties) { | ||
jseg.props = { ...this.properties }; | ||
} | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's document why we can't convert this yet and/or leave a TODO to come back later and clean this up.
* @internal | ||
*/ | ||
export const compareNumbers = (a: number, b: number) => a - b; | ||
export const compareNumbers = (a: number, b: number): number => a - b; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Not for this PR) I'm surprised we need to export these from the package, even if just @internal
. Curious if anyone knows why these are currently being shared.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like they're used in partialLengths and a few tests, but I'm not sure if there's a specific reason to export them from here over defining them when needed. @Abe27342 thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it does look like they are exported from the package, so the tag does nothing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@anthony-murphy should it be removed then? I'm tracking some other cleanup with AB#8760, so I can do it as part of that work if so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
at minimum, the tag should be removed :-). We could also consider inlining them in the test cases we use them in if they're not strictly related (the comparisons are the idiomatic ones), but i'm kinda indifferent there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whatever you do here, follow-up pr is obviously fine. pre-existing.
@@ -121,6 +121,7 @@ export interface IMergeTreeInsertMsg extends IMergeTreeDelta { | |||
relativePos1?: IRelativePosition; | |||
pos2?: number; | |||
relativePos2?: IRelativePosition; | |||
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we leave explanations/TODOs for these disables?
assert( | ||
maxCount <= 16 && actualCount <= maxCount, | ||
0x3f0 /* count must be less than max, and max must be 16 or less */, | ||
); | ||
|
||
const ordinalWidth = 1 << (maxCount - actualCount); | ||
let ordinal: string; | ||
if (previousOrdinal === undefined) { | ||
ordinal = parentOrdinal + String.fromCharCode(ordinalWidth - 1); | ||
if (previousOrdinal === undefined || previousOrdinal === "") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appears to a behavioral change. Is this intentional / safe to make?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intentional - to find the parent ordinal, the tests use fromCodePoint, which returns an empty string if the result of making the elements passed in into a string has a length of 0. fromCharCode returned undefined, but the tests were not accurately determining if there was a parentOrdinal without the extra check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gives me a minor amount of pause toward actually following this lint rule as per other comment. I'm pretty confident our tests exercise the cases we care about here though, so not a huge deal.
@@ -381,6 +381,8 @@ export class PartialSequenceLengths { | |||
} | |||
|
|||
/** | |||
* PartialLengths for the leaves of a given MergeBlock. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This isn't clear to me. Might be helpful to write this out as a complete sentence for clarity.
@@ -140,7 +140,7 @@ describe("obliterate partial lengths", () => { | |||
refSeq, | |||
); | |||
}); | |||
|
|||
// here are tests failing w nouncheckedindexaccess |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a few more suggestions / nitpicks, but overall looks good. Thanks for working through all of this - this was one of the biggest packages left to migrate!
Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com>
@@ -320,6 +325,7 @@ export class AttributionCollection implements IAttributionCollection<Attribution | |||
if (i !== 0 || !areEqualAttributionKeys(lastEntry, other.keys[i])) { | |||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |||
this.offsets.push(other.offsets[i]! + this.length); | |||
// TODO: see line 305 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jzaffiro, just calling your attention here - was merged like this
// just compute an arbitrarily big ordinal | ||
// we base it on the depth of the tree | ||
// to ensure it is bigger than all ordinals in | ||
// the tree, as each layer appends to the previous | ||
return String.fromCharCode(0xffff).repeat(this.endpointSegmentProps().depth); | ||
return String.fromCodePoint(0xffff).repeat(this.endpointSegmentProps().depth); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
though it's not a huge deal and I think the behavior here is correct, I would have leaned toward disabling the lint rule when dealing with ordinals. The intent of this lint rule is probably "you probably don't support unicode correctly if you're using this and likely have bugs", but for ordinals we obviously aren't actually ever dealing with user input, we're just using it for dictionary order.
i think it's fine as is, but worth a comment. whatever you go with, just be consistent that all logic related to ordinals uses a consistent method.
return siblingExists; | ||
}; | ||
|
||
const onLeaf = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I would not remove onLeaf
and continueFrom
from their scope. this makes the code harder to read than before, even if it is technically not necessary to create a new function on each execution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if they had informative names perhaps it'd be ok as well.
feel free to address all my comments in a follow-up PR. I don't really want to look at this again and it'll be easier to audit that way anyway. |
Co-authored-by: Abram Sanderson <Abram.sanderson@gmail.com>
Minor improvements to #21684, including disabling [prefer-code-point](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-code-point.md) for ordinals, reverting function scope changes, and updating documentation. --------- Co-authored-by: Abram Sanderson <Abram.sanderson@gmail.com>
Updates the eslint config from minimal-deprecated to recommended, and addresses the resulting violations. [AB#3014](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/3014) --------- Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com> Co-authored-by: Abram Sanderson <Abram.sanderson@gmail.com>
Minor improvements to microsoft#21684, including disabling [prefer-code-point](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-code-point.md) for ordinals, reverting function scope changes, and updating documentation. --------- Co-authored-by: Abram Sanderson <Abram.sanderson@gmail.com>
Updates the eslint config from minimal-deprecated to recommended, and addresses the resulting violations.
AB#3014