feat: introduce a quicker selection algo if applicable

This commit is contained in:
Cheng Liu 2024-10-21 19:34:28 -07:00
parent 90cb8f6008
commit 9ba131bc76
No known key found for this signature in database
GPG key ID: BDD2FDCF5AC296BA
6 changed files with 596 additions and 65 deletions

View file

@ -22,6 +22,7 @@
"@rushstack/terminal": "~0.8.1",
"git-repo-info": "~2.1.1",
"inversify": "~6.0.2",
"npm-package-arg": "~6.1.0",
"reflect-metadata": "~0.2.1",
"semver": "~7.6.0",
"update-notifier": "~5.1.0",
@ -32,6 +33,7 @@
"@rushstack/heft-node-rig": "2.4.5",
"@types/heft-jest": "1.0.6",
"@types/node": "20.11.16",
"@types/npm-package-arg": "6.1.0",
"@types/semver": "7.5.7",
"@types/update-notifier": "6.0.8",
"@types/yargs": "17.0.32",

View file

@ -0,0 +1,178 @@
// This is copied from rush.js source code
// https://github.com/microsoft/rushstack/blob/312b8bc554e64d66b586c65499a512dbf1c329ff/libraries/rush-lib/src/logic/DependencySpecifier.ts
import npmPackageArg from 'npm-package-arg';
import { InternalError } from '@rushstack/node-core-library';
/**
* match workspace protocol in dependencies value declaration in `package.json`
* example:
* `"workspace:*"`
* `"workspace:alias@1.2.3"`
*/
const WORKSPACE_PREFIX_REGEX: RegExp = /^workspace:((?<alias>[^._/][^@]*)@)?(?<version>.*)$/;
/**
* resolve workspace protocol(from `@pnpm/workspace.spec-parser`).
* used by pnpm. see [pkgs-graph](https://github.com/pnpm/pnpm/blob/27c33f0319f86c45c1645d064cd9c28aada80780/workspace/pkgs-graph/src/index.ts#L49)
*/
class WorkspaceSpec {
public readonly alias?: string;
public readonly version: string;
public readonly versionSpecifier: string;
public constructor(version: string, alias?: string) {
this.version = version;
this.alias = alias;
this.versionSpecifier = alias ? `${alias}@${version}` : version;
}
public static tryParse(pref: string): WorkspaceSpec | undefined {
const parts: RegExpExecArray | null = WORKSPACE_PREFIX_REGEX.exec(pref);
if (parts?.groups) {
return new WorkspaceSpec(parts.groups.version, parts.groups.alias);
}
}
public toString(): `workspace:${string}` {
return `workspace:${this.versionSpecifier}`;
}
}
/**
* The parsed format of a provided version specifier.
*/
export enum DependencySpecifierType {
/**
* A git repository
*/
Git = 'Git',
/**
* A tagged version, e.g. "example@latest"
*/
Tag = 'Tag',
/**
* A specific version number, e.g. "example@1.2.3"
*/
Version = 'Version',
/**
* A version range, e.g. "example@2.x"
*/
Range = 'Range',
/**
* A local .tar.gz, .tar or .tgz file
*/
File = 'File',
/**
* A local directory
*/
Directory = 'Directory',
/**
* An HTTP url to a .tar.gz, .tar or .tgz file
*/
Remote = 'Remote',
/**
* A package alias, e.g. "npm:other-package@^1.2.3"
*/
Alias = 'Alias',
/**
* A package specified using workspace protocol, e.g. "workspace:^1.2.3"
*/
Workspace = 'Workspace'
}
/**
* An NPM "version specifier" is a string that can appear as a package.json "dependencies" value.
* Example version specifiers: `^1.2.3`, `file:./blah.tgz`, `npm:other-package@~1.2.3`, and so forth.
* A "dependency specifier" is the version specifier information, combined with the dependency package name.
*/
export class DependencySpecifier {
/**
* The dependency package name, i.e. the key from a "dependencies" key/value table.
*/
public readonly packageName: string;
/**
* The dependency version specifier, i.e. the value from a "dependencies" key/value table.
* Example values: `^1.2.3`, `file:./blah.tgz`, `npm:other-package@~1.2.3`
*/
public readonly versionSpecifier: string;
/**
* The type of the `versionSpecifier`.
*/
public readonly specifierType: DependencySpecifierType;
/**
* If `specifierType` is `alias`, then this is the parsed target dependency.
* For example, if version specifier i `"npm:other-package@^1.2.3"` then this is the parsed object for
* `other-package@^1.2.3`.
*/
public readonly aliasTarget: DependencySpecifier | undefined;
public constructor(packageName: string, versionSpecifier: string) {
this.packageName = packageName;
this.versionSpecifier = versionSpecifier;
// Workspace ranges are a feature from PNPM and Yarn. Set the version specifier
// to the trimmed version range.
const workspaceSpecResult: WorkspaceSpec | undefined = WorkspaceSpec.tryParse(versionSpecifier);
if (workspaceSpecResult) {
this.specifierType = DependencySpecifierType.Workspace;
this.versionSpecifier = workspaceSpecResult.versionSpecifier;
if (workspaceSpecResult.alias) {
// "workspace:some-package@^1.2.3" should be resolved as alias
this.aliasTarget = new DependencySpecifier(workspaceSpecResult.alias, workspaceSpecResult.version);
} else {
this.aliasTarget = undefined;
}
return;
}
const result: npmPackageArg.Result = npmPackageArg.resolve(packageName, versionSpecifier);
this.specifierType = DependencySpecifier.getDependencySpecifierType(result.type);
if (this.specifierType === DependencySpecifierType.Alias) {
const aliasResult: npmPackageArg.AliasResult = result as npmPackageArg.AliasResult;
if (!aliasResult.subSpec || !aliasResult.subSpec.name) {
throw new InternalError('Unexpected result from npm-package-arg');
}
this.aliasTarget = new DependencySpecifier(aliasResult.subSpec.name, aliasResult.subSpec.rawSpec);
} else {
this.aliasTarget = undefined;
}
}
public static getDependencySpecifierType(specifierType: string): DependencySpecifierType {
switch (specifierType) {
case 'git':
return DependencySpecifierType.Git;
case 'tag':
return DependencySpecifierType.Tag;
case 'version':
return DependencySpecifierType.Version;
case 'range':
return DependencySpecifierType.Range;
case 'file':
return DependencySpecifierType.File;
case 'directory':
return DependencySpecifierType.Directory;
case 'remote':
return DependencySpecifierType.Remote;
case 'alias':
return DependencySpecifierType.Alias;
default:
throw new InternalError(`Unexpected npm-package-arg result type "${specifierType}"`);
}
}
}

View file

@ -0,0 +1,117 @@
import * as path from 'path';
import * as semver from 'semver';
import { JsonFile } from '@rushstack/node-core-library';
import { DependencySpecifier, DependencySpecifierType } from './DependencySpecifier';
export interface IProjectJson {
packageName: string;
projectFolder: string;
decoupledLocalDependencies?: string[];
cyclicDependencyProjects?: string[];
}
/**
* A slim version of RushConfigurationProject
*/
export class RushProjectSlim {
public packageName: string;
public projectFolder: string;
public relativeProjectFolder: string;
public packageJson: {
name: string;
version: string;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
optionalDependencies?: Record<string, string>;
};
public decoupledLocalDependencies: Set<string>;
private _packageNameToRushProjectSlim: Map<string, RushProjectSlim>;
private _dependencyProjects: Set<RushProjectSlim> | undefined;
private _consumingProjects: Set<RushProjectSlim> | undefined;
public constructor(
projectJson: IProjectJson,
repoRootPath: string,
packageNameToRushProjectSlim: Map<string, RushProjectSlim>
) {
this.packageName = projectJson.packageName;
this.projectFolder = path.resolve(repoRootPath, projectJson.projectFolder);
this.relativeProjectFolder = projectJson.projectFolder;
const packageJsonPath: string = path.resolve(this.projectFolder, 'package.json');
this.packageJson = JsonFile.load(packageJsonPath);
this._packageNameToRushProjectSlim = packageNameToRushProjectSlim;
this.decoupledLocalDependencies = new Set<string>();
if (projectJson.cyclicDependencyProjects || projectJson.decoupledLocalDependencies) {
if (projectJson.cyclicDependencyProjects && projectJson.decoupledLocalDependencies) {
throw new Error(
'A project configuration cannot specify both "decoupledLocalDependencies" and "cyclicDependencyProjects". Please use "decoupledLocalDependencies" only -- the other name is deprecated.'
);
}
for (const cyclicDependencyProject of projectJson.cyclicDependencyProjects ||
projectJson.decoupledLocalDependencies ||
[]) {
this.decoupledLocalDependencies.add(cyclicDependencyProject);
}
}
}
public get dependencyProjects(): ReadonlySet<RushProjectSlim> {
if (this._dependencyProjects) {
return this._dependencyProjects;
}
const dependencyProjects: Set<RushProjectSlim> = new Set<RushProjectSlim>();
const { packageJson } = this;
for (const dependencySet of [
packageJson.dependencies,
packageJson.devDependencies,
packageJson.optionalDependencies
]) {
if (dependencySet) {
for (const [dependency, version] of Object.entries(dependencySet)) {
const dependencySpecifier: DependencySpecifier = new DependencySpecifier(dependency, version);
const dependencyName: string =
dependencySpecifier.aliasTarget?.packageName ?? dependencySpecifier.packageName;
// Skip if we can't find the local project or it's a cyclic dependency
const localProject: RushProjectSlim | undefined =
this._packageNameToRushProjectSlim.get(dependencyName);
if (localProject && !this.decoupledLocalDependencies.has(dependency)) {
// Set the value if it's a workspace project, or if we have a local project and the semver is satisfied
switch (dependencySpecifier.specifierType) {
case DependencySpecifierType.Version:
case DependencySpecifierType.Range:
if (
semver.satisfies(localProject.packageJson.version, dependencySpecifier.versionSpecifier)
) {
dependencyProjects.add(localProject);
}
break;
case DependencySpecifierType.Workspace:
dependencyProjects.add(localProject);
break;
}
}
}
}
}
this._dependencyProjects = dependencyProjects;
return this._dependencyProjects;
}
public get consumingProjects(): ReadonlySet<RushProjectSlim> {
if (!this._consumingProjects) {
// Force initialize all dependencies relationship
for (const project of this._packageNameToRushProjectSlim.values()) {
project._consumingProjects = new Set();
}
for (const project of this._packageNameToRushProjectSlim.values()) {
for (const dependency of project.dependencyProjects) {
dependency._consumingProjects!.add(project);
}
}
}
return this._consumingProjects!;
}
}

View file

@ -1,10 +1,10 @@
import * as path from 'path';
import * as child_process from 'child_process';
import { inject } from 'inversify';
import { Service } from '../decorator';
import { GitService } from './GitService';
import { TerminalService } from './TerminalService';
import { Executable, FileSystem, JsonFile, JsonSyntax } from '@rushstack/node-core-library';
import { SelectionParameterService } from './SelectionParameterService';
import { FileSystem, JsonFile, JsonSyntax } from '@rushstack/node-core-library';
import { Stopwatch } from '../logic/Stopwatch';
import type { ISelection } from '../logic/SparoProfile';
@ -26,6 +26,7 @@ export interface IRushProject {
export class GitSparseCheckoutService {
@inject(GitService) private _gitService!: GitService;
@inject(TerminalService) private _terminalService!: TerminalService;
@inject(SelectionParameterService) private _selectionParameterService!: SelectionParameterService;
private _rushConfigLoaded: boolean = false;
private _rushProjects: IRushProject[] = [];
@ -156,16 +157,46 @@ export class GitSparseCheckoutService {
}
}
// Validate incorrect package names
const unfoundedPackages: string[] = [];
for (const selector of [...toSelectors, ...fromSelectors]) {
if (selector.indexOf(':') < 0) {
const packageName: string = selector;
let additionalFullPackageNames: string[] = [];
for (const selectorArg of toSelectors) {
if (selectorArg.indexOf(':') < 0) {
const packageName: string = selectorArg;
const result: string | undefined = this._findProjectByShorthandName(packageName);
if (!result) {
unfoundedPackages.push(packageName);
} else {
// Ensure full package name used
toSelectors.delete(packageName);
// DO NOT add back to selectors in this loop
additionalFullPackageNames.push(result);
}
}
}
for (const fullPackageName of additionalFullPackageNames) {
toSelectors.add(fullPackageName);
}
additionalFullPackageNames = [];
for (const selectorArg of fromSelectors) {
if (selectorArg.indexOf(':') < 0) {
const packageName: string = selectorArg;
const result: string | undefined = this._findProjectByShorthandName(packageName);
if (!result) {
unfoundedPackages.push(packageName);
} else {
// Ensure full package name used
fromSelectors.delete(packageName);
// DO NOT add back to selectors in this loop
additionalFullPackageNames.push(result);
}
}
}
for (const fullPackageName of additionalFullPackageNames) {
fromSelectors.add(fullPackageName);
}
additionalFullPackageNames = [];
if (unfoundedPackages.length > 0) {
throw new Error(`These packages: ${unfoundedPackages.join(', ')} does not exist in rush.json`);
@ -175,11 +206,13 @@ export class GitSparseCheckoutService {
if (toSelectors.size !== 0 || fromSelectors.size !== 0) {
const stopwatch: Stopwatch = Stopwatch.start();
targetFolders = this._getTargetFoldersByRushList({ toSelectors, fromSelectors });
terminal.writeVerboseLine(`Run rush list command. (${stopwatch.toString()})`);
targetFolders = this._selectionParameterService.getSelectedFolders({ toSelectors, fromSelectors });
terminal.writeVerboseLine(`Get selected folders. (${stopwatch.toString()})`);
stopwatch.stop();
} else {
terminal.writeDebugLine('Skip rush list regarding the absence of from selectors and to selectors');
terminal.writeDebugLine(
'Skip getting selected folders regarding the absence of from selectors and to selectors'
);
}
// include rule
@ -393,61 +426,4 @@ export class GitSparseCheckoutService {
}
return packageName;
}
private _getTargetFoldersByRushList({
toSelectors,
fromSelectors
}: {
toSelectors: Iterable<string>;
fromSelectors: Iterable<string>;
}): string[] {
const { terminal } = this._terminalService;
const args: string[] = ['list', '--json'];
for (const toSelector of toSelectors) {
args.push('--to');
args.push(toSelector);
}
for (const fromSelector of fromSelectors) {
args.push('--from');
args.push(fromSelector);
}
terminal.writeVerboseLine(`Run command: rush ${args.join(' ')}`);
const result: child_process.SpawnSyncReturns<string> = Executable.spawnSync('rush', args, {
stdio: ['pipe', 'pipe', 'pipe']
});
if (result.status !== 0) {
throw new Error(`Failed to evaluate the Sparo profile's project selectors:\nstdout: ${result.stdout}\nstderr: ${result.stderr}`);
}
const processedResult: string = this._processListResult(result.stdout.toString());
terminal.writeVerboseLine(processedResult);
const { projects: targetDeps } = JSON.parse(processedResult) as {
projects: { path: string }[];
};
return targetDeps.map((targetDep) => targetDep.path);
}
private _processListResult(input: string): string {
const stringList: string[] = input.split('\n');
let endOfInstallScript: number = -1;
for (let i: number = 0; i < stringList.length; ++i) {
if (stringList[i][0] === '{') {
endOfInstallScript = i;
break;
}
}
const jsonStringList: string[] = stringList.slice(endOfInstallScript);
return jsonStringList.join('\n');
}
}

View file

@ -0,0 +1,200 @@
import * as child_process from 'child_process';
import { inject } from 'inversify';
import { Service } from '../decorator';
import { TerminalService } from './TerminalService';
import { Executable, JsonFile, Sort } from '@rushstack/node-core-library';
import { GitService } from './GitService';
import { RushProjectSlim } from '../logic/RushProjectSlim';
@Service()
export class SelectionParameterService {
@inject(GitService) private _gitService!: GitService;
@inject(TerminalService) private _terminalService!: TerminalService;
public getSelectedFolders({
toSelectors,
fromSelectors
}: {
toSelectors: Iterable<string>;
fromSelectors: Iterable<string>;
}): string[] {
let hasProtocolSelection: boolean = false;
if (!hasProtocolSelection) {
for (const toSelector of toSelectors) {
if (toSelector.indexOf(':') >= 0) {
hasProtocolSelection = true;
break;
}
}
}
if (!hasProtocolSelection) {
for (const fromSelector of fromSelectors) {
if (fromSelector.indexOf(':') >= 0) {
hasProtocolSelection = true;
break;
}
}
}
if (!hasProtocolSelection) {
this._terminalService.terminal.writeDebugLine(`Opt in strategy without running rush list`);
// Selectors passed down here are ensured to contain full package names only
return this._getSelectedFoldersByNamedProjectsOnly({
toSelectors,
fromSelectors
});
} else {
return this._getSelectedFoldersByRushList({
toSelectors,
fromSelectors
});
}
}
/**
* This function provides a quick way to get selected folders by
* avoiding call "rush list" command
*/
private _getSelectedFoldersByNamedProjectsOnly({
toSelectors,
fromSelectors
}: {
toSelectors: Iterable<string>;
fromSelectors: Iterable<string>;
}): string[] {
let rushJson: { projects?: { packageName: string; projectFolder: string }[] } = {};
const root: string = this._gitService.getRepoInfo().root;
try {
rushJson = JsonFile.load(`${root}/rush.json`);
} catch (e) {
// no-catch
}
const packageNameToRushProjectSlim: Map<string, RushProjectSlim> = new Map<string, RushProjectSlim>();
if (Array.isArray(rushJson.projects)) {
const { projects } = rushJson;
for (const project of projects) {
const rushProjectSlim: RushProjectSlim = new RushProjectSlim(
project,
root,
packageNameToRushProjectSlim
);
packageNameToRushProjectSlim.set(project.packageName, rushProjectSlim);
}
}
const selectedProjects: Set<RushProjectSlim> = new Set<RushProjectSlim>();
const evalToSelectorForProject = (
rushProjectSlim: RushProjectSlim,
visited: Set<RushProjectSlim> = new Set<RushProjectSlim>()
): void => {
if (visited.has(rushProjectSlim)) {
return;
}
visited.add(rushProjectSlim);
selectedProjects.add(rushProjectSlim);
for (const dependencyProject of rushProjectSlim.dependencyProjects) {
evalToSelectorForProject(dependencyProject, visited);
}
};
for (const toSelector of toSelectors) {
const rushProjectSlim: RushProjectSlim | undefined = packageNameToRushProjectSlim.get(toSelector);
if (!rushProjectSlim) {
throw new Error(`Can not found project definition for "${toSelector}"`);
}
evalToSelectorForProject(rushProjectSlim);
}
const evalFromSelectorForProject = (
rushProjectSlim: RushProjectSlim,
visited: Set<RushProjectSlim> = new Set<RushProjectSlim>()
): void => {
if (visited.has(rushProjectSlim)) {
return;
}
visited.add(rushProjectSlim);
selectedProjects.add(rushProjectSlim);
for (const dependencyProject of rushProjectSlim.dependencyProjects) {
evalToSelectorForProject(dependencyProject);
}
for (const consumingProject of rushProjectSlim.consumingProjects) {
evalFromSelectorForProject(consumingProject, visited);
}
};
for (const fromSelector of fromSelectors) {
const rushProjectSlim: RushProjectSlim | undefined = packageNameToRushProjectSlim.get(fromSelector);
if (!rushProjectSlim) {
throw new Error(`Can not found project definition for "${fromSelector}"`);
}
evalFromSelectorForProject(rushProjectSlim);
}
const { terminal } = this._terminalService;
terminal.writeDebugLine(`Selected ${selectedProjects.size} projects:`);
Sort.sortSetBy(selectedProjects, (x) => x.packageName);
for (const project of selectedProjects) {
terminal.writeDebugLine(project.packageName);
}
return Array.from(selectedProjects).map((x) => x.relativeProjectFolder);
}
private _getSelectedFoldersByRushList({
toSelectors,
fromSelectors
}: {
toSelectors: Iterable<string>;
fromSelectors: Iterable<string>;
}): string[] {
const { terminal } = this._terminalService;
const args: string[] = ['list', '--json'];
for (const toSelector of toSelectors) {
args.push('--to');
args.push(toSelector);
}
for (const fromSelector of fromSelectors) {
args.push('--from');
args.push(fromSelector);
}
terminal.writeVerboseLine(`Run command: rush ${args.join(' ')}`);
const result: child_process.SpawnSyncReturns<string> = Executable.spawnSync('rush', args, {
stdio: ['pipe', 'pipe', 'pipe']
});
if (result.status !== 0) {
throw new Error(
`Failed to evaluate the Sparo profile's project selectors:\nstdout: ${result.stdout}\nstderr: ${result.stderr}`
);
}
const processedResult: string = this._processListResult(result.stdout.toString());
terminal.writeVerboseLine(processedResult);
const { projects: targetDeps } = JSON.parse(processedResult) as {
projects: { path: string }[];
};
return targetDeps.map((targetDep) => targetDep.path);
}
private _processListResult(input: string): string {
const stringList: string[] = input.split('\n');
let endOfInstallScript: number = -1;
for (let i: number = 0; i < stringList.length; ++i) {
if (stringList[i][0] === '{') {
endOfInstallScript = i;
break;
}
}
const jsonStringList: string[] = stringList.slice(endOfInstallScript);
return jsonStringList.join('\n');
}
}

View file

@ -55,6 +55,9 @@ importers:
inversify:
specifier: ~6.0.2
version: 6.0.2
npm-package-arg:
specifier: ~6.1.0
version: 6.1.0
reflect-metadata:
specifier: ~0.2.1
version: 0.2.1
@ -80,6 +83,9 @@ importers:
'@types/node':
specifier: 20.11.16
version: 20.11.16
'@types/npm-package-arg':
specifier: 6.1.0
version: 6.1.0
'@types/semver':
specifier: 7.5.7
version: 7.5.7
@ -3956,6 +3962,10 @@ packages:
dependencies:
undici-types: 5.26.5
/@types/npm-package-arg@6.1.0:
resolution: {integrity: sha512-vbt5fb0y1svMhu++1lwtKmZL76d0uPChFlw7kEzyUmTwfmpHRcFb8i0R8ElT69q/L+QLgK2hgECivIAvaEDwag==}
dev: true
/@types/parse-json@4.0.2:
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
dev: false
@ -4890,6 +4900,10 @@ packages:
/buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
/builtins@1.0.3:
resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==}
dev: false
/bytes@3.0.0:
resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
engines: {node: '>= 0.8'}
@ -7286,6 +7300,10 @@ packages:
react-is: 16.13.1
dev: false
/hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: false
/hpack.js@2.1.6:
resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==}
dependencies:
@ -9506,6 +9524,15 @@ packages:
resolution: {integrity: sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==}
dev: false
/npm-package-arg@6.1.0:
resolution: {integrity: sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==}
dependencies:
hosted-git-info: 2.8.9
osenv: 0.1.5
semver: 5.7.2
validate-npm-package-name: 3.0.0
dev: false
/npm-run-path@4.0.1:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'}
@ -9629,6 +9656,24 @@ packages:
type-check: 0.4.0
dev: true
/os-homedir@1.0.2:
resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==}
engines: {node: '>=0.10.0'}
dev: false
/os-tmpdir@1.0.2:
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
engines: {node: '>=0.10.0'}
dev: false
/osenv@0.1.5:
resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==}
deprecated: This package is no longer supported.
dependencies:
os-homedir: 1.0.2
os-tmpdir: 1.0.2
dev: false
/p-cancelable@1.1.0:
resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==}
engines: {node: '>=6'}
@ -11096,6 +11141,11 @@ packages:
semver: 7.6.0
dev: false
/semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
dev: false
/semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@ -12079,6 +12129,12 @@ packages:
convert-source-map: 2.0.0
dev: true
/validate-npm-package-name@3.0.0:
resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==}
dependencies:
builtins: 1.0.3
dev: false
/validator@13.11.0:
resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==}
engines: {node: '>= 0.10'}
@ -12570,6 +12626,7 @@ time:
/@rushstack/terminal@0.8.1: '2024-02-20T21:48:45.317Z'
/@types/heft-jest@1.0.6: '2023-11-07T07:22:05.861Z'
/@types/node@20.11.16: '2024-02-01T17:35:24.929Z'
/@types/npm-package-arg@6.1.0: '2018-11-01T17:41:31.538Z'
/@types/react-dom@18.2.19: '2024-02-07T17:06:58.412Z'
/@types/react@18.2.59: '2024-02-26T19:07:45.111Z'
/@types/semver@7.5.7: '2024-02-11T14:35:16.597Z'
@ -12583,6 +12640,7 @@ time:
/jest-diff@29.7.0: '2023-09-12T06:43:43.883Z'
/lunr-languages@1.14.0: '2023-10-09T20:33:09.608Z'
/lunr@2.3.9: '2020-08-19T20:30:07.948Z'
/npm-package-arg@6.1.0: '2018-04-10T17:45:45.725Z'
/prism-react-renderer@2.3.1: '2023-12-18T14:23:38.265Z'
/prismjs@1.29.0: '2022-08-23T10:42:14.395Z'
/react-dom@18.2.0: '2022-06-14T19:46:48.370Z'