From 86d04c2e417828d77ce17e2a038d1c13412d3a43 Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Thu, 2 Oct 2025 13:13:01 -0400 Subject: [PATCH 01/11] Fork npm-check into rush-lib --- .../config/api-documenter.json | 6 + .../rush/browser-approved-packages.json | 52 +++++++ .../build-tests-subspace/pnpm-lock.yaml | 2 +- .../config/subspaces/default/pnpm-lock.yaml | 2 +- libraries/rush-lib/package.json | 16 +- .../rush-lib/src/logic/InteractiveUpgrader.ts | 17 +-- .../rush-lib/src/logic/PackageJsonUpdater.ts | 15 +- .../rush-lib/src/types/depcheck-typings.d.ts | 71 +++++++++ .../rush-lib/src/types/giturl-typings.d.ts | 4 + .../src/types/global-modules-typings.d.ts | 4 + .../src/{ => types}/npm-check-typings.d.ts | 0 .../rush-lib/src/types/xtend-typings.d.ts | 6 + .../src/utilities/InteractiveUpgradeUI.ts | 37 +++-- .../utilities/npmCheck/BestGuessHomepage.ts | 25 ++++ .../npmCheck/CreatePackageSummary.ts | 139 ++++++++++++++++++ .../src/utilities/npmCheck/FindModulePath.ts | 26 ++++ .../npmCheck/GetLatestFromRegistry.ts | 46 ++++++ .../utilities/npmCheck/GetUnusedPackages.ts | 91 ++++++++++++ .../src/utilities/npmCheck/LocalNpmCheck.ts | 45 ++++++ .../src/utilities/npmCheck/NpmCheckState.ts | 29 ++++ .../src/utilities/npmCheck/ReadPackageJson.ts | 20 +++ .../npmCheck/interfaces/INpmCheck.ts | 63 ++++++++ .../interfaces/INpmCheckPackageSummary.ts | 38 +++++ .../npmCheck/interfaces/INpmCheckRegistry.ts | 26 ++++ .../npmCheck/test/BestGuessHomepage.test.ts | 60 ++++++++ .../test/CreatePackageSummary.test.ts | 87 +++++++++++ .../npmCheck/test/FindModulePath.test.ts | 44 ++++++ .../test/GetLatestFromRegistry.test.ts | 54 +++++++ .../utilities/npmCheck/test/NpmCheck.test.ts | 88 +++++++++++ .../npmCheck/test/NpmCheckState.test.ts | 24 +++ .../npmCheck/test/ReadPackageJson.test.ts | 16 ++ 31 files changed, 1112 insertions(+), 41 deletions(-) create mode 100644 build-tests/api-documenter-scenarios/config/api-documenter.json create mode 100644 libraries/rush-lib/src/types/depcheck-typings.d.ts create mode 100644 libraries/rush-lib/src/types/giturl-typings.d.ts create mode 100644 libraries/rush-lib/src/types/global-modules-typings.d.ts rename libraries/rush-lib/src/{ => types}/npm-check-typings.d.ts (100%) create mode 100644 libraries/rush-lib/src/types/xtend-typings.d.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/GetUnusedPackages.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckRegistry.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/BestGuessHomepage.test.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/GetLatestFromRegistry.test.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts create mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts diff --git a/build-tests/api-documenter-scenarios/config/api-documenter.json b/build-tests/api-documenter-scenarios/config/api-documenter.json new file mode 100644 index 00000000000..f8bd0690ce3 --- /dev/null +++ b/build-tests/api-documenter-scenarios/config/api-documenter.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-documenter.schema.json", + "outputTarget": "markdown", + "tableOfContents": {}, + "showInheritedMembers": true +} diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 23c585145b8..76724fd839f 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -58,18 +58,58 @@ "name": "axios", "allowedCategories": [ "libraries" ] }, + { + "name": "chalk", + "allowedCategories": [ "libraries" ] + }, + { + "name": "co", + "allowedCategories": [ "libraries" ] + }, + { + "name": "depcheck", + "allowedCategories": [ "libraries" ] + }, { "name": "dependency-path", "allowedCategories": [ "libraries" ] }, + { + "name": "giturl", + "allowedCategories": [ "libraries" ] + }, + { + "name": "global-modules", + "allowedCategories": [ "libraries" ] + }, + { + "name": "node-emoji", + "allowedCategories": [ "libraries" ] + }, { "name": "office-ui-fabric-core", "allowedCategories": [ "libraries" ] }, + { + "name": "ora", + "allowedCategories": [ "libraries" ] + }, + { + "name": "package-json", + "allowedCategories": [ "libraries" ] + }, + { + "name": "path-exists", + "allowedCategories": [ "libraries" ] + }, { "name": "prism-react-renderer", "allowedCategories": [ "libraries" ] }, + { + "name": "rc-config-loader", + "allowedCategories": [ "libraries" ] + }, { "name": "react", "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] @@ -98,10 +138,22 @@ "name": "scheduler", "allowedCategories": [ "vscode-extensions" ] }, + { + "name": "semver-diff", + "allowedCategories": [ "libraries" ] + }, + { + "name": "throat", + "allowedCategories": [ "libraries" ] + }, { "name": "tslib", "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, + { + "name": "xtend", + "allowedCategories": [ "libraries" ] + }, { "name": "zod", "allowedCategories": [ "libraries" ] diff --git a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml index 0827f796a75..7db38bb789d 100644 --- a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml +++ b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml @@ -7366,4 +7366,4 @@ packages: - node-notifier - supports-color - ts-node - dev: true + dev: true \ No newline at end of file diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index b9ceb1be59b..864abc4be22 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -31065,4 +31065,4 @@ packages: /zwitch@1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} - dev: true + dev: true \ No newline at end of file diff --git a/libraries/rush-lib/package.json b/libraries/rush-lib/package.json index 5d8d72f60a2..1f687ab8d78 100644 --- a/libraries/rush-lib/package.json +++ b/libraries/rush-lib/package.json @@ -44,21 +44,30 @@ "@rushstack/ts-command-line": "workspace:*", "@yarnpkg/lockfile": "~1.0.2", "builtin-modules": "~3.1.0", + "chalk": "^5.6.2", "cli-table": "~0.3.1", + "co": "^4.6.0", + "depcheck": "^1.4.7", "dependency-path": "~9.2.8", "dotenv": "~16.4.7", "fast-glob": "~3.3.1", "figures": "3.0.0", + "giturl": "^2.0.0", "git-repo-info": "~2.1.0", "glob-escape": "~0.0.2", + "global-modules": "^2.0.0", "https-proxy-agent": "~5.0.0", "ignore": "~5.1.6", "inquirer": "~8.2.7", "js-yaml": "~4.1.0", - "npm-check": "~6.0.1", + "lodash": "~4.17.15", + "node-emoji": "^2.2.0", "npm-package-arg": "~6.1.0", "object-hash": "3.0.0", + "package-json": "^10.0.1", + "path-exists": "4.0.0", "pnpm-sync-lib": "0.3.2", + "rc-config-loader": "^4.1.3", "read-package-tree": "~5.1.5", "rxjs": "~6.6.7", "semver": "~7.5.4", @@ -66,7 +75,9 @@ "strict-uri-encode": "~2.0.0", "tapable": "2.2.1", "tar": "~6.2.1", - "true-case-path": "~2.2.1" + "throat": "^6.0.2", + "true-case-path": "~2.2.1", + "xtend": "^4.0.2" }, "devDependencies": { "@pnpm/lockfile.types": "~1.0.3", @@ -79,6 +90,7 @@ "@types/cli-table": "0.3.0", "@types/inquirer": "7.3.1", "@types/js-yaml": "4.0.9", + "@types/lodash": "4.14.116", "@types/npm-package-arg": "6.1.0", "@types/object-hash": "~3.0.6", "@types/read-package-tree": "5.1.0", diff --git a/libraries/rush-lib/src/logic/InteractiveUpgrader.ts b/libraries/rush-lib/src/logic/InteractiveUpgrader.ts index 3eb8d238a7e..8589dd81f12 100644 --- a/libraries/rush-lib/src/logic/InteractiveUpgrader.ts +++ b/libraries/rush-lib/src/logic/InteractiveUpgrader.ts @@ -1,18 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -/// -import npmCheck from 'npm-check'; -import type * as NpmCheck from 'npm-check'; import Prompt from 'inquirer/lib/ui/prompt'; - import { Colorize } from '@rushstack/terminal'; import type { RushConfiguration } from '../api/RushConfiguration'; import { upgradeInteractive, type IDepsToUpgradeAnswers } from '../utilities/InteractiveUpgradeUI'; import type { RushConfigurationProject } from '../api/RushConfigurationProject'; import { SearchListPrompt } from '../utilities/prompts/SearchListPrompt'; +import LocalNpmCheck from '../utilities/npmCheck/LocalNpmCheck'; +import type { INpmCheckState } from '../utilities/npmCheck/interfaces/INpmCheck'; +import type { INpmCheckPackageSummary } from '../utilities/npmCheck/interfaces/INpmCheckPackageSummary'; interface IUpgradeInteractiveDeps { projects: RushConfigurationProject[]; @@ -29,7 +28,7 @@ export class InteractiveUpgrader { public async upgradeAsync(): Promise { const rushProject: RushConfigurationProject = await this._getUserSelectedProjectForUpgradeAsync(); - const dependenciesState: NpmCheck.INpmCheckPackage[] = + const dependenciesState: INpmCheckPackageSummary[] = await this._getPackageDependenciesStatusAsync(rushProject); const depsToUpgrade: IDepsToUpgradeAnswers = @@ -38,7 +37,7 @@ export class InteractiveUpgrader { } private async _getUserSelectedDependenciesToUpgradeAsync( - packages: NpmCheck.INpmCheckPackage[] + packages: INpmCheckPackageSummary[] ): Promise { return upgradeInteractive(packages); } @@ -69,14 +68,14 @@ export class InteractiveUpgrader { private async _getPackageDependenciesStatusAsync( rushProject: RushConfigurationProject - ): Promise { + ): Promise { const { projectFolder } = rushProject; - const currentState: NpmCheck.INpmCheckCurrentState = await npmCheck({ + const newState: INpmCheckState = await LocalNpmCheck({ cwd: projectFolder, skipUnused: true }); - return currentState.get('packages'); + return newState.packages ?? []; } } diff --git a/libraries/rush-lib/src/logic/PackageJsonUpdater.ts b/libraries/rush-lib/src/logic/PackageJsonUpdater.ts index 4a6091984d6..96850171aad 100644 --- a/libraries/rush-lib/src/logic/PackageJsonUpdater.ts +++ b/libraries/rush-lib/src/logic/PackageJsonUpdater.ts @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -/// - import * as semver from 'semver'; import type * as NpmCheck from 'npm-check'; @@ -31,6 +29,7 @@ import { SemVerStyle } from './PackageJsonUpdaterTypes'; import type { Subspace } from '../api/Subspace'; +import type { INpmCheckPackageSummary } from '../utilities/npmCheck/interfaces/INpmCheckPackageSummary'; /** * Options for adding a dependency to a particular project. @@ -43,7 +42,7 @@ export interface IPackageJsonUpdaterRushUpgradeOptions { /** * The dependencies to be added. */ - packagesToAdd: NpmCheck.INpmCheckPackage[]; + packagesToAdd: INpmCheckPackageSummary[]; /** * If specified, other packages that use this dependency will also have their package.json's updated. */ @@ -137,13 +136,7 @@ export class PackageJsonUpdater { const devDependenciesToUpdate: Record = {}; const peerDependenciesToUpdate: Record = {}; - for (const { - moduleName, - latest: latestVersion, - packageJson, - devDependency, - peerDependency - } of packagesToAdd) { + for (const { moduleName, latest: latestVersion, packageJson, devDependency } of packagesToAdd) { const inferredRangeStyle: SemVerStyle = this._cheaplyDetectSemVerRangeStyle(packageJson); const implicitlyPreferredVersion: string | undefined = implicitlyPreferredVersionByPackageName.get(moduleName); @@ -163,8 +156,6 @@ export class PackageJsonUpdater { if (devDependency) { devDependenciesToUpdate[moduleName] = version; - } else if (peerDependency) { - peerDependenciesToUpdate[moduleName] = version; } else { dependenciesToUpdate[moduleName] = version; } diff --git a/libraries/rush-lib/src/types/depcheck-typings.d.ts b/libraries/rush-lib/src/types/depcheck-typings.d.ts new file mode 100644 index 00000000000..a7a85840241 --- /dev/null +++ b/libraries/rush-lib/src/types/depcheck-typings.d.ts @@ -0,0 +1,71 @@ +declare function depcheck(rootDir: string, options: depcheck.Options): Promise; + +declare function depcheck( + rootDir: string, + options: depcheck.Options, + callback: (results: depcheck.Results) => T +): Promise; + +declare namespace depcheck { + type Node = Record; + + type Parser = (filePath: string, deps: ReadonlyArray, rootDir: string) => Node | string[]; + + type Detector = (node: Node) => ReadonlyArray | string; + + interface PackageDependencies { + [dependencyName: string]: string; + } + + interface Options { + ignoreBinPackage?: boolean; + skipMissing?: boolean; + ignoreMatches?: ReadonlyArray; + ignoreDirs?: ReadonlyArray; + ignorePath?: string; + ignorePatterns?: ReadonlyArray; + package?: { + dependencies?: PackageDependencies; + devDependencies?: PackageDependencies; + peerDependencies?: PackageDependencies; + optionalDependencies?: PackageDependencies; + }; + parsers?: { + [match: string]: Parser; + }; + detectors?: ReadonlyArray; + specials?: ReadonlyArray; + } + + interface Config { + ignoreBinPackage?: Options['ignoreBinPackage']; + skipMissing?: Options['skipMissing']; + json?: boolean; + ignores?: Options['ignoreMatches']; + ignoreDirs?: Options['ignoreDirs']; + ignorePath?: Options['ignorePath']; + ignorePatterns?: Options['ignorePatterns']; + parsers?: { [match: string]: keyof typeof parser | ReadonlyArray }; + detectors?: ReadonlyArray; + specials?: ReadonlyArray; + } + + interface Results { + dependencies: string[]; + devDependencies: string[]; + using: { + [dependencyName: string]: string[]; + }; + missing: { + [dependencyName: string]: string[]; + }; + invalidFiles: { + [filePath: string]: any; + }; + invalidDirs: { + [filePath: string]: any; + }; + } +} + +export = depcheck; diff --git a/libraries/rush-lib/src/types/giturl-typings.d.ts b/libraries/rush-lib/src/types/giturl-typings.d.ts new file mode 100644 index 00000000000..3202ff78a60 --- /dev/null +++ b/libraries/rush-lib/src/types/giturl-typings.d.ts @@ -0,0 +1,4 @@ +declare module 'giturl' { + function parse(url: string): string; + export { parse }; +} diff --git a/libraries/rush-lib/src/types/global-modules-typings.d.ts b/libraries/rush-lib/src/types/global-modules-typings.d.ts new file mode 100644 index 00000000000..9584bee2567 --- /dev/null +++ b/libraries/rush-lib/src/types/global-modules-typings.d.ts @@ -0,0 +1,4 @@ +declare module 'global-modules' { + const globalModulesPath: string; + export default globalModulesPath; +} diff --git a/libraries/rush-lib/src/npm-check-typings.d.ts b/libraries/rush-lib/src/types/npm-check-typings.d.ts similarity index 100% rename from libraries/rush-lib/src/npm-check-typings.d.ts rename to libraries/rush-lib/src/types/npm-check-typings.d.ts diff --git a/libraries/rush-lib/src/types/xtend-typings.d.ts b/libraries/rush-lib/src/types/xtend-typings.d.ts new file mode 100644 index 00000000000..3d1fa5067a8 --- /dev/null +++ b/libraries/rush-lib/src/types/xtend-typings.d.ts @@ -0,0 +1,6 @@ +declare module 'xtend' { + function extend(a: T, b: U): T & U; + function extend(a: T, b: U, c: V): T & U & V; + function extend(...args: any[]): any; + export = extend; +} diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts b/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts index 11e9640de3c..3a4f6cdcb62 100644 --- a/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts @@ -5,14 +5,11 @@ // https://github.com/dylang/npm-check/blob/master/lib/out/interactive-update.js // Extended to use one type of text table -/// - import inquirer from 'inquirer'; import CliTable from 'cli-table'; import type Separator from 'inquirer/lib/objects/separator'; -import type * as NpmCheck from 'npm-check'; - import { AnsiEscape, Colorize } from '@rushstack/terminal'; +import type { INpmCheckPackageSummary } from './npmCheck/interfaces/INpmCheckPackageSummary'; export interface IUIGroup { title: string; @@ -25,11 +22,11 @@ export interface IUIGroup { } export interface IDepsToUpgradeAnswers { - packages: NpmCheck.INpmCheckPackage[]; + packages: INpmCheckPackageSummary[]; } export interface IUpgradeInteractiveDepChoice { - value: NpmCheck.INpmCheckPackage; + value: INpmCheckPackageSummary; name: string | string[]; short: string; } @@ -82,7 +79,7 @@ export const UI_GROUPS: IUIGroup[] = [ } ]; -function label(dep: NpmCheck.INpmCheckPackage): string[] { +function label(dep: INpmCheckPackageSummary): string[] { const bumpInstalled: string = dep.bump ? dep.installed : ''; const installed: string = dep.mismatch ? dep.packageJson : bumpInstalled; const name: string = Colorize.yellow(dep.moduleName); @@ -95,15 +92,25 @@ function label(dep: NpmCheck.INpmCheckPackage): string[] { installed, installed && '>', Colorize.bold(dep.latest || ''), - dep.latest ? homepage : dep.regError || dep.pkgError + dep.latest ? homepage : getErrorDep(dep) ]; } -function short(dep: NpmCheck.INpmCheckPackage): string { +function getErrorDep(dep: INpmCheckPackageSummary): string { + if (dep.regError !== undefined && dep.regError && dep.regError instanceof Error) { + return dep.regError.message; + } else if (dep.pkgError !== undefined && dep.pkgError && dep.pkgError instanceof Error) { + return dep.pkgError.message; + } + + return ''; +} + +function short(dep: INpmCheckPackageSummary): string { return `${dep.moduleName}@${dep.latest}`; } -function getChoice(dep: NpmCheck.INpmCheckPackage): IUpgradeInteractiveDepChoice | boolean | Separator { +function getChoice(dep: INpmCheckPackageSummary): IUpgradeInteractiveDepChoice | boolean | Separator { if (!dep.mismatch && !dep.bump && !dep.notInstalled) { return false; } @@ -119,9 +126,9 @@ function unselectable(options?: { title: string }): Separator { return new inquirer.Separator(AnsiEscape.removeCodes(options ? options.title : '')); } -function createChoices(packages: NpmCheck.INpmCheckPackage[], options: IUIGroup): ChoiceTable { +function createChoices(packages: INpmCheckPackageSummary[], options: IUIGroup): ChoiceTable { const { filter } = options; - const filteredChoices: NpmCheck.INpmCheckPackage[] = packages.filter((pkg: NpmCheck.INpmCheckPackage) => { + const filteredChoices: INpmCheckPackageSummary[] = packages.filter((pkg: INpmCheckPackageSummary) => { if ('mismatch' in filter && pkg.mismatch !== filter.mismatch) { return false; } else if ('bump' in filter && pkg.bump !== filter.bump) { @@ -131,7 +138,7 @@ function createChoices(packages: NpmCheck.INpmCheckPackage[], options: IUIGroup) } else { return true; } - }) as NpmCheck.INpmCheckPackage[]; + }) as INpmCheckPackageSummary[]; const choices: (IUpgradeInteractiveDepChoice | Separator | boolean)[] = filteredChoices .map(getChoice) @@ -179,9 +186,7 @@ function createChoices(packages: NpmCheck.INpmCheckPackage[], options: IUIGroup) } } -export const upgradeInteractive = async ( - pkgs: NpmCheck.INpmCheckPackage[] -): Promise => { +export const upgradeInteractive = async (pkgs: INpmCheckPackageSummary[]): Promise => { const choicesGrouped: ChoiceTable[] = UI_GROUPS.map((group) => createChoices(pkgs, group)).filter(Boolean); const choices: ChoiceTable = []; diff --git a/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts b/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts new file mode 100644 index 00000000000..12b496f4181 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/// + +import gitUrl from 'giturl'; +import type { INpmCheckPackageVersion, INpmCheckRegistryData } from './interfaces/INpmCheckRegistry'; + +export default function bestGuessHomepage(data: INpmCheckRegistryData | undefined): string | false { + if (!data) { + return false; + } + const packageDataForLatest: INpmCheckPackageVersion = data.versions[data['dist-tags'].latest]; + + return packageDataForLatest + ? packageDataForLatest.homepage || + (packageDataForLatest.bugs && + packageDataForLatest.bugs.url && + gitUrl.parse(packageDataForLatest.bugs.url.trim())) || + (packageDataForLatest.repository && + packageDataForLatest.repository.url && + gitUrl.parse(packageDataForLatest.repository.url.trim())) || + false + : false; +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts b/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts new file mode 100644 index 00000000000..d82540f0cc3 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import _ from 'lodash'; +import path from 'path'; +import pathExists from 'path-exists'; +import semver from 'semver'; + +import type { INpmCheckState, INpmCheckPackageJson } from './interfaces/INpmCheck'; +import type { INpmCheckPackageSummary, INpmCheckVersionBumpType } from './interfaces/INpmCheckPackageSummary'; +import findModulePath from './FindModulePath'; +import readPackageJson from './ReadPackageJson'; +import getLatestFromRegistry from './GetLatestFromRegistry'; +import type { INpmRegistryInfo } from './interfaces/INpmCheckRegistry'; + +export default async function createPackageSummary( + moduleName: string, + state: INpmCheckState +): Promise { + const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; + + const modulePath: string = findModulePath(moduleName, state); + const packageIsInstalled: boolean = pathExists.sync(modulePath); + const modulePackageJson: INpmCheckPackageJson = readPackageJson(path.join(modulePath, 'package.json')); + + // Ignore private packages + const isPrivate: boolean = Boolean(modulePackageJson.private); + if (isPrivate) { + return false; + } + + // Ignore packages that are using github or file urls + const packageJsonVersion: string | undefined = + cwdPackageJson?.dependencies[moduleName] || cwdPackageJson?.devDependencies[moduleName]; + if (packageJsonVersion && !semver.validRange(packageJsonVersion)) { + return false; + } + + const unusedDependencies: boolean | string[] | undefined = state.unusedDependencies; + const missingFromPackageJson: Record | undefined = state.missingFromPackageJson; + + return getLatestFromRegistry(moduleName).then((fromRegistry: INpmRegistryInfo) => { + const installedVersion: string | undefined = modulePackageJson.version; + const latest: string | undefined = + installedVersion && + fromRegistry.latest && + fromRegistry.next && + semver.gt(installedVersion, fromRegistry.latest) + ? fromRegistry.next + : fromRegistry.latest; + const versions: string[] = fromRegistry.versions || []; + let versionWanted: string | null = null; + if (packageJsonVersion) { + versionWanted = semver.maxSatisfying(versions, packageJsonVersion); + } + const versionToUse: string | undefined | null = installedVersion || versionWanted; + const usingNonSemver: boolean | '' | null = + latest !== undefined && semver.valid(latest) && semver.lt(latest, '1.0.0-pre'); + + let bump: INpmCheckVersionBumpType; + const bumpRaw: INpmCheckVersionBumpType = + semver.valid(latest) && + semver.valid(versionToUse) && + (usingNonSemver && versionToUse && latest + ? semver.diff(versionToUse, latest) + ? 'nonSemver' + : semver.diff(versionToUse, latest) + : versionToUse && latest + ? semver.diff(versionToUse, latest) + : undefined); + if (bumpRaw && bumpRaw !== null) { + bump = bumpRaw as INpmCheckVersionBumpType; + } else { + bump = undefined; + } + + const unused: boolean = _.includes( + Array.isArray(unusedDependencies) ? unusedDependencies : [], + moduleName + ); + + return { + // info + moduleName: moduleName, + homepage: fromRegistry.homepage ?? '', + regError: new Error(fromRegistry.error), + pkgError: modulePackageJson.error, + + // versions + latest: latest ?? '', + installed: versionToUse === null ? '' : versionToUse, + isInstalled: packageIsInstalled, + notInstalled: !packageIsInstalled, + packageWanted: versionWanted === null ? '' : versionWanted, + packageJson: packageJsonVersion ?? '', + + // Missing from package json + notInPackageJson: missingFromPackageJson + ? foundIn(state, missingFromPackageJson[moduleName]) + : undefined, + + // meta + devDependency: _.has(cwdPackageJson?.devDependencies, moduleName), + usedInScripts: + cwdPackageJson?.scripts && cwdPackageJson.scripts !== null + ? Object.keys(cwdPackageJson.scripts).filter((scriptName) => { + if (cwdPackageJson.scripts) { + return cwdPackageJson.scripts[scriptName].includes(moduleName); + } + }) + : undefined, + mismatch: + packageJsonVersion !== undefined && + versionToUse !== null && + semver.validRange(packageJsonVersion) && + semver.valid(versionToUse) + ? !semver.satisfies(versionToUse, packageJsonVersion) + : false, + semverValid: semver.valid(versionToUse) ?? '', + easyUpgrade: !!( + latest !== undefined && + packageJsonVersion !== undefined && + semver.validRange(packageJsonVersion) && + semver.valid(versionToUse) && + semver.satisfies(latest, packageJsonVersion) && + bump !== 'major' + ), + bump: bump, + unused: unused + }; + }); +} + +function foundIn(state: INpmCheckState, files?: string[]): string | undefined { + if (!files) { + return undefined; + } + return 'Found in: ' + files.map((filepath) => filepath.replace(state.cwd || '', '')).join(', '); +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts b/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts new file mode 100644 index 00000000000..58fa5e02350 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import Module from 'module'; +import path from 'path'; +import pathExists from 'path-exists'; +import type { INpmCheckState } from './interfaces/INpmCheck'; + +/** + * Searches the directory hierarchy to return the path to the requested node module. + * If the module can't be found, returns the initial (deepest) tried path. + */ +export default function findModulePath(moduleName: string, currentState: INpmCheckState): string { + const cwd: string = currentState.cwd; + + // Module._nodeModulePaths does not include some places the node module resolver searches, such as + // the global prefix or other special directories. This is desirable because if a module is missing + // in the project directory we want to be sure to report it as missing. + // We can't use require.resolve because it fails if the module doesn't have an entry point. + // @ts-ignore + const nodeModulesPaths: string[] = Module._nodeModulePaths(cwd); + const possibleModulePaths: string[] = nodeModulesPaths.map((x) => path.join(x, moduleName)); + const modulePath: string | undefined = possibleModulePaths.find((p) => pathExists.sync(p)); + // if no existing path was found, return the first tried path anyway + return modulePath || path.join(cwd, moduleName); +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts b/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts new file mode 100644 index 00000000000..be1573825e8 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import _ from 'lodash'; +import bestGuessHomepage from './BestGuessHomepage'; +import semver from 'semver'; +import packageJson from 'package-json'; +import os from 'os'; +import throat from 'throat'; +import type { INpmRegistryInfo } from './interfaces/INpmCheckRegistry'; + +const cpuCount: number = os.cpus().length; + +export default async function getNpmInfo(packageName: string): Promise { + const limit: () => Promise = throat(cpuCount, () => + packageJson(packageName, { fullMetadata: true, allVersions: true }) + ); + return limit() + .then((rawData: packageJson.FullMetadata) => { + const CRAZY_HIGH_SEMVER: string = '8000.0.0'; + const sortedVersions: string[] = _(rawData.versions) + .keys() + .remove(_.partial(semver.gt, CRAZY_HIGH_SEMVER)) + .sort(semver.compare) + .valueOf(); + + const latest: string = rawData['dist-tags'].latest; + const next: string = rawData['dist-tags'].next; + const latestStableRelease: string | undefined = semver.satisfies(latest, '*') + ? latest + : semver.maxSatisfying(sortedVersions, '*') || ''; + + return { + latest: latestStableRelease, + next: next, + versions: sortedVersions, + homepage: bestGuessHomepage(rawData) || '' + }; + }) + .catch((error) => { + const errorMessage: string = `Registry error ${error.message}`; + return { + error: errorMessage + }; + }); +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/GetUnusedPackages.ts b/libraries/rush-lib/src/utilities/npmCheck/GetUnusedPackages.ts new file mode 100644 index 00000000000..c4736a2e6f2 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/GetUnusedPackages.ts @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/// + +import depcheck from 'depcheck'; +import _ from 'lodash'; +import { rcFile } from 'rc-config-loader'; +import type { INpmCheckPackageJson, INpmCheckState } from './interfaces/INpmCheck'; + +function skipUnused(currentState: INpmCheckState): boolean { + return ( + currentState.skipUnused || // manual option to ignore this + currentState.update || // in the process of doing an update + !currentState.cwdPackageJson?.name + ); // there's no package.json +} + +type RcFileResult = { config: Record; filePath: string } | undefined; + +function loadRcFile(rcFileName: string): Record { + try { + const results: RcFileResult = rcFile(rcFileName); + if (!results) { + return {}; + } + return results.config; + } catch (error) { + // eslint-disable-next-line no-console + console.error(`Error parsing rc file; skipping it; error: ${error.message}`); + return {}; + } +} + +export default async function getUnusedPackages(currentState: INpmCheckState): Promise { + if (skipUnused(currentState)) { + return currentState; + } + + // removed check for specials as rush doesn't use it + const depcheckDefaults: depcheck.Options = { + ignoreDirs: ['sandbox', 'dist', 'generated', '.generated', 'build', 'fixtures', 'jspm_packages'], + ignoreMatches: [ + 'gulp-*', + 'grunt-*', + 'karma-*', + 'angular-*', + 'babel-*', + 'metalsmith-*', + 'eslint-plugin-*', + '@types/*', + 'grunt', + 'mocha', + 'ava' + ] + }; + + const npmCheckRc: Record = loadRcFile('npmcheck'); + + const depcheckOptions: depcheck.Options = { + ...depcheckDefaults, + ...(npmCheckRc.depcheck || {}) + }; + + if (!currentState.cwd || typeof currentState.cwd !== 'string') { + throw new Error('currentState.cwd must be a defined string for depcheck.'); + } + + return depcheck(currentState.cwd, depcheckOptions) + .then((depCheckResults: depcheck.Results) => { + const unusedDependencies: string[] = ([] as string[]).concat( + depCheckResults.dependencies, + depCheckResults.devDependencies + ); + currentState.unusedDependencies = unusedDependencies; + + const cwdPackageJson: INpmCheckPackageJson | undefined = currentState.cwdPackageJson; + + // currently missing will return devDependencies that aren't really missing + const missingFromPackageJson: Record = _.omit( + depCheckResults.missing || {}, + ...(cwdPackageJson?.dependencies ? Object.keys(cwdPackageJson.dependencies) : []), + ...(cwdPackageJson?.devDependencies ? Object.keys(cwdPackageJson.devDependencies) : []) + ); + currentState.missingFromPackageJson = missingFromPackageJson; + return currentState; + }) + .catch((error: Error) => { + return currentState; + }); +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts b/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts new file mode 100644 index 00000000000..70f9a5ccfa4 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import extend from 'xtend'; + +import getUnusedPackages from './GetUnusedPackages'; +import type { INpmCheckOptions, INpmCheckPackageJson, INpmCheckState } from './interfaces/INpmCheck'; +import initializeState from './NpmCheckState'; +import createPackageSummary from './CreatePackageSummary'; +import type { INpmCheckPackageSummary } from './interfaces/INpmCheckPackageSummary'; + +export default async function LocalNpmCheck(initialOptions?: INpmCheckOptions): Promise { + const initialState: INpmCheckState = await initializeState(initialOptions); + const state: INpmCheckState = await getUnusedPackages(initialState); + const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; + const allDependencies: Record | undefined = getDependencies(state, cwdPackageJson); + const allDependenciesIncludingMissing: string[] | undefined = + allDependencies && state.missingFromPackageJson + ? Object.keys(extend(allDependencies, state.missingFromPackageJson)) + : []; + + let packages: INpmCheckPackageSummary[] = []; + if (allDependenciesIncludingMissing) { + const packageSummaryPromises: Promise[] = + allDependenciesIncludingMissing.map((moduleName: string) => createPackageSummary(moduleName, state)); + packages = await Promise.all(packageSummaryPromises).then( + (results: (INpmCheckPackageSummary | false)[]) => { + return results.filter((pkg): pkg is INpmCheckPackageSummary => pkg !== false); + } + ); + } + + return { ...state, packages }; +} + +export function getDependencies( + state: INpmCheckState, + pkg: INpmCheckPackageJson | undefined +): Record | undefined { + if (!pkg) { + return undefined; + } + + return extend(pkg.dependencies, pkg.devDependencies); +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts b/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts new file mode 100644 index 00000000000..52704df54b6 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'path'; +import extend from 'xtend'; +import { + DefaultNpmCheckOptions, + type INpmCheckOptions, + type INpmCheckPackageJson, + type INpmCheckState +} from './interfaces/INpmCheck'; +import readPackageJson from './ReadPackageJson'; + +export default async function initializeState(initialOptions?: INpmCheckOptions): Promise { + const state: INpmCheckState = extend(DefaultNpmCheckOptions, initialOptions); + + if (state.cwd) { + const cwd: string = path.resolve(state.cwd); + const pkg: INpmCheckPackageJson = readPackageJson(path.join(cwd, 'package.json')); + state.cwdPackageJson = pkg; + state.cwd = cwd; + } + + if (state.cwdPackageJson?.error) { + return Promise.reject(state.cwdPackageJson.error); + } + + return Promise.resolve(state); +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts b/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts new file mode 100644 index 00000000000..a2af393237c --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import extend from 'xtend'; +import type { INpmCheckPackageJson } from './interfaces/INpmCheck'; + +export default function readPackageJson(filename: string): INpmCheckPackageJson { + let pkg: INpmCheckPackageJson | undefined = undefined; + let error: Error | undefined = undefined; + try { + pkg = require(filename); + } catch (e: unknown) { + if (e && typeof e === 'object' && 'code' in e && e.code === 'MODULE_NOT_FOUND') { + error = new Error(`A package.json was not found at ${filename}`); + } else { + error = new Error(`A package.json was found at ${filename}, but it is not valid.`); + } + } + return extend({ devDependencies: {}, dependencies: {}, error: error }, pkg); +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts new file mode 100644 index 00000000000..29e6f28baea --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { INpmCheckPackageSummary } from './INpmCheckPackageSummary'; + +export interface INpmCheckPackageJson { + name?: string; + version?: string; + devDependencies: Record; + dependencies: Record; + error?: Error; + scripts?: Record; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +} + +export interface INpmCheckOptions { + cwd: string; + skipUnused?: boolean; +} + +// Removed +// - global +// - globalPackages +// - devOnly +// - ignore +// - ignoreDev +// from INpmCheckState as they were not used anywhere +export interface INpmCheckState { + update?: boolean; + updateAll?: boolean; + cwd: string; + skipUnused?: boolean; + forceColor?: boolean; + saveExact?: boolean; + specials?: string; + debug?: boolean; + emoji?: boolean; + spinner?: boolean; + installer?: string; + cwdPackageJson?: INpmCheckPackageJson; + packages?: INpmCheckPackageSummary[]; + unusedDependencies?: boolean | string[]; + missingFromPackageJson?: Record; +} + +export const DefaultNpmCheckOptions: INpmCheckState = { + update: false, + updateAll: false, + cwd: process.cwd(), + skipUnused: false, + forceColor: false, + saveExact: false, + specials: '', + debug: false, + emoji: true, + spinner: false, + installer: 'npm', + cwdPackageJson: { devDependencies: {}, dependencies: {} }, + packages: undefined, + unusedDependencies: false, + missingFromPackageJson: {} +}; diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts new file mode 100644 index 00000000000..84bc82f7f18 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// semverDiff still returns null +export type INpmCheckVersionBumpType = + | '' + | 'build' + | 'major' + | 'premajor' + | 'minor' + | 'preminor' + | 'patch' + | 'prepatch' + | 'prerelease' + | 'nonSemver' + | undefined + // eslint-disable-next-line @rushstack/no-new-null + | null; + +export interface INpmCheckPackageSummary { + moduleName: string; // name of the module. + homepage: string; // url to the home page. + regError?: Error; // error communicating with the registry + pkgError?: Error; // error reading the package.json + latest: string; // latest according to the registry. + installed: string; // version installed + isInstalled: boolean; // Is it installed? + notInstalled: boolean; // Is it installed? + packageWanted: string; // Requested version from the package.json. + packageJson: string; // Version or range requested in the parent package.json. + devDependency: boolean; // Is this a devDependency? + usedInScripts: undefined | string[]; // Array of `scripts` in package.json that use this module. + mismatch: boolean; // Does the version installed not match the range in package.json? + semverValid: string; // Is the installed version valid semver? + easyUpgrade: boolean; // Will running just `npm install` upgrade the module? + bump?: INpmCheckVersionBumpType; // What kind of bump is required to get the latest + unused: boolean; // Is this module used in the code? +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckRegistry.ts b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckRegistry.ts new file mode 100644 index 00000000000..ce40274d25a --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckRegistry.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export interface INpmRegistryInfo { + latest?: string; + next?: string; + versions?: string[]; + homepage?: string; + error?: string; +} + +interface INpmCheckRegistryInfoBugs { + url?: string; +} +interface INpmCheckRepository { + url?: string; +} +export interface INpmCheckPackageVersion { + homepage?: string; + bugs?: INpmCheckRegistryInfoBugs; + repository?: INpmCheckRepository; +} +export interface INpmCheckRegistryData { + versions: Record; + ['dist-tags']: { latest: string }; +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/BestGuessHomepage.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/BestGuessHomepage.test.ts new file mode 100644 index 00000000000..de5a63c3fe1 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/test/BestGuessHomepage.test.ts @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// Mock gitUrl.parse +jest.mock('giturl', () => ({ parse: (url: string) => url })); + +import bestGuessHomepage from '../BestGuessHomepage'; +import type { INpmCheckRegistryData } from '../interfaces/INpmCheckRegistry'; + +describe('bestGuessHomepage', () => { + it('returns false if data is undefined', () => { + expect(bestGuessHomepage(undefined)).toBe(false); + }); + + it('returns homepage if present', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + homepage: 'https://homepage.com' + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://homepage.com'); + }); + + it('returns bugs.url if homepage is missing', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + bugs: { url: 'https://bugs.com' } + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://bugs.com'); + }); + + it('returns repository.url if homepage and bugs.url are missing', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + repository: { url: 'https://repo.com' } + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://repo.com'); + }); + + it('returns false if no homepage, bugs.url, or repository.url', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: {} + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe(false); + }); +}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts new file mode 100644 index 00000000000..fab14803031 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +jest.mock('../GetLatestFromRegistry'); +jest.mock('../ReadPackageJson'); +jest.mock('../FindModulePath'); + +jest.mock('path-exists', () => ({ + sync: jest.fn(() => true) +})); + +import createPackageSummary from '../CreatePackageSummary'; +import getLatestFromRegistry from '../GetLatestFromRegistry'; +import readPackageJson from '../ReadPackageJson'; +import findModulePath from '../FindModulePath'; +import type { INpmCheckPackageJson, INpmCheckState } from '../interfaces/INpmCheck'; +import type { INpmRegistryInfo } from '../interfaces/INpmCheckRegistry'; +import type { INpmCheckPackageSummary } from '../interfaces/INpmCheckPackageSummary'; + +const mockGetLatestFromRegistry = getLatestFromRegistry as jest.MockedFunction; +const mockReadPackageJson = readPackageJson as jest.MockedFunction; +const mockFindModulePath = findModulePath as jest.MockedFunction; + +describe('createPackageSummary', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('returns false for private package', async () => { + mockFindModulePath.mockReturnValue('/mock/path/private-pkg'); + mockReadPackageJson.mockReturnValue({ + dependencies: {}, + devDependencies: {}, + private: true + } as INpmCheckPackageJson); + const state: INpmCheckState = { + cwd: process.cwd(), + cwdPackageJson: { dependencies: {}, devDependencies: {} } + }; + const result: INpmCheckPackageSummary | boolean = await createPackageSummary('private-pkg', state); + expect(result).toBe(false); + }); + + it('returns false for invalid semver range', async () => { + mockFindModulePath.mockReturnValue('/mock/path'); + mockReadPackageJson.mockReturnValue({ + dependencies: {}, + devDependencies: {} + } as INpmCheckPackageJson); + const state: INpmCheckState = { + cwd: process.cwd(), + cwdPackageJson: { + dependencies: { 'bad-pkg': 'github:foo/bar' }, + devDependencies: {} + } + }; + const result: INpmCheckPackageSummary | boolean = await createPackageSummary('bad-pkg', state); + expect(result).toBe(false); + }); + + it('returns summary for valid package', async () => { + mockFindModulePath.mockReturnValue('/mock/path'); + mockReadPackageJson.mockReturnValue({ + dependencies: {}, + devDependencies: {} + } as INpmCheckPackageJson); + mockGetLatestFromRegistry.mockResolvedValue({ + latest: '2.0.0', + next: '3.0.0', + versions: ['1.0.0', '2.0.0', '3.0.0'], + homepage: 'https://homepage.com' + } as INpmRegistryInfo); + const state: INpmCheckState = { + cwd: process.cwd(), + cwdPackageJson: { dependencies: { 'good-pkg': '1.0.0' }, devDependencies: {} }, + unusedDependencies: ['good-pkg'], + missingFromPackageJson: {} + } as INpmCheckState; + const result: INpmCheckPackageSummary | boolean = await createPackageSummary('good-pkg', state); + expect(result).toBeTruthy(); + expect(result).toHaveProperty('moduleName', 'good-pkg'); + expect(result).toHaveProperty('homepage', 'https://homepage.com'); + expect(result).toHaveProperty('latest', '2.0.0'); + expect(result).toHaveProperty('installed', '1.0.0'); + expect(result).toHaveProperty('unused', true); + }); +}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts new file mode 100644 index 00000000000..dd638e56832 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +jest.mock('path', () => ({ + join: jest.fn((...args) => args.join('/')) + // Add other path methods as needed +})); + +jest.mock('path-exists', () => ({ + sync: jest.fn((p: string) => { + // Return true or false based on your test scenario + return p === '/mock/path/node_modules/my-module'; + }) +})); + +import findModulePath from '../FindModulePath'; +import type { INpmCheckState } from '../interfaces/INpmCheck'; +import path from 'path'; + +const Module = require('module'); + +describe('findModulePath', () => { + beforeAll(() => { + jest + .spyOn(Module, '_nodeModulePaths') + .mockImplementation(() => ['/mock/path/node_modules', '/another/mock/path/node_modules']); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('returns found path', () => { + const state: INpmCheckState = { cwd: '/test/cwd', global: false } as INpmCheckState; + const result = findModulePath('my-module', state); + expect(result).toBe(path.join('/mock/path/node_modules', 'my-module')); + }); + + it('returns first tried path', () => { + const state: INpmCheckState = { cwd: '/test/cwd', global: false } as INpmCheckState; + const result = findModulePath('missing-module', state); + expect(result).toBe(path.join('/test/cwd', 'missing-module')); + }); +}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/GetLatestFromRegistry.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/GetLatestFromRegistry.test.ts new file mode 100644 index 00000000000..948d3ec5054 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/test/GetLatestFromRegistry.test.ts @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +jest.mock('package-json'); + +import getNpmInfo from '../GetLatestFromRegistry'; +import packageJson from 'package-json'; +import type { INpmRegistryInfo } from '../interfaces/INpmCheckRegistry'; + +const mockPackageJson = packageJson as jest.MockedFunction; + +describe('getNpmInfo', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns registry info with homepage', async () => { + mockPackageJson.mockResolvedValue({ + versions: { + '1.0.0': { + homepage: 'https://homepage.com' + }, + '2.0.0': { + bugs: { url: 'https://bugs.com' } + } + }, + 'dist-tags': { latest: '1.0.0', next: '2.0.0' } + } as unknown as packageJson.FullMetadata); + const result: INpmRegistryInfo = await getNpmInfo('test-package'); + expect(result).toHaveProperty('latest', '1.0.0'); + expect(result).toHaveProperty('next', '2.0.0'); + expect(result).toHaveProperty('versions', ['1.0.0', '2.0.0']); + expect(result).toHaveProperty('homepage', 'https://homepage.com'); + }); + + it('returns error if packageJson throws', async () => { + mockPackageJson.mockRejectedValue(new Error('Registry down')); + const result: INpmRegistryInfo = await getNpmInfo('test-package'); + expect(result).toHaveProperty('error'); + expect(result.error).toBe('Registry error Registry down'); + }); + + it('returns "" homepage if not present', async () => { + mockPackageJson.mockResolvedValue({ + versions: { + '1.0.0': {}, + '2.0.0': {} + }, + 'dist-tags': { latest: '1.0.0', next: '2.0.0' } + } as unknown as packageJson.FullMetadata); + const result: INpmRegistryInfo = await getNpmInfo('test-package'); + expect(result).toHaveProperty('homepage', ''); + }); +}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts new file mode 100644 index 00000000000..29a157c0553 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +jest.mock('../CreatePackageSummary', () => ({ + __esModule: true, + default: jest.fn(async () => ({})) +})); + +jest.mock('../GetUnusedPackages', () => ({ + __esModule: true, + default: jest.fn(async (state) => { + if (state.skipUnused) { + return state; + } + state.unusedDependencies = ['mock-unused']; + state.missingFromPackageJson = { 'mock-missing': ['index.js'] }; + return state; + }) +})); + +import createPackageSummary from '../CreatePackageSummary'; +const mockCreatePackageSummary = createPackageSummary as jest.MockedFunction; + +import type { INpmCheckState } from '../interfaces/INpmCheck'; +import LocalNpmCheck from '../LocalNpmCheck'; + +describe('NpmCheck', () => { + it('should set unusedDependencies, missingFromPackageJson, and package summaries', async () => { + mockCreatePackageSummary.mockImplementation(async (moduleName) => ({ + moduleName, + homepage: '', + latest: '', + installed: '', + isInstalled: false, + notInstalled: true, + packageWanted: '', + packageJson: '', + notInPackageJson: undefined, + devDependency: false, + peerDependency: false, + usedInScripts: [], + mismatch: false, + semverValid: '', + easyUpgrade: false, + bump: undefined, + unused: false + })); + const result: INpmCheckState = await LocalNpmCheck(); + expect(result.unusedDependencies).toStrictEqual(['mock-unused']); + expect(result.missingFromPackageJson).toStrictEqual({ 'mock-missing': ['index.js'] }); + expect(result.packages).toBeDefined(); + if (result.packages && result.packages.length > 0) { + expect(result.packages[0]).toHaveProperty('moduleName'); + } + }); + + it('should mimic rush initial options', async () => { + mockCreatePackageSummary.mockImplementation(async (moduleName) => ({ + moduleName, + homepage: '', + latest: '', + installed: '', + isInstalled: false, + notInstalled: true, + packageWanted: '', + packageJson: '', + notInPackageJson: undefined, + devDependency: false, + peerDependency: false, + usedInScripts: [], + mismatch: false, + semverValid: '', + easyUpgrade: false, + bump: undefined, + unused: false + })); + const result: INpmCheckState = await LocalNpmCheck({ + cwd: process.cwd(), + skipUnused: true + }); + expect(result.unusedDependencies).toBeFalsy(); + expect(result.missingFromPackageJson).toStrictEqual({}); + expect(result.packages).toBeDefined(); + if (result.packages && result.packages.length > 0) { + expect(result.packages[0]).toHaveProperty('moduleName'); + } + }); +}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts new file mode 100644 index 00000000000..655ae281213 --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { INpmCheckState } from '../interfaces/INpmCheck'; +import initializeState from '../NpmCheckState'; + +describe('NpmCheckState', () => { + it('should create with default options', async () => { + const state: INpmCheckState = await initializeState(); + expect(state).toBeDefined(); + expect(state.cwd).toBe(process.cwd()); + expect(state.cwdPackageJson).toHaveProperty('name'); + expect(state.cwdPackageJson).toHaveProperty('version'); + }); + + it('should create with rush options', async () => { + const state: INpmCheckState = await initializeState({ + cwd: process.cwd(), + skipUnused: true + }); + expect(state).toBeDefined(); + expect(state.skipUnused).toBeTruthy(); + }); +}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts new file mode 100644 index 00000000000..97a19845cee --- /dev/null +++ b/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'path'; +import readPackageJson from '../ReadPackageJson'; +import type { INpmCheckPackageJson } from '../interfaces/INpmCheck'; + +describe('readPackageJson', () => { + it('should return valid packageJson if it exists', async () => { + const fileName: string = path.join(process.cwd(), 'package.json'); + const result: INpmCheckPackageJson = await readPackageJson(fileName); + + expect(result).toBeDefined(); + expect(result).toHaveProperty('name'); + }); +}); From 1cded4e388a929f2051f4fdc5d93c387ec608ec8 Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Thu, 2 Oct 2025 13:17:45 -0400 Subject: [PATCH 02/11] Add rush change file --- ...onzo-npmaudit-replacenpmcheck_2025-10-02-17-17.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-17-17.json diff --git a/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-17-17.json b/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-17-17.json new file mode 100644 index 00000000000..962647d4626 --- /dev/null +++ b/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-17-17.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Fork npm-check into rush-lib", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file From 3c81472ad9a37aab34a49437673a2e363355e819 Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Thu, 2 Oct 2025 14:44:57 -0400 Subject: [PATCH 03/11] Append change file --- ...onzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json diff --git a/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json b/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json new file mode 100644 index 00000000000..bd7ff97cb34 --- /dev/null +++ b/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file From 4394ca3865b4304e77b25fb5087454f17cc6911a Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Mon, 6 Oct 2025 15:20:21 -0700 Subject: [PATCH 04/11] Remove unused packages --- .../build-tests-subspace/pnpm-lock.yaml | 72 ++- .../build-tests-subspace/repo-state.json | 4 +- .../config/subspaces/default/pnpm-lock.yaml | 546 ++---------------- .../config/subspaces/default/repo-state.json | 2 +- libraries/rush-lib/package.json | 10 +- .../rush-lib/src/logic/InteractiveUpgrader.ts | 5 +- .../rush-lib/src/logic/PackageJsonUpdater.ts | 3 +- .../rush-lib/src/types/npm-check-typings.d.ts | 47 -- .../rush-lib/src/types/xtend-typings.d.ts | 6 - .../src/utilities/InteractiveUpgradeUI.ts | 2 + .../utilities/npmCheck/BestGuessHomepage.ts | 1 + .../npmCheck/CreatePackageSummary.ts | 49 +- .../src/utilities/npmCheck/FindModulePath.ts | 9 +- .../npmCheck/GetLatestFromRegistry.ts | 6 +- .../utilities/npmCheck/GetUnusedPackages.ts | 91 --- .../src/utilities/npmCheck/LocalNpmCheck.ts | 26 +- .../src/utilities/npmCheck/NpmCheckState.ts | 8 +- .../src/utilities/npmCheck/ReadPackageJson.ts | 5 +- .../npmCheck/interfaces/INpmCheck.ts | 23 +- .../interfaces/INpmCheckPackageSummary.ts | 28 +- .../test/CreatePackageSummary.test.ts | 5 - .../npmCheck/test/FindModulePath.test.ts | 13 +- .../utilities/npmCheck/test/NpmCheck.test.ts | 53 +- .../npmCheck/test/NpmCheckState.test.ts | 9 - .../npmCheck/test/ReadPackageJson.test.ts | 2 +- 25 files changed, 169 insertions(+), 856 deletions(-) delete mode 100644 libraries/rush-lib/src/types/npm-check-typings.d.ts delete mode 100644 libraries/rush-lib/src/types/xtend-typings.d.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/GetUnusedPackages.ts diff --git a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml index 7db38bb789d..d07c2f6ba03 100644 --- a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml +++ b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml @@ -105,10 +105,10 @@ importers: version: file:../../../apps/heft(@types/node@20.17.19) '@rushstack/heft-lint-plugin': specifier: file:../../heft-plugins/heft-lint-plugin - version: file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19) + version: file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19) '@rushstack/heft-typescript-plugin': specifier: file:../../heft-plugins/heft-typescript-plugin - version: file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19) + version: file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19) eslint: specifier: ~9.25.1 version: 9.25.1 @@ -2114,10 +2114,13 @@ packages: ansi-styles: 4.3.0 supports-color: 7.2.0 + /chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + /char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - dev: true /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} @@ -2426,7 +2429,7 @@ packages: deps-regex: 0.2.0 findup-sync: 5.0.0 ignore: 5.3.2 - is-core-module: 2.15.1 + is-core-module: 2.16.1 js-yaml: 3.14.1 json5: 2.2.3 lodash: 4.17.21 @@ -2527,6 +2530,9 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + /emojilib@2.4.0: + resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + /encode-registry@3.0.1: resolution: {integrity: sha512-6qOwkl1g0fv0DN3Y3ggr2EaZXN71aoAqPp3p/pVaWSBSIo+YjLOWN61Fva43oVyQNPf7kgm8lkudzlzojwE2jw==} engines: {node: '>=10'} @@ -3368,6 +3374,10 @@ packages: resolution: {integrity: sha512-qVDEXufVtYUzYqI5hoDUONh9GCEPi0n+e35KNDafdsNt9fPxB0nvFW/kFiw7W42wkg8TUyhBqb+t24yyaoc87A==} engines: {node: '>= 0.10.0'} + /giturl@2.0.0: + resolution: {integrity: sha512-FB0MmghWLcqsyrBZyqsLCNeS2kIzYymT34t/6BxM5R0/9Pxvj0K1eK25SBbwRHMjKMLgQ7nYqBSduF6XyfkgFg==} + engines: {node: '>= 14.17.0'} + /glob-escape@0.0.2: resolution: {integrity: sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==} engines: {node: '>= 0.10'} @@ -3812,7 +3822,6 @@ packages: engines: {node: '>= 0.4'} dependencies: hasown: 2.0.2 - dev: true /is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} @@ -4996,6 +5005,15 @@ packages: dependencies: lodash: 4.17.21 + /node-emoji@2.2.0: + resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} + engines: {node: '>=18'} + dependencies: + '@sindresorhus/is': 4.6.0 + char-regex: 1.0.2 + emojilib: 2.4.0 + skin-tone: 2.0.0 + /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true @@ -5929,6 +5947,12 @@ packages: /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + /skin-tone@2.0.0: + resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} + engines: {node: '>=8'} + dependencies: + unicode-emoji-modifier-base: 1.0.0 + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -6469,6 +6493,10 @@ packages: /undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + /unicode-emoji-modifier-base@1.0.0: + resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} + engines: {node: '>=4'} + /unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -7010,7 +7038,7 @@ packages: - supports-color dev: true - file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19): + file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19): resolution: {directory: ../../../heft-plugins/heft-api-extractor-plugin, type: directory} id: file:../../../heft-plugins/heft-api-extractor-plugin name: '@rushstack/heft-api-extractor-plugin' @@ -7024,7 +7052,7 @@ packages: - '@types/node' dev: true - file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19)(jest-environment-node@29.5.0): + file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19)(jest-environment-node@29.5.0): resolution: {directory: ../../../heft-plugins/heft-jest-plugin, type: directory} id: file:../../../heft-plugins/heft-jest-plugin name: '@rushstack/heft-jest-plugin' @@ -7059,7 +7087,7 @@ packages: - ts-node dev: true - file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19): + file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19): resolution: {directory: ../../../heft-plugins/heft-lint-plugin, type: directory} id: file:../../../heft-plugins/heft-lint-plugin name: '@rushstack/heft-lint-plugin' @@ -7074,7 +7102,7 @@ packages: - '@types/node' dev: true - file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19): + file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19): resolution: {directory: ../../../heft-plugins/heft-typescript-plugin, type: directory} id: file:../../../heft-plugins/heft-typescript-plugin name: '@rushstack/heft-typescript-plugin' @@ -7232,21 +7260,31 @@ packages: '@rushstack/zipsync': file:../../../apps/zipsync(@types/node@20.17.19) '@yarnpkg/lockfile': 1.0.2 builtin-modules: 3.1.0 + chalk: 5.6.2 cli-table: 0.3.11 + co: 4.6.0 + depcheck: 1.4.7 dependency-path: 9.2.8 dotenv: 16.4.7 fast-glob: 3.3.2 figures: 3.0.0 git-repo-info: 2.1.1 + giturl: 2.0.0 glob-escape: 0.0.2 + global-modules: 2.0.0 https-proxy-agent: 5.0.1 ignore: 5.1.9 inquirer: 8.2.7(@types/node@20.17.19) js-yaml: 4.1.0 + lodash: 4.17.21 + node-emoji: 2.2.0 npm-check: 6.0.1 npm-package-arg: 6.1.1 object-hash: 3.0.0 + package-json: 7.0.0 + path-exists: 4.0.0 pnpm-sync-lib: 0.3.2 + rc-config-loader: 4.1.3 read-package-tree: 5.1.6 rxjs: 6.6.7 semver: 7.5.4 @@ -7254,8 +7292,10 @@ packages: strict-uri-encode: 2.0.0 tapable: 2.2.1 tar: 6.2.1 + throat: 6.0.2 true-case-path: 2.2.1 uuid: 8.3.2 + xtend: 4.0.2 transitivePeerDependencies: - '@types/node' - supports-color @@ -7317,7 +7357,7 @@ packages: transitivePeerDependencies: - '@types/node' - file:../../../rigs/heft-node-rig(@rushstack/heft@1.0.0)(@types/node@20.17.19): + file:../../../rigs/heft-node-rig(@rushstack/heft@1.1.0)(@types/node@20.17.19): resolution: {directory: ../../../rigs/heft-node-rig, type: directory} id: file:../../../rigs/heft-node-rig name: '@rushstack/heft-node-rig' @@ -7327,10 +7367,10 @@ packages: '@microsoft/api-extractor': file:../../../apps/api-extractor(@types/node@20.17.19) '@rushstack/eslint-config': file:../../../eslint/eslint-config(eslint@9.25.1)(typescript@5.8.2) '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) - '@rushstack/heft-api-extractor-plugin': file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19) - '@rushstack/heft-jest-plugin': file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19)(jest-environment-node@29.5.0) - '@rushstack/heft-lint-plugin': file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19) - '@rushstack/heft-typescript-plugin': file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@1.0.0)(@types/node@20.17.19) + '@rushstack/heft-api-extractor-plugin': file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19) + '@rushstack/heft-jest-plugin': file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19)(jest-environment-node@29.5.0) + '@rushstack/heft-lint-plugin': file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19) + '@rushstack/heft-typescript-plugin': file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@1.1.0)(@types/node@20.17.19) '@types/heft-jest': 1.0.1 eslint: 9.25.1 jest-environment-node: 29.5.0 @@ -7352,7 +7392,7 @@ packages: '@microsoft/api-extractor': file:../../../apps/api-extractor(@types/node@20.17.19) '@rushstack/eslint-patch': file:../../../eslint/eslint-patch '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) - '@rushstack/heft-node-rig': file:../../../rigs/heft-node-rig(@rushstack/heft@1.0.0)(@types/node@20.17.19) + '@rushstack/heft-node-rig': file:../../../rigs/heft-node-rig(@rushstack/heft@1.1.0)(@types/node@20.17.19) '@types/heft-jest': 1.0.1 '@types/node': 20.17.19 eslint: 9.25.1 @@ -7366,4 +7406,4 @@ packages: - node-notifier - supports-color - ts-node - dev: true \ No newline at end of file + dev: true diff --git a/common/config/subspaces/build-tests-subspace/repo-state.json b/common/config/subspaces/build-tests-subspace/repo-state.json index 1d2781d97f7..99f9de2920d 100644 --- a/common/config/subspaces/build-tests-subspace/repo-state.json +++ b/common/config/subspaces/build-tests-subspace/repo-state.json @@ -1,6 +1,6 @@ // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. { - "pnpmShrinkwrapHash": "f2c06df75a96061e31624e1a556e34660a569623", + "pnpmShrinkwrapHash": "82149a06198f594f663110cba84ee91c83d95977", "preferredVersionsHash": "550b4cee0bef4e97db6c6aad726df5149d20e7d9", - "packageJsonInjectedDependenciesHash": "14f4881943e5d03a361f079944eb76e1501b3e18" + "packageJsonInjectedDependenciesHash": "5f1e5d69f74ed68acee2abcdc822fd58e74f35fc" } diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 864abc4be22..5100b7a34ec 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -3663,6 +3663,9 @@ importers: git-repo-info: specifier: ~2.1.0 version: 2.1.1 + giturl: + specifier: ^2.0.0 + version: 2.0.0 glob-escape: specifier: ~0.0.2 version: 0.0.2 @@ -3678,15 +3681,18 @@ importers: js-yaml: specifier: ~4.1.0 version: 4.1.0 - npm-check: - specifier: ~6.0.1 - version: 6.0.1 + lodash: + specifier: ~4.17.15 + version: 4.17.21 npm-package-arg: specifier: ~6.1.0 version: 6.1.1 object-hash: specifier: 3.0.0 version: 3.0.0 + package-json: + specifier: ^7 + version: 7.0.0 pnpm-sync-lib: specifier: 0.3.2 version: 0.3.2 @@ -3711,6 +3717,9 @@ importers: tar: specifier: ~6.2.1 version: 6.2.1 + throat: + specifier: ^6.0.2 + version: 6.0.2 true-case-path: specifier: ~2.2.1 version: 2.2.1 @@ -3745,6 +3754,9 @@ importers: '@types/js-yaml': specifier: 4.0.9 version: 4.0.9 + '@types/lodash': + specifier: 4.14.116 + version: 4.14.116 '@types/npm-package-arg': specifier: 6.1.0 version: 6.1.0 @@ -7616,12 +7628,6 @@ packages: dev: true optional: true - /@devexpress/error-stack-parser@2.0.6: - resolution: {integrity: sha512-fneVypElGUH6Be39mlRZeAu00pccTlf4oVuzf9xPJD1cdEqI8NyAiQua/EW7lZdrbMUbgyXcJmfKPefhYius3A==} - dependencies: - stackframe: 1.3.4 - dev: false - /@discoveryjs/json-ext@0.5.7: resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} @@ -14013,10 +14019,6 @@ packages: /@types/mime@3.0.4: resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} - /@types/minimatch@3.0.5: - resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - dev: false - /@types/minimatch@6.0.0: resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. @@ -14024,10 +14026,6 @@ packages: minimatch: 10.0.3 dev: true - /@types/minimist@1.2.5: - resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - dev: false - /@types/mocha@10.0.6: resolution: {integrity: sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==} dev: true @@ -14075,6 +14073,7 @@ packages: /@types/normalize-package-data@2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + dev: true /@types/npm-package-arg@6.1.0: resolution: {integrity: sha512-vbt5fb0y1svMhu++1lwtKmZL76d0uPChFlw7kEzyUmTwfmpHRcFb8i0R8ElT69q/L+QLgK2hgECivIAvaEDwag==} @@ -15155,48 +15154,6 @@ packages: - supports-color dev: false - /@vue/compiler-core@3.4.21: - resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==} - dependencies: - '@babel/parser': 7.24.0 - '@vue/shared': 3.4.21 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.1.0 - dev: false - - /@vue/compiler-dom@3.4.21: - resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} - dependencies: - '@vue/compiler-core': 3.4.21 - '@vue/shared': 3.4.21 - dev: false - - /@vue/compiler-sfc@3.4.21: - resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==} - dependencies: - '@babel/parser': 7.24.0 - '@vue/compiler-core': 3.4.21 - '@vue/compiler-dom': 3.4.21 - '@vue/compiler-ssr': 3.4.21 - '@vue/shared': 3.4.21 - estree-walker: 2.0.2 - magic-string: 0.30.8 - postcss: 8.4.36 - source-map-js: 1.1.0 - dev: false - - /@vue/compiler-ssr@3.4.21: - resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==} - dependencies: - '@vue/compiler-dom': 3.4.21 - '@vue/shared': 3.4.21 - dev: false - - /@vue/shared@3.4.21: - resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} - dev: false - /@webassemblyjs/ast@1.14.1: resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} dependencies: @@ -15835,11 +15792,6 @@ packages: call-bound: 1.0.4 is-array-buffer: 3.0.5 - /array-differ@3.0.0: - resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} - engines: {node: '>=8'} - dev: false - /array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} @@ -15878,6 +15830,7 @@ packages: /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + dev: true /array-uniq@1.0.3: resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} @@ -15997,14 +15950,10 @@ packages: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 - /arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} - dev: false - /arrify@2.0.1: resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} engines: {node: '>=8'} + dev: true /asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} @@ -16624,7 +16573,7 @@ packages: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 - chalk: 5.3.0 + chalk: 5.6.2 cli-boxes: 3.0.0 string-width: 5.1.2 type-fest: 2.19.0 @@ -16953,22 +16902,6 @@ packages: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} dev: true - /callsite-record@4.1.5: - resolution: {integrity: sha512-OqeheDucGKifjQRx524URgV4z4NaKjocGhygTptDea+DLROre4ZEecA4KXDq+P7qlGCohYVNOh3qr+y5XH5Ftg==} - dependencies: - '@devexpress/error-stack-parser': 2.0.6 - '@types/lodash': 4.14.116 - callsite: 1.0.0 - chalk: 2.4.2 - highlight-es: 1.0.3 - lodash: 4.17.21 - pinkie-promise: 2.0.1 - dev: false - - /callsite@1.0.0: - resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} - dev: false - /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -16984,15 +16917,6 @@ packages: engines: {node: '>= 6'} dev: true - /camelcase-keys@6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - dev: false - /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -17071,8 +16995,8 @@ packages: ansi-styles: 4.3.0 supports-color: 7.2.0 - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + /chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: true @@ -17092,10 +17016,6 @@ packages: resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} dev: true - /chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: false - /chardet@2.1.0: resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} dev: false @@ -17297,6 +17217,7 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + dev: true /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} @@ -18140,17 +18061,10 @@ packages: deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. dev: false - /decamelize-keys@1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} - dependencies: - decamelize: 1.2.0 - map-obj: 1.0.1 - dev: false - /decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} + dev: true /decamelize@4.0.0: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} @@ -18290,38 +18204,6 @@ packages: immer: 9.0.21 dev: true - /depcheck@1.4.7: - resolution: {integrity: sha512-1lklS/bV5chOxwNKA/2XUUk/hPORp8zihZsXflr8x0kLwmcZ9Y9BsS6Hs3ssvA+2wUVbG0U2Ciqvm1SokNjPkA==} - engines: {node: '>=10'} - hasBin: true - dependencies: - '@babel/parser': 7.24.0 - '@babel/traverse': 7.24.0 - '@vue/compiler-sfc': 3.4.21 - callsite: 1.0.0 - camelcase: 6.3.0 - cosmiconfig: 7.1.0 - debug: 4.4.0(supports-color@8.1.1) - deps-regex: 0.2.0 - findup-sync: 5.0.0 - ignore: 5.3.1 - is-core-module: 2.16.1 - js-yaml: 3.14.1 - json5: 2.2.3 - lodash: 4.17.21 - minimatch: 7.4.6 - multimatch: 5.0.0 - please-upgrade-node: 3.2.0 - readdirp: 3.6.0 - require-package-name: 2.0.1 - resolve: 1.22.8 - resolve-from: 5.0.0 - semver: 7.5.4 - yargs: 16.2.0 - transitivePeerDependencies: - - supports-color - dev: false - /depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -18341,10 +18223,6 @@ packages: semver: 7.5.4 dev: false - /deps-regex@0.2.0: - resolution: {integrity: sha512-PwuBojGMQAYbWkMXOY9Pd/NWCDNHVH12pnS7WHqZkTSeMESe4hwnKKRp0yR87g37113x4JPbo/oIvXY+s/f56Q==} - dev: false - /des.js@1.1.0: resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} dependencies: @@ -18365,11 +18243,6 @@ packages: repeat-string: 1.6.1 dev: true - /detect-file@1.0.0: - resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} - engines: {node: '>=0.10.0'} - dev: false - /detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -18452,6 +18325,7 @@ packages: engines: {node: '>=8'} dependencies: path-type: 4.0.0 + dev: true /dns-packet@5.6.1: resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} @@ -19318,10 +19192,6 @@ packages: '@esbuild/win32-x64': 0.20.2 dev: true - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - /escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -20142,10 +20012,6 @@ packages: - supports-color dev: true - /estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: false - /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -20238,13 +20104,6 @@ packages: dev: false optional: true - /expand-tilde@2.0.2: - resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} - engines: {node: '>=0.10.0'} - dependencies: - homedir-polyfill: 1.0.3 - dev: false - /expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -20352,15 +20211,6 @@ packages: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} dev: true - /external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - dev: false - /extglob@2.0.4: resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} engines: {node: '>=0.10.0'} @@ -20684,23 +20534,6 @@ packages: locate-path: 6.0.0 path-exists: 4.0.0 - /find-yarn-workspace-root2@1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} - dependencies: - micromatch: 4.0.5 - pkg-dir: 4.2.0 - dev: false - - /findup-sync@5.0.0: - resolution: {integrity: sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==} - engines: {node: '>= 10.13.0'} - dependencies: - detect-file: 1.0.0 - is-glob: 4.0.3 - micromatch: 4.0.5 - resolve-dir: 1.0.1 - dev: false - /flat-cache@2.0.1: resolution: {integrity: sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==} engines: {node: '>=4'} @@ -21082,6 +20915,7 @@ packages: /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + dev: true /get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} @@ -21175,9 +21009,9 @@ packages: resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} dev: true - /giturl@1.0.3: - resolution: {integrity: sha512-qVDEXufVtYUzYqI5hoDUONh9GCEPi0n+e35KNDafdsNt9fPxB0nvFW/kFiw7W42wkg8TUyhBqb+t24yyaoc87A==} - engines: {node: '>= 0.10.0'} + /giturl@2.0.0: + resolution: {integrity: sha512-FB0MmghWLcqsyrBZyqsLCNeS2kIzYymT34t/6BxM5R0/9Pxvj0K1eK25SBbwRHMjKMLgQ7nYqBSduF6XyfkgFg==} + engines: {node: '>= 14.17.0'} dev: false /glob-escape@0.0.2: @@ -21271,42 +21105,6 @@ packages: dependencies: ini: 2.0.0 - /global-modules@1.0.0: - resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} - engines: {node: '>=0.10.0'} - dependencies: - global-prefix: 1.0.2 - is-windows: 1.0.2 - resolve-dir: 1.0.1 - dev: false - - /global-modules@2.0.0: - resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} - engines: {node: '>=6'} - dependencies: - global-prefix: 3.0.0 - dev: false - - /global-prefix@1.0.2: - resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} - engines: {node: '>=0.10.0'} - dependencies: - expand-tilde: 2.0.2 - homedir-polyfill: 1.0.3 - ini: 1.3.8 - is-windows: 1.0.2 - which: 1.3.1 - dev: false - - /global-prefix@3.0.0: - resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} - engines: {node: '>=6'} - dependencies: - ini: 1.3.8 - kind-of: 6.0.3 - which: 1.3.1 - dev: false - /global@4.4.0: resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} dependencies: @@ -21359,6 +21157,7 @@ packages: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 + dev: true /globby@9.2.0: resolution: {integrity: sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==} @@ -21438,11 +21237,6 @@ packages: uglify-js: 3.17.4 dev: true - /hard-rejection@2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} - dev: false - /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -21618,14 +21412,6 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - /highlight-es@1.0.3: - resolution: {integrity: sha512-s/SIX6yp/5S1p8aC/NRDC1fwEb+myGIfp8/TzZz0rtAv8fzsdX7vGl3Q1TrXCsczFq8DI3CBFBCySPClfBSdbg==} - dependencies: - chalk: 2.4.2 - is-es2016-keyword: 1.0.0 - js-tokens: 3.0.2 - dev: false - /highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} dev: true @@ -21648,13 +21434,6 @@ packages: dependencies: react-is: 16.13.1 - /homedir-polyfill@1.0.3: - resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} - engines: {node: '>=0.10.0'} - dependencies: - parse-passwd: 1.0.0 - dev: false - /hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -22032,6 +21811,7 @@ packages: /indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + dev: true /indent-string@5.0.0: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} @@ -22068,25 +21848,6 @@ packages: resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} dev: true - /inquirer@7.3.3: - resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} - engines: {node: '>=8.0.0'} - dependencies: - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - external-editor: 3.1.0 - figures: 3.0.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - run-async: 2.4.1 - rxjs: 6.6.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - dev: false - /inquirer@8.2.7: resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} engines: {node: '>=12.0.0'} @@ -22342,10 +22103,6 @@ packages: is-window: 1.0.2 dev: true - /is-es2016-keyword@1.0.0: - resolution: {integrity: sha512-JtZWPUwjdbQ1LIo9OSZ8MdkWEve198ors27vH+RzUUvZXXZkzXCxFnlUhzWYxy5IexQSRiXVw9j2q/tHMmkVYQ==} - dev: false - /is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} @@ -22482,11 +22239,6 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - /is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - dev: false - /is-plain-obj@2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} engines: {node: '>=8'} @@ -23487,10 +23239,6 @@ packages: engines: {node: '>= 0.8'} dev: true - /js-tokens@3.0.2: - resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} - dev: false - /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -23923,16 +23671,6 @@ packages: type-fest: 0.6.0 dev: false - /load-yaml-file@0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 - dev: false - /loader-runner@2.4.0: resolution: {integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==} engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} @@ -24126,13 +23864,6 @@ packages: dependencies: yallist: 4.0.0 - /magic-string@0.30.8: - resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - dev: false - /make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -24191,16 +23922,6 @@ packages: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} engines: {node: '>=0.10.0'} - /map-obj@1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} - dev: false - - /map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - dev: false - /map-or-similar@1.5.0: resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} dev: true @@ -24339,24 +24060,6 @@ packages: errno: 0.1.8 readable-stream: 2.3.8 - /meow@9.0.0: - resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} - engines: {node: '>=10'} - dependencies: - '@types/minimist': 1.2.5 - camelcase-keys: 6.2.2 - decamelize: 1.2.0 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 3.0.3 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.18.1 - yargs-parser: 20.2.9 - dev: false - /merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -24471,6 +24174,7 @@ packages: /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + dev: true /mini-css-extract-plugin@2.5.3(webpack@5.98.0): resolution: {integrity: sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==} @@ -24513,13 +24217,6 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch@7.4.6: - resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} - engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: false - /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -24533,15 +24230,6 @@ packages: dependencies: brace-expansion: 2.0.1 - /minimist-options@4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} - dependencies: - arrify: 1.0.1 - is-plain-obj: 1.1.0 - kind-of: 6.0.3 - dev: false - /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -24712,17 +24400,6 @@ packages: thunky: 1.1.0 dev: false - /multimatch@5.0.0: - resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} - engines: {node: '>=10'} - dependencies: - '@types/minimatch': 3.0.5 - array-differ: 3.0.0 - array-union: 2.1.0 - arrify: 2.0.1 - minimatch: 3.1.2 - dev: false - /mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} dev: false @@ -24834,12 +24511,6 @@ packages: minimatch: 3.1.2 dev: true - /node-emoji@1.11.0: - resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} - dependencies: - lodash: 4.17.21 - dev: false - /node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -24963,42 +24634,6 @@ packages: npm-normalize-package-bin: 1.0.1 dev: false - /npm-check@6.0.1: - resolution: {integrity: sha512-tlEhXU3689VLUHYEZTS/BC61vfeN2xSSZwoWDT6WLuenZTpDmGmNT5mtl15erTR0/A15ldK06/NEKg9jYJ9OTQ==} - engines: {node: '>=10.9.0'} - hasBin: true - dependencies: - callsite-record: 4.1.5 - chalk: 4.1.2 - co: 4.6.0 - depcheck: 1.4.7 - execa: 5.1.1 - giturl: 1.0.3 - global-modules: 2.0.0 - globby: 11.1.0 - inquirer: 7.3.3 - is-ci: 2.0.0 - lodash: 4.17.21 - meow: 9.0.0 - minimatch: 3.1.2 - node-emoji: 1.11.0 - ora: 5.4.1 - package-json: 7.0.0 - path-exists: 4.0.0 - pkg-dir: 5.0.0 - preferred-pm: 3.1.3 - rc-config-loader: 4.1.3 - semver: 7.5.4 - semver-diff: 3.1.1 - strip-ansi: 6.0.1 - text-table: 0.2.0 - throat: 6.0.2 - update-notifier: 5.1.0 - xtend: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: false - /npm-normalize-package-bin@1.0.1: resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} dev: false @@ -25503,11 +25138,6 @@ packages: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - /parse-passwd@1.0.0: - resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} - engines: {node: '>=0.10.0'} - dev: false - /parse-semver@1.1.1: resolution: {integrity: sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==} dependencies: @@ -25647,18 +25277,6 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - /pinkie-promise@2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} - dependencies: - pinkie: 2.0.4 - dev: false - - /pinkie@2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - dev: false - /pino-std-serializers@3.2.0: resolution: {integrity: sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==} dev: false @@ -25696,12 +25314,14 @@ packages: engines: {node: '>=8'} dependencies: find-up: 4.1.0 + dev: true /pkg-dir@5.0.0: resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} engines: {node: '>=10'} dependencies: find-up: 5.0.0 + dev: true /pkg-up@3.1.0: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} @@ -25710,12 +25330,6 @@ packages: find-up: 3.0.0 dev: true - /please-upgrade-node@3.2.0: - resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} - dependencies: - semver-compare: 1.0.0 - dev: false - /pnp-webpack-plugin@1.6.4(typescript@5.8.2): resolution: {integrity: sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==} engines: {node: '>=6'} @@ -26214,16 +25828,6 @@ packages: dev: false optional: true - /preferred-pm@3.1.3: - resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} - engines: {node: '>=10'} - dependencies: - find-up: 5.0.0 - find-yarn-workspace-root2: 1.2.16 - path-exists: 4.0.0 - which-pm: 2.0.0 - dev: false - /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -26515,11 +26119,6 @@ packages: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} dev: false - /quick-lru@4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} - dev: false - /quick-lru@5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} @@ -26577,17 +26176,6 @@ packages: webpack: 4.47.0 dev: true - /rc-config-loader@4.1.3: - resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} - dependencies: - debug: 4.4.0(supports-color@8.1.1) - js-yaml: 4.1.0 - json5: 2.2.3 - require-from-string: 2.0.2 - transitivePeerDependencies: - - supports-color - dev: false - /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -26898,6 +26486,7 @@ packages: find-up: 4.1.0 read-pkg: 5.2.0 type-fest: 0.8.1 + dev: true /read-pkg@5.2.0: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} @@ -26907,6 +26496,7 @@ packages: normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 + dev: true /read-yaml-file@2.1.0: resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} @@ -27008,14 +26598,6 @@ packages: resolve: 1.22.8 dev: true - /redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - dev: false - /redux-thunk@2.4.2(redux@4.2.1): resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==} peerDependencies: @@ -27242,6 +26824,7 @@ packages: /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + dev: true /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -27251,10 +26834,6 @@ packages: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true - /require-package-name@2.0.1: - resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==} - dev: false - /requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -27272,14 +26851,6 @@ packages: resolve-from: 5.0.0 dev: true - /resolve-dir@1.0.1: - resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} - engines: {node: '>=0.10.0'} - dependencies: - expand-tilde: 2.0.2 - global-modules: 1.0.0 - dev: false - /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -27864,10 +27435,6 @@ packages: node-forge: 1.3.1 dev: false - /semver-compare@1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - dev: false - /semver-diff@3.1.1: resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} engines: {node: '>=8'} @@ -28491,6 +28058,7 @@ packages: /stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + dev: true /state-toggle@1.0.3: resolution: {integrity: sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==} @@ -28774,6 +28342,7 @@ packages: engines: {node: '>=8'} dependencies: min-indent: 1.0.1 + dev: true /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} @@ -29131,6 +28700,7 @@ packages: /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true /thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} @@ -29201,13 +28771,6 @@ packages: picomatch: 4.0.2 dev: false - /tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - dependencies: - os-tmpdir: 1.0.2 - dev: false - /tmp@0.2.3: resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} engines: {node: '>=14.14'} @@ -29294,11 +28857,6 @@ packages: dependencies: tslib: 2.8.1 - /trim-newlines@3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} - dev: false - /trim-trailing-lines@1.1.4: resolution: {integrity: sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==} dev: true @@ -29546,11 +29104,6 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} - /type-fest@0.18.1: - resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} - engines: {node: '>=10'} - dev: false - /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} @@ -29566,6 +29119,7 @@ packages: /type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} + dev: true /type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} @@ -29918,7 +29472,7 @@ packages: browserslist: '>= 4.21.0' dependencies: browserslist: 4.23.0 - escalade: 3.1.2 + escalade: 3.2.0 picocolors: 1.0.0 /update-browserslist-db@1.1.2(browserslist@4.24.4): @@ -30667,14 +30221,6 @@ packages: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} dev: true - /which-pm@2.0.0: - resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} - engines: {node: '>=8.15'} - dependencies: - load-yaml-file: 0.2.0 - path-exists: 4.0.0 - dev: false - /which-typed-array@1.1.15: resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} @@ -30702,6 +30248,7 @@ packages: hasBin: true dependencies: isexe: 2.0.0 + dev: true /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -30914,6 +30461,7 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + dev: true /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -30947,6 +30495,7 @@ packages: /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} + dev: true /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} @@ -30985,19 +30534,20 @@ packages: engines: {node: '>=10'} dependencies: cliui: 7.0.4 - escalade: 3.1.2 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 + dev: true /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} dependencies: cliui: 8.0.1 - escalade: 3.1.2 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 @@ -31065,4 +30615,4 @@ packages: /zwitch@1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} - dev: true \ No newline at end of file + dev: true diff --git a/common/config/subspaces/default/repo-state.json b/common/config/subspaces/default/repo-state.json index eb22dd89def..d802676c1a2 100644 --- a/common/config/subspaces/default/repo-state.json +++ b/common/config/subspaces/default/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": "c643d2cc7e2c60ef034a6557fc89760140d42f66", + "pnpmShrinkwrapHash": "0fff1877fd4051bdbc1b709d4e6382761efebb73", "preferredVersionsHash": "61cd419c533464b580f653eb5f5a7e27fe7055ca" } diff --git a/libraries/rush-lib/package.json b/libraries/rush-lib/package.json index 1f687ab8d78..65cfd91809d 100644 --- a/libraries/rush-lib/package.json +++ b/libraries/rush-lib/package.json @@ -44,10 +44,7 @@ "@rushstack/ts-command-line": "workspace:*", "@yarnpkg/lockfile": "~1.0.2", "builtin-modules": "~3.1.0", - "chalk": "^5.6.2", "cli-table": "~0.3.1", - "co": "^4.6.0", - "depcheck": "^1.4.7", "dependency-path": "~9.2.8", "dotenv": "~16.4.7", "fast-glob": "~3.3.1", @@ -55,19 +52,15 @@ "giturl": "^2.0.0", "git-repo-info": "~2.1.0", "glob-escape": "~0.0.2", - "global-modules": "^2.0.0", "https-proxy-agent": "~5.0.0", "ignore": "~5.1.6", "inquirer": "~8.2.7", "js-yaml": "~4.1.0", "lodash": "~4.17.15", - "node-emoji": "^2.2.0", "npm-package-arg": "~6.1.0", "object-hash": "3.0.0", "package-json": "^10.0.1", - "path-exists": "4.0.0", "pnpm-sync-lib": "0.3.2", - "rc-config-loader": "^4.1.3", "read-package-tree": "~5.1.5", "rxjs": "~6.6.7", "semver": "~7.5.4", @@ -76,8 +69,7 @@ "tapable": "2.2.1", "tar": "~6.2.1", "throat": "^6.0.2", - "true-case-path": "~2.2.1", - "xtend": "^4.0.2" + "true-case-path": "~2.2.1" }, "devDependencies": { "@pnpm/lockfile.types": "~1.0.3", diff --git a/libraries/rush-lib/src/logic/InteractiveUpgrader.ts b/libraries/rush-lib/src/logic/InteractiveUpgrader.ts index 8589dd81f12..59174b77721 100644 --- a/libraries/rush-lib/src/logic/InteractiveUpgrader.ts +++ b/libraries/rush-lib/src/logic/InteractiveUpgrader.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. - import Prompt from 'inquirer/lib/ui/prompt'; + import { Colorize } from '@rushstack/terminal'; import type { RushConfiguration } from '../api/RushConfiguration'; @@ -72,8 +72,7 @@ export class InteractiveUpgrader { const { projectFolder } = rushProject; const newState: INpmCheckState = await LocalNpmCheck({ - cwd: projectFolder, - skipUnused: true + cwd: projectFolder }); return newState.packages ?? []; diff --git a/libraries/rush-lib/src/logic/PackageJsonUpdater.ts b/libraries/rush-lib/src/logic/PackageJsonUpdater.ts index 96850171aad..4ad01cee792 100644 --- a/libraries/rush-lib/src/logic/PackageJsonUpdater.ts +++ b/libraries/rush-lib/src/logic/PackageJsonUpdater.ts @@ -2,7 +2,6 @@ // See LICENSE in the project root for license information. import * as semver from 'semver'; -import type * as NpmCheck from 'npm-check'; import { Colorize, type ITerminal } from '@rushstack/terminal'; @@ -893,7 +892,7 @@ export class PackageJsonUpdater { } } - private _normalizeDepsToUpgrade(deps: NpmCheck.INpmCheckPackage[]): IPackageForRushAdd[] { + private _normalizeDepsToUpgrade(deps: INpmCheckPackageSummary[]): IPackageForRushAdd[] { return deps.map((dep) => { return { packageName: dep.moduleName, diff --git a/libraries/rush-lib/src/types/npm-check-typings.d.ts b/libraries/rush-lib/src/types/npm-check-typings.d.ts deleted file mode 100644 index 5c25d1d8c93..00000000000 --- a/libraries/rush-lib/src/types/npm-check-typings.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -declare module 'npm-check' { - interface INpmCheckOptions { - global?: boolean; - update?: boolean; - skipUnused?: boolean; - devOnly?: boolean; - ignoreDev?: boolean; - cwd?: string; - saveExact?: boolean; - currentState?: Object; - } - - type INpmCheckGetSetValues = 'packages' | 'debug' | 'global' | 'cwd' | 'cwdPackageJson' | 'emoji'; - - type INpmVersionBumpType = 'patch' | 'minor' | 'major' | 'prerelease' | 'build' | 'nonSemver' | null; - - interface INpmCheckCurrentState { - get: (key: INpmCheckGetSetValues) => INpmCheckPackage[]; - set: (key: INpmCheckGetSetValues, val: any) => void; - } - - interface INpmCheckPackage { - moduleName: string; // name of the module. - homepage: string; // url to the home page. - regError: any; // error communicating with the registry - pkgError: any; // error reading the package.json - latest: string; // latest according to the registry. - installed: string; // version installed - isInstalled: boolean; // Is it installed? - notInstalled: boolean; // Is it installed? - packageWanted: string; // Requested version from the package.json. - packageJson: string; // Version or range requested in the parent package.json. - devDependency: boolean; // Is this a devDependency? - peerDependency: boolean; // Is this a peerDependency? - usedInScripts: undefined | string[]; // Array of `scripts` in package.json that use this module. - mismatch: boolean; // Does the version installed not match the range in package.json? - semverValid: string; // Is the installed version valid semver? - easyUpgrade: boolean; // Will running just `npm install` upgrade the module? - bump: INpmVersionBumpType; // What kind of bump is required to get the latest - unused: boolean; // Is this module used in the code? - } - - //The default function returns a promise - export default function (options: INpmCheckOptions): { - then(stateFn: (state: INpmCheckCurrentState) => void): void; - }; -} diff --git a/libraries/rush-lib/src/types/xtend-typings.d.ts b/libraries/rush-lib/src/types/xtend-typings.d.ts deleted file mode 100644 index 3d1fa5067a8..00000000000 --- a/libraries/rush-lib/src/types/xtend-typings.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module 'xtend' { - function extend(a: T, b: U): T & U; - function extend(a: T, b: U, c: V): T & U & V; - function extend(...args: any[]): any; - export = extend; -} diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts b/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts index 3a4f6cdcb62..4b8e6dd91cc 100644 --- a/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts @@ -8,7 +8,9 @@ import inquirer from 'inquirer'; import CliTable from 'cli-table'; import type Separator from 'inquirer/lib/objects/separator'; + import { AnsiEscape, Colorize } from '@rushstack/terminal'; + import type { INpmCheckPackageSummary } from './npmCheck/interfaces/INpmCheckPackageSummary'; export interface IUIGroup { diff --git a/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts b/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts index 12b496f4181..22db96bc03a 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts @@ -4,6 +4,7 @@ /// import gitUrl from 'giturl'; + import type { INpmCheckPackageVersion, INpmCheckRegistryData } from './interfaces/INpmCheckRegistry'; export default function bestGuessHomepage(data: INpmCheckRegistryData | undefined): string | false { diff --git a/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts b/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts index d82540f0cc3..7c84ced2002 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { existsSync } from 'node:fs'; +import path from 'node:path'; + import _ from 'lodash'; -import path from 'path'; -import pathExists from 'path-exists'; import semver from 'semver'; import type { INpmCheckState, INpmCheckPackageJson } from './interfaces/INpmCheck'; @@ -20,7 +21,7 @@ export default async function createPackageSummary( const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; const modulePath: string = findModulePath(moduleName, state); - const packageIsInstalled: boolean = pathExists.sync(modulePath); + const packageIsInstalled: boolean = existsSync(modulePath); const modulePackageJson: INpmCheckPackageJson = readPackageJson(path.join(modulePath, 'package.json')); // Ignore private packages @@ -36,9 +37,6 @@ export default async function createPackageSummary( return false; } - const unusedDependencies: boolean | string[] | undefined = state.unusedDependencies; - const missingFromPackageJson: Record | undefined = state.missingFromPackageJson; - return getLatestFromRegistry(moduleName).then((fromRegistry: INpmRegistryInfo) => { const installedVersion: string | undefined = modulePackageJson.version; const latest: string | undefined = @@ -74,11 +72,6 @@ export default async function createPackageSummary( bump = undefined; } - const unused: boolean = _.includes( - Array.isArray(unusedDependencies) ? unusedDependencies : [], - moduleName - ); - return { // info moduleName: moduleName, @@ -89,26 +82,11 @@ export default async function createPackageSummary( // versions latest: latest ?? '', installed: versionToUse === null ? '' : versionToUse, - isInstalled: packageIsInstalled, notInstalled: !packageIsInstalled, - packageWanted: versionWanted === null ? '' : versionWanted, packageJson: packageJsonVersion ?? '', - // Missing from package json - notInPackageJson: missingFromPackageJson - ? foundIn(state, missingFromPackageJson[moduleName]) - : undefined, - // meta devDependency: _.has(cwdPackageJson?.devDependencies, moduleName), - usedInScripts: - cwdPackageJson?.scripts && cwdPackageJson.scripts !== null - ? Object.keys(cwdPackageJson.scripts).filter((scriptName) => { - if (cwdPackageJson.scripts) { - return cwdPackageJson.scripts[scriptName].includes(moduleName); - } - }) - : undefined, mismatch: packageJsonVersion !== undefined && versionToUse !== null && @@ -116,24 +94,7 @@ export default async function createPackageSummary( semver.valid(versionToUse) ? !semver.satisfies(versionToUse, packageJsonVersion) : false, - semverValid: semver.valid(versionToUse) ?? '', - easyUpgrade: !!( - latest !== undefined && - packageJsonVersion !== undefined && - semver.validRange(packageJsonVersion) && - semver.valid(versionToUse) && - semver.satisfies(latest, packageJsonVersion) && - bump !== 'major' - ), - bump: bump, - unused: unused + bump: bump }; }); } - -function foundIn(state: INpmCheckState, files?: string[]): string | undefined { - if (!files) { - return undefined; - } - return 'Found in: ' + files.map((filepath) => filepath.replace(state.cwd || '', '')).join(', '); -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts b/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts index 58fa5e02350..95065e33bea 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import Module from 'module'; -import path from 'path'; -import pathExists from 'path-exists'; +import { existsSync } from 'node:fs'; +import Module from 'node:module'; +import path from 'node:path'; + import type { INpmCheckState } from './interfaces/INpmCheck'; /** @@ -20,7 +21,7 @@ export default function findModulePath(moduleName: string, currentState: INpmChe // @ts-ignore const nodeModulesPaths: string[] = Module._nodeModulePaths(cwd); const possibleModulePaths: string[] = nodeModulesPaths.map((x) => path.join(x, moduleName)); - const modulePath: string | undefined = possibleModulePaths.find((p) => pathExists.sync(p)); + const modulePath: string | undefined = possibleModulePaths.find((p) => existsSync(p)); // if no existing path was found, return the first tried path anyway return modulePath || path.join(cwd, moduleName); } diff --git a/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts b/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts index be1573825e8..b75413f71c2 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts @@ -1,12 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import os from 'node:os'; + import _ from 'lodash'; -import bestGuessHomepage from './BestGuessHomepage'; import semver from 'semver'; import packageJson from 'package-json'; -import os from 'os'; import throat from 'throat'; + +import bestGuessHomepage from './BestGuessHomepage'; import type { INpmRegistryInfo } from './interfaces/INpmCheckRegistry'; const cpuCount: number = os.cpus().length; diff --git a/libraries/rush-lib/src/utilities/npmCheck/GetUnusedPackages.ts b/libraries/rush-lib/src/utilities/npmCheck/GetUnusedPackages.ts deleted file mode 100644 index c4736a2e6f2..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/GetUnusedPackages.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/// - -import depcheck from 'depcheck'; -import _ from 'lodash'; -import { rcFile } from 'rc-config-loader'; -import type { INpmCheckPackageJson, INpmCheckState } from './interfaces/INpmCheck'; - -function skipUnused(currentState: INpmCheckState): boolean { - return ( - currentState.skipUnused || // manual option to ignore this - currentState.update || // in the process of doing an update - !currentState.cwdPackageJson?.name - ); // there's no package.json -} - -type RcFileResult = { config: Record; filePath: string } | undefined; - -function loadRcFile(rcFileName: string): Record { - try { - const results: RcFileResult = rcFile(rcFileName); - if (!results) { - return {}; - } - return results.config; - } catch (error) { - // eslint-disable-next-line no-console - console.error(`Error parsing rc file; skipping it; error: ${error.message}`); - return {}; - } -} - -export default async function getUnusedPackages(currentState: INpmCheckState): Promise { - if (skipUnused(currentState)) { - return currentState; - } - - // removed check for specials as rush doesn't use it - const depcheckDefaults: depcheck.Options = { - ignoreDirs: ['sandbox', 'dist', 'generated', '.generated', 'build', 'fixtures', 'jspm_packages'], - ignoreMatches: [ - 'gulp-*', - 'grunt-*', - 'karma-*', - 'angular-*', - 'babel-*', - 'metalsmith-*', - 'eslint-plugin-*', - '@types/*', - 'grunt', - 'mocha', - 'ava' - ] - }; - - const npmCheckRc: Record = loadRcFile('npmcheck'); - - const depcheckOptions: depcheck.Options = { - ...depcheckDefaults, - ...(npmCheckRc.depcheck || {}) - }; - - if (!currentState.cwd || typeof currentState.cwd !== 'string') { - throw new Error('currentState.cwd must be a defined string for depcheck.'); - } - - return depcheck(currentState.cwd, depcheckOptions) - .then((depCheckResults: depcheck.Results) => { - const unusedDependencies: string[] = ([] as string[]).concat( - depCheckResults.dependencies, - depCheckResults.devDependencies - ); - currentState.unusedDependencies = unusedDependencies; - - const cwdPackageJson: INpmCheckPackageJson | undefined = currentState.cwdPackageJson; - - // currently missing will return devDependencies that aren't really missing - const missingFromPackageJson: Record = _.omit( - depCheckResults.missing || {}, - ...(cwdPackageJson?.dependencies ? Object.keys(cwdPackageJson.dependencies) : []), - ...(cwdPackageJson?.devDependencies ? Object.keys(cwdPackageJson.devDependencies) : []) - ); - currentState.missingFromPackageJson = missingFromPackageJson; - return currentState; - }) - .catch((error: Error) => { - return currentState; - }); -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts b/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts index 70f9a5ccfa4..2c659ab1cce 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts @@ -1,28 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import extend from 'xtend'; +import _ from 'lodash'; -import getUnusedPackages from './GetUnusedPackages'; import type { INpmCheckOptions, INpmCheckPackageJson, INpmCheckState } from './interfaces/INpmCheck'; import initializeState from './NpmCheckState'; import createPackageSummary from './CreatePackageSummary'; import type { INpmCheckPackageSummary } from './interfaces/INpmCheckPackageSummary'; export default async function LocalNpmCheck(initialOptions?: INpmCheckOptions): Promise { - const initialState: INpmCheckState = await initializeState(initialOptions); - const state: INpmCheckState = await getUnusedPackages(initialState); + const state: INpmCheckState = await initializeState(initialOptions); const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; - const allDependencies: Record | undefined = getDependencies(state, cwdPackageJson); - const allDependenciesIncludingMissing: string[] | undefined = - allDependencies && state.missingFromPackageJson - ? Object.keys(extend(allDependencies, state.missingFromPackageJson)) - : []; + const allDependencies: Record | undefined = getDependencies(cwdPackageJson); let packages: INpmCheckPackageSummary[] = []; - if (allDependenciesIncludingMissing) { - const packageSummaryPromises: Promise[] = - allDependenciesIncludingMissing.map((moduleName: string) => createPackageSummary(moduleName, state)); + if (allDependencies) { + const packageSummaryPromises: Promise[] = Object.keys( + allDependencies + ).map((moduleName: string) => createPackageSummary(moduleName, state)); packages = await Promise.all(packageSummaryPromises).then( (results: (INpmCheckPackageSummary | false)[]) => { return results.filter((pkg): pkg is INpmCheckPackageSummary => pkg !== false); @@ -33,13 +28,10 @@ export default async function LocalNpmCheck(initialOptions?: INpmCheckOptions): return { ...state, packages }; } -export function getDependencies( - state: INpmCheckState, - pkg: INpmCheckPackageJson | undefined -): Record | undefined { +export function getDependencies(pkg: INpmCheckPackageJson | undefined): Record | undefined { if (!pkg) { return undefined; } - return extend(pkg.dependencies, pkg.devDependencies); + return _.extend(pkg.dependencies, pkg.devDependencies); } diff --git a/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts b/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts index 52704df54b6..1f73e8ec888 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import path from 'path'; -import extend from 'xtend'; +import path from 'node:path'; + +import _ from 'lodash'; + import { DefaultNpmCheckOptions, type INpmCheckOptions, @@ -12,7 +14,7 @@ import { import readPackageJson from './ReadPackageJson'; export default async function initializeState(initialOptions?: INpmCheckOptions): Promise { - const state: INpmCheckState = extend(DefaultNpmCheckOptions, initialOptions); + const state: INpmCheckState = _.extend(DefaultNpmCheckOptions, initialOptions); if (state.cwd) { const cwd: string = path.resolve(state.cwd); diff --git a/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts b/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts index a2af393237c..10a9f824916 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import extend from 'xtend'; +import _ from 'lodash'; + import type { INpmCheckPackageJson } from './interfaces/INpmCheck'; export default function readPackageJson(filename: string): INpmCheckPackageJson { @@ -16,5 +17,5 @@ export default function readPackageJson(filename: string): INpmCheckPackageJson error = new Error(`A package.json was found at ${filename}, but it is not valid.`); } } - return extend({ devDependencies: {}, dependencies: {}, error: error }, pkg); + return _.extend({ devDependencies: {}, dependencies: {}, error: error }, pkg); } diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts index 29e6f28baea..b92cfe09248 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts @@ -16,7 +16,6 @@ export interface INpmCheckPackageJson { export interface INpmCheckOptions { cwd: string; - skipUnused?: boolean; } // Removed @@ -25,39 +24,31 @@ export interface INpmCheckOptions { // - devOnly // - ignore // - ignoreDev +// - unusedDependencies +// - missingFromPackageJson +// - spinner +// - emoji +// - forceColor +// - saveExact // from INpmCheckState as they were not used anywhere export interface INpmCheckState { update?: boolean; updateAll?: boolean; cwd: string; - skipUnused?: boolean; - forceColor?: boolean; - saveExact?: boolean; specials?: string; debug?: boolean; - emoji?: boolean; - spinner?: boolean; installer?: string; cwdPackageJson?: INpmCheckPackageJson; packages?: INpmCheckPackageSummary[]; - unusedDependencies?: boolean | string[]; - missingFromPackageJson?: Record; } export const DefaultNpmCheckOptions: INpmCheckState = { update: false, updateAll: false, cwd: process.cwd(), - skipUnused: false, - forceColor: false, - saveExact: false, specials: '', debug: false, - emoji: true, - spinner: false, installer: 'npm', cwdPackageJson: { devDependencies: {}, dependencies: {} }, - packages: undefined, - unusedDependencies: false, - missingFromPackageJson: {} + packages: undefined }; diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts index 84bc82f7f18..241b865a3b2 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts @@ -18,21 +18,15 @@ export type INpmCheckVersionBumpType = | null; export interface INpmCheckPackageSummary { - moduleName: string; // name of the module. - homepage: string; // url to the home page. - regError?: Error; // error communicating with the registry - pkgError?: Error; // error reading the package.json - latest: string; // latest according to the registry. - installed: string; // version installed - isInstalled: boolean; // Is it installed? - notInstalled: boolean; // Is it installed? - packageWanted: string; // Requested version from the package.json. - packageJson: string; // Version or range requested in the parent package.json. - devDependency: boolean; // Is this a devDependency? - usedInScripts: undefined | string[]; // Array of `scripts` in package.json that use this module. - mismatch: boolean; // Does the version installed not match the range in package.json? - semverValid: string; // Is the installed version valid semver? - easyUpgrade: boolean; // Will running just `npm install` upgrade the module? - bump?: INpmCheckVersionBumpType; // What kind of bump is required to get the latest - unused: boolean; // Is this module used in the code? + moduleName: string; //USED, name of the module. + homepage: string; // USED, url to the home page. + regError?: Error; // USED, error communicating with the registry + pkgError?: Error; // USED, error reading the package.json + latest: string; // USED, latest according to the registry. + installed: string; // USED, version installed + notInstalled: boolean; // USED Is it installed? + packageJson: string; // USED, Version or range requested in the parent package.json. + devDependency: boolean; // USED, Is this a devDependency? + mismatch: boolean; // USED, Does the version installed not match the range in package.json? + bump?: INpmCheckVersionBumpType; //USED, What kind of bump is required to get the latest } diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts index fab14803031..eea8079e742 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts @@ -5,10 +5,6 @@ jest.mock('../GetLatestFromRegistry'); jest.mock('../ReadPackageJson'); jest.mock('../FindModulePath'); -jest.mock('path-exists', () => ({ - sync: jest.fn(() => true) -})); - import createPackageSummary from '../CreatePackageSummary'; import getLatestFromRegistry from '../GetLatestFromRegistry'; import readPackageJson from '../ReadPackageJson'; @@ -82,6 +78,5 @@ describe('createPackageSummary', () => { expect(result).toHaveProperty('homepage', 'https://homepage.com'); expect(result).toHaveProperty('latest', '2.0.0'); expect(result).toHaveProperty('installed', '1.0.0'); - expect(result).toHaveProperty('unused', true); }); }); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts index dd638e56832..9e62d16a9d4 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts @@ -6,18 +6,11 @@ jest.mock('path', () => ({ // Add other path methods as needed })); -jest.mock('path-exists', () => ({ - sync: jest.fn((p: string) => { - // Return true or false based on your test scenario - return p === '/mock/path/node_modules/my-module'; - }) -})); - import findModulePath from '../FindModulePath'; import type { INpmCheckState } from '../interfaces/INpmCheck'; -import path from 'path'; +import path from 'node:path'; -const Module = require('module'); +const Module = require('node:module'); describe('findModulePath', () => { beforeAll(() => { @@ -33,7 +26,7 @@ describe('findModulePath', () => { it('returns found path', () => { const state: INpmCheckState = { cwd: '/test/cwd', global: false } as INpmCheckState; const result = findModulePath('my-module', state); - expect(result).toBe(path.join('/mock/path/node_modules', 'my-module')); + expect(result).toBe(path.join('/test/cwd', 'my-module')); }); it('returns first tried path', () => { diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts index 29a157c0553..e6147b48366 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts @@ -6,18 +6,6 @@ jest.mock('../CreatePackageSummary', () => ({ default: jest.fn(async () => ({})) })); -jest.mock('../GetUnusedPackages', () => ({ - __esModule: true, - default: jest.fn(async (state) => { - if (state.skipUnused) { - return state; - } - state.unusedDependencies = ['mock-unused']; - state.missingFromPackageJson = { 'mock-missing': ['index.js'] }; - return state; - }) -})); - import createPackageSummary from '../CreatePackageSummary'; const mockCreatePackageSummary = createPackageSummary as jest.MockedFunction; @@ -25,61 +13,24 @@ import type { INpmCheckState } from '../interfaces/INpmCheck'; import LocalNpmCheck from '../LocalNpmCheck'; describe('NpmCheck', () => { - it('should set unusedDependencies, missingFromPackageJson, and package summaries', async () => { - mockCreatePackageSummary.mockImplementation(async (moduleName) => ({ - moduleName, - homepage: '', - latest: '', - installed: '', - isInstalled: false, - notInstalled: true, - packageWanted: '', - packageJson: '', - notInPackageJson: undefined, - devDependency: false, - peerDependency: false, - usedInScripts: [], - mismatch: false, - semverValid: '', - easyUpgrade: false, - bump: undefined, - unused: false - })); - const result: INpmCheckState = await LocalNpmCheck(); - expect(result.unusedDependencies).toStrictEqual(['mock-unused']); - expect(result.missingFromPackageJson).toStrictEqual({ 'mock-missing': ['index.js'] }); - expect(result.packages).toBeDefined(); - if (result.packages && result.packages.length > 0) { - expect(result.packages[0]).toHaveProperty('moduleName'); - } - }); - it('should mimic rush initial options', async () => { mockCreatePackageSummary.mockImplementation(async (moduleName) => ({ moduleName, homepage: '', latest: '', installed: '', - isInstalled: false, notInstalled: true, packageWanted: '', packageJson: '', notInPackageJson: undefined, devDependency: false, peerDependency: false, - usedInScripts: [], mismatch: false, - semverValid: '', - easyUpgrade: false, - bump: undefined, - unused: false + bump: undefined })); const result: INpmCheckState = await LocalNpmCheck({ - cwd: process.cwd(), - skipUnused: true + cwd: process.cwd() }); - expect(result.unusedDependencies).toBeFalsy(); - expect(result.missingFromPackageJson).toStrictEqual({}); expect(result.packages).toBeDefined(); if (result.packages && result.packages.length > 0) { expect(result.packages[0]).toHaveProperty('moduleName'); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts index 655ae281213..9164c60ea6e 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts @@ -12,13 +12,4 @@ describe('NpmCheckState', () => { expect(state.cwdPackageJson).toHaveProperty('name'); expect(state.cwdPackageJson).toHaveProperty('version'); }); - - it('should create with rush options', async () => { - const state: INpmCheckState = await initializeState({ - cwd: process.cwd(), - skipUnused: true - }); - expect(state).toBeDefined(); - expect(state.skipUnused).toBeTruthy(); - }); }); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts index 97a19845cee..2787d50265f 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import path from 'path'; +import path from 'node:path'; import readPackageJson from '../ReadPackageJson'; import type { INpmCheckPackageJson } from '../interfaces/INpmCheck'; From ced1f1075f898d784f5a82447c8e3a2d935b3d56 Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Mon, 6 Oct 2025 15:29:06 -0700 Subject: [PATCH 05/11] Remove unused packages in browser-approved-packages --- .../rush/browser-approved-packages.json | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 76724fd839f..3821a9a3bcf 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -58,18 +58,6 @@ "name": "axios", "allowedCategories": [ "libraries" ] }, - { - "name": "chalk", - "allowedCategories": [ "libraries" ] - }, - { - "name": "co", - "allowedCategories": [ "libraries" ] - }, - { - "name": "depcheck", - "allowedCategories": [ "libraries" ] - }, { "name": "dependency-path", "allowedCategories": [ "libraries" ] @@ -78,38 +66,18 @@ "name": "giturl", "allowedCategories": [ "libraries" ] }, - { - "name": "global-modules", - "allowedCategories": [ "libraries" ] - }, - { - "name": "node-emoji", - "allowedCategories": [ "libraries" ] - }, { "name": "office-ui-fabric-core", "allowedCategories": [ "libraries" ] }, - { - "name": "ora", - "allowedCategories": [ "libraries" ] - }, { "name": "package-json", "allowedCategories": [ "libraries" ] }, - { - "name": "path-exists", - "allowedCategories": [ "libraries" ] - }, { "name": "prism-react-renderer", "allowedCategories": [ "libraries" ] }, - { - "name": "rc-config-loader", - "allowedCategories": [ "libraries" ] - }, { "name": "react", "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] @@ -138,10 +106,6 @@ "name": "scheduler", "allowedCategories": [ "vscode-extensions" ] }, - { - "name": "semver-diff", - "allowedCategories": [ "libraries" ] - }, { "name": "throat", "allowedCategories": [ "libraries" ] @@ -150,10 +114,6 @@ "name": "tslib", "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, - { - "name": "xtend", - "allowedCategories": [ "libraries" ] - }, { "name": "zod", "allowedCategories": [ "libraries" ] From fd4ec7c1309f55ab718be85c0cdd52d02d0c1ffc Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Mon, 6 Oct 2025 15:30:27 -0700 Subject: [PATCH 06/11] Remove unused typings --- .../rush-lib/src/types/depcheck-typings.d.ts | 71 ------------------- .../src/types/global-modules-typings.d.ts | 4 -- 2 files changed, 75 deletions(-) delete mode 100644 libraries/rush-lib/src/types/depcheck-typings.d.ts delete mode 100644 libraries/rush-lib/src/types/global-modules-typings.d.ts diff --git a/libraries/rush-lib/src/types/depcheck-typings.d.ts b/libraries/rush-lib/src/types/depcheck-typings.d.ts deleted file mode 100644 index a7a85840241..00000000000 --- a/libraries/rush-lib/src/types/depcheck-typings.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -declare function depcheck(rootDir: string, options: depcheck.Options): Promise; - -declare function depcheck( - rootDir: string, - options: depcheck.Options, - callback: (results: depcheck.Results) => T -): Promise; - -declare namespace depcheck { - type Node = Record; - - type Parser = (filePath: string, deps: ReadonlyArray, rootDir: string) => Node | string[]; - - type Detector = (node: Node) => ReadonlyArray | string; - - interface PackageDependencies { - [dependencyName: string]: string; - } - - interface Options { - ignoreBinPackage?: boolean; - skipMissing?: boolean; - ignoreMatches?: ReadonlyArray; - ignoreDirs?: ReadonlyArray; - ignorePath?: string; - ignorePatterns?: ReadonlyArray; - package?: { - dependencies?: PackageDependencies; - devDependencies?: PackageDependencies; - peerDependencies?: PackageDependencies; - optionalDependencies?: PackageDependencies; - }; - parsers?: { - [match: string]: Parser; - }; - detectors?: ReadonlyArray; - specials?: ReadonlyArray; - } - - interface Config { - ignoreBinPackage?: Options['ignoreBinPackage']; - skipMissing?: Options['skipMissing']; - json?: boolean; - ignores?: Options['ignoreMatches']; - ignoreDirs?: Options['ignoreDirs']; - ignorePath?: Options['ignorePath']; - ignorePatterns?: Options['ignorePatterns']; - parsers?: { [match: string]: keyof typeof parser | ReadonlyArray }; - detectors?: ReadonlyArray; - specials?: ReadonlyArray; - } - - interface Results { - dependencies: string[]; - devDependencies: string[]; - using: { - [dependencyName: string]: string[]; - }; - missing: { - [dependencyName: string]: string[]; - }; - invalidFiles: { - [filePath: string]: any; - }; - invalidDirs: { - [filePath: string]: any; - }; - } -} - -export = depcheck; diff --git a/libraries/rush-lib/src/types/global-modules-typings.d.ts b/libraries/rush-lib/src/types/global-modules-typings.d.ts deleted file mode 100644 index 9584bee2567..00000000000 --- a/libraries/rush-lib/src/types/global-modules-typings.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'global-modules' { - const globalModulesPath: string; - export default globalModulesPath; -} From 85f4a6e319f175d2fd8abc7ab920c9447d566ede Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Mon, 6 Oct 2025 15:32:29 -0700 Subject: [PATCH 07/11] Remove unused files --- .../config/api-documenter.json | 6 ------ ...onzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json | 10 ---------- 2 files changed, 16 deletions(-) delete mode 100644 build-tests/api-documenter-scenarios/config/api-documenter.json delete mode 100644 common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json diff --git a/build-tests/api-documenter-scenarios/config/api-documenter.json b/build-tests/api-documenter-scenarios/config/api-documenter.json deleted file mode 100644 index f8bd0690ce3..00000000000 --- a/build-tests/api-documenter-scenarios/config/api-documenter.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-documenter.schema.json", - "outputTarget": "markdown", - "tableOfContents": {}, - "showInheritedMembers": true -} diff --git a/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json b/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json deleted file mode 100644 index bd7ff97cb34..00000000000 --- a/common/changes/@microsoft/rush/user-cmalonzo-npmaudit-replacenpmcheck_2025-10-02-18-44.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@microsoft/rush", - "comment": "", - "type": "none" - } - ], - "packageName": "@microsoft/rush" -} \ No newline at end of file From 989683406606d726bc16e89b94a6c2e0b56b6e98 Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Mon, 6 Oct 2025 15:39:41 -0700 Subject: [PATCH 08/11] Remove unused properties from INpmCheckState --- .../src/utilities/npmCheck/LocalNpmCheck.ts | 4 +-- .../src/utilities/npmCheck/NpmCheckState.ts | 3 +-- .../npmCheck/interfaces/INpmCheck.ts | 27 ------------------- 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts b/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts index 2c659ab1cce..e59f01bc8a9 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts @@ -3,12 +3,12 @@ import _ from 'lodash'; -import type { INpmCheckOptions, INpmCheckPackageJson, INpmCheckState } from './interfaces/INpmCheck'; +import type { INpmCheckPackageJson, INpmCheckState } from './interfaces/INpmCheck'; import initializeState from './NpmCheckState'; import createPackageSummary from './CreatePackageSummary'; import type { INpmCheckPackageSummary } from './interfaces/INpmCheckPackageSummary'; -export default async function LocalNpmCheck(initialOptions?: INpmCheckOptions): Promise { +export default async function LocalNpmCheck(initialOptions?: INpmCheckState): Promise { const state: INpmCheckState = await initializeState(initialOptions); const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; const allDependencies: Record | undefined = getDependencies(cwdPackageJson); diff --git a/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts b/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts index 1f73e8ec888..4a0d884ffe7 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts @@ -7,13 +7,12 @@ import _ from 'lodash'; import { DefaultNpmCheckOptions, - type INpmCheckOptions, type INpmCheckPackageJson, type INpmCheckState } from './interfaces/INpmCheck'; import readPackageJson from './ReadPackageJson'; -export default async function initializeState(initialOptions?: INpmCheckOptions): Promise { +export default async function initializeState(initialOptions?: INpmCheckState): Promise { const state: INpmCheckState = _.extend(DefaultNpmCheckOptions, initialOptions); if (state.cwd) { diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts index b92cfe09248..4e6a6cadac8 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts @@ -14,41 +14,14 @@ export interface INpmCheckPackageJson { [key: string]: any; } -export interface INpmCheckOptions { - cwd: string; -} - -// Removed -// - global -// - globalPackages -// - devOnly -// - ignore -// - ignoreDev -// - unusedDependencies -// - missingFromPackageJson -// - spinner -// - emoji -// - forceColor -// - saveExact -// from INpmCheckState as they were not used anywhere export interface INpmCheckState { - update?: boolean; - updateAll?: boolean; cwd: string; - specials?: string; - debug?: boolean; - installer?: string; cwdPackageJson?: INpmCheckPackageJson; packages?: INpmCheckPackageSummary[]; } export const DefaultNpmCheckOptions: INpmCheckState = { - update: false, - updateAll: false, cwd: process.cwd(), - specials: '', - debug: false, - installer: 'npm', cwdPackageJson: { devDependencies: {}, dependencies: {} }, packages: undefined }; From 330aa25d97849b4544529423e7fc1057617a77fc Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Mon, 6 Oct 2025 15:42:50 -0700 Subject: [PATCH 09/11] Update comments --- .../interfaces/INpmCheckPackageSummary.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts index 241b865a3b2..2207a50a4cd 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts +++ b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts @@ -18,15 +18,15 @@ export type INpmCheckVersionBumpType = | null; export interface INpmCheckPackageSummary { - moduleName: string; //USED, name of the module. - homepage: string; // USED, url to the home page. - regError?: Error; // USED, error communicating with the registry - pkgError?: Error; // USED, error reading the package.json - latest: string; // USED, latest according to the registry. - installed: string; // USED, version installed - notInstalled: boolean; // USED Is it installed? - packageJson: string; // USED, Version or range requested in the parent package.json. - devDependency: boolean; // USED, Is this a devDependency? - mismatch: boolean; // USED, Does the version installed not match the range in package.json? - bump?: INpmCheckVersionBumpType; //USED, What kind of bump is required to get the latest + moduleName: string; // name of the module. + homepage: string; // url to the home page. + regError?: Error; // error communicating with the registry + pkgError?: Error; // error reading the package.json + latest: string; // latest according to the registry. + installed: string; // version installed + notInstalled: boolean; // Is it installed? + packageJson: string; // Version or range requested in the parent package.json. + devDependency: boolean; // Is this a devDependency? + mismatch: boolean; // Does the version installed not match the range in package.json? + bump?: INpmCheckVersionBumpType; // What kind of bump is required to get the latest } From 7c999cc6920f902e78e2cfc68477e12581ae8e51 Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Mon, 6 Oct 2025 16:34:44 -0700 Subject: [PATCH 10/11] Move all helpers into one class --- .../rush-lib/src/logic/InteractiveUpgrader.ts | 21 +- .../rush-lib/src/logic/PackageJsonUpdater.ts | 6 +- .../src/utilities/InteractiveUpgradeUI.ts | 22 +- .../InteractiveUpgraderPackages.ts | 224 ++++++++++++++++++ .../interfaces/INpmRegistryInfo.ts} | 14 +- .../interfaces/IPackageInfo.ts} | 8 +- .../types/giturl-typings.d.ts | 0 .../utilities/npmCheck/BestGuessHomepage.ts | 26 -- .../npmCheck/CreatePackageSummary.ts | 100 -------- .../src/utilities/npmCheck/FindModulePath.ts | 27 --- .../npmCheck/GetLatestFromRegistry.ts | 48 ---- .../src/utilities/npmCheck/LocalNpmCheck.ts | 37 --- .../src/utilities/npmCheck/NpmCheckState.ts | 30 --- .../src/utilities/npmCheck/ReadPackageJson.ts | 21 -- .../npmCheck/interfaces/INpmCheck.ts | 27 --- .../npmCheck/test/BestGuessHomepage.test.ts | 60 ----- .../test/CreatePackageSummary.test.ts | 82 ------- .../npmCheck/test/FindModulePath.test.ts | 37 --- .../test/GetLatestFromRegistry.test.ts | 54 ----- .../utilities/npmCheck/test/NpmCheck.test.ts | 39 --- .../npmCheck/test/NpmCheckState.test.ts | 15 -- .../npmCheck/test/ReadPackageJson.test.ts | 16 -- 22 files changed, 259 insertions(+), 655 deletions(-) create mode 100644 libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages.ts rename libraries/rush-lib/src/utilities/{npmCheck/interfaces/INpmCheckRegistry.ts => InteractiveUpgraderPackages/interfaces/INpmRegistryInfo.ts} (58%) rename libraries/rush-lib/src/utilities/{npmCheck/interfaces/INpmCheckPackageSummary.ts => InteractiveUpgraderPackages/interfaces/IPackageInfo.ts} (82%) rename libraries/rush-lib/src/{ => utilities/InteractiveUpgraderPackages}/types/giturl-typings.d.ts (100%) delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/BestGuessHomepage.test.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/GetLatestFromRegistry.test.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts delete mode 100644 libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts diff --git a/libraries/rush-lib/src/logic/InteractiveUpgrader.ts b/libraries/rush-lib/src/logic/InteractiveUpgrader.ts index 59174b77721..aa5db6dcb8f 100644 --- a/libraries/rush-lib/src/logic/InteractiveUpgrader.ts +++ b/libraries/rush-lib/src/logic/InteractiveUpgrader.ts @@ -9,9 +9,8 @@ import type { RushConfiguration } from '../api/RushConfiguration'; import { upgradeInteractive, type IDepsToUpgradeAnswers } from '../utilities/InteractiveUpgradeUI'; import type { RushConfigurationProject } from '../api/RushConfigurationProject'; import { SearchListPrompt } from '../utilities/prompts/SearchListPrompt'; -import LocalNpmCheck from '../utilities/npmCheck/LocalNpmCheck'; -import type { INpmCheckState } from '../utilities/npmCheck/interfaces/INpmCheck'; -import type { INpmCheckPackageSummary } from '../utilities/npmCheck/interfaces/INpmCheckPackageSummary'; +import InteractiveUpgraderPackages from '../utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages'; +import type { IPackageInfo } from '../utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo'; interface IUpgradeInteractiveDeps { projects: RushConfigurationProject[]; @@ -20,16 +19,17 @@ interface IUpgradeInteractiveDeps { export class InteractiveUpgrader { private readonly _rushConfiguration: RushConfiguration; + private _interactiveUpgraderGetPackages: InteractiveUpgraderPackages; public constructor(rushConfiguration: RushConfiguration) { this._rushConfiguration = rushConfiguration; + this._interactiveUpgraderGetPackages = new InteractiveUpgraderPackages(); } public async upgradeAsync(): Promise { const rushProject: RushConfigurationProject = await this._getUserSelectedProjectForUpgradeAsync(); - const dependenciesState: INpmCheckPackageSummary[] = - await this._getPackageDependenciesStatusAsync(rushProject); + const dependenciesState: IPackageInfo[] = await this._getPackageDependenciesStatusAsync(rushProject); const depsToUpgrade: IDepsToUpgradeAnswers = await this._getUserSelectedDependenciesToUpgradeAsync(dependenciesState); @@ -37,7 +37,7 @@ export class InteractiveUpgrader { } private async _getUserSelectedDependenciesToUpgradeAsync( - packages: INpmCheckPackageSummary[] + packages: IPackageInfo[] ): Promise { return upgradeInteractive(packages); } @@ -68,13 +68,12 @@ export class InteractiveUpgrader { private async _getPackageDependenciesStatusAsync( rushProject: RushConfigurationProject - ): Promise { + ): Promise { const { projectFolder } = rushProject; - const newState: INpmCheckState = await LocalNpmCheck({ - cwd: projectFolder - }); + const packages: IPackageInfo[] = + await this._interactiveUpgraderGetPackages.getPackagesAsync(projectFolder); - return newState.packages ?? []; + return packages ?? []; } } diff --git a/libraries/rush-lib/src/logic/PackageJsonUpdater.ts b/libraries/rush-lib/src/logic/PackageJsonUpdater.ts index 4ad01cee792..6b6599992d4 100644 --- a/libraries/rush-lib/src/logic/PackageJsonUpdater.ts +++ b/libraries/rush-lib/src/logic/PackageJsonUpdater.ts @@ -28,7 +28,7 @@ import { SemVerStyle } from './PackageJsonUpdaterTypes'; import type { Subspace } from '../api/Subspace'; -import type { INpmCheckPackageSummary } from '../utilities/npmCheck/interfaces/INpmCheckPackageSummary'; +import type { IPackageInfo } from '../utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo'; /** * Options for adding a dependency to a particular project. @@ -41,7 +41,7 @@ export interface IPackageJsonUpdaterRushUpgradeOptions { /** * The dependencies to be added. */ - packagesToAdd: INpmCheckPackageSummary[]; + packagesToAdd: IPackageInfo[]; /** * If specified, other packages that use this dependency will also have their package.json's updated. */ @@ -892,7 +892,7 @@ export class PackageJsonUpdater { } } - private _normalizeDepsToUpgrade(deps: INpmCheckPackageSummary[]): IPackageForRushAdd[] { + private _normalizeDepsToUpgrade(deps: IPackageInfo[]): IPackageForRushAdd[] { return deps.map((dep) => { return { packageName: dep.moduleName, diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts b/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts index 4b8e6dd91cc..8ee163147b6 100644 --- a/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts @@ -11,7 +11,7 @@ import type Separator from 'inquirer/lib/objects/separator'; import { AnsiEscape, Colorize } from '@rushstack/terminal'; -import type { INpmCheckPackageSummary } from './npmCheck/interfaces/INpmCheckPackageSummary'; +import type { IPackageInfo } from './InteractiveUpgraderPackages/interfaces/IPackageInfo'; export interface IUIGroup { title: string; @@ -24,11 +24,11 @@ export interface IUIGroup { } export interface IDepsToUpgradeAnswers { - packages: INpmCheckPackageSummary[]; + packages: IPackageInfo[]; } export interface IUpgradeInteractiveDepChoice { - value: INpmCheckPackageSummary; + value: IPackageInfo; name: string | string[]; short: string; } @@ -81,7 +81,7 @@ export const UI_GROUPS: IUIGroup[] = [ } ]; -function label(dep: INpmCheckPackageSummary): string[] { +function label(dep: IPackageInfo): string[] { const bumpInstalled: string = dep.bump ? dep.installed : ''; const installed: string = dep.mismatch ? dep.packageJson : bumpInstalled; const name: string = Colorize.yellow(dep.moduleName); @@ -98,7 +98,7 @@ function label(dep: INpmCheckPackageSummary): string[] { ]; } -function getErrorDep(dep: INpmCheckPackageSummary): string { +function getErrorDep(dep: IPackageInfo): string { if (dep.regError !== undefined && dep.regError && dep.regError instanceof Error) { return dep.regError.message; } else if (dep.pkgError !== undefined && dep.pkgError && dep.pkgError instanceof Error) { @@ -108,11 +108,11 @@ function getErrorDep(dep: INpmCheckPackageSummary): string { return ''; } -function short(dep: INpmCheckPackageSummary): string { +function short(dep: IPackageInfo): string { return `${dep.moduleName}@${dep.latest}`; } -function getChoice(dep: INpmCheckPackageSummary): IUpgradeInteractiveDepChoice | boolean | Separator { +function getChoice(dep: IPackageInfo): IUpgradeInteractiveDepChoice | boolean | Separator { if (!dep.mismatch && !dep.bump && !dep.notInstalled) { return false; } @@ -128,9 +128,9 @@ function unselectable(options?: { title: string }): Separator { return new inquirer.Separator(AnsiEscape.removeCodes(options ? options.title : '')); } -function createChoices(packages: INpmCheckPackageSummary[], options: IUIGroup): ChoiceTable { +function createChoices(packages: IPackageInfo[], options: IUIGroup): ChoiceTable { const { filter } = options; - const filteredChoices: INpmCheckPackageSummary[] = packages.filter((pkg: INpmCheckPackageSummary) => { + const filteredChoices: IPackageInfo[] = packages.filter((pkg: IPackageInfo) => { if ('mismatch' in filter && pkg.mismatch !== filter.mismatch) { return false; } else if ('bump' in filter && pkg.bump !== filter.bump) { @@ -140,7 +140,7 @@ function createChoices(packages: INpmCheckPackageSummary[], options: IUIGroup): } else { return true; } - }) as INpmCheckPackageSummary[]; + }) as IPackageInfo[]; const choices: (IUpgradeInteractiveDepChoice | Separator | boolean)[] = filteredChoices .map(getChoice) @@ -188,7 +188,7 @@ function createChoices(packages: INpmCheckPackageSummary[], options: IUIGroup): } } -export const upgradeInteractive = async (pkgs: INpmCheckPackageSummary[]): Promise => { +export const upgradeInteractive = async (pkgs: IPackageInfo[]): Promise => { const choicesGrouped: ChoiceTable[] = UI_GROUPS.map((group) => createChoices(pkgs, group)).filter(Boolean); const choices: ChoiceTable = []; diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages.ts b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages.ts new file mode 100644 index 00000000000..8f30ccc5b3f --- /dev/null +++ b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages.ts @@ -0,0 +1,224 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/// + +import { existsSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import gitUrl from 'giturl'; +import _ from 'lodash'; +import packageJson from 'package-json'; +import semver from 'semver'; +import throat from 'throat'; + +import type { IPackageJson } from '@rushstack/node-core-library'; + +import type { IPackageInfo, IVersionBumpType } from './interfaces/IPackageInfo'; +import type { INpmRegistryInfo, IPackageVersion, IRegistryData } from './interfaces/INpmRegistryInfo'; + +interface IPackageJsonWithError extends IPackageJson { + error?: Error; +} + +export default class InteractiveUpgraderPackages { + private _cpuCount: number = os.cpus().length; + + public constructor() { + this._cpuCount = os.cpus().length; + } + public async getPackagesAsync(projectFolder: string): Promise { + const cwd: string = path.resolve(projectFolder); + const cwdPackageJson: IPackageJsonWithError = this._getPackageJson(path.join(cwd, 'package.json')); + + const allDependencies: Record | undefined = this._getAllDependencies(cwdPackageJson); + + let packages: IPackageInfo[] = []; + if (allDependencies) { + const packageSummaryPromises: Promise[] = Object.keys(allDependencies).map( + (moduleName: string) => this._createPackageSummary(moduleName, cwd, cwdPackageJson) + ); + packages = await Promise.all(packageSummaryPromises).then((results: (IPackageInfo | boolean)[]) => { + return results.filter((pkg): pkg is IPackageInfo => pkg !== false); + }); + } + + return packages; + } + + private _getPackageJson(filename: string): IPackageJsonWithError { + let pkg: IPackageJsonWithError | undefined = undefined; + let error: Error | undefined = undefined; + try { + pkg = require(filename); + } catch (e: unknown) { + if (e && typeof e === 'object' && 'code' in e && e.code === 'MODULE_NOT_FOUND') { + error = new Error(`A package.json was not found at ${filename}`); + } else { + error = new Error(`A package.json was found at ${filename}, but it is not valid.`); + } + } + return _.extend({ devDependencies: {}, dependencies: {}, error: error }, pkg); + } + + private _getAllDependencies(pkg: IPackageJsonWithError | undefined): Record | undefined { + if (!pkg) { + return undefined; + } + + return _.extend(pkg.dependencies, pkg.devDependencies); + } + + private _createPackageSummary( + moduleName: string, + cwd: string, + cwdPackageJson: IPackageJsonWithError + ): Promise { + const modulePath: string = this._findModulePath(moduleName, cwd); + const packageIsInstalled: boolean = existsSync(modulePath); + const modulePackageJson: IPackageJsonWithError = this._getPackageJson( + path.join(modulePath, 'package.json') + ); + + // Ignore private packages + const isPrivate: boolean = Boolean(modulePackageJson.private); + if (isPrivate) { + return Promise.resolve(false); + } + + // Ignore packages that are using github or file urls + const packageJsonVersion: string | undefined = + cwdPackageJson && + (cwdPackageJson.dependencies?.[moduleName] ?? cwdPackageJson.devDependencies?.[moduleName]); + if (packageJsonVersion && !semver.validRange(packageJsonVersion)) { + return Promise.resolve(false); + } + + return this._getLatestFromRegistry(moduleName).then((fromRegistry: INpmRegistryInfo) => { + const installedVersion: string | undefined = modulePackageJson.version; + const latest: string | undefined = + installedVersion && + fromRegistry.latest && + fromRegistry.next && + semver.gt(installedVersion, fromRegistry.latest) + ? fromRegistry.next + : fromRegistry.latest; + const versions: string[] = fromRegistry.versions || []; + let versionWanted: string | null = null; + if (packageJsonVersion) { + versionWanted = semver.maxSatisfying(versions, packageJsonVersion); + } + const versionToUse: string | undefined | null = installedVersion || versionWanted; + const usingNonSemver: boolean | '' | null = + latest !== undefined && semver.valid(latest) && semver.lt(latest, '1.0.0-pre'); + + let bump: IVersionBumpType; + const bumpRaw: IVersionBumpType = + semver.valid(latest) && + semver.valid(versionToUse) && + (usingNonSemver && versionToUse && latest + ? semver.diff(versionToUse, latest) + ? 'nonSemver' + : semver.diff(versionToUse, latest) + : versionToUse && latest + ? semver.diff(versionToUse, latest) + : undefined); + if (bumpRaw && bumpRaw !== null) { + bump = bumpRaw as IVersionBumpType; + } else { + bump = undefined; + } + + return { + // info + moduleName: moduleName, + homepage: fromRegistry.homepage ?? '', + regError: new Error(fromRegistry.error), + pkgError: modulePackageJson.error, + + // versions + latest: latest ?? '', + installed: versionToUse === null ? '' : versionToUse, + notInstalled: !packageIsInstalled, + packageJson: packageJsonVersion ?? '', + + // meta + devDependency: _.has(cwdPackageJson?.devDependencies, moduleName), + mismatch: + packageJsonVersion !== undefined && + versionToUse !== null && + semver.validRange(packageJsonVersion) && + semver.valid(versionToUse) + ? !semver.satisfies(versionToUse, packageJsonVersion) + : false, + bump: bump + }; + }); + } + + private _findModulePath(moduleName: string, cwd: string): string { + // Module._nodeModulePaths does not include some places the node module resolver searches, such as + // the global prefix or other special directories. This is desirable because if a module is missing + // in the project directory we want to be sure to report it as missing. + // We can't use require.resolve because it fails if the module doesn't have an entry point. + // @ts-ignore + const nodeModulesPaths: string[] = Module._nodeModulePaths(cwd); + const possibleModulePaths: string[] = nodeModulesPaths.map((x) => path.join(x, moduleName)); + const modulePath: string | undefined = possibleModulePaths.find((p) => existsSync(p)); + // if no existing path was found, return the first tried path anyway + return modulePath || path.join(cwd, moduleName); + } + + private _getLatestFromRegistry(packageName: string): Promise { + const limit: () => Promise = throat(this._cpuCount, () => + packageJson(packageName, { fullMetadata: true, allVersions: true }) + ); + return limit() + .then((rawData: packageJson.FullMetadata) => { + const CRAZY_HIGH_SEMVER: string = '8000.0.0'; + const sortedVersions: string[] = _(rawData.versions) + .keys() + .remove(_.partial(semver.gt, CRAZY_HIGH_SEMVER)) + .sort(semver.compare) + .valueOf(); + + const latest: string = rawData['dist-tags'].latest; + const next: string = rawData['dist-tags'].next; + const latestStableRelease: string | undefined = semver.satisfies(latest, '*') + ? latest + : semver.maxSatisfying(sortedVersions, '*') || ''; + + return { + latest: latestStableRelease, + next: next, + versions: sortedVersions, + homepage: this._getBestGuessHomepage(rawData) || '' + }; + }) + .catch((error) => { + const errorMessage: string = `Registry error ${error.message}`; + return { + error: errorMessage + }; + }); + } + + private _getBestGuessHomepage(data: IRegistryData | undefined): string | false { + if (!data) { + return false; + } + const packageDataForLatest: IPackageVersion = data.versions[data['dist-tags'].latest]; + + return packageDataForLatest + ? packageDataForLatest.homepage || + (packageDataForLatest.bugs && + packageDataForLatest.bugs.url && + gitUrl.parse(packageDataForLatest.bugs.url.trim())) || + (packageDataForLatest.repository && + packageDataForLatest.repository.url && + gitUrl.parse(packageDataForLatest.repository.url.trim())) || + false + : false; + } +} diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckRegistry.ts b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/INpmRegistryInfo.ts similarity index 58% rename from libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckRegistry.ts rename to libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/INpmRegistryInfo.ts index ce40274d25a..d7318aab7a2 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckRegistry.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/INpmRegistryInfo.ts @@ -9,18 +9,18 @@ export interface INpmRegistryInfo { error?: string; } -interface INpmCheckRegistryInfoBugs { +interface IRegistryInfoBugs { url?: string; } -interface INpmCheckRepository { +interface IRepository { url?: string; } -export interface INpmCheckPackageVersion { +export interface IPackageVersion { homepage?: string; - bugs?: INpmCheckRegistryInfoBugs; - repository?: INpmCheckRepository; + bugs?: IRegistryInfoBugs; + repository?: IRepository; } -export interface INpmCheckRegistryData { - versions: Record; +export interface IRegistryData { + versions: Record; ['dist-tags']: { latest: string }; } diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo.ts similarity index 82% rename from libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts rename to libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo.ts index 2207a50a4cd..567d08bcb29 100644 --- a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheckPackageSummary.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo.ts @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -// semverDiff still returns null -export type INpmCheckVersionBumpType = +export type IVersionBumpType = | '' | 'build' | 'major' @@ -14,10 +13,11 @@ export type INpmCheckVersionBumpType = | 'prerelease' | 'nonSemver' | undefined + // semver.diff can return null // eslint-disable-next-line @rushstack/no-new-null | null; -export interface INpmCheckPackageSummary { +export interface IPackageInfo { moduleName: string; // name of the module. homepage: string; // url to the home page. regError?: Error; // error communicating with the registry @@ -28,5 +28,5 @@ export interface INpmCheckPackageSummary { packageJson: string; // Version or range requested in the parent package.json. devDependency: boolean; // Is this a devDependency? mismatch: boolean; // Does the version installed not match the range in package.json? - bump?: INpmCheckVersionBumpType; // What kind of bump is required to get the latest + bump?: IVersionBumpType; // What kind of bump is required to get the latest } diff --git a/libraries/rush-lib/src/types/giturl-typings.d.ts b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/types/giturl-typings.d.ts similarity index 100% rename from libraries/rush-lib/src/types/giturl-typings.d.ts rename to libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/types/giturl-typings.d.ts diff --git a/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts b/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts deleted file mode 100644 index 22db96bc03a..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/BestGuessHomepage.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/// - -import gitUrl from 'giturl'; - -import type { INpmCheckPackageVersion, INpmCheckRegistryData } from './interfaces/INpmCheckRegistry'; - -export default function bestGuessHomepage(data: INpmCheckRegistryData | undefined): string | false { - if (!data) { - return false; - } - const packageDataForLatest: INpmCheckPackageVersion = data.versions[data['dist-tags'].latest]; - - return packageDataForLatest - ? packageDataForLatest.homepage || - (packageDataForLatest.bugs && - packageDataForLatest.bugs.url && - gitUrl.parse(packageDataForLatest.bugs.url.trim())) || - (packageDataForLatest.repository && - packageDataForLatest.repository.url && - gitUrl.parse(packageDataForLatest.repository.url.trim())) || - false - : false; -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts b/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts deleted file mode 100644 index 7c84ced2002..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/CreatePackageSummary.ts +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { existsSync } from 'node:fs'; -import path from 'node:path'; - -import _ from 'lodash'; -import semver from 'semver'; - -import type { INpmCheckState, INpmCheckPackageJson } from './interfaces/INpmCheck'; -import type { INpmCheckPackageSummary, INpmCheckVersionBumpType } from './interfaces/INpmCheckPackageSummary'; -import findModulePath from './FindModulePath'; -import readPackageJson from './ReadPackageJson'; -import getLatestFromRegistry from './GetLatestFromRegistry'; -import type { INpmRegistryInfo } from './interfaces/INpmCheckRegistry'; - -export default async function createPackageSummary( - moduleName: string, - state: INpmCheckState -): Promise { - const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; - - const modulePath: string = findModulePath(moduleName, state); - const packageIsInstalled: boolean = existsSync(modulePath); - const modulePackageJson: INpmCheckPackageJson = readPackageJson(path.join(modulePath, 'package.json')); - - // Ignore private packages - const isPrivate: boolean = Boolean(modulePackageJson.private); - if (isPrivate) { - return false; - } - - // Ignore packages that are using github or file urls - const packageJsonVersion: string | undefined = - cwdPackageJson?.dependencies[moduleName] || cwdPackageJson?.devDependencies[moduleName]; - if (packageJsonVersion && !semver.validRange(packageJsonVersion)) { - return false; - } - - return getLatestFromRegistry(moduleName).then((fromRegistry: INpmRegistryInfo) => { - const installedVersion: string | undefined = modulePackageJson.version; - const latest: string | undefined = - installedVersion && - fromRegistry.latest && - fromRegistry.next && - semver.gt(installedVersion, fromRegistry.latest) - ? fromRegistry.next - : fromRegistry.latest; - const versions: string[] = fromRegistry.versions || []; - let versionWanted: string | null = null; - if (packageJsonVersion) { - versionWanted = semver.maxSatisfying(versions, packageJsonVersion); - } - const versionToUse: string | undefined | null = installedVersion || versionWanted; - const usingNonSemver: boolean | '' | null = - latest !== undefined && semver.valid(latest) && semver.lt(latest, '1.0.0-pre'); - - let bump: INpmCheckVersionBumpType; - const bumpRaw: INpmCheckVersionBumpType = - semver.valid(latest) && - semver.valid(versionToUse) && - (usingNonSemver && versionToUse && latest - ? semver.diff(versionToUse, latest) - ? 'nonSemver' - : semver.diff(versionToUse, latest) - : versionToUse && latest - ? semver.diff(versionToUse, latest) - : undefined); - if (bumpRaw && bumpRaw !== null) { - bump = bumpRaw as INpmCheckVersionBumpType; - } else { - bump = undefined; - } - - return { - // info - moduleName: moduleName, - homepage: fromRegistry.homepage ?? '', - regError: new Error(fromRegistry.error), - pkgError: modulePackageJson.error, - - // versions - latest: latest ?? '', - installed: versionToUse === null ? '' : versionToUse, - notInstalled: !packageIsInstalled, - packageJson: packageJsonVersion ?? '', - - // meta - devDependency: _.has(cwdPackageJson?.devDependencies, moduleName), - mismatch: - packageJsonVersion !== undefined && - versionToUse !== null && - semver.validRange(packageJsonVersion) && - semver.valid(versionToUse) - ? !semver.satisfies(versionToUse, packageJsonVersion) - : false, - bump: bump - }; - }); -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts b/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts deleted file mode 100644 index 95065e33bea..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/FindModulePath.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { existsSync } from 'node:fs'; -import Module from 'node:module'; -import path from 'node:path'; - -import type { INpmCheckState } from './interfaces/INpmCheck'; - -/** - * Searches the directory hierarchy to return the path to the requested node module. - * If the module can't be found, returns the initial (deepest) tried path. - */ -export default function findModulePath(moduleName: string, currentState: INpmCheckState): string { - const cwd: string = currentState.cwd; - - // Module._nodeModulePaths does not include some places the node module resolver searches, such as - // the global prefix or other special directories. This is desirable because if a module is missing - // in the project directory we want to be sure to report it as missing. - // We can't use require.resolve because it fails if the module doesn't have an entry point. - // @ts-ignore - const nodeModulesPaths: string[] = Module._nodeModulePaths(cwd); - const possibleModulePaths: string[] = nodeModulesPaths.map((x) => path.join(x, moduleName)); - const modulePath: string | undefined = possibleModulePaths.find((p) => existsSync(p)); - // if no existing path was found, return the first tried path anyway - return modulePath || path.join(cwd, moduleName); -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts b/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts deleted file mode 100644 index b75413f71c2..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/GetLatestFromRegistry.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import os from 'node:os'; - -import _ from 'lodash'; -import semver from 'semver'; -import packageJson from 'package-json'; -import throat from 'throat'; - -import bestGuessHomepage from './BestGuessHomepage'; -import type { INpmRegistryInfo } from './interfaces/INpmCheckRegistry'; - -const cpuCount: number = os.cpus().length; - -export default async function getNpmInfo(packageName: string): Promise { - const limit: () => Promise = throat(cpuCount, () => - packageJson(packageName, { fullMetadata: true, allVersions: true }) - ); - return limit() - .then((rawData: packageJson.FullMetadata) => { - const CRAZY_HIGH_SEMVER: string = '8000.0.0'; - const sortedVersions: string[] = _(rawData.versions) - .keys() - .remove(_.partial(semver.gt, CRAZY_HIGH_SEMVER)) - .sort(semver.compare) - .valueOf(); - - const latest: string = rawData['dist-tags'].latest; - const next: string = rawData['dist-tags'].next; - const latestStableRelease: string | undefined = semver.satisfies(latest, '*') - ? latest - : semver.maxSatisfying(sortedVersions, '*') || ''; - - return { - latest: latestStableRelease, - next: next, - versions: sortedVersions, - homepage: bestGuessHomepage(rawData) || '' - }; - }) - .catch((error) => { - const errorMessage: string = `Registry error ${error.message}`; - return { - error: errorMessage - }; - }); -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts b/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts deleted file mode 100644 index e59f01bc8a9..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/LocalNpmCheck.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import _ from 'lodash'; - -import type { INpmCheckPackageJson, INpmCheckState } from './interfaces/INpmCheck'; -import initializeState from './NpmCheckState'; -import createPackageSummary from './CreatePackageSummary'; -import type { INpmCheckPackageSummary } from './interfaces/INpmCheckPackageSummary'; - -export default async function LocalNpmCheck(initialOptions?: INpmCheckState): Promise { - const state: INpmCheckState = await initializeState(initialOptions); - const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; - const allDependencies: Record | undefined = getDependencies(cwdPackageJson); - - let packages: INpmCheckPackageSummary[] = []; - if (allDependencies) { - const packageSummaryPromises: Promise[] = Object.keys( - allDependencies - ).map((moduleName: string) => createPackageSummary(moduleName, state)); - packages = await Promise.all(packageSummaryPromises).then( - (results: (INpmCheckPackageSummary | false)[]) => { - return results.filter((pkg): pkg is INpmCheckPackageSummary => pkg !== false); - } - ); - } - - return { ...state, packages }; -} - -export function getDependencies(pkg: INpmCheckPackageJson | undefined): Record | undefined { - if (!pkg) { - return undefined; - } - - return _.extend(pkg.dependencies, pkg.devDependencies); -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts b/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts deleted file mode 100644 index 4a0d884ffe7..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/NpmCheckState.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import path from 'node:path'; - -import _ from 'lodash'; - -import { - DefaultNpmCheckOptions, - type INpmCheckPackageJson, - type INpmCheckState -} from './interfaces/INpmCheck'; -import readPackageJson from './ReadPackageJson'; - -export default async function initializeState(initialOptions?: INpmCheckState): Promise { - const state: INpmCheckState = _.extend(DefaultNpmCheckOptions, initialOptions); - - if (state.cwd) { - const cwd: string = path.resolve(state.cwd); - const pkg: INpmCheckPackageJson = readPackageJson(path.join(cwd, 'package.json')); - state.cwdPackageJson = pkg; - state.cwd = cwd; - } - - if (state.cwdPackageJson?.error) { - return Promise.reject(state.cwdPackageJson.error); - } - - return Promise.resolve(state); -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts b/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts deleted file mode 100644 index 10a9f824916..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/ReadPackageJson.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import _ from 'lodash'; - -import type { INpmCheckPackageJson } from './interfaces/INpmCheck'; - -export default function readPackageJson(filename: string): INpmCheckPackageJson { - let pkg: INpmCheckPackageJson | undefined = undefined; - let error: Error | undefined = undefined; - try { - pkg = require(filename); - } catch (e: unknown) { - if (e && typeof e === 'object' && 'code' in e && e.code === 'MODULE_NOT_FOUND') { - error = new Error(`A package.json was not found at ${filename}`); - } else { - error = new Error(`A package.json was found at ${filename}, but it is not valid.`); - } - } - return _.extend({ devDependencies: {}, dependencies: {}, error: error }, pkg); -} diff --git a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts b/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts deleted file mode 100644 index 4e6a6cadac8..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/interfaces/INpmCheck.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import type { INpmCheckPackageSummary } from './INpmCheckPackageSummary'; - -export interface INpmCheckPackageJson { - name?: string; - version?: string; - devDependencies: Record; - dependencies: Record; - error?: Error; - scripts?: Record; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; -} - -export interface INpmCheckState { - cwd: string; - cwdPackageJson?: INpmCheckPackageJson; - packages?: INpmCheckPackageSummary[]; -} - -export const DefaultNpmCheckOptions: INpmCheckState = { - cwd: process.cwd(), - cwdPackageJson: { devDependencies: {}, dependencies: {} }, - packages: undefined -}; diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/BestGuessHomepage.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/BestGuessHomepage.test.ts deleted file mode 100644 index de5a63c3fe1..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/test/BestGuessHomepage.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -// Mock gitUrl.parse -jest.mock('giturl', () => ({ parse: (url: string) => url })); - -import bestGuessHomepage from '../BestGuessHomepage'; -import type { INpmCheckRegistryData } from '../interfaces/INpmCheckRegistry'; - -describe('bestGuessHomepage', () => { - it('returns false if data is undefined', () => { - expect(bestGuessHomepage(undefined)).toBe(false); - }); - - it('returns homepage if present', () => { - const data: INpmCheckRegistryData = { - versions: { - latest: { - homepage: 'https://homepage.com' - } - }, - 'dist-tags': { latest: 'latest' } - }; - expect(bestGuessHomepage(data)).toBe('https://homepage.com'); - }); - - it('returns bugs.url if homepage is missing', () => { - const data: INpmCheckRegistryData = { - versions: { - latest: { - bugs: { url: 'https://bugs.com' } - } - }, - 'dist-tags': { latest: 'latest' } - }; - expect(bestGuessHomepage(data)).toBe('https://bugs.com'); - }); - - it('returns repository.url if homepage and bugs.url are missing', () => { - const data: INpmCheckRegistryData = { - versions: { - latest: { - repository: { url: 'https://repo.com' } - } - }, - 'dist-tags': { latest: 'latest' } - }; - expect(bestGuessHomepage(data)).toBe('https://repo.com'); - }); - - it('returns false if no homepage, bugs.url, or repository.url', () => { - const data: INpmCheckRegistryData = { - versions: { - latest: {} - }, - 'dist-tags': { latest: 'latest' } - }; - expect(bestGuessHomepage(data)).toBe(false); - }); -}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts deleted file mode 100644 index eea8079e742..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/test/CreatePackageSummary.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -jest.mock('../GetLatestFromRegistry'); -jest.mock('../ReadPackageJson'); -jest.mock('../FindModulePath'); - -import createPackageSummary from '../CreatePackageSummary'; -import getLatestFromRegistry from '../GetLatestFromRegistry'; -import readPackageJson from '../ReadPackageJson'; -import findModulePath from '../FindModulePath'; -import type { INpmCheckPackageJson, INpmCheckState } from '../interfaces/INpmCheck'; -import type { INpmRegistryInfo } from '../interfaces/INpmCheckRegistry'; -import type { INpmCheckPackageSummary } from '../interfaces/INpmCheckPackageSummary'; - -const mockGetLatestFromRegistry = getLatestFromRegistry as jest.MockedFunction; -const mockReadPackageJson = readPackageJson as jest.MockedFunction; -const mockFindModulePath = findModulePath as jest.MockedFunction; - -describe('createPackageSummary', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - it('returns false for private package', async () => { - mockFindModulePath.mockReturnValue('/mock/path/private-pkg'); - mockReadPackageJson.mockReturnValue({ - dependencies: {}, - devDependencies: {}, - private: true - } as INpmCheckPackageJson); - const state: INpmCheckState = { - cwd: process.cwd(), - cwdPackageJson: { dependencies: {}, devDependencies: {} } - }; - const result: INpmCheckPackageSummary | boolean = await createPackageSummary('private-pkg', state); - expect(result).toBe(false); - }); - - it('returns false for invalid semver range', async () => { - mockFindModulePath.mockReturnValue('/mock/path'); - mockReadPackageJson.mockReturnValue({ - dependencies: {}, - devDependencies: {} - } as INpmCheckPackageJson); - const state: INpmCheckState = { - cwd: process.cwd(), - cwdPackageJson: { - dependencies: { 'bad-pkg': 'github:foo/bar' }, - devDependencies: {} - } - }; - const result: INpmCheckPackageSummary | boolean = await createPackageSummary('bad-pkg', state); - expect(result).toBe(false); - }); - - it('returns summary for valid package', async () => { - mockFindModulePath.mockReturnValue('/mock/path'); - mockReadPackageJson.mockReturnValue({ - dependencies: {}, - devDependencies: {} - } as INpmCheckPackageJson); - mockGetLatestFromRegistry.mockResolvedValue({ - latest: '2.0.0', - next: '3.0.0', - versions: ['1.0.0', '2.0.0', '3.0.0'], - homepage: 'https://homepage.com' - } as INpmRegistryInfo); - const state: INpmCheckState = { - cwd: process.cwd(), - cwdPackageJson: { dependencies: { 'good-pkg': '1.0.0' }, devDependencies: {} }, - unusedDependencies: ['good-pkg'], - missingFromPackageJson: {} - } as INpmCheckState; - const result: INpmCheckPackageSummary | boolean = await createPackageSummary('good-pkg', state); - expect(result).toBeTruthy(); - expect(result).toHaveProperty('moduleName', 'good-pkg'); - expect(result).toHaveProperty('homepage', 'https://homepage.com'); - expect(result).toHaveProperty('latest', '2.0.0'); - expect(result).toHaveProperty('installed', '1.0.0'); - }); -}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts deleted file mode 100644 index 9e62d16a9d4..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/test/FindModulePath.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -jest.mock('path', () => ({ - join: jest.fn((...args) => args.join('/')) - // Add other path methods as needed -})); - -import findModulePath from '../FindModulePath'; -import type { INpmCheckState } from '../interfaces/INpmCheck'; -import path from 'node:path'; - -const Module = require('node:module'); - -describe('findModulePath', () => { - beforeAll(() => { - jest - .spyOn(Module, '_nodeModulePaths') - .mockImplementation(() => ['/mock/path/node_modules', '/another/mock/path/node_modules']); - }); - - afterAll(() => { - jest.restoreAllMocks(); - }); - - it('returns found path', () => { - const state: INpmCheckState = { cwd: '/test/cwd', global: false } as INpmCheckState; - const result = findModulePath('my-module', state); - expect(result).toBe(path.join('/test/cwd', 'my-module')); - }); - - it('returns first tried path', () => { - const state: INpmCheckState = { cwd: '/test/cwd', global: false } as INpmCheckState; - const result = findModulePath('missing-module', state); - expect(result).toBe(path.join('/test/cwd', 'missing-module')); - }); -}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/GetLatestFromRegistry.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/GetLatestFromRegistry.test.ts deleted file mode 100644 index 948d3ec5054..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/test/GetLatestFromRegistry.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -jest.mock('package-json'); - -import getNpmInfo from '../GetLatestFromRegistry'; -import packageJson from 'package-json'; -import type { INpmRegistryInfo } from '../interfaces/INpmCheckRegistry'; - -const mockPackageJson = packageJson as jest.MockedFunction; - -describe('getNpmInfo', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('returns registry info with homepage', async () => { - mockPackageJson.mockResolvedValue({ - versions: { - '1.0.0': { - homepage: 'https://homepage.com' - }, - '2.0.0': { - bugs: { url: 'https://bugs.com' } - } - }, - 'dist-tags': { latest: '1.0.0', next: '2.0.0' } - } as unknown as packageJson.FullMetadata); - const result: INpmRegistryInfo = await getNpmInfo('test-package'); - expect(result).toHaveProperty('latest', '1.0.0'); - expect(result).toHaveProperty('next', '2.0.0'); - expect(result).toHaveProperty('versions', ['1.0.0', '2.0.0']); - expect(result).toHaveProperty('homepage', 'https://homepage.com'); - }); - - it('returns error if packageJson throws', async () => { - mockPackageJson.mockRejectedValue(new Error('Registry down')); - const result: INpmRegistryInfo = await getNpmInfo('test-package'); - expect(result).toHaveProperty('error'); - expect(result.error).toBe('Registry error Registry down'); - }); - - it('returns "" homepage if not present', async () => { - mockPackageJson.mockResolvedValue({ - versions: { - '1.0.0': {}, - '2.0.0': {} - }, - 'dist-tags': { latest: '1.0.0', next: '2.0.0' } - } as unknown as packageJson.FullMetadata); - const result: INpmRegistryInfo = await getNpmInfo('test-package'); - expect(result).toHaveProperty('homepage', ''); - }); -}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts deleted file mode 100644 index e6147b48366..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheck.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -jest.mock('../CreatePackageSummary', () => ({ - __esModule: true, - default: jest.fn(async () => ({})) -})); - -import createPackageSummary from '../CreatePackageSummary'; -const mockCreatePackageSummary = createPackageSummary as jest.MockedFunction; - -import type { INpmCheckState } from '../interfaces/INpmCheck'; -import LocalNpmCheck from '../LocalNpmCheck'; - -describe('NpmCheck', () => { - it('should mimic rush initial options', async () => { - mockCreatePackageSummary.mockImplementation(async (moduleName) => ({ - moduleName, - homepage: '', - latest: '', - installed: '', - notInstalled: true, - packageWanted: '', - packageJson: '', - notInPackageJson: undefined, - devDependency: false, - peerDependency: false, - mismatch: false, - bump: undefined - })); - const result: INpmCheckState = await LocalNpmCheck({ - cwd: process.cwd() - }); - expect(result.packages).toBeDefined(); - if (result.packages && result.packages.length > 0) { - expect(result.packages[0]).toHaveProperty('moduleName'); - } - }); -}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts deleted file mode 100644 index 9164c60ea6e..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/test/NpmCheckState.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import type { INpmCheckState } from '../interfaces/INpmCheck'; -import initializeState from '../NpmCheckState'; - -describe('NpmCheckState', () => { - it('should create with default options', async () => { - const state: INpmCheckState = await initializeState(); - expect(state).toBeDefined(); - expect(state.cwd).toBe(process.cwd()); - expect(state.cwdPackageJson).toHaveProperty('name'); - expect(state.cwdPackageJson).toHaveProperty('version'); - }); -}); diff --git a/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts b/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts deleted file mode 100644 index 2787d50265f..00000000000 --- a/libraries/rush-lib/src/utilities/npmCheck/test/ReadPackageJson.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import path from 'node:path'; -import readPackageJson from '../ReadPackageJson'; -import type { INpmCheckPackageJson } from '../interfaces/INpmCheck'; - -describe('readPackageJson', () => { - it('should return valid packageJson if it exists', async () => { - const fileName: string = path.join(process.cwd(), 'package.json'); - const result: INpmCheckPackageJson = await readPackageJson(fileName); - - expect(result).toBeDefined(); - expect(result).toHaveProperty('name'); - }); -}); From b56df0f7c832f4aac12aedbd8a9478cdb27ce3fc Mon Sep 17 00:00:00 2001 From: Camille Malonzo Date: Wed, 8 Oct 2025 11:09:59 -0700 Subject: [PATCH 11/11] Update license information --- .../InteractiveUpgraderPackages.ts | 3 +-- .../interfaces/INpmRegistryInfo.ts | 3 +-- .../interfaces/IPackageInfo.ts | 3 +-- .../npm-check-license | 21 +++++++++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/npm-check-license diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages.ts b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages.ts index 8f30ccc5b3f..490584cf1de 100644 --- a/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/InteractiveUpgraderPackages.ts @@ -1,5 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. +// See npm-check-license for license information. /// diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/INpmRegistryInfo.ts b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/INpmRegistryInfo.ts index d7318aab7a2..91073c322f0 100644 --- a/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/INpmRegistryInfo.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/INpmRegistryInfo.ts @@ -1,5 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. +// See npm-check-license for license information. export interface INpmRegistryInfo { latest?: string; diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo.ts b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo.ts index 567d08bcb29..667366dc498 100644 --- a/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/interfaces/IPackageInfo.ts @@ -1,5 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. +// See npm-check-license for license information. export type IVersionBumpType = | '' diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/npm-check-license b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/npm-check-license new file mode 100644 index 00000000000..b7b33fb69c7 --- /dev/null +++ b/libraries/rush-lib/src/utilities/InteractiveUpgraderPackages/npm-check-license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Dylan Greene + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file