Skip to content

Commit

Permalink
[node-core-library] Remove node < 11 sort
Browse files Browse the repository at this point in the history
  • Loading branch information
dmichon-msft committed Aug 18, 2022
1 parent ca05a9f commit 577494d
Show file tree
Hide file tree
Showing 8 changed files with 31 additions and 59 deletions.
4 changes: 2 additions & 2 deletions apps/api-extractor/src/collector/MessageRouter.ts
Expand Up @@ -4,7 +4,7 @@
import colors from 'colors';
import * as ts from 'typescript';
import * as tsdoc from '@microsoft/tsdoc';
import { Sort, InternalError, LegacyAdapters } from '@rushstack/node-core-library';
import { Sort, InternalError } from '@rushstack/node-core-library';

import { AstDeclaration } from '../analyzer/AstDeclaration';
import { AstSymbol } from '../analyzer/AstSymbol';
Expand Down Expand Up @@ -635,7 +635,7 @@ export class MessageRouter {
* Sorts an array of messages according to a reasonable ordering
*/
private _sortMessagesForOutput(messages: ExtractorMessage[]): void {
LegacyAdapters.sortStable(messages, (a, b) => {
messages.sort((a, b) => {
let diff: number;
// First sort by file name
diff = Sort.compareByValue(a.sourceFilePath, b.sourceFilePath);
Expand Down
9 changes: 1 addition & 8 deletions common/config/rush/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion common/config/rush/repo-state.json
@@ -1,5 +1,5 @@
// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush.
{
"pnpmShrinkwrapHash": "cbd21b0d7df471c98d131b4da57ccf42b4e01395",
"pnpmShrinkwrapHash": "4d6e5e67bed9cdd954bad49e75c121160092f083",
"preferredVersionsHash": "d15f901c51f5b82ae0cc4cc0a2cdc9a5e451367c"
}
5 changes: 3 additions & 2 deletions common/reviews/api/node-core-library.api.md
Expand Up @@ -691,6 +691,7 @@ export class LegacyAdapters {
// (undocumented)
static convertCallbackToPromise<TResult, TError, TArg1, TArg2, TArg3, TArg4>(fn: (arg1: TArg1, arg2: TArg2, arg3: TArg3, arg4: TArg4, cb: LegacyCallback<TResult, TError>) => void, arg1: TArg1, arg2: TArg2, arg3: TArg3, arg4: TArg4): Promise<TResult>;
static scrubError(error: Error | string | any): Error;
// @deprecated
static sortStable<T>(array: T[], compare?: (a: T, b: T) => number): void;
}

Expand Down Expand Up @@ -805,8 +806,8 @@ export class ProtectableMap<K, V> {
// @public
export class Sort {
static compareByValue(x: any, y: any): number;
static isSorted<T>(array: T[], comparer?: (x: any, y: any) => number): boolean;
static isSortedBy<T>(array: T[], keySelector: (element: T) => any, comparer?: (x: any, y: any) => number): boolean;
static isSorted<T>(collection: Iterable<T>, comparer?: (x: any, y: any) => number): boolean;
static isSortedBy<T>(collection: Iterable<T>, keySelector: (element: T) => any, comparer?: (x: any, y: any) => number): boolean;
static sortBy<T>(array: T[], keySelector: (element: T) => any, comparer?: (x: any, y: any) => number): void;
static sortMapKeys<K, V>(map: Map<K, V>, keyComparer?: (x: K, y: K) => number): void;
static sortSet<T>(set: Set<T>, comparer?: (x: T, y: T) => number): void;
Expand Down
Expand Up @@ -16,7 +16,7 @@ import { ApiClass } from '../model/ApiClass';
import { ApiInterface } from '../model/ApiInterface';
import { ExcerptToken, ExcerptTokenKind } from './Excerpt';
import { IFindApiItemsResult, IFindApiItemsMessage, FindApiItemsMessageId } from './IFindApiItemsResult';
import { InternalError, LegacyAdapters } from '@rushstack/node-core-library';
import { InternalError } from '@rushstack/node-core-library';
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference';
import { HeritageType } from '../model/HeritageType';
import { IResolveDeclarationReferenceResult } from '../model/ModelReferenceResolver';
Expand Down Expand Up @@ -231,7 +231,7 @@ export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
/** @override */
public get members(): ReadonlyArray<ApiItem> {
if (!this[_membersSorted] && !this[_preserveMemberOrder]) {
LegacyAdapters.sortStable(this[_members], (x, y) => x.getSortKey().localeCompare(y.getSortKey()));
this[_members].sort((x, y) => x.getSortKey().localeCompare(y.getSortKey()));
this[_membersSorted] = true;
}

Expand Down Expand Up @@ -433,9 +433,7 @@ export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
//
// interface FooBar extends Foo, Bar {}
// ```
LegacyAdapters.sortStable(extendedItems, (x: ApiItem, y: ApiItem) =>
x.getSortKey().localeCompare(y.getSortKey())
);
extendedItems.sort((x: ApiItem, y: ApiItem) => x.getSortKey().localeCompare(y.getSortKey()));

toVisit.push(...extendedItems);
next = toVisit.shift();
Expand All @@ -448,9 +446,7 @@ export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
for (const members of membersByKind.values()) {
items.push(...members);
}
LegacyAdapters.sortStable(items, (x: ApiItem, y: ApiItem) =>
x.getSortKey().localeCompare(y.getSortKey())
);
items.sort((x: ApiItem, y: ApiItem) => x.getSortKey().localeCompare(y.getSortKey()));

return {
items,
Expand Down
4 changes: 1 addition & 3 deletions libraries/node-core-library/package.json
Expand Up @@ -23,7 +23,6 @@
"jju": "~1.4.0",
"resolve": "~1.17.0",
"semver": "~7.3.0",
"timsort": "~0.3.0",
"z-schema": "~5.0.2"
},
"devDependencies": {
Expand All @@ -34,7 +33,6 @@
"@types/heft-jest": "1.0.1",
"@types/jju": "1.4.1",
"@types/resolve": "1.17.1",
"@types/semver": "7.3.5",
"@types/timsort": "0.3.0"
"@types/semver": "7.3.5"
}
}
17 changes: 3 additions & 14 deletions libraries/node-core-library/src/LegacyAdapters.ts
@@ -1,9 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { sort as timsort } from 'timsort';
import * as semver from 'semver';

/**
* Callback used by {@link LegacyAdapters}.
* @public
Expand All @@ -16,8 +13,6 @@ export type LegacyCallback<TResult, TError> = (error: TError | null | undefined,
* @public
*/
export class LegacyAdapters {
private static _useTimsort: boolean | undefined = undefined;

/**
* This function wraps a function with a callback in a promise.
*/
Expand Down Expand Up @@ -106,18 +101,12 @@ export class LegacyAdapters {
* Prior to Node 11.x, the `Array.sort()` algorithm is not guaranteed to be stable.
* If you need a stable sort, you can use `sortStable()` as a workaround.
*
* @deprecated
* Use native Array.sort(), since Node &lt; 14 is no longer supported
* @remarks
* On NodeJS 11.x and later, this method simply calls the native `Array.sort()`.
* For earlier versions, it uses an implementation of Timsort, which is the same algorithm used by modern NodeJS.
*/
public static sortStable<T>(array: T[], compare?: (a: T, b: T) => number): void {
if (LegacyAdapters._useTimsort === undefined) {
LegacyAdapters._useTimsort = semver.major(process.versions.node) < 11;
}
if (LegacyAdapters._useTimsort) {
timsort(array, compare);
} else {
Array.prototype.sort.call(array, compare);
}
Array.prototype.sort.call(array, compare);
}
}
37 changes: 16 additions & 21 deletions libraries/node-core-library/src/Sort.ts
@@ -1,15 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { LegacyAdapters } from './LegacyAdapters';

/**
* Operations for sorting collections.
*
* @remarks
* NOTE: Prior to Node 11.x, the `Array.sort()` algorithm is not guaranteed to be stable. For maximum
* compatibility, consider using {@link LegacyAdapters.sortStable} instead of `Array.sort()`.
*
* @public
*/
export class Sort {
Expand Down Expand Up @@ -82,16 +76,19 @@ export class Sort {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
comparer: (x: any, y: any) => number = Sort.compareByValue
): void {
LegacyAdapters.sortStable(array, (x, y) => comparer(keySelector(x), keySelector(y)));
array.sort((x, y) => comparer(keySelector(x), keySelector(y)));
}

/**
* Returns true if the array is already sorted.
* Returns true if the collection is already sorted.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public static isSorted<T>(array: T[], comparer: (x: any, y: any) => number = Sort.compareByValue): boolean {
public static isSorted<T>(
collection: Iterable<T>,
comparer: (x: any, y: any) => number = Sort.compareByValue
): boolean {
let previous: T | undefined = undefined;
for (const element of array) {
for (const element of collection) {
if (comparer(previous, element) > 0) {
return false;
}
Expand All @@ -101,7 +98,7 @@ export class Sort {
}

/**
* Returns true if the array is already sorted by the specified key.
* Returns true if the collection is already sorted by the specified key.
*
* @example
*
Expand All @@ -111,14 +108,14 @@ export class Sort {
* ```
*/
public static isSortedBy<T>(
array: T[],
collection: Iterable<T>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
keySelector: (element: T) => any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
comparer: (x: any, y: any) => number = Sort.compareByValue
): boolean {
let previousKey: T | undefined = undefined;
for (const element of array) {
for (const element of collection) {
const key: T = keySelector(element);
if (comparer(previousKey, key) > 0) {
return false;
Expand Down Expand Up @@ -183,14 +180,13 @@ export class Sort {
keySelector: (element: T) => any,
keyComparer: (x: T, y: T) => number = Sort.compareByValue
): void {
const array: T[] = Array.from(set);

// Sorting a set is expensive, so first check whether it's already sorted.
if (Sort.isSortedBy(array, keySelector, keyComparer)) {
if (Sort.isSortedBy(set, keySelector, keyComparer)) {
return;
}

LegacyAdapters.sortStable(array, (x, y) => keyComparer(keySelector(x), keySelector(y)));
const array: T[] = Array.from(set);
array.sort((x, y) => keyComparer(keySelector(x), keySelector(y)));

set.clear();
for (const item of array) {
Expand All @@ -214,14 +210,13 @@ export class Sort {
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public static sortSet<T>(set: Set<T>, comparer: (x: T, y: T) => number = Sort.compareByValue): void {
const array: T[] = Array.from(set);

// Sorting a set is expensive, so first check whether it's already sorted.
if (Sort.isSorted(array, comparer)) {
if (Sort.isSorted(set, comparer)) {
return;
}

LegacyAdapters.sortStable(array, (x, y) => comparer(x, y));
const array: T[] = Array.from(set);
array.sort((x, y) => comparer(x, y));

set.clear();
for (const item of array) {
Expand Down

0 comments on commit 577494d

Please sign in to comment.