From 3fbced5c0cd6741c38839e93a4c906f4012abb40 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Tue, 3 Dec 2024 12:26:00 +0100 Subject: [PATCH 01/19] Remove peer dependency and add runner --- package.json | 7 +- src/benchmark/benchmarkFn.ts | 140 ++++++++++++++++++ src/{mochaPlugin => benchmark}/globalState.ts | 6 +- src/{ => benchmark}/run.ts | 47 ++++-- src/{mochaPlugin => benchmark}/runBenchFn.ts | 1 + src/benchmark/runner.ts | 0 yarn.lock | 57 +++++++ 7 files changed, 240 insertions(+), 18 deletions(-) create mode 100644 src/benchmark/benchmarkFn.ts rename src/{mochaPlugin => benchmark}/globalState.ts (52%) rename src/{ => benchmark}/run.ts (61%) rename src/{mochaPlugin => benchmark}/runBenchFn.ts (98%) create mode 100644 src/benchmark/runner.ts diff --git a/package.json b/package.json index f561105..d26cc3e 100644 --- a/package.json +++ b/package.json @@ -60,13 +60,12 @@ "dependencies": { "@actions/cache": "^1.0.7", "@actions/github": "^5.0.0", + "@vitest/runner": "^2.1.6", "ajv": "^8.6.0", "aws-sdk": "^2.932.0", "csv-parse": "^4.16.0", "csv-stringify": "^5.6.2", - "yargs": "^17.7.2" - }, - "peerDependencies": { - "mocha": ">10.0.0" + "yargs": "^17.7.2", + "log-symbols": "^7.0.0" } } diff --git a/src/benchmark/benchmarkFn.ts b/src/benchmark/benchmarkFn.ts new file mode 100644 index 0000000..b8619a4 --- /dev/null +++ b/src/benchmark/benchmarkFn.ts @@ -0,0 +1,140 @@ +import fs from "node:fs"; +import path from "node:path"; +import {getCurrentSuite, getCurrentTest, test} from "@vitest/runner"; + +import {optsByRootSuite, optsMap, resultsByRootSuite} from "./globalState.js"; +import {BenchmarkOpts, BenchmarkResult} from "../types.js"; +import {runBenchFn} from "./runBenchFn.js"; + +export type BenchmarkRunOptsWithFn = BenchmarkOpts & { + id: string; + fn: (arg: T) => void | Promise; + before?: () => T2 | Promise; + beforeEach?: (arg: T2, i: number) => T | Promise; +}; + +type PartialBy = Omit & Partial>; + +function getOptsFromParent(parent: object): BenchmarkOpts { + // console.trace("Called to get options."); + const optsArr: BenchmarkOpts[] = []; + getOptsFromSuite(parent, optsArr); + // Merge opts, highest parent = lowest priority + return Object.assign({}, ...optsArr.reverse()) as BenchmarkOpts; +} + +/** + * Recursively append suite opts from child to parent. + * + * @returns `[suiteChildOpts, suiteParentOpts, suiteParentParentOpts]` + */ +function getOptsFromSuite(suite: object, optsArr: BenchmarkOpts[]): void { + const suiteOpts = optsMap.get(suite); + if (suiteOpts) { + optsArr.push(suiteOpts); + } +} + +const itBenchFn: ItBenchFn = function itBench( + this: object, + idOrOpts: string | PartialBy, "fn">, + fn?: (arg: T) => void | Promise +): void { + let opts = coerceToOptsObj(idOrOpts, fn); + const itFn = opts.only ? test : opts.skip ? test.skip : test; + + itFn(opts.id, async () => { + const parent = getCurrentTest(); + const optsParent = parent ? getOptsFromParent(parent) : undefined; + + // Get results array from root suite + const rootSuite = getCurrentSuite(); + let results = resultsByRootSuite.get(rootSuite); + let rootOpts = optsByRootSuite.get(rootSuite); + + if (!results) { + results = new Map(); + resultsByRootSuite.set(rootSuite, results); + } + + if (!rootOpts) { + rootOpts = {}; + optsByRootSuite.set(rootSuite, rootOpts); + } + + opts = Object.assign({}, rootOpts, optsParent, opts); + + // Ensure bench id is unique + if (results.has(opts.id)) { + throw Error(`test titles must be unique, duplicated: '${opts.id}'`); + } + + // Extend timeout if maxMs is set + if (opts.timeoutBench !== undefined) { + // this.timeout(opts.timeoutBench); + } else { + // const timeout = this.timeout(); + // if (opts.maxMs && opts.maxMs > timeout) { + // this.timeout(opts.maxMs * 1.5); + // } else if (opts.minMs && opts.minMs > timeout) { + // this.timeout(opts.minMs * 1.5); + // } + } + + // Persist full results if requested. dir is created in `beforeAll` + const benchmarkResultsCsvDir = process.env.BENCHMARK_RESULTS_CSV_DIR; + const persistRunsNs = Boolean(benchmarkResultsCsvDir); + + const {result, runsNs} = await runBenchFn(opts, persistRunsNs); + + // Store result for: + // - to persist benchmark data latter + // - to render with the custom reporter + results.set(opts.id, result); + + if (benchmarkResultsCsvDir) { + fs.mkdirSync(benchmarkResultsCsvDir, {recursive: true}); + const filename = `${result.id}.csv`; + const filepath = path.join(benchmarkResultsCsvDir, filename); + fs.writeFileSync(filepath, runsNs.join("\n")); + } + }); +}; + +interface ItBenchFn { + (opts: BenchmarkRunOptsWithFn): void; + (idOrOpts: string | Omit, "fn">, fn: (arg: T) => void): void; + ( + idOrOpts: string | PartialBy, "fn">, + fn?: (arg: T) => void | Promise + ): void; +} + +interface ItBench extends ItBenchFn { + only: ItBenchFn; + skip: ItBenchFn; +} + +function coerceToOptsObj( + idOrOpts: string | PartialBy, "fn">, + fn?: (arg: T) => void | Promise +): BenchmarkRunOptsWithFn { + let opts: BenchmarkRunOptsWithFn; + + if (typeof idOrOpts === "string") { + if (!fn) throw Error("fn arg must be set"); + opts = {id: idOrOpts, fn}; + } else { + if (fn) { + opts = {...idOrOpts, fn}; + } else { + const optsWithFn = idOrOpts as BenchmarkRunOptsWithFn; + if (!optsWithFn.fn) throw Error("opts.fn arg must be set"); + opts = optsWithFn; + } + } + + return opts; +} + +export const itBench = itBenchFn as ItBench; diff --git a/src/mochaPlugin/globalState.ts b/src/benchmark/globalState.ts similarity index 52% rename from src/mochaPlugin/globalState.ts rename to src/benchmark/globalState.ts index edb3e50..1886c25 100644 --- a/src/mochaPlugin/globalState.ts +++ b/src/benchmark/globalState.ts @@ -4,14 +4,14 @@ import {BenchmarkResult, BenchmarkOpts} from "../types"; * Map of results by root suie. * Before running mocha, you must register the root suite here */ -export const resultsByRootSuite = new WeakMap>(); +export const resultsByRootSuite = new WeakMap>(); /** * Global opts from CLI */ -export const optsByRootSuite = new WeakMap(); +export const optsByRootSuite = new WeakMap(); /** * Map to persist options set in describe blocks */ -export const optsMap = new Map(); +export const optsMap = new WeakMap(); diff --git a/src/run.ts b/src/benchmark/run.ts similarity index 61% rename from src/run.ts rename to src/benchmark/run.ts index e5b6aa7..98d172f 100644 --- a/src/run.ts +++ b/src/benchmark/run.ts @@ -1,14 +1,15 @@ import * as github from "@actions/github"; -import {getHistoryProvider} from "./history/index.js"; -import {resolveShouldPersist} from "./history/shouldPersist.js"; -import {validateBenchmark} from "./history/schema.js"; -import {Benchmark, BenchmarkOpts, Opts} from "./types.js"; -import {renderCompareWith, resolveCompareWith, resolvePrevBenchmark} from "./compare/index.js"; -import {parseBranchFromRef, getCurrentCommitInfo, shell, getCurrentBranch} from "./utils/index.js"; -import {runMochaBenchmark} from "./mochaPlugin/mochaRunner.js"; -import {computeBenchComparision} from "./compare/compute.js"; -import {postGaComment} from "./github/comment.js"; -import {isGaRun} from "./github/context.js"; +import {getHistoryProvider} from "../history/index.js"; +import {resolveShouldPersist} from "../history/shouldPersist.js"; +import {validateBenchmark} from "../history/schema.js"; +import {Benchmark, BenchmarkOpts, BenchmarkResult, BenchmarkResults, Opts} from "../types.js"; +import {renderCompareWith, resolveCompareWith, resolvePrevBenchmark} from "../compare/index.js"; +import {parseBranchFromRef, getCurrentCommitInfo, shell, getCurrentBranch} from "../utils/index.js"; +import {runMochaBenchmark} from "../mochaPlugin/mochaRunner.js"; +import {computeBenchComparision} from "../compare/compute.js"; +import {postGaComment} from "../github/comment.js"; +import {isGaRun} from "../github/context.js"; +import {startTests} from "@vitest/runner"; /* eslint-disable no-console */ @@ -30,9 +31,33 @@ export async function run(opts: Opts & BenchmarkOpts): Promise { console.log(`No previous bencharmk found for ${renderCompareWith(compareWith)}`); } + const res = await startTests(["/Users/nazar/Hub/Lodestar/Projects/benchmark/test/perf/iteration.test.ts"], { + config: { + root: "", + sequence: {seed: 1234, hooks: "list", setupFiles: "list"}, + passWithNoTests: false, + maxConcurrency: 1, + hookTimeout: 10_0000, + testTimeout: 10_0000, + setupFiles: [], + retry: 0, + }, + importFile: async (file, source) => { + await import(file); + }, + // async runSuite(suite): Promise { + // console.log(suite); + // }, + + // async runTask(): Promise { + // throw new Error("`test()` and `it()` is only available in test mode."); + // }, + }); + // TODO: Forward all options to mocha // Run benchmarks with mocha programatically - const results = await runMochaBenchmark(opts, prevBench); + // const results = await runMochaBenchmark(opts, prevBench); + const results: BenchmarkResults = []; if (results.length === 0) { throw Error("No benchmark result was produced"); } diff --git a/src/mochaPlugin/runBenchFn.ts b/src/benchmark/runBenchFn.ts similarity index 98% rename from src/mochaPlugin/runBenchFn.ts rename to src/benchmark/runBenchFn.ts index 9e5adb0..39ca723 100644 --- a/src/mochaPlugin/runBenchFn.ts +++ b/src/benchmark/runBenchFn.ts @@ -15,6 +15,7 @@ export async function runBenchFn( opts: BenchmarkRunOptsWithFn, persistRunsNs?: boolean ): Promise<{result: BenchmarkResult; runsNs: bigint[]}> { + console.trace("runBenchFn", opts, persistRunsNs); const minRuns = opts.minRuns || 1; const maxRuns = opts.maxRuns || Infinity; const maxMs = opts.maxMs || Infinity; diff --git a/src/benchmark/runner.ts b/src/benchmark/runner.ts new file mode 100644 index 0000000..e69de29 diff --git a/yarn.lock b/yarn.lock index 6657bab..ad5a994 100644 --- a/yarn.lock +++ b/yarn.lock @@ -592,6 +592,30 @@ "@typescript-eslint/types" "8.16.0" eslint-visitor-keys "^4.2.0" +"@vitest/pretty-format@2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.6.tgz#9bc642047a3efc637b41492b1f222c43be3822e4" + integrity sha512-exZyLcEnHgDMKc54TtHca4McV4sKT+NKAe9ix/yhd/qkYb/TP8HTyXRFDijV19qKqTZM0hPL4753zU/U8L/gAA== + dependencies: + tinyrainbow "^1.2.0" + +"@vitest/runner@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.6.tgz#948cad2cccfe2e56be5b3f9979cf9a417ca59737" + integrity sha512-SjkRGSFyrA82m5nz7To4CkRSEVWn/rwQISHoia/DB8c6IHIhaE/UNAo+7UfeaeJRE979XceGl00LNkIz09RFsA== + dependencies: + "@vitest/utils" "2.1.6" + pathe "^1.1.2" + +"@vitest/utils@2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.6.tgz#2af6a82c5c45da35ecd322d0568247a6e9c95c5f" + integrity sha512-ixNkFy3k4vokOUTU2blIUvOgKq/N2PW8vKIjZZYsGJCMX69MRa9J2sKqX5hY/k5O5Gty3YJChepkqZ3KM9LyIQ== + dependencies: + "@vitest/pretty-format" "2.1.6" + loupe "^3.1.2" + tinyrainbow "^1.2.0" + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -1950,6 +1974,11 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-unicode-supported@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== + is-weakmap@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" @@ -2059,6 +2088,14 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-symbols@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-7.0.0.tgz#953999bb9cec27a09049c8f45e1154ec81163061" + integrity sha512-zrc91EDk2M+2AXo/9BTvK91pqb7qrPg2nX/Hy+u8a5qQlbaOflCKO+6SqgZ+M+xUFxGdKTgwnGiL96b1W3ikRA== + dependencies: + is-unicode-supported "^2.0.0" + yoctocolors "^2.1.1" + loupe@^2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" @@ -2066,6 +2103,11 @@ loupe@^2.3.6: dependencies: get-func-name "^2.0.1" +loupe@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" + integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -2293,6 +2335,11 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" @@ -2647,6 +2694,11 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +tinyrainbow@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" + integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -3030,3 +3082,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yoctocolors@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.1.1.tgz#e0167474e9fbb9e8b3ecca738deaa61dd12e56fc" + integrity sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ== From b9fb450c986638d2478c0f494f5165182ebeae94 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Tue, 3 Dec 2024 12:26:54 +0100 Subject: [PATCH 02/19] Add new runner implementation --- src/benchmark/benchmarkFn.ts | 158 +++++----- src/{mochaPlugin => benchmark}/format.ts | 0 src/benchmark/globalState.ts | 35 ++- src/benchmark/index.ts | 1 + src/benchmark/reporter.ts | 107 +++++++ src/benchmark/run.ts | 31 +- src/benchmark/runBenchFn.ts | 1 - src/benchmark/runner.ts | 67 ++++ src/index.ts | 2 +- src/mochaPlugin/index.ts | 375 ++++++++++++----------- src/mochaPlugin/mochaRunner.ts | 50 --- src/mochaPlugin/reporter.ts | 102 ------ src/mochaPlugin/utils.ts | 38 +-- src/utils/output.ts | 42 +++ 14 files changed, 545 insertions(+), 464 deletions(-) rename src/{mochaPlugin => benchmark}/format.ts (100%) create mode 100644 src/benchmark/index.ts create mode 100644 src/benchmark/reporter.ts delete mode 100644 src/mochaPlugin/mochaRunner.ts delete mode 100644 src/mochaPlugin/reporter.ts create mode 100644 src/utils/output.ts diff --git a/src/benchmark/benchmarkFn.ts b/src/benchmark/benchmarkFn.ts index b8619a4..dba09de 100644 --- a/src/benchmark/benchmarkFn.ts +++ b/src/benchmark/benchmarkFn.ts @@ -1,9 +1,9 @@ import fs from "node:fs"; import path from "node:path"; -import {getCurrentSuite, getCurrentTest, test} from "@vitest/runner"; - -import {optsByRootSuite, optsMap, resultsByRootSuite} from "./globalState.js"; -import {BenchmarkOpts, BenchmarkResult} from "../types.js"; +import {getCurrentSuite, Suite, SuiteCollector} from "@vitest/runner"; +import {createChainable} from "@vitest/runner/utils"; +import {store} from "./globalState.js"; +import {BenchmarkOpts} from "../types.js"; import {runBenchFn} from "./runBenchFn.js"; export type BenchmarkRunOptsWithFn = BenchmarkOpts & { @@ -15,93 +15,97 @@ export type BenchmarkRunOptsWithFn = BenchmarkOpts & { type PartialBy = Omit & Partial>; -function getOptsFromParent(parent: object): BenchmarkOpts { - // console.trace("Called to get options."); - const optsArr: BenchmarkOpts[] = []; - getOptsFromSuite(parent, optsArr); - // Merge opts, highest parent = lowest priority - return Object.assign({}, ...optsArr.reverse()) as BenchmarkOpts; -} +export function getRootSuite(suite: Suite | SuiteCollector): Suite { + if (suite.type === "collector") { + return getRootSuite(suite.tasks[0] as Suite); + } -/** - * Recursively append suite opts from child to parent. - * - * @returns `[suiteChildOpts, suiteParentOpts, suiteParentParentOpts]` - */ -function getOptsFromSuite(suite: object, optsArr: BenchmarkOpts[]): void { - const suiteOpts = optsMap.get(suite); - if (suiteOpts) { - optsArr.push(suiteOpts); + if (suite.name === "") { + return suite; + } else if (suite.suite) { + getRootSuite(suite.suite); + } else { + return suite; } + + throw new Error("Can not find root suite"); } -const itBenchFn: ItBenchFn = function itBench( - this: object, +export const bench = createBenchmarkFunction(function ( idOrOpts: string | PartialBy, "fn">, fn?: (arg: T) => void | Promise -): void { - let opts = coerceToOptsObj(idOrOpts, fn); - const itFn = opts.only ? test : opts.skip ? test.skip : test; - - itFn(opts.id, async () => { - const parent = getCurrentTest(); - const optsParent = parent ? getOptsFromParent(parent) : undefined; - - // Get results array from root suite - const rootSuite = getCurrentSuite(); - let results = resultsByRootSuite.get(rootSuite); - let rootOpts = optsByRootSuite.get(rootSuite); - - if (!results) { - results = new Map(); - resultsByRootSuite.set(rootSuite, results); - } - - if (!rootOpts) { - rootOpts = {}; - optsByRootSuite.set(rootSuite, rootOpts); - } - - opts = Object.assign({}, rootOpts, optsParent, opts); - - // Ensure bench id is unique - if (results.has(opts.id)) { - throw Error(`test titles must be unique, duplicated: '${opts.id}'`); - } - - // Extend timeout if maxMs is set - if (opts.timeoutBench !== undefined) { +) { + const {fn: benchTask, ...opts} = coerceToOptsObj(idOrOpts, fn); + + const task = getCurrentSuite().task(opts.id, { + skip: opts.skip, + only: opts.only, + sequential: true, + concurrent: false, + meta: { + "dapplion/benchmark": true, + }, + async handler(context) { + const parentSuite = context.task.suite; + const parentOpts = parentSuite ? store.getOptions(parentSuite) : {}; + + // TODO: Find better way to point to root suite + const rootSuite = context.task.suite; + const rootOpts = rootSuite ? store.getRootOptions(rootSuite) : {}; + + const fullOptions = Object.assign({}, rootOpts, parentOpts, opts); + + // Ensure bench id is unique + if (store.getResult(opts.id)) { + throw Error(`test titles must be unique, duplicated: '${opts.id}'`); + } + + // Extend timeout if maxMs is set + // if (opts.timeoutBench !== undefined) { // this.timeout(opts.timeoutBench); - } else { + // } else { // const timeout = this.timeout(); // if (opts.maxMs && opts.maxMs > timeout) { // this.timeout(opts.maxMs * 1.5); // } else if (opts.minMs && opts.minMs > timeout) { // this.timeout(opts.minMs * 1.5); // } - } + // } - // Persist full results if requested. dir is created in `beforeAll` - const benchmarkResultsCsvDir = process.env.BENCHMARK_RESULTS_CSV_DIR; - const persistRunsNs = Boolean(benchmarkResultsCsvDir); + // Persist full results if requested. dir is created in `beforeAll` + const benchmarkResultsCsvDir = process.env.BENCHMARK_RESULTS_CSV_DIR; + const persistRunsNs = Boolean(benchmarkResultsCsvDir); - const {result, runsNs} = await runBenchFn(opts, persistRunsNs); + const {result, runsNs} = await runBenchFn({...fullOptions, fn: benchTask}, persistRunsNs); - // Store result for: - // - to persist benchmark data latter - // - to render with the custom reporter - results.set(opts.id, result); + // Store result for: + // - to persist benchmark data latter + // - to render with the custom reporter + store.setResult(opts.id, result); - if (benchmarkResultsCsvDir) { - fs.mkdirSync(benchmarkResultsCsvDir, {recursive: true}); - const filename = `${result.id}.csv`; - const filepath = path.join(benchmarkResultsCsvDir, filename); - fs.writeFileSync(filepath, runsNs.join("\n")); - } + if (benchmarkResultsCsvDir) { + fs.mkdirSync(benchmarkResultsCsvDir, {recursive: true}); + const filename = `${result.id}.csv`; + const filepath = path.join(benchmarkResultsCsvDir, filename); + fs.writeFileSync(filepath, runsNs.join("\n")); + } + }, }); -}; -interface ItBenchFn { + store.setOptions(task, opts); +}); + +function createBenchmarkFunction( + fn: ( + this: Record<"skip" | "only", boolean | undefined>, + idOrOpts: string | PartialBy, "fn">, + fn?: (arg: T) => void | Promise + ) => void +): BenchFuncApi { + return createChainable(["skip", "only"], fn) as BenchFuncApi; +} + +interface BenchFuncApi { (opts: BenchmarkRunOptsWithFn): void; (idOrOpts: string | Omit, "fn">, fn: (arg: T) => void): void; ( @@ -110,11 +114,13 @@ interface ItBenchFn { ): void; } -interface ItBench extends ItBenchFn { - only: ItBenchFn; - skip: ItBenchFn; +interface BenchApi extends BenchFuncApi { + only: BenchFuncApi; + skip: BenchFuncApi; } +export const itBench = bench as BenchApi; + function coerceToOptsObj( idOrOpts: string | PartialBy, "fn">, fn?: (arg: T) => void | Promise @@ -136,5 +142,3 @@ function coerceToOptsObj( return opts; } - -export const itBench = itBenchFn as ItBench; diff --git a/src/mochaPlugin/format.ts b/src/benchmark/format.ts similarity index 100% rename from src/mochaPlugin/format.ts rename to src/benchmark/format.ts diff --git a/src/benchmark/globalState.ts b/src/benchmark/globalState.ts index 1886c25..3309fc5 100644 --- a/src/benchmark/globalState.ts +++ b/src/benchmark/globalState.ts @@ -1,17 +1,42 @@ -import {BenchmarkResult, BenchmarkOpts} from "../types"; +import type {Suite, Task} from "@vitest/runner"; +import {BenchmarkResult, BenchmarkOpts, BenchmarkResults} from "../types.js"; /**t - * Map of results by root suie. + * Map of results by root suite. * Before running mocha, you must register the root suite here */ -export const resultsByRootSuite = new WeakMap>(); +const results = new Map(); /** * Global opts from CLI */ -export const optsByRootSuite = new WeakMap(); +const optsByRootSuite = new WeakMap(); /** * Map to persist options set in describe blocks */ -export const optsMap = new WeakMap(); +const optsMap = new WeakMap(); + +export const store = { + getResult(id: string): BenchmarkResult | undefined { + return results.get(id); + }, + setResult(id: string, result: BenchmarkResult): void { + results.set(id, result); + }, + getAllResults(): BenchmarkResults { + return [...results.values()]; + }, + getOptions(suite: Task): BenchmarkOpts | undefined { + return optsMap.get(suite); + }, + setOptions(suite: Task, opts: BenchmarkOpts): void { + optsMap.set(suite, opts); + }, + getRootOptions(suite: Suite): BenchmarkOpts | undefined { + return optsByRootSuite.get(suite); + }, + setRootOptions(suite: Suite, opts: BenchmarkOpts): void { + optsByRootSuite.set(suite, opts); + }, +}; diff --git a/src/benchmark/index.ts b/src/benchmark/index.ts new file mode 100644 index 0000000..99c72e3 --- /dev/null +++ b/src/benchmark/index.ts @@ -0,0 +1 @@ +export * from "./benchmarkFn.js"; diff --git a/src/benchmark/reporter.ts b/src/benchmark/reporter.ts new file mode 100644 index 0000000..51fea36 --- /dev/null +++ b/src/benchmark/reporter.ts @@ -0,0 +1,107 @@ +import {Task, Suite, File} from "@vitest/runner"; +import {color, consoleLog, symbols} from "../utils/output.js"; +import {store} from "./globalState.js"; +import {Benchmark, BenchmarkResult} from "../types.js"; +import {formatResultRow} from "./format.js"; + +export class BenchmarkReporter { + indents = 0; + failed = 0; + passed = 0; + skipped = 0; + + readonly prevResults: Map; + readonly threshold: number; + + constructor(prevBench: Benchmark | null, threshold: number) { + this.prevResults = new Map(); + this.threshold = threshold; + + if (prevBench) { + for (const bench of prevBench.results) { + this.prevResults.set(bench.id, bench); + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onTestStarted(_task: Task): void { + // this.log(task.name, "started"); + } + + onTestFinished(task: Task): void { + const {result} = task; + + if (!result) { + consoleLog(`${this.indent()}${color("pending", " - %s")}`, `${task.name} - can not find result`); + return; + } + + switch (result.state) { + case "skip": { + this.skipped++; + consoleLog(`${this.indent()}${color("pending", " - %s")}`, task.name); + break; + } + case "fail": { + this.failed++; + consoleLog(this.indent() + color("fail", " %d) %s"), ++this.failed, task.name); + consoleLog(task.result?.errors); + break; + } + case "pass": { + try { + const result = store.getResult(task.name); + + if (result) { + // Render benchmark + const prevResult = this.prevResults.get(result.id) ?? null; + + const resultRow = formatResultRow(result, prevResult, result.threshold ?? this.threshold); + const fmt = this.indent() + color("checkmark", " " + symbols.ok) + " " + resultRow; + consoleLog(fmt); + } else { + // Render regular test + const fmt = this.indent() + color("checkmark", " " + symbols.ok) + color("pass", " %s"); + consoleLog(fmt, task.name); + } + this.passed++; + } catch (e) { + this.failed++; + // Log error manually since mocha doesn't log errors thrown here + consoleLog(e); + process.exitCode = 1; + throw e; + } + } + } + } + + onSuiteStarted(suite: Suite): void { + this.indents++; + consoleLog(color("suite", "%s%s"), this.indent(), suite.name); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onSuiteFinished(_suite: Suite): void { + --this.indents; + + if (this.indents === 1) { + consoleLog(); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onComplete(_files: File[]): void { + consoleLog(); + this.indents += 2; + consoleLog(color("checkmark", "%s%s"), this.indent(), `${this.passed} passing`); + consoleLog(color("fail", "%s%s"), this.indent(), `${this.failed} failed`); + consoleLog(color("pending", "%s%s"), this.indent(), `${this.skipped} pending`); + consoleLog(); + } + + protected indent(): string { + return Array(this.indents).join(" "); + } +} diff --git a/src/benchmark/run.ts b/src/benchmark/run.ts index 98d172f..ec0e1b5 100644 --- a/src/benchmark/run.ts +++ b/src/benchmark/run.ts @@ -5,11 +5,10 @@ import {validateBenchmark} from "../history/schema.js"; import {Benchmark, BenchmarkOpts, BenchmarkResult, BenchmarkResults, Opts} from "../types.js"; import {renderCompareWith, resolveCompareWith, resolvePrevBenchmark} from "../compare/index.js"; import {parseBranchFromRef, getCurrentCommitInfo, shell, getCurrentBranch} from "../utils/index.js"; -import {runMochaBenchmark} from "../mochaPlugin/mochaRunner.js"; import {computeBenchComparision} from "../compare/compute.js"; import {postGaComment} from "../github/comment.js"; import {isGaRun} from "../github/context.js"; -import {startTests} from "@vitest/runner"; +import {BenchmarkRunner} from "./runner.js"; /* eslint-disable no-console */ @@ -31,33 +30,9 @@ export async function run(opts: Opts & BenchmarkOpts): Promise { console.log(`No previous bencharmk found for ${renderCompareWith(compareWith)}`); } - const res = await startTests(["/Users/nazar/Hub/Lodestar/Projects/benchmark/test/perf/iteration.test.ts"], { - config: { - root: "", - sequence: {seed: 1234, hooks: "list", setupFiles: "list"}, - passWithNoTests: false, - maxConcurrency: 1, - hookTimeout: 10_0000, - testTimeout: 10_0000, - setupFiles: [], - retry: 0, - }, - importFile: async (file, source) => { - await import(file); - }, - // async runSuite(suite): Promise { - // console.log(suite); - // }, + const runner = new BenchmarkRunner({prevBench}); + const results = await runner.process(["/Users/nazar/Hub/Lodestar/Projects/benchmark/test/perf/iteration.test.ts"]); - // async runTask(): Promise { - // throw new Error("`test()` and `it()` is only available in test mode."); - // }, - }); - - // TODO: Forward all options to mocha - // Run benchmarks with mocha programatically - // const results = await runMochaBenchmark(opts, prevBench); - const results: BenchmarkResults = []; if (results.length === 0) { throw Error("No benchmark result was produced"); } diff --git a/src/benchmark/runBenchFn.ts b/src/benchmark/runBenchFn.ts index 39ca723..9e5adb0 100644 --- a/src/benchmark/runBenchFn.ts +++ b/src/benchmark/runBenchFn.ts @@ -15,7 +15,6 @@ export async function runBenchFn( opts: BenchmarkRunOptsWithFn, persistRunsNs?: boolean ): Promise<{result: BenchmarkResult; runsNs: bigint[]}> { - console.trace("runBenchFn", opts, persistRunsNs); const minRuns = opts.minRuns || 1; const maxRuns = opts.maxRuns || Infinity; const maxMs = opts.maxMs || Infinity; diff --git a/src/benchmark/runner.ts b/src/benchmark/runner.ts index e69de29..ff5c444 100644 --- a/src/benchmark/runner.ts +++ b/src/benchmark/runner.ts @@ -0,0 +1,67 @@ +import { + File, + startTests, + Suite, + Task, + VitestRunner, + VitestRunnerConfig, + VitestRunnerImportSource, +} from "@vitest/runner"; +import {Benchmark, BenchmarkResults} from "../types.js"; +import {BenchmarkReporter} from "./reporter.js"; +import {store} from "./globalState.js"; + +export class BenchmarkRunner implements VitestRunner { + config: VitestRunnerConfig; + reporter: BenchmarkReporter; + + constructor(protected opts: {prevBench: Benchmark | null}) { + this.config = { + root: "", + sequence: {seed: 1234, hooks: "list", setupFiles: "list"}, + passWithNoTests: false, + maxConcurrency: 1, + hookTimeout: 10_0000, + testTimeout: 10_0000, + setupFiles: [], + retry: 0, + }; + this.reporter = new BenchmarkReporter(opts.prevBench, 0.2); + } + + onBeforeRunSuite(suite: Suite): void { + this.reporter.onSuiteStarted(suite); + } + + onAfterRunSuite(suite: Suite): void { + this.reporter.onSuiteFinished(suite); + } + + onBeforeRunTask(task: Task): void { + this.reporter.onTestStarted(task); + } + + onAfterRunTask(task: Task): void { + this.reporter.onTestFinished(task); + } + + onAfterRunFiles(files: File[]): void { + this.reporter.onComplete(files); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async importFile(filepath: string, _source: VitestRunnerImportSource): Promise { + // TODO: Implement file caching mechanism later + await import(filepath); + } + + async process(files: string[]): Promise { + const res = await startTests(files, this); + + if (res[0].result?.state === "pass") { + return store.getAllResults(); + } + + return store.getAllResults(); + } +} diff --git a/src/index.ts b/src/index.ts index 12b30d0..bcd26c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export * from "./mochaPlugin/index.js"; +export * from "./benchmark/index.js"; diff --git a/src/mochaPlugin/index.ts b/src/mochaPlugin/index.ts index a1e9cef..84cc875 100644 --- a/src/mochaPlugin/index.ts +++ b/src/mochaPlugin/index.ts @@ -1,182 +1,193 @@ -import fs from "node:fs"; -import path from "node:path"; -import {BenchmarkOpts} from "../types.js"; -import {optsByRootSuite, optsMap, resultsByRootSuite} from "./globalState.js"; -import {BenchmarkRunOptsWithFn, runBenchFn} from "./runBenchFn.js"; -import {getRootSuite, getParentSuite} from "./utils.js"; - -type PartialBy = Omit & Partial>; - -const itBenchFn: ItBenchFn = function itBench( - idOrOpts: string | PartialBy, "fn">, - fn?: (arg: T) => void | Promise -): void { - // TODO: - // Implement reporter - // Implement grouping functionality - - // if (this.averageNs === null) this.averageNs = result.averageNs; - // result.factor = result.averageNs / this.averageNs; - - let opts = coerceToOptsObj(idOrOpts, fn); - - // Apply mocha it opts - const itFn = opts.only ? it.only : opts.skip ? it.skip : it; - - itFn(opts.id, async function () { - const parent = getParentSuite(this); - const optsParent = getOptsFromParent(parent); - - // Get results array from root suite - const rootSuite = getRootSuite(parent); - const results = resultsByRootSuite.get(rootSuite); - const rootOpts = optsByRootSuite.get(rootSuite); - if (!results || !rootOpts) throw Error("root suite not found"); - - opts = Object.assign({}, rootOpts, optsParent, opts); - - // Ensure bench id is unique - if (results.has(opts.id)) { - throw Error(`test titles must be unique, duplicated: '${opts.id}'`); - } - - // Extend timeout if maxMs is set - if (opts.timeoutBench !== undefined) { - this.timeout(opts.timeoutBench); - } else { - const timeout = this.timeout(); - if (opts.maxMs && opts.maxMs > timeout) { - this.timeout(opts.maxMs * 1.5); - } else if (opts.minMs && opts.minMs > timeout) { - this.timeout(opts.minMs * 1.5); - } - } - - // Persist full results if requested. dir is created in `beforeAll` - const benchmarkResultsCsvDir = process.env.BENCHMARK_RESULTS_CSV_DIR; - const persistRunsNs = Boolean(benchmarkResultsCsvDir); - - const {result, runsNs} = await runBenchFn(opts, persistRunsNs); - - // Store result for: - // - to persist benchmark data latter - // - to render with the custom reporter - results.set(opts.id, result); - - if (benchmarkResultsCsvDir) { - fs.mkdirSync(benchmarkResultsCsvDir, {recursive: true}); - const filename = `${result.id}.csv`; - const filepath = path.join(benchmarkResultsCsvDir, filename); - fs.writeFileSync(filepath, runsNs.join("\n")); - } - }); -}; - -interface ItBenchFn { - (opts: BenchmarkRunOptsWithFn): void; - (idOrOpts: string | Omit, "fn">, fn: (arg: T) => void): void; - ( - idOrOpts: string | PartialBy, "fn">, - fn?: (arg: T) => void | Promise - ): void; -} - -interface ItBench extends ItBenchFn { - only: ItBenchFn; - skip: ItBenchFn; -} - -export const itBench = itBenchFn as ItBench; - -itBench.only = function itBench(idOrOpts, fn): void { - const opts = coerceToOptsObj(idOrOpts, fn); - opts.only = true; - itBenchFn(opts); -} as ItBenchFn; - -itBench.skip = function itBench(idOrOpts, fn): void { - const opts = coerceToOptsObj(idOrOpts, fn); - opts.skip = true; - itBenchFn(opts); -} as ItBenchFn; - -function coerceToOptsObj( - idOrOpts: string | PartialBy, "fn">, - fn?: (arg: T) => void | Promise -): BenchmarkRunOptsWithFn { - let opts: BenchmarkRunOptsWithFn; - - if (typeof idOrOpts === "string") { - if (!fn) throw Error("fn arg must be set"); - opts = {id: idOrOpts, fn}; - } else { - if (fn) { - opts = {...idOrOpts, fn}; - } else { - const optsWithFn = idOrOpts as BenchmarkRunOptsWithFn; - if (!optsWithFn.fn) throw Error("opts.fn arg must be set"); - opts = optsWithFn; - } - } - - return opts; -} - -/** - * Customize benchmark opts for a describe block. Affects only tests within that Mocha.Suite - * ```ts - * describe("suite A1", function () { - * setBenchOpts({runs: 100}); - * // 100 runs - * itBench("bench A1.1", function() {}); - * itBench("bench A1.2", function() {}); - * // 300 runs - * itBench({id: "bench A1.3", runs: 300}, function() {}); - * - * // Supports nesting, child has priority over parent. - * // Arrow functions can be used, won't break it. - * describe("suite A2", () => { - * setBenchOpts({runs: 200}); - * // 200 runs. - * itBench("bench A2.1", () => {}); - * }) - * }) - * ``` - */ -export function setBenchOpts(opts: BenchmarkOpts): void { - before(function () { - if (this.currentTest?.parent) { - optsMap.set(this.currentTest?.parent, opts); - } - }); - - after(function () { - // Clean-up to allow garbage collection - if (this.currentTest?.parent) { - optsMap.delete(this.currentTest?.parent); - } - }); -} - -function getOptsFromParent(parent: Mocha.Suite): BenchmarkOpts { - const optsArr: BenchmarkOpts[] = []; - getOptsFromSuite(parent, optsArr); - // Merge opts, highest parent = lowest priority - return Object.assign({}, ...optsArr.reverse()) as BenchmarkOpts; -} - -/** - * Recursively append suite opts from child to parent. - * - * @returns `[suiteChildOpts, suiteParentOpts, suiteParentParentOpts]` - */ -function getOptsFromSuite(suite: Mocha.Suite, optsArr: BenchmarkOpts[]): void { - const suiteOpts = optsMap.get(suite); - if (suiteOpts) { - optsArr.push(suiteOpts); - } - - if (suite.parent) { - getOptsFromSuite(suite.parent, optsArr); - } -} +// // import fs from "node:fs"; +// // import path from "node:path"; +// import {BenchmarkOpts} from "../types.js"; +// // import {optsByRootSuite, optsMap, resultsByRootSuite} from "./globalState.js"; +// import {BenchmarkRunOptsWithFn, runBenchFn} from "../benchmark/runBenchFn.js"; +// // import {getRootSuite, getParentSuite} from "./utils.js"; +// import {test} from "@vitest/runner"; + +// type PartialBy = Omit & Partial>; + +// const itBenchFn: ItBenchFn = function itBench( +// this: object, +// idOrOpts: string | PartialBy, "fn">, +// fn?: (arg: T) => void | Promise +// ): void { +// // TODO: +// // Implement reporter +// // Implement grouping functionality + +// // if (this.averageNs === null) this.averageNs = result.averageNs; +// // result.factor = result.averageNs / this.averageNs; + +// console.trace(idOrOpts); + +// const opts = coerceToOptsObj(idOrOpts, fn); + +// // Apply mocha it opts +// const itFn = opts.only ? test : opts.skip ? test.skip : test; + +// // console.trace(itFn, opts.id); + +// itFn(opts.id, async () => { +// // console.log("%%%%%%%%%%%%%%%%%%", opts.id, getCurrentSuite()); +// // const parent = getCurrentSuite(); +// // const optsParent = getOptsFromParent(parent); + +// // Get results array from root suite +// // const rootSuite = getRootSuite(parent.); +// // const rootSuite = parent; +// // console.log({rootSuite}); +// // const results = resultsByRootSuite.get(rootSuite); +// // const rootOpts = optsByRootSuite.get(rootSuite); +// // if (!results || !rootOpts) throw Error("root suite not found"); + +// // opts = Object.assign({}, rootOpts, optsParent, opts); + +// // // Ensure bench id is unique +// // if (results.has(opts.id)) { +// // throw Error(`test titles must be unique, duplicated: '${opts.id}'`); +// // } + +// // // Extend timeout if maxMs is set +// // // if (opts.timeoutBench !== undefined) { +// // // this.timeout(opts.timeoutBench); +// // // } else { +// // // const timeout = this.timeout(); +// // // if (opts.maxMs && opts.maxMs > timeout) { +// // // this.timeout(opts.maxMs * 1.5); +// // // } else if (opts.minMs && opts.minMs > timeout) { +// // // this.timeout(opts.minMs * 1.5); +// // // } +// // // } + +// // // Persist full results if requested. dir is created in `beforeAll` +// // const benchmarkResultsCsvDir = process.env.BENCHMARK_RESULTS_CSV_DIR; +// // const persistRunsNs = Boolean(benchmarkResultsCsvDir); + +// // console.log({opts, persistRunsNs}); +// // const {result, runsNs} = await runBenchFn(opts, persistRunsNs); +// await runBenchFn(opts, false); + +// // // Store result for: +// // // - to persist benchmark data latter +// // // - to render with the custom reporter +// // results.set(opts.id, result); + +// // if (benchmarkResultsCsvDir) { +// // fs.mkdirSync(benchmarkResultsCsvDir, {recursive: true}); +// // const filename = `${result.id}.csv`; +// // const filepath = path.join(benchmarkResultsCsvDir, filename); +// // fs.writeFileSync(filepath, runsNs.join("\n")); +// // } +// }); +// }; + +// interface ItBenchFn { +// (opts: BenchmarkRunOptsWithFn): void; +// (idOrOpts: string | Omit, "fn">, fn: (arg: T) => void): void; +// ( +// idOrOpts: string | PartialBy, "fn">, +// fn?: (arg: T) => void | Promise +// ): void; +// } + +// interface ItBench extends ItBenchFn { +// only: ItBenchFn; +// skip: ItBenchFn; +// } + +// export const itBench = itBenchFn as ItBench; + +// itBench.only = function itBench(idOrOpts, fn): void { +// const opts = coerceToOptsObj(idOrOpts, fn); +// opts.only = true; +// itBenchFn(opts); +// } as ItBenchFn; + +// itBench.skip = function itBench(idOrOpts, fn): void { +// const opts = coerceToOptsObj(idOrOpts, fn); +// opts.skip = true; +// itBenchFn(opts); +// } as ItBenchFn; + +// function coerceToOptsObj( +// idOrOpts: string | PartialBy, "fn">, +// fn?: (arg: T) => void | Promise +// ): BenchmarkRunOptsWithFn { +// let opts: BenchmarkRunOptsWithFn; + +// if (typeof idOrOpts === "string") { +// if (!fn) throw Error("fn arg must be set"); +// opts = {id: idOrOpts, fn}; +// } else { +// if (fn) { +// opts = {...idOrOpts, fn}; +// } else { +// const optsWithFn = idOrOpts as BenchmarkRunOptsWithFn; +// if (!optsWithFn.fn) throw Error("opts.fn arg must be set"); +// opts = optsWithFn; +// } +// } + +// return opts; +// } + +// /** +// * Customize benchmark opts for a describe block. Affects only tests within that Mocha.Suite +// * ```ts +// * describe("suite A1", function () { +// * setBenchOpts({runs: 100}); +// * // 100 runs +// * itBench("bench A1.1", function() {}); +// * itBench("bench A1.2", function() {}); +// * // 300 runs +// * itBench({id: "bench A1.3", runs: 300}, function() {}); +// * +// * // Supports nesting, child has priority over parent. +// * // Arrow functions can be used, won't break it. +// * describe("suite A2", () => { +// * setBenchOpts({runs: 200}); +// * // 200 runs. +// * itBench("bench A2.1", () => {}); +// * }) +// * }) +// * ``` +// */ +// export function setBenchOpts(opts: BenchmarkOpts): void { +// before(function () { +// if (this.currentTest?.parent) { +// optsMap.set(this.currentTest?.parent, opts); +// } +// }); + +// after(function () { +// // Clean-up to allow garbage collection +// if (this.currentTest?.parent) { +// optsMap.delete(this.currentTest?.parent); +// } +// }); +// } + +// function getOptsFromParent(parent: object): BenchmarkOpts { +// const optsArr: BenchmarkOpts[] = []; +// getOptsFromSuite(parent, optsArr); +// // Merge opts, highest parent = lowest priority +// return Object.assign({}, ...optsArr.reverse()) as BenchmarkOpts; +// } + +// /** +// * Recursively append suite opts from child to parent. +// * +// * @returns `[suiteChildOpts, suiteParentOpts, suiteParentParentOpts]` +// */ +// function getOptsFromSuite(suite: object, optsArr: BenchmarkOpts[]): void { +// const suiteOpts = optsMap.get(suite); +// if (suiteOpts) { +// optsArr.push(suiteOpts); +// } + +// if (suite.parent) { +// getOptsFromSuite(suite.parent, optsArr); +// } +// } diff --git a/src/mochaPlugin/mochaRunner.ts b/src/mochaPlugin/mochaRunner.ts deleted file mode 100644 index 1a2b53c..0000000 --- a/src/mochaPlugin/mochaRunner.ts +++ /dev/null @@ -1,50 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import Mocha from "mocha"; -import {optsByRootSuite, resultsByRootSuite} from "./globalState.js"; -import {collectFiles, FileCollectionOptions} from "../utils/mochaCliExports.js"; -import {benchmarkReporterWithPrev} from "./reporter.js"; -import {Benchmark, BenchmarkOpts, BenchmarkResult, onlyBenchmarkOpts, Opts} from "../types.js"; - -export async function runMochaBenchmark( - opts: Opts & BenchmarkOpts, - prevBench: Benchmark | null -): Promise { - const mocha = new Mocha({ - // Pass all options to mocha, from upstream CLI - ...opts, - reporter: benchmarkReporterWithPrev(prevBench, opts.threshold), - // rootHooks: {beforeAll}, - }); - - // Register mocha root suite to append results on it() blocks - const results = new Map(); - resultsByRootSuite.set(mocha.suite, results); - optsByRootSuite.set(mocha.suite, onlyBenchmarkOpts(opts)); - - // Recreate `singleRun()` function - https://github.com/mochajs/mocha/blob/dcad90ad6e79864c871e2bc55b22c79ac6952991/lib/cli/run-helpers.js#L120 - const fileCollectParams: FileCollectionOptions = { - ignore: opts.ignore ?? [], - extension: opts.extension ?? [], - file: opts.file ?? [], - recursive: opts.recursive ?? false, - sort: opts.sort ?? false, - spec: opts.spec ?? [], - }; - const files = collectFiles(fileCollectParams); - mocha.files = files.files; - await mocha.loadFilesAsync(); - - // Run the tests. - await new Promise((resolve, reject) => { - mocha.run(function (failures) { - // process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures - if (failures > 0) { - reject(Error("Some tests failed")); - } else { - resolve(); - } - }); - }); - - return Array.from(results.values()); -} diff --git a/src/mochaPlugin/reporter.ts b/src/mochaPlugin/reporter.ts deleted file mode 100644 index f1771dd..0000000 --- a/src/mochaPlugin/reporter.ts +++ /dev/null @@ -1,102 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import Mocha from "mocha"; -import {Benchmark, BenchmarkResult} from "../types.js"; -import {resultsByRootSuite} from "./globalState.js"; -import {formatResultRow} from "./format.js"; -import {getRootSuite} from "./utils.js"; - -const { - EVENT_RUN_BEGIN, - EVENT_RUN_END, - EVENT_TEST_FAIL, - EVENT_TEST_PASS, - EVENT_TEST_PENDING, - EVENT_SUITE_BEGIN, - EVENT_SUITE_END, -} = Mocha.Runner.constants; - -interface ReporterConstructor { - new (runner: Mocha.Runner, options: Mocha.MochaOptions): Mocha.reporters.Base; -} - -export function benchmarkReporterWithPrev(prevBench: Benchmark | null, threshold: number): ReporterConstructor { - const prevResults = new Map(); - if (prevBench) { - for (const bench of prevBench.results) { - prevResults.set(bench.id, bench); - } - } - - return class BenchmarkReporter extends Mocha.reporters.Base { - constructor(runner: Mocha.Runner, options?: Mocha.MochaOptions) { - super(runner, options); - - // eslint-disable-next-line no-console - const consoleLog = console.log; - const color = Mocha.reporters.Base.color; - const symbols = Mocha.reporters.Base.symbols; - - let indents = 0; - let n = 0; - - function indent(): string { - return Array(indents).join(" "); - } - - runner.on(EVENT_RUN_BEGIN, function () { - consoleLog(); - }); - - runner.on(EVENT_SUITE_BEGIN, function (suite) { - ++indents; - consoleLog(color("suite", "%s%s"), indent(), suite.title); - }); - - runner.on(EVENT_SUITE_END, function () { - --indents; - if (indents === 1) { - consoleLog(); - } - }); - - runner.on(EVENT_TEST_PENDING, function (test) { - const fmt = indent() + color("pending", " - %s"); - consoleLog(fmt, test.title); - }); - - runner.on(EVENT_TEST_PASS, function (test) { - try { - if (!test.parent) throw Error("test has no parent"); - const rootSuite = getRootSuite(test.parent); - const results = resultsByRootSuite.get(rootSuite); - if (!results) throw Error("root suite not found"); - - const result = results.get(test.title); - if (result) { - // Render benchmark - const prevResult = prevResults.get(result.id) ?? null; - - const resultRow = formatResultRow(result, prevResult, result.threshold ?? threshold); - const fmt = indent() + color("checkmark", " " + symbols.ok) + " " + resultRow; - consoleLog(fmt); - } else { - // Render regular test - const fmt = indent() + color("checkmark", " " + symbols.ok) + color("pass", " %s"); - consoleLog(fmt, test.title); - } - } catch (e) { - // Log error manually since mocha doesn't log errors thrown here - consoleLog(e); - process.exitCode = 1; - throw e; - } - }); - - runner.on(EVENT_TEST_FAIL, function (test) { - consoleLog(indent() + color("fail", " %d) %s"), ++n, test.title); - }); - - runner.once(EVENT_RUN_END, this.epilogue.bind(this)); - } - }; -} diff --git a/src/mochaPlugin/utils.ts b/src/mochaPlugin/utils.ts index db7aa04..55cbaa7 100644 --- a/src/mochaPlugin/utils.ts +++ b/src/mochaPlugin/utils.ts @@ -1,3 +1,5 @@ +import {Suite} from "@vitest/runner"; + export function getParentSuite(ctx: Mocha.Context): Mocha.Suite { const test = ctx.currentTest ?? ctx.test; if (!test) throw Error("this.test not set"); @@ -5,27 +7,27 @@ export function getParentSuite(ctx: Mocha.Context): Mocha.Suite { return test.parent; } -export function getRootSuite(suite: Mocha.Suite): Mocha.Suite { - if (!suite.parent) return suite; - return getRootSuite(suite.parent); +export function getRootSuite(suite: Suite): Suite { + if (!suite.suite) return suite; + return getRootSuite(suite.suite); } -export function getAllTestsInRootSuite(ctx: Mocha.Context): Mocha.Test[] { - const parent = getParentSuite(ctx); - const rootSuite = getRootSuite(parent); +// export function getAllTestsInRootSuite(ctx: Mocha.Context): Mocha.Test[] { +// const parent = getParentSuite(ctx); +// const rootSuite = getRootSuite(parent); - const tests: Mocha.Test[] = []; +// const tests: Mocha.Test[] = []; - function getTests(suite: Mocha.Suite): void { - for (const test of suite.tests) { - tests.push(test); - } - for (const childSuite of suite.suites) { - getTests(childSuite); - } - } +// function getTests(suite: Mocha.Suite): void { +// for (const test of suite.tests) { +// tests.push(test); +// } +// for (const childSuite of suite.suites) { +// getTests(childSuite); +// } +// } - getTests(rootSuite); +// getTests(rootSuite); - return tests; -} +// return tests; +// } diff --git a/src/utils/output.ts b/src/utils/output.ts new file mode 100644 index 0000000..c953b71 --- /dev/null +++ b/src/utils/output.ts @@ -0,0 +1,42 @@ +import logSymbols from "log-symbols"; + +const colors = { + pass: 90, + fail: 31, + "bright pass": 92, + "bright fail": 91, + "bright yellow": 93, + pending: 36, + suite: 0, + "error title": 0, + "error message": 31, + "error stack": 90, + checkmark: 32, + fast: 90, + medium: 33, + slow: 31, + green: 32, + light: 90, + "diff gutter": 90, + "diff added": 32, + "diff removed": 31, + "diff added inline": "30;42", + "diff removed inline": "30;41", +}; + +export const symbols = { + ok: logSymbols.success, + err: logSymbols.error, + dot: ".", + comma: ",", + bang: "!", +}; + +export function color(type: keyof typeof colors, str: string): string { + return "\u001b[" + colors[type] + "m" + str + "\u001b[0m"; +} + +export function consoleLog(...args: unknown[]): void { + // eslint-disable-next-line no-console + console.log(...args); +} From 4981b7919689a356cd874fdf19d18faff179dd3f Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Tue, 3 Dec 2024 12:29:49 +0100 Subject: [PATCH 03/19] Move cli to a directory --- bin/index.cjs | 2 +- scripts/writeOptionsMd.ts | 2 +- src/{ => cli}/cli.ts | 6 +++--- src/{ => cli}/options.ts | 4 ++-- src/history/index.ts | 2 +- src/history/location.ts | 2 +- src/mochaPlugin/utils.ts | 20 -------------------- 7 files changed, 9 insertions(+), 29 deletions(-) rename src/{ => cli}/cli.ts (93%) rename src/{ => cli}/options.ts (97%) diff --git a/bin/index.cjs b/bin/index.cjs index 3f43cca..8b259be 100755 --- a/bin/index.cjs +++ b/bin/index.cjs @@ -1,3 +1,3 @@ #!/usr/bin/env node -require('../lib/cjs/cli.js'); +require("../lib/cjs/cli/cli.js"); diff --git a/scripts/writeOptionsMd.ts b/scripts/writeOptionsMd.ts index 48f80ee..3ec3d73 100644 --- a/scripts/writeOptionsMd.ts +++ b/scripts/writeOptionsMd.ts @@ -1,4 +1,4 @@ -import {options} from "../src/options.js"; +import {options} from "../src/cli/options.js"; const sections: string[] = []; diff --git a/src/cli.ts b/src/cli/cli.ts similarity index 93% rename from src/cli.ts rename to src/cli/cli.ts index c02649d..e3e7a05 100644 --- a/src/cli.ts +++ b/src/cli/cli.ts @@ -1,9 +1,9 @@ // Must not use `* as yargs`, see https://github.com/yargs/yargs/issues/1131 import yargs from "yargs"; -import {loadOptions, handleRequires} from "./utils/mochaCliExports.js"; +import {loadOptions, handleRequires} from "../utils/mochaCliExports.js"; import {options, optionsDefault} from "./options.js"; -import {run} from "./run.js"; -import {Opts} from "./types.js"; +import {run} from "../benchmark/run.js"; +import {Opts} from "../types.js"; /** * Common factory for running the CLI and running integration tests diff --git a/src/options.ts b/src/cli/options.ts similarity index 97% rename from src/options.ts rename to src/cli/options.ts index 8569a6b..f69a9d8 100644 --- a/src/options.ts +++ b/src/cli/options.ts @@ -1,6 +1,6 @@ import {Options} from "yargs"; -import {Opts, BenchmarkOpts} from "./types.js"; -import {FileCollectionOptions} from "./utils/mochaCliExports.js"; +import {Opts, BenchmarkOpts} from "../types.js"; +import {FileCollectionOptions} from "../utils/mochaCliExports.js"; export const optionsDefault = { threshold: 2, diff --git a/src/history/index.ts b/src/history/index.ts index cab1900..f973867 100644 --- a/src/history/index.ts +++ b/src/history/index.ts @@ -3,7 +3,7 @@ import {resolveHistoryLocation} from "./location.js"; import {LocalHistoryProvider} from "./local.js"; import {getGaCacheHistoryProvider} from "./gaCache.js"; import {IHistoryProvider} from "./provider.js"; -import {optionsDefault} from "../options.js"; +import {optionsDefault} from "../cli/options.js"; import {S3HistoryProvider} from "./s3.js"; export {resolveHistoryLocation}; diff --git a/src/history/location.ts b/src/history/location.ts index cef5c0a..1f2d7d4 100644 --- a/src/history/location.ts +++ b/src/history/location.ts @@ -1,6 +1,6 @@ import {isGaRun} from "../github/context.js"; import {Opts} from "../types.js"; -import {optionsDefault} from "../options.js"; +import {optionsDefault} from "../cli/options.js"; export type HistoryLocation = {type: "local"; path: string} | {type: "ga-cache"; key: string}; diff --git a/src/mochaPlugin/utils.ts b/src/mochaPlugin/utils.ts index 55cbaa7..69dd1d7 100644 --- a/src/mochaPlugin/utils.ts +++ b/src/mochaPlugin/utils.ts @@ -11,23 +11,3 @@ export function getRootSuite(suite: Suite): Suite { if (!suite.suite) return suite; return getRootSuite(suite.suite); } - -// export function getAllTestsInRootSuite(ctx: Mocha.Context): Mocha.Test[] { -// const parent = getParentSuite(ctx); -// const rootSuite = getRootSuite(parent); - -// const tests: Mocha.Test[] = []; - -// function getTests(suite: Mocha.Suite): void { -// for (const test of suite.tests) { -// tests.push(test); -// } -// for (const childSuite of suite.suites) { -// getTests(childSuite); -// } -// } - -// getTests(rootSuite); - -// return tests; -// } From 7fef1de91372bc6e0099901647862918d75ce496 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Tue, 3 Dec 2024 15:50:50 +0100 Subject: [PATCH 04/19] Update the cli to support file spec options --- package.json | 7 +- src/benchmark/benchmarkFn.ts | 7 +- src/benchmark/runner.ts | 1 + src/cli/cli.ts | 29 ++--- src/cli/options.ts | 89 +++++++++++----- src/{benchmark => cli}/run.ts | 29 +++-- src/compare/index.ts | 6 +- src/history/append.ts | 4 +- src/history/index.ts | 4 +- src/history/location.ts | 4 +- src/history/shouldPersist.ts | 4 +- src/index.ts | 6 +- src/mochaPlugin/index.ts | 193 --------------------------------- src/mochaPlugin/utils.ts | 13 --- src/types.ts | 14 ++- src/utils/defaultBranch.ts | 4 +- src/utils/file.ts | 37 +++++++ src/utils/mochaCliExports.ts | 44 -------- test/perf/iteration.test.ts | 4 +- yarn.lock | 194 +++++++++++++++++++++++++++------- 20 files changed, 327 insertions(+), 366 deletions(-) rename src/{benchmark => cli}/run.ts (72%) delete mode 100644 src/mochaPlugin/index.ts delete mode 100644 src/mochaPlugin/utils.ts delete mode 100644 src/utils/mochaCliExports.ts diff --git a/package.json b/package.json index d26cc3e..cc5becb 100644 --- a/package.json +++ b/package.json @@ -33,14 +33,14 @@ "test:unit": "mocha test/unit/**/*.test.ts", "lint": "eslint --color src/ test/", "prepublishOnly": "yarn build", - "benchmark": "node --loader ts-node/esm ./src/cli.ts 'test/perf/**/*.test.ts'", + "benchmark": "node --loader ts-node/esm ./src/cli/cli.ts 'test/perf/**/*.test.ts'", "writeDocs": "node --loader ts-node/esm scripts/writeOptionsMd.ts" }, "devDependencies": { "@types/chai": "^4.2.19", "@types/mocha": "^10.0.9", "@types/node": "^18.15.3", - "@types/rimraf": "^3.0.0", + "@types/rimraf": "^4.0.5", "@types/yargs": "^17.0.33", "chai": "^4.5.0", "dotenv": "^10.0.0", @@ -52,7 +52,7 @@ "eslint-config-prettier": "^9.1.0", "mocha": "^10.8.2", "prettier": "^3.4.0", - "rimraf": "^3.0.2", + "rimraf": "^6.0.1", "ts-node": "^10.9.2", "typescript": "^5.6.3", "typescript-eslint": "^8.16.0" @@ -65,6 +65,7 @@ "aws-sdk": "^2.932.0", "csv-parse": "^4.16.0", "csv-stringify": "^5.6.2", + "glob": "^11.0.0", "yargs": "^17.7.2", "log-symbols": "^7.0.0" } diff --git a/src/benchmark/benchmarkFn.ts b/src/benchmark/benchmarkFn.ts index dba09de..e56e0f0 100644 --- a/src/benchmark/benchmarkFn.ts +++ b/src/benchmark/benchmarkFn.ts @@ -32,14 +32,15 @@ export function getRootSuite(suite: Suite | SuiteCollector): Suite { } export const bench = createBenchmarkFunction(function ( + this: Record<"skip" | "only", boolean | undefined>, idOrOpts: string | PartialBy, "fn">, fn?: (arg: T) => void | Promise ) { const {fn: benchTask, ...opts} = coerceToOptsObj(idOrOpts, fn); const task = getCurrentSuite().task(opts.id, { - skip: opts.skip, - only: opts.only, + skip: opts.skip ?? this.skip, + only: opts.only ?? this.only, sequential: true, concurrent: false, meta: { @@ -56,7 +57,7 @@ export const bench = createBenchmarkFunction(function ( const fullOptions = Object.assign({}, rootOpts, parentOpts, opts); // Ensure bench id is unique - if (store.getResult(opts.id)) { + if (store.getResult(opts.id) && !opts.skip) { throw Error(`test titles must be unique, duplicated: '${opts.id}'`); } diff --git a/src/benchmark/runner.ts b/src/benchmark/runner.ts index ff5c444..26a0269 100644 --- a/src/benchmark/runner.ts +++ b/src/benchmark/runner.ts @@ -3,6 +3,7 @@ import { startTests, Suite, Task, + TaskResultPack, VitestRunner, VitestRunnerConfig, VitestRunnerImportSource, diff --git a/src/cli/cli.ts b/src/cli/cli.ts index e3e7a05..7c9e327 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -1,32 +1,20 @@ // Must not use `* as yargs`, see https://github.com/yargs/yargs/issues/1131 import yargs from "yargs"; -import {loadOptions, handleRequires} from "../utils/mochaCliExports.js"; -import {options, optionsDefault} from "./options.js"; -import {run} from "../benchmark/run.js"; -import {Opts} from "../types.js"; +import {hideBin} from "yargs/helpers"; -/** - * Common factory for running the CLI and running integration tests - * The CLI must actually be executed in a different script - */ -const argv = process.argv.slice(2); +import {benchmarkOptions, CLIOptions, fileCollectionOptions, storageOptions} from "./options.js"; +import {run} from "./run.js"; -const args = loadOptions(argv); - -void yargs() +void yargs(hideBin(process.argv)) .env("BENCHMARK") .scriptName("benchmark") .command({ command: ["$0 [spec..]", "inspect"], describe: "Run benchmarks", handler: async (argv) => { - // Copied from mocha source to load ts-node properly. - // It's on the CLI middleware of mocha so it does not get run when calling mocha programatically - // https://github.com/mochajs/mocha/blob/014e47a8b07809e73b1598c7abeafe7a3b57a8f7/lib/cli/run.js#L353 - const plugins = await handleRequires(argv.require as string[]); - Object.assign(argv, plugins); + const cliOpts = {...argv} as unknown as CLIOptions & {spec: string[]}; - await run({threshold: optionsDefault.threshold, ...argv} as Opts); + await run(cliOpts); }, }) @@ -39,7 +27,7 @@ void yargs() "short-option-groups": false, "strip-aliased": true, }) - .options(options) + .options({...fileCollectionOptions, ...storageOptions, ...benchmarkOptions}) .usage( `Benchmark runner to track performance. @@ -67,5 +55,4 @@ void yargs() console.error(` ✖ ${errorMessage}\n`); process.exit(1); }) - .config(args) - .parse(args._); + .parse(); diff --git a/src/cli/options.ts b/src/cli/options.ts index f69a9d8..e16152f 100644 --- a/src/cli/options.ts +++ b/src/cli/options.ts @@ -1,6 +1,5 @@ import {Options} from "yargs"; -import {Opts, BenchmarkOpts} from "../types.js"; -import {FileCollectionOptions} from "../utils/mochaCliExports.js"; +import {StorageOptions, BenchmarkOpts, FileCollectionOptions} from "../types.js"; export const optionsDefault = { threshold: 2, @@ -8,57 +7,88 @@ export const optionsDefault = { historyCacheKey: "benchmark_data", }; +type CLIFileCollectionOptions = Omit; +type CLIStorageOptions = StorageOptions; +type CLIBenchmarkOptions = Omit; type ICliCommandOptions = Required<{[key in keyof OwnArgs]: Options}>; -type CliOpts = Omit & - Omit; -export const options: ICliCommandOptions = { +export type CLIOptions = CLIFileCollectionOptions & CLIStorageOptions & CLIBenchmarkOptions; + +const fileGroup = "Files options:"; +const storageGroup = "Storage options:"; +const benchmarkGroup = "Benchmark options:"; + +export const fileCollectionOptions: ICliCommandOptions = { + extension: { + description: "File extension(s) to load", + type: "array", + alias: "ext", + default: ["js", "cjs", "mjs"], + group: fileGroup, + }, + ignore: { + description: "Ignore file(s) or glob pattern(s)", + type: "array", + alias: "exclude", + group: fileGroup, + }, + recursive: { + description: "Look for tests in subdirectories", + type: "boolean", + default: false, + group: fileGroup, + }, +}; + +export const storageOptions: ICliCommandOptions = { defaultBranch: { description: "Provide the default branch of this repository to prevent fetching from Github", type: "string", - group: "Options:", + group: storageGroup, }, persistBranches: { description: "Choose what branches to persist benchmark data", type: "array", defaultDescription: "default-branch", + group: storageGroup, }, benchmarksPerBranch: { description: "Limit number of benchmarks persisted per branch", type: "number", defaultDescription: "Infinity", - }, - threshold: { - description: - "Ratio of new average time per run vs previos time per run to consider a failure. Set to 'Infinity' to disable it.", - type: "number", - default: optionsDefault.threshold, + group: storageGroup, }, compareBranch: { description: "Compare new benchmark data against the latest available benchmark in this branch", type: "string", defaultDescription: "default-branch", + group: storageGroup, }, compareCommit: { description: "Compare new benchmark data against the benchmark data associated with a specific commit", type: "string", + group: storageGroup, }, prune: { description: "When persisting history, delete benchmark data associated with commits that are no longer in the current git history", type: "boolean", + group: storageGroup, }, persist: { description: "Force persisting benchmark data in history", type: "boolean", + group: storageGroup, }, noThrow: { description: "Exit cleanly even if a preformance regression was found", type: "boolean", + group: storageGroup, }, skipPostComment: { description: "Skip post Github comment step if run on Github CI", type: "boolean", + group: storageGroup, }, historyLocal: { alias: ["local"], @@ -66,6 +96,7 @@ export const options: ICliCommandOptions = { "Persist benchmark history locally. May specify just a boolean to use a default path, or provide a path", type: "string", defaultDescription: optionsDefault.historyLocalPath, + group: storageGroup, }, historyGaCache: { alias: ["ga-cache"], @@ -73,67 +104,77 @@ export const options: ICliCommandOptions = { "Persist benchmark history in Github Actions cache. Requires Github authentication. May specify just a boolean to use a default cache key or provide a custom key", type: "string", defaultDescription: optionsDefault.historyCacheKey, + group: storageGroup, }, historyS3: { alias: ["s3"], description: "Persist benchmark history in an Amazon S3 bucket. Requires Github authentication", type: "string", + group: storageGroup, }, +}; - // BenchmarkOpts - +// BenchmarkOpts +export const benchmarkOptions: ICliCommandOptions = { + threshold: { + description: + "Ratio of new average time per run vs previos time per run to consider a failure. Set to 'Infinity' to disable it.", + type: "number", + default: optionsDefault.threshold, + group: benchmarkGroup, + }, maxRuns: { type: "number", description: "Max number of fn() runs, after which the benchmark stops", - group: "itBench() options", + group: benchmarkGroup, }, minRuns: { type: "number", description: "Min number of fn() runs before considering stopping the benchmark after converging", - group: "itBench() options", + group: benchmarkGroup, }, maxMs: { type: "number", description: "Max total miliseconds of runs, after which the benchmark stops", - group: "itBench() options", + group: benchmarkGroup, }, minMs: { type: "number", description: "Min total miiliseconds of runs before considering stopping the benchmark after converging", - group: "itBench() options", + group: benchmarkGroup, }, maxWarmUpMs: { type: "number", description: "Maximum real benchmark function run time before starting to count towards results. Set to 0 to not warm-up. May warm up for less ms if the `maxWarmUpRuns` condition is met first.", - group: "itBench() options", + group: benchmarkGroup, }, maxWarmUpRuns: { type: "number", description: "Maximum benchmark function runs before starting to count towards results. Set to 0 to not warm-up. May warm up for less ms if the `maxWarmUpMs` condition is met first.", - group: "itBench() options", + group: benchmarkGroup, }, convergeFactor: { type: "number", description: "Convergance factor (0,1) at which the benchmark automatically stops. Set to 1 to disable", - group: "itBench() options", + group: benchmarkGroup, }, runsFactor: { type: "number", description: "If fn() contains a foor loop repeating a task N times, you may set runsFactor = N to scale down the results.", - group: "itBench() options", + group: benchmarkGroup, }, yieldEventLoopAfterEach: { type: "boolean", description: "Run `sleep(0)` after each fn() call. Use when the event loop needs to tick to free resources created by fn()", - group: "itBench() options", + group: benchmarkGroup, }, timeoutBench: { type: "number", description: "Hard timeout, enforced by mocha.", - group: "itBench() options", + group: benchmarkGroup, }, }; diff --git a/src/benchmark/run.ts b/src/cli/run.ts similarity index 72% rename from src/benchmark/run.ts rename to src/cli/run.ts index ec0e1b5..3175a3c 100644 --- a/src/benchmark/run.ts +++ b/src/cli/run.ts @@ -2,17 +2,20 @@ import * as github from "@actions/github"; import {getHistoryProvider} from "../history/index.js"; import {resolveShouldPersist} from "../history/shouldPersist.js"; import {validateBenchmark} from "../history/schema.js"; -import {Benchmark, BenchmarkOpts, BenchmarkResult, BenchmarkResults, Opts} from "../types.js"; +import {Benchmark, BenchmarkOpts, FileCollectionOptions, StorageOptions} from "../types.js"; import {renderCompareWith, resolveCompareWith, resolvePrevBenchmark} from "../compare/index.js"; -import {parseBranchFromRef, getCurrentCommitInfo, shell, getCurrentBranch} from "../utils/index.js"; +import {parseBranchFromRef, getCurrentCommitInfo, shell, getCurrentBranch, collectFiles} from "../utils/index.js"; import {computeBenchComparision} from "../compare/compute.js"; import {postGaComment} from "../github/comment.js"; import {isGaRun} from "../github/context.js"; -import {BenchmarkRunner} from "./runner.js"; +import {BenchmarkRunner} from "../benchmark/runner.js"; +import {optionsDefault} from "./options.js"; /* eslint-disable no-console */ -export async function run(opts: Opts & BenchmarkOpts): Promise { +export async function run(opts_: FileCollectionOptions & StorageOptions & BenchmarkOpts): Promise { + const opts = Object.assign({}, optionsDefault, opts_); + // Sanitize opts if (isNaN(opts.threshold)) throw Error("opts.threshold is not a number"); @@ -27,11 +30,25 @@ export async function run(opts: Opts & BenchmarkOpts): Promise { console.log(`Found previous benchmark for ${renderCompareWith(compareWith)}, at commit ${prevBench.commitSha}`); validateBenchmark(prevBench); } else { - console.log(`No previous bencharmk found for ${renderCompareWith(compareWith)}`); + console.log(`No previous benchmark found for ${renderCompareWith(compareWith)}`); + } + + const {files, unmatchedFiles} = await collectFiles(opts).catch((err) => { + console.log("Error loading up spec patterns"); + throw err; + }); + + if (unmatchedFiles.length > 0) { + console.log(`Found unmatched files: \n${unmatchedFiles.join("\n")}\n`); + } + + if (files.length === 0) { + console.log(`Can not find any matching spec file for ${opts.spec.join(",")}\n`); + process.exit(1); } const runner = new BenchmarkRunner({prevBench}); - const results = await runner.process(["/Users/nazar/Hub/Lodestar/Projects/benchmark/test/perf/iteration.test.ts"]); + const results = await runner.process(files); if (results.length === 0) { throw Error("No benchmark result was produced"); diff --git a/src/compare/index.ts b/src/compare/index.ts index c9615ed..2fd0d6a 100644 --- a/src/compare/index.ts +++ b/src/compare/index.ts @@ -1,5 +1,5 @@ import * as github from "@actions/github"; -import {Benchmark, Opts} from "../types.js"; +import {Benchmark, StorageOptions} from "../types.js"; import {getGithubEventData, GithubActionsEventData, parseBranchFromRef, getDefaultBranch} from "../utils/index.js"; import {isGaRun} from "../github/context.js"; import {IHistoryProvider} from "../history/provider.js"; @@ -14,7 +14,7 @@ export type CompareWith = | {type: CompareWithType.latestCommitInBranch; branch: string; before?: string} | {type: CompareWithType.exactCommit; commitSha: string}; -export async function resolveCompare(provider: IHistoryProvider, opts: Opts): Promise { +export async function resolveCompare(provider: IHistoryProvider, opts: StorageOptions): Promise { const compareWith = await resolveCompareWith(opts); const prevBench = await resolvePrevBenchmark(compareWith, provider); if (!prevBench) return null; @@ -54,7 +54,7 @@ export function renderCompareWith(compareWith: CompareWith): string { } } -export async function resolveCompareWith(opts: Opts): Promise { +export async function resolveCompareWith(opts: StorageOptions): Promise { // compare may be a branch or commit if (opts.compareBranch) { return {type: CompareWithType.latestCommitInBranch, branch: opts.compareBranch}; diff --git a/src/history/append.ts b/src/history/append.ts index 6d6d571..6234c27 100644 --- a/src/history/append.ts +++ b/src/history/append.ts @@ -1,10 +1,10 @@ -import {Benchmark, BenchmarkHistory, Opts} from "../types.js"; +import {Benchmark, BenchmarkHistory, StorageOptions} from "../types.js"; export function appendBenchmarkToHistoryAndPrune( history: BenchmarkHistory, newBench: Benchmark, branch: string, - opts: Opts + opts: StorageOptions ): void { if (opts.benchmarksPerBranch) { limitBenchmarksPerBranch(history, opts.benchmarksPerBranch); diff --git a/src/history/index.ts b/src/history/index.ts index f973867..2f8db67 100644 --- a/src/history/index.ts +++ b/src/history/index.ts @@ -1,4 +1,4 @@ -import {Opts} from "../types.js"; +import {StorageOptions} from "../types.js"; import {resolveHistoryLocation} from "./location.js"; import {LocalHistoryProvider} from "./local.js"; import {getGaCacheHistoryProvider} from "./gaCache.js"; @@ -7,7 +7,7 @@ import {optionsDefault} from "../cli/options.js"; import {S3HistoryProvider} from "./s3.js"; export {resolveHistoryLocation}; -export function getHistoryProvider(opts: Opts): IHistoryProvider { +export function getHistoryProvider(opts: StorageOptions): IHistoryProvider { if (opts.historyGaCache) { const cacheKey = typeof opts.historyGaCache === "string" ? opts.historyGaCache : optionsDefault.historyCacheKey; return getGaCacheHistoryProvider(cacheKey); diff --git a/src/history/location.ts b/src/history/location.ts index 1f2d7d4..e064130 100644 --- a/src/history/location.ts +++ b/src/history/location.ts @@ -1,10 +1,10 @@ import {isGaRun} from "../github/context.js"; -import {Opts} from "../types.js"; +import {StorageOptions} from "../types.js"; import {optionsDefault} from "../cli/options.js"; export type HistoryLocation = {type: "local"; path: string} | {type: "ga-cache"; key: string}; -export function resolveHistoryLocation(opts: Opts): HistoryLocation { +export function resolveHistoryLocation(opts: StorageOptions): HistoryLocation { if (opts.historyLocal && opts.historyGaCache) { throw Error("Must not set 'historyLocal' and 'historyGaCache'"); } diff --git a/src/history/shouldPersist.ts b/src/history/shouldPersist.ts index b4a8507..280c333 100644 --- a/src/history/shouldPersist.ts +++ b/src/history/shouldPersist.ts @@ -1,7 +1,7 @@ -import {Opts} from "../types.js"; +import {StorageOptions} from "../types.js"; import {getDefaultBranch} from "../utils/defaultBranch.js"; -export async function resolveShouldPersist(opts: Opts, branch: string): Promise { +export async function resolveShouldPersist(opts: StorageOptions, branch: string): Promise { // Force persist if (opts.persist === true) return true; // Do not persist diff --git a/src/index.ts b/src/index.ts index bcd26c4..f32b255 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,5 @@ -export * from "./benchmark/index.js"; +import {suite, test} from "@vitest/runner"; + +export {bench, itBench} from "./benchmark/index.js"; +export const describe = suite; +export const it = test; diff --git a/src/mochaPlugin/index.ts b/src/mochaPlugin/index.ts deleted file mode 100644 index 84cc875..0000000 --- a/src/mochaPlugin/index.ts +++ /dev/null @@ -1,193 +0,0 @@ -// // import fs from "node:fs"; -// // import path from "node:path"; -// import {BenchmarkOpts} from "../types.js"; -// // import {optsByRootSuite, optsMap, resultsByRootSuite} from "./globalState.js"; -// import {BenchmarkRunOptsWithFn, runBenchFn} from "../benchmark/runBenchFn.js"; -// // import {getRootSuite, getParentSuite} from "./utils.js"; -// import {test} from "@vitest/runner"; - -// type PartialBy = Omit & Partial>; - -// const itBenchFn: ItBenchFn = function itBench( -// this: object, -// idOrOpts: string | PartialBy, "fn">, -// fn?: (arg: T) => void | Promise -// ): void { -// // TODO: -// // Implement reporter -// // Implement grouping functionality - -// // if (this.averageNs === null) this.averageNs = result.averageNs; -// // result.factor = result.averageNs / this.averageNs; - -// console.trace(idOrOpts); - -// const opts = coerceToOptsObj(idOrOpts, fn); - -// // Apply mocha it opts -// const itFn = opts.only ? test : opts.skip ? test.skip : test; - -// // console.trace(itFn, opts.id); - -// itFn(opts.id, async () => { -// // console.log("%%%%%%%%%%%%%%%%%%", opts.id, getCurrentSuite()); -// // const parent = getCurrentSuite(); -// // const optsParent = getOptsFromParent(parent); - -// // Get results array from root suite -// // const rootSuite = getRootSuite(parent.); -// // const rootSuite = parent; -// // console.log({rootSuite}); -// // const results = resultsByRootSuite.get(rootSuite); -// // const rootOpts = optsByRootSuite.get(rootSuite); -// // if (!results || !rootOpts) throw Error("root suite not found"); - -// // opts = Object.assign({}, rootOpts, optsParent, opts); - -// // // Ensure bench id is unique -// // if (results.has(opts.id)) { -// // throw Error(`test titles must be unique, duplicated: '${opts.id}'`); -// // } - -// // // Extend timeout if maxMs is set -// // // if (opts.timeoutBench !== undefined) { -// // // this.timeout(opts.timeoutBench); -// // // } else { -// // // const timeout = this.timeout(); -// // // if (opts.maxMs && opts.maxMs > timeout) { -// // // this.timeout(opts.maxMs * 1.5); -// // // } else if (opts.minMs && opts.minMs > timeout) { -// // // this.timeout(opts.minMs * 1.5); -// // // } -// // // } - -// // // Persist full results if requested. dir is created in `beforeAll` -// // const benchmarkResultsCsvDir = process.env.BENCHMARK_RESULTS_CSV_DIR; -// // const persistRunsNs = Boolean(benchmarkResultsCsvDir); - -// // console.log({opts, persistRunsNs}); -// // const {result, runsNs} = await runBenchFn(opts, persistRunsNs); -// await runBenchFn(opts, false); - -// // // Store result for: -// // // - to persist benchmark data latter -// // // - to render with the custom reporter -// // results.set(opts.id, result); - -// // if (benchmarkResultsCsvDir) { -// // fs.mkdirSync(benchmarkResultsCsvDir, {recursive: true}); -// // const filename = `${result.id}.csv`; -// // const filepath = path.join(benchmarkResultsCsvDir, filename); -// // fs.writeFileSync(filepath, runsNs.join("\n")); -// // } -// }); -// }; - -// interface ItBenchFn { -// (opts: BenchmarkRunOptsWithFn): void; -// (idOrOpts: string | Omit, "fn">, fn: (arg: T) => void): void; -// ( -// idOrOpts: string | PartialBy, "fn">, -// fn?: (arg: T) => void | Promise -// ): void; -// } - -// interface ItBench extends ItBenchFn { -// only: ItBenchFn; -// skip: ItBenchFn; -// } - -// export const itBench = itBenchFn as ItBench; - -// itBench.only = function itBench(idOrOpts, fn): void { -// const opts = coerceToOptsObj(idOrOpts, fn); -// opts.only = true; -// itBenchFn(opts); -// } as ItBenchFn; - -// itBench.skip = function itBench(idOrOpts, fn): void { -// const opts = coerceToOptsObj(idOrOpts, fn); -// opts.skip = true; -// itBenchFn(opts); -// } as ItBenchFn; - -// function coerceToOptsObj( -// idOrOpts: string | PartialBy, "fn">, -// fn?: (arg: T) => void | Promise -// ): BenchmarkRunOptsWithFn { -// let opts: BenchmarkRunOptsWithFn; - -// if (typeof idOrOpts === "string") { -// if (!fn) throw Error("fn arg must be set"); -// opts = {id: idOrOpts, fn}; -// } else { -// if (fn) { -// opts = {...idOrOpts, fn}; -// } else { -// const optsWithFn = idOrOpts as BenchmarkRunOptsWithFn; -// if (!optsWithFn.fn) throw Error("opts.fn arg must be set"); -// opts = optsWithFn; -// } -// } - -// return opts; -// } - -// /** -// * Customize benchmark opts for a describe block. Affects only tests within that Mocha.Suite -// * ```ts -// * describe("suite A1", function () { -// * setBenchOpts({runs: 100}); -// * // 100 runs -// * itBench("bench A1.1", function() {}); -// * itBench("bench A1.2", function() {}); -// * // 300 runs -// * itBench({id: "bench A1.3", runs: 300}, function() {}); -// * -// * // Supports nesting, child has priority over parent. -// * // Arrow functions can be used, won't break it. -// * describe("suite A2", () => { -// * setBenchOpts({runs: 200}); -// * // 200 runs. -// * itBench("bench A2.1", () => {}); -// * }) -// * }) -// * ``` -// */ -// export function setBenchOpts(opts: BenchmarkOpts): void { -// before(function () { -// if (this.currentTest?.parent) { -// optsMap.set(this.currentTest?.parent, opts); -// } -// }); - -// after(function () { -// // Clean-up to allow garbage collection -// if (this.currentTest?.parent) { -// optsMap.delete(this.currentTest?.parent); -// } -// }); -// } - -// function getOptsFromParent(parent: object): BenchmarkOpts { -// const optsArr: BenchmarkOpts[] = []; -// getOptsFromSuite(parent, optsArr); -// // Merge opts, highest parent = lowest priority -// return Object.assign({}, ...optsArr.reverse()) as BenchmarkOpts; -// } - -// /** -// * Recursively append suite opts from child to parent. -// * -// * @returns `[suiteChildOpts, suiteParentOpts, suiteParentParentOpts]` -// */ -// function getOptsFromSuite(suite: object, optsArr: BenchmarkOpts[]): void { -// const suiteOpts = optsMap.get(suite); -// if (suiteOpts) { -// optsArr.push(suiteOpts); -// } - -// if (suite.parent) { -// getOptsFromSuite(suite.parent, optsArr); -// } -// } diff --git a/src/mochaPlugin/utils.ts b/src/mochaPlugin/utils.ts deleted file mode 100644 index 69dd1d7..0000000 --- a/src/mochaPlugin/utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {Suite} from "@vitest/runner"; - -export function getParentSuite(ctx: Mocha.Context): Mocha.Suite { - const test = ctx.currentTest ?? ctx.test; - if (!test) throw Error("this.test not set"); - if (!test.parent) throw Error("this.test.parent not set"); - return test.parent; -} - -export function getRootSuite(suite: Suite): Suite { - if (!suite.suite) return suite; - return getRootSuite(suite.suite); -} diff --git a/src/types.ts b/src/types.ts index f3f7cd4..f5a10f8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,18 @@ -import {FileCollectionOptions} from "./utils/mochaCliExports.js"; +export interface FileCollectionOptions { + /** File extensions to use */ + extension: string[]; + /** Files, dirs, globs to ignore */ + ignore: string[]; + /** Find files recursively */ + recursive: boolean; + /** Glob pattern to load spec */ + spec: string[]; +} -export type Opts = Partial & { +export type StorageOptions = { defaultBranch?: string; persistBranches?: string[]; benchmarksPerBranch?: number; - threshold: number; compareBranch?: string; compareCommit?: string; prune?: boolean; diff --git a/src/utils/defaultBranch.ts b/src/utils/defaultBranch.ts index fbae6cf..20c0fd0 100644 --- a/src/utils/defaultBranch.ts +++ b/src/utils/defaultBranch.ts @@ -1,6 +1,6 @@ import {isGaRun} from "../github/context.js"; import {getGithubDefaultBranch} from "../github/octokit.js"; -import {Opts} from "../types.js"; +import {StorageOptions} from "../types.js"; import {shell} from "./shell.js"; let defaultBranch: string | null = null; @@ -8,7 +8,7 @@ let defaultBranch: string | null = null; /** * Return a cached value of a best guess of the repo's default branch */ -export async function getDefaultBranch(opts?: Pick): Promise { +export async function getDefaultBranch(opts?: Pick): Promise { if (opts?.defaultBranch) { return opts.defaultBranch; } diff --git a/src/utils/file.ts b/src/utils/file.ts index 91ce55d..1c46267 100644 --- a/src/utils/file.ts +++ b/src/utils/file.ts @@ -1,6 +1,9 @@ import fs from "node:fs"; +import path from "node:path"; +import {glob} from "glob"; import csvParse from "csv-parse/lib/sync.js"; import csvStringify from "csv-stringify/lib/sync.js"; +import {FileCollectionOptions} from "../types.js"; type CsvMetadata = Record; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -72,3 +75,37 @@ function splitCsvMetadata(str: string): {csv: string; metadata: Record { + const matchedFiles = new Set(); + const unmatchedFiles: string[] = []; + + // Normalize extensions to ensure leading dots + const normalizedExtensions = extension.map((ext) => (ext.startsWith(".") ? ext : `.${ext}`)); + + for (const pattern of spec) { + const files = await glob(pattern, { + ignore, + nodir: true, + cwd: process.cwd(), + absolute: true, + follow: recursive, + }); + + for (const file of files) { + if (normalizedExtensions.includes(path.extname(file))) { + matchedFiles.add(file); + } else { + unmatchedFiles.push(file); + } + } + } + + return { + files: Array.from(matchedFiles), + unmatchedFiles, + }; +} diff --git a/src/utils/mochaCliExports.ts b/src/utils/mochaCliExports.ts deleted file mode 100644 index cb6fb6d..0000000 --- a/src/utils/mochaCliExports.ts +++ /dev/null @@ -1,44 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -// eslint-disable-next-line import/no-extraneous-dependencies -import {lookupFiles as lookupFilesMocha, loadOptions as loadOptionsMocha} from "mocha/lib/cli/index.js"; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -// eslint-disable-next-line import/no-extraneous-dependencies -import collectFilesMocha from "mocha/lib/cli/collect-files.js"; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -// eslint-disable-next-line import/no-extraneous-dependencies -import {handleRequires as handleRequiresMocha} from "mocha/lib/cli/run-helpers.js"; - -export const lookupFiles = lookupFilesMocha as ( - filepath: string, - extensions?: string[], - recursive?: boolean -) => string[]; - -export const loadOptions = loadOptionsMocha as (argv: string[]) => Record & {_: string[]}; - -export interface FileCollectionOptions { - /** File extensions to use */ - extension: string[]; - /** Files, dirs, globs to run */ - spec: string[]; - /** Files, dirs, globs to ignore */ - ignore: string[]; - /** List of additional files to include */ - file: string[]; - /** Find files recursively */ - recursive: boolean; - /** Sort test files */ - sort: boolean; -} - -export interface FileCollectionResponse { - files: string[]; - unmatchedFiles: string[]; -} - -export const collectFiles = collectFilesMocha as (fileCollectParams: FileCollectionOptions) => FileCollectionResponse; - -export const handleRequires = handleRequiresMocha as (requires?: string[]) => Promise; diff --git a/test/perf/iteration.test.ts b/test/perf/iteration.test.ts index 135eaeb..0caa63c 100644 --- a/test/perf/iteration.test.ts +++ b/test/perf/iteration.test.ts @@ -1,5 +1,5 @@ import assert from "node:assert"; -import {itBench, setBenchOpts} from "../../src/index.js"; +import {itBench, describe, it} from "../../src/index.js"; // As of Jun 17 2021 // Compare state root @@ -9,7 +9,7 @@ import {itBench, setBenchOpts} from "../../src/index.js"; // byteArrayEquals with valueOf() 853971.0 ops/s 1.171000 us/op 9963051 runs 16.07 s describe("Array iteration", () => { - setBenchOpts({maxMs: 60 * 1000, convergeFactor: 0.1 / 100}); + // setBenchOpts({maxMs: 60 * 1000, convergeFactor: 0.1 / 100}); it("Regular test", () => { assert.strictEqual(1 + 2, 3); diff --git a/yarn.lock b/yarn.lock index ad5a994..fd045e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -256,6 +256,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@jridgewell/resolve-uri@^3.0.3": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" @@ -436,14 +448,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== -"@types/glob@*": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" - integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -454,11 +458,6 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/minimatch@*": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" - integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== - "@types/mocha@^10.0.9": version "10.0.9" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.9.tgz#101e9da88d2c02e5ac8952982c23b224524d662a" @@ -484,13 +483,12 @@ dependencies: undici-types "~5.26.4" -"@types/rimraf@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.0.tgz#b9d03f090ece263671898d57bb7bb007023ac19f" - integrity sha512-7WhJ0MdpFgYQPXlF4Dx+DhgvlPCfz/x5mHaeDQAKhcenvQP1KCpLQ18JklAqeGMYSAT2PxLpzd0g2/HE7fj7hQ== +"@types/rimraf@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-4.0.5.tgz#7a59be11605c22ea3959c21ff8b28b9df1bae1b2" + integrity sha512-DTCZoIQotB2SUJnYgrEx43cQIUYOlNZz0AZPbKU4PSLYTUdML5Gox0++z4F9kQocxStrCmRNhi4x5x/UlwtKUA== dependencies: - "@types/glob" "*" - "@types/node" "*" + rimraf "*" "@types/tunnel@^0.0.1": version "0.0.1" @@ -675,6 +673,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -682,6 +685,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -980,7 +988,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.5: +cross-spawn@^7.0.0, cross-spawn@^7.0.5: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -1114,11 +1122,21 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + enhanced-resolve@^5.15.0: version "5.17.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" @@ -1499,6 +1517,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -1612,17 +1638,17 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.3: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== +glob@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" + integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" glob@^8.1.0: version "8.1.0" @@ -2014,6 +2040,13 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +jackspeak@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" + integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== + dependencies: + "@isaacs/cliui" "^8.0.2" + jmespath@0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" @@ -2108,6 +2141,11 @@ loupe@^3.1.2: resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== +lru-cache@^11.0.0: + version "11.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" + integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -2138,6 +2176,13 @@ mime-types@^2.1.12: dependencies: mime-db "1.48.0" +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -2176,6 +2221,11 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mocha@^10.8.2: version "10.8.2" resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.8.2.tgz#8d8342d016ed411b12a429eb731b825f961afb96" @@ -2308,6 +2358,11 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -2320,11 +2375,6 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -2335,6 +2385,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" @@ -2478,12 +2536,13 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== +rimraf@*, rimraf@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-6.0.1.tgz#ffb8ad8844dd60332ab15f52bc104bc3ed71ea4e" + integrity sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A== dependencies: - glob "^7.1.3" + glob "^11.0.0" + package-json-from-dist "^1.0.0" run-parallel@^1.1.9: version "1.2.0" @@ -2592,6 +2651,20 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" @@ -2610,6 +2683,15 @@ string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -2638,6 +2720,13 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -2652,6 +2741,13 @@ strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -2977,6 +3073,15 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -2986,6 +3091,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" From 166e025112498a2474fc33cc4aa510701d05e389 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 4 Dec 2024 15:08:13 +0100 Subject: [PATCH 05/19] Add support for custom options --- src/benchmark/benchmarkFn.ts | 75 ++++++++++++------- src/benchmark/globalState.ts | 19 +++-- src/benchmark/reporter.ts | 7 +- .../{runBenchFn.ts => runBenchmarkFn.ts} | 0 src/benchmark/runner.ts | 29 +++++-- src/cli/options.ts | 4 +- src/cli/run.ts | 19 +++-- src/index.ts | 2 +- test/perf/iteration.test.ts | 4 +- 9 files changed, 100 insertions(+), 59 deletions(-) rename src/benchmark/{runBenchFn.ts => runBenchmarkFn.ts} (100%) diff --git a/src/benchmark/benchmarkFn.ts b/src/benchmark/benchmarkFn.ts index e56e0f0..42e6d79 100644 --- a/src/benchmark/benchmarkFn.ts +++ b/src/benchmark/benchmarkFn.ts @@ -4,7 +4,8 @@ import {getCurrentSuite, Suite, SuiteCollector} from "@vitest/runner"; import {createChainable} from "@vitest/runner/utils"; import {store} from "./globalState.js"; import {BenchmarkOpts} from "../types.js"; -import {runBenchFn} from "./runBenchFn.js"; +import {runBenchFn} from "./runBenchmarkFn.js"; +import {optionsDefault} from "../cli/options.js"; export type BenchmarkRunOptsWithFn = BenchmarkOpts & { id: string; @@ -37,47 +38,42 @@ export const bench = createBenchmarkFunction(function ( fn?: (arg: T) => void | Promise ) { const {fn: benchTask, ...opts} = coerceToOptsObj(idOrOpts, fn); + const currentSuite = getCurrentSuite(); - const task = getCurrentSuite().task(opts.id, { + const globalOptions = store.getGlobalOptions() ?? {}; + const parentOptions = store.getOptions(getCurrentSuite()) ?? {}; + const options = {...globalOptions, ...parentOptions, ...opts}; + const {timeoutBench, maxMs, minMs} = options; + + let timeout = timeoutBench ?? optionsDefault.timeoutBench; + if (maxMs && maxMs > timeout) { + timeout = maxMs * 1.5; + } + + if (minMs && minMs > timeout) { + timeout = minMs * 1.5; + } + + const task = currentSuite.task(opts.id, { skip: opts.skip ?? this.skip, only: opts.only ?? this.only, sequential: true, concurrent: false, + timeout, meta: { "dapplion/benchmark": true, }, - async handler(context) { - const parentSuite = context.task.suite; - const parentOpts = parentSuite ? store.getOptions(parentSuite) : {}; - - // TODO: Find better way to point to root suite - const rootSuite = context.task.suite; - const rootOpts = rootSuite ? store.getRootOptions(rootSuite) : {}; - - const fullOptions = Object.assign({}, rootOpts, parentOpts, opts); - + async handler() { // Ensure bench id is unique if (store.getResult(opts.id) && !opts.skip) { throw Error(`test titles must be unique, duplicated: '${opts.id}'`); } - // Extend timeout if maxMs is set - // if (opts.timeoutBench !== undefined) { - // this.timeout(opts.timeoutBench); - // } else { - // const timeout = this.timeout(); - // if (opts.maxMs && opts.maxMs > timeout) { - // this.timeout(opts.maxMs * 1.5); - // } else if (opts.minMs && opts.minMs > timeout) { - // this.timeout(opts.minMs * 1.5); - // } - // } - // Persist full results if requested. dir is created in `beforeAll` const benchmarkResultsCsvDir = process.env.BENCHMARK_RESULTS_CSV_DIR; const persistRunsNs = Boolean(benchmarkResultsCsvDir); - const {result, runsNs} = await runBenchFn({...fullOptions, fn: benchTask}, persistRunsNs); + const {result, runsNs} = await runBenchFn({...options, fn: benchTask}, persistRunsNs); // Store result for: // - to persist benchmark data latter @@ -130,7 +126,7 @@ function coerceToOptsObj( if (typeof idOrOpts === "string") { if (!fn) throw Error("fn arg must be set"); - opts = {id: idOrOpts, fn}; + opts = {id: idOrOpts, fn, threshold: optionsDefault.threshold}; } else { if (fn) { opts = {...idOrOpts, fn}; @@ -143,3 +139,30 @@ function coerceToOptsObj( return opts; } + +/** + * Customize benchmark opts for a describe block. Affects only tests within that Mocha.Suite + * ```ts + * describe("suite A1", function () { + * setBenchOpts({runs: 100}); + * // 100 runs + * itBench("bench A1.1", function() {}); + * itBench("bench A1.2", function() {}); + * // 300 runs + * itBench({id: "bench A1.3", runs: 300}, function() {}); + * + * // Supports nesting, child has priority over parent. + * // Arrow functions can be used, won't break it. + * describe("suite A2", () => { + * setBenchOpts({runs: 200}); + * // 200 runs. + * itBench("bench A2.1", () => {}); + * }) + * }) + * ``` + */ +export function setBenchOpts(opts: BenchmarkOpts): void { + store.setOptions(getCurrentSuite(), opts); +} + +export const setBenchmarkOptions = setBenchOpts; diff --git a/src/benchmark/globalState.ts b/src/benchmark/globalState.ts index 3309fc5..53b2ea3 100644 --- a/src/benchmark/globalState.ts +++ b/src/benchmark/globalState.ts @@ -1,4 +1,4 @@ -import type {Suite, Task} from "@vitest/runner"; +import type {Suite, SuiteCollector, Task} from "@vitest/runner"; import {BenchmarkResult, BenchmarkOpts, BenchmarkResults} from "../types.js"; /**t @@ -10,7 +10,7 @@ const results = new Map(); /** * Global opts from CLI */ -const optsByRootSuite = new WeakMap(); +let globalOpts: BenchmarkOpts | undefined; /** * Map to persist options set in describe blocks @@ -27,16 +27,19 @@ export const store = { getAllResults(): BenchmarkResults { return [...results.values()]; }, - getOptions(suite: Task): BenchmarkOpts | undefined { + getOptions(suite: Task | Suite | SuiteCollector): BenchmarkOpts | undefined { return optsMap.get(suite); }, - setOptions(suite: Task, opts: BenchmarkOpts): void { + setOptions(suite: Task | Suite | SuiteCollector, opts: BenchmarkOpts): void { optsMap.set(suite, opts); }, - getRootOptions(suite: Suite): BenchmarkOpts | undefined { - return optsByRootSuite.get(suite); + removeOptions(suite: Task | Suite): void { + optsMap.delete(suite); }, - setRootOptions(suite: Suite, opts: BenchmarkOpts): void { - optsByRootSuite.set(suite, opts); + setGlobalOptions(opts: Partial): void { + globalOpts = opts; + }, + getGlobalOptions(): BenchmarkOpts | undefined { + return globalOpts; }, }; diff --git a/src/benchmark/reporter.ts b/src/benchmark/reporter.ts index 51fea36..94a854a 100644 --- a/src/benchmark/reporter.ts +++ b/src/benchmark/reporter.ts @@ -1,8 +1,9 @@ import {Task, Suite, File} from "@vitest/runner"; import {color, consoleLog, symbols} from "../utils/output.js"; import {store} from "./globalState.js"; -import {Benchmark, BenchmarkResult} from "../types.js"; +import {Benchmark, BenchmarkOpts, BenchmarkResult} from "../types.js"; import {formatResultRow} from "./format.js"; +import {optionsDefault} from "../cli/options.js"; export class BenchmarkReporter { indents = 0; @@ -13,9 +14,9 @@ export class BenchmarkReporter { readonly prevResults: Map; readonly threshold: number; - constructor(prevBench: Benchmark | null, threshold: number) { + constructor({prevBench, benchmarkOpts}: {prevBench: Benchmark | null; benchmarkOpts: BenchmarkOpts}) { this.prevResults = new Map(); - this.threshold = threshold; + this.threshold = benchmarkOpts.threshold ?? optionsDefault.threshold; if (prevBench) { for (const bench of prevBench.results) { diff --git a/src/benchmark/runBenchFn.ts b/src/benchmark/runBenchmarkFn.ts similarity index 100% rename from src/benchmark/runBenchFn.ts rename to src/benchmark/runBenchmarkFn.ts diff --git a/src/benchmark/runner.ts b/src/benchmark/runner.ts index 26a0269..1897226 100644 --- a/src/benchmark/runner.ts +++ b/src/benchmark/runner.ts @@ -3,20 +3,21 @@ import { startTests, Suite, Task, - TaskResultPack, VitestRunner, VitestRunnerConfig, VitestRunnerImportSource, } from "@vitest/runner"; -import {Benchmark, BenchmarkResults} from "../types.js"; +import {Benchmark, BenchmarkOpts, BenchmarkResults} from "../types.js"; import {BenchmarkReporter} from "./reporter.js"; import {store} from "./globalState.js"; export class BenchmarkRunner implements VitestRunner { - config: VitestRunnerConfig; - reporter: BenchmarkReporter; + readonly config: VitestRunnerConfig; + readonly reporter: BenchmarkReporter; + readonly prevBench: Benchmark | null; + readonly benchmarkOpts: BenchmarkOpts; - constructor(protected opts: {prevBench: Benchmark | null}) { + constructor({prevBench, benchmarkOpts}: {prevBench: Benchmark | null; benchmarkOpts: BenchmarkOpts}) { this.config = { root: "", sequence: {seed: 1234, hooks: "list", setupFiles: "list"}, @@ -27,7 +28,9 @@ export class BenchmarkRunner implements VitestRunner { setupFiles: [], retry: 0, }; - this.reporter = new BenchmarkReporter(opts.prevBench, 0.2); + this.prevBench = prevBench; + this.benchmarkOpts = benchmarkOpts; + this.reporter = new BenchmarkReporter({prevBench, benchmarkOpts}); } onBeforeRunSuite(suite: Suite): void { @@ -57,12 +60,22 @@ export class BenchmarkRunner implements VitestRunner { } async process(files: string[]): Promise { + store.setGlobalOptions(this.benchmarkOpts); + const res = await startTests(files, this); - if (res[0].result?.state === "pass") { + const passed = res.filter((r) => r.result?.state == "pass"); + const skipped = res.filter((r) => r.result?.state == "skip"); + const failed = res.filter((r) => r.result?.state == "fail"); + + if (failed.length > 0) { + throw failed[0].result?.errors; + } + + if (passed.length + skipped.length === res.length) { return store.getAllResults(); } - return store.getAllResults(); + throw new Error("Some tests cause returned with unknown state"); } } diff --git a/src/cli/options.ts b/src/cli/options.ts index e16152f..5821fdf 100644 --- a/src/cli/options.ts +++ b/src/cli/options.ts @@ -3,6 +3,7 @@ import {StorageOptions, BenchmarkOpts, FileCollectionOptions} from "../types.js" export const optionsDefault = { threshold: 2, + timeoutBench: 10_000, historyLocalPath: "./benchmark_data", historyCacheKey: "benchmark_data", }; @@ -174,7 +175,8 @@ export const benchmarkOptions: ICliCommandOptions = { }, timeoutBench: { type: "number", - description: "Hard timeout, enforced by mocha.", + description: "Hard timeout for each benchmark", + default: optionsDefault.timeoutBench, group: benchmarkGroup, }, }; diff --git a/src/cli/run.ts b/src/cli/run.ts index 3175a3c..d0ecfbd 100644 --- a/src/cli/run.ts +++ b/src/cli/run.ts @@ -10,8 +10,7 @@ import {postGaComment} from "../github/comment.js"; import {isGaRun} from "../github/context.js"; import {BenchmarkRunner} from "../benchmark/runner.js"; import {optionsDefault} from "./options.js"; - -/* eslint-disable no-console */ +import {consoleLog} from "../utils/output.js"; export async function run(opts_: FileCollectionOptions & StorageOptions & BenchmarkOpts): Promise { const opts = Object.assign({}, optionsDefault, opts_); @@ -21,33 +20,33 @@ export async function run(opts_: FileCollectionOptions & StorageOptions & Benchm // Retrieve history const historyProvider = getHistoryProvider(opts); - console.log(`Connected to historyProvider: ${historyProvider.providerInfo()}`); + consoleLog(`Connected to historyProvider: ${historyProvider.providerInfo()}`); // Select prev benchmark to compare against const compareWith = await resolveCompareWith(opts); const prevBench = await resolvePrevBenchmark(compareWith, historyProvider); if (prevBench) { - console.log(`Found previous benchmark for ${renderCompareWith(compareWith)}, at commit ${prevBench.commitSha}`); + consoleLog(`Found previous benchmark for ${renderCompareWith(compareWith)}, at commit ${prevBench.commitSha}`); validateBenchmark(prevBench); } else { - console.log(`No previous benchmark found for ${renderCompareWith(compareWith)}`); + consoleLog(`No previous benchmark found for ${renderCompareWith(compareWith)}`); } const {files, unmatchedFiles} = await collectFiles(opts).catch((err) => { - console.log("Error loading up spec patterns"); + consoleLog("Error loading up spec patterns"); throw err; }); if (unmatchedFiles.length > 0) { - console.log(`Found unmatched files: \n${unmatchedFiles.join("\n")}\n`); + consoleLog(`Found unmatched files: \n${unmatchedFiles.join("\n")}\n`); } if (files.length === 0) { - console.log(`Can not find any matching spec file for ${opts.spec.join(",")}\n`); + consoleLog(`Can not find any matching spec file for ${opts.spec.join(",")}\n`); process.exit(1); } - const runner = new BenchmarkRunner({prevBench}); + const runner = new BenchmarkRunner({prevBench, benchmarkOpts: opts}); const results = await runner.process(files); if (results.length === 0) { @@ -66,7 +65,7 @@ export async function run(opts_: FileCollectionOptions & StorageOptions & Benchm if (shouldPersist === true) { const refStr = github.context.ref || (await shell("git symbolic-ref HEAD")); const branch = parseBranchFromRef(refStr); - console.log(`Persisting new benchmark data for branch '${branch}' commit '${currBench.commitSha}'`); + consoleLog(`Persisting new benchmark data for branch '${branch}' commit '${currBench.commitSha}'`); // TODO: prune and limit total entries // appendBenchmarkToHistoryAndPrune(history, currBench, branch, opts); await historyProvider.writeLatestInBranch(branch, currBench); diff --git a/src/index.ts b/src/index.ts index f32b255..9a64862 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import {suite, test} from "@vitest/runner"; -export {bench, itBench} from "./benchmark/index.js"; +export {bench, itBench, setBenchOpts, setBenchmarkOptions} from "./benchmark/index.js"; export const describe = suite; export const it = test; diff --git a/test/perf/iteration.test.ts b/test/perf/iteration.test.ts index 0caa63c..21ac852 100644 --- a/test/perf/iteration.test.ts +++ b/test/perf/iteration.test.ts @@ -1,5 +1,5 @@ import assert from "node:assert"; -import {itBench, describe, it} from "../../src/index.js"; +import {itBench, describe, it, setBenchOpts} from "../../src/index.js"; // As of Jun 17 2021 // Compare state root @@ -9,7 +9,7 @@ import {itBench, describe, it} from "../../src/index.js"; // byteArrayEquals with valueOf() 853971.0 ops/s 1.171000 us/op 9963051 runs 16.07 s describe("Array iteration", () => { - // setBenchOpts({maxMs: 60 * 1000, convergeFactor: 0.1 / 100}); + setBenchOpts({maxMs: 60 * 1000, convergeFactor: 0.1 / 100}); it("Regular test", () => { assert.strictEqual(1 + 2, 3); From 4c689b1b41cfe0b8810759b11864c1df1e28d61d Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 4 Dec 2024 15:14:26 +0100 Subject: [PATCH 06/19] Fix unit tests --- test/unit/history/local.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/history/local.test.ts b/test/unit/history/local.test.ts index 0b58df0..ac8c9dd 100644 --- a/test/unit/history/local.test.ts +++ b/test/unit/history/local.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import {expect} from "chai"; -import rimraf from "rimraf"; +import {rimrafSync} from "rimraf"; import {Benchmark} from "../../../src/types.js"; import {LocalHistoryProvider} from "../../../src/history/local.js"; @@ -15,7 +15,7 @@ describe("benchmark history local", () => { const historyProvider = new LocalHistoryProvider(testDir); after(() => { - rimraf.sync(testDir); + rimrafSync(testDir); }); it("Should write and read history", async () => { From cd0b93099bbfed88727d57f157a8b96cd10b5997 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 4 Dec 2024 15:21:00 +0100 Subject: [PATCH 07/19] Fix the build for node 18 --- package.json | 6 +- test/unit/history/local.test.ts | 4 +- yarn.lock | 222 +++++++++++--------------------- 3 files changed, 79 insertions(+), 153 deletions(-) diff --git a/package.json b/package.json index cc5becb..b1d2569 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@types/chai": "^4.2.19", "@types/mocha": "^10.0.9", "@types/node": "^18.15.3", - "@types/rimraf": "^4.0.5", + "@types/rimraf": "^3.0.0", "@types/yargs": "^17.0.33", "chai": "^4.5.0", "dotenv": "^10.0.0", @@ -52,7 +52,7 @@ "eslint-config-prettier": "^9.1.0", "mocha": "^10.8.2", "prettier": "^3.4.0", - "rimraf": "^6.0.1", + "rimraf": "^3.0.2", "ts-node": "^10.9.2", "typescript": "^5.6.3", "typescript-eslint": "^8.16.0" @@ -65,7 +65,7 @@ "aws-sdk": "^2.932.0", "csv-parse": "^4.16.0", "csv-stringify": "^5.6.2", - "glob": "^11.0.0", + "glob": "^9.3.5", "yargs": "^17.7.2", "log-symbols": "^7.0.0" } diff --git a/test/unit/history/local.test.ts b/test/unit/history/local.test.ts index ac8c9dd..0b58df0 100644 --- a/test/unit/history/local.test.ts +++ b/test/unit/history/local.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import {expect} from "chai"; -import {rimrafSync} from "rimraf"; +import rimraf from "rimraf"; import {Benchmark} from "../../../src/types.js"; import {LocalHistoryProvider} from "../../../src/history/local.js"; @@ -15,7 +15,7 @@ describe("benchmark history local", () => { const historyProvider = new LocalHistoryProvider(testDir); after(() => { - rimrafSync(testDir); + rimraf.sync(testDir); }); it("Should write and read history", async () => { diff --git a/yarn.lock b/yarn.lock index fd045e1..8c65b4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -256,18 +256,6 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - "@jridgewell/resolve-uri@^3.0.3": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" @@ -448,6 +436,14 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== +"@types/glob@*": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" + integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== + dependencies: + "@types/minimatch" "^5.1.2" + "@types/node" "*" + "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -458,6 +454,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/minimatch@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + "@types/mocha@^10.0.9": version "10.0.9" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.9.tgz#101e9da88d2c02e5ac8952982c23b224524d662a" @@ -483,12 +484,13 @@ dependencies: undici-types "~5.26.4" -"@types/rimraf@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-4.0.5.tgz#7a59be11605c22ea3959c21ff8b28b9df1bae1b2" - integrity sha512-DTCZoIQotB2SUJnYgrEx43cQIUYOlNZz0AZPbKU4PSLYTUdML5Gox0++z4F9kQocxStrCmRNhi4x5x/UlwtKUA== +"@types/rimraf@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.2.tgz#a63d175b331748e5220ad48c901d7bbf1f44eef8" + integrity sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ== dependencies: - rimraf "*" + "@types/glob" "*" + "@types/node" "*" "@types/tunnel@^0.0.1": version "0.0.1" @@ -673,11 +675,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -685,11 +682,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -988,7 +980,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.0, cross-spawn@^7.0.5: +cross-spawn@^7.0.5: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -1122,21 +1114,11 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - enhanced-resolve@^5.15.0: version "5.17.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" @@ -1517,14 +1499,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -foreground-child@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" - integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^4.0.1" - form-data@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -1638,17 +1612,17 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" - integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: - foreground-child "^3.1.0" - jackspeak "^4.0.1" - minimatch "^10.0.0" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^2.0.0" + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" glob@^8.1.0: version "8.1.0" @@ -1661,6 +1635,16 @@ glob@^8.1.0: minimatch "^5.0.1" once "^1.3.0" +glob@^9.3.5: + version "9.3.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" + integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== + dependencies: + fs.realpath "^1.0.0" + minimatch "^8.0.2" + minipass "^4.2.4" + path-scurry "^1.6.1" + globals@^14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" @@ -2040,13 +2024,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -jackspeak@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" - integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== - dependencies: - "@isaacs/cliui" "^8.0.2" - jmespath@0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" @@ -2141,10 +2118,10 @@ loupe@^3.1.2: resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== -lru-cache@^11.0.0: - version "11.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" - integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== make-error@^1.1.1: version "1.3.6" @@ -2176,13 +2153,6 @@ mime-types@^2.1.12: dependencies: mime-db "1.48.0" -minimatch@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" - integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== - dependencies: - brace-expansion "^2.0.1" - minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -2190,7 +2160,7 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^3.1.2: +minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -2204,6 +2174,13 @@ minimatch@^5.0.1, minimatch@^5.1.6: dependencies: brace-expansion "^2.0.1" +minimatch@^8.0.2: + version "8.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" + integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== + dependencies: + brace-expansion "^2.0.1" + minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -2221,7 +2198,12 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^7.1.2: +minipass@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" + integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== @@ -2358,11 +2340,6 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -2375,6 +2352,11 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -2385,13 +2367,13 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" - integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== +path-scurry@^1.6.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== dependencies: - lru-cache "^11.0.0" - minipass "^7.1.2" + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" pathe@^1.1.2: version "1.1.2" @@ -2536,13 +2518,12 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@*, rimraf@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-6.0.1.tgz#ffb8ad8844dd60332ab15f52bc104bc3ed71ea4e" - integrity sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A== +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: - glob "^11.0.0" - package-json-from-dist "^1.0.0" + glob "^7.1.3" run-parallel@^1.1.9: version "1.2.0" @@ -2651,20 +2632,6 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" @@ -2683,15 +2650,6 @@ string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -2720,13 +2678,6 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -2741,13 +2692,6 @@ strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -3073,15 +3017,6 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -3091,15 +3026,6 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" From 2eaa693d46ac2d5a874b93e75ccf1551e6ea24f1 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 4 Dec 2024 15:46:55 +0100 Subject: [PATCH 08/19] Fix type issue with resolution --- package.json | 8 +- test/unit/history/local.test.ts | 4 +- yarn.lock | 243 ++++++++++++++++++++++---------- 3 files changed, 179 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index b1d2569..a8273f3 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "@types/chai": "^4.2.19", "@types/mocha": "^10.0.9", "@types/node": "^18.15.3", - "@types/rimraf": "^3.0.0", "@types/yargs": "^17.0.33", "chai": "^4.5.0", "dotenv": "^10.0.0", @@ -52,7 +51,7 @@ "eslint-config-prettier": "^9.1.0", "mocha": "^10.8.2", "prettier": "^3.4.0", - "rimraf": "^3.0.2", + "rimraf": "^6.0.1", "ts-node": "^10.9.2", "typescript": "^5.6.3", "typescript-eslint": "^8.16.0" @@ -65,8 +64,11 @@ "aws-sdk": "^2.932.0", "csv-parse": "^4.16.0", "csv-stringify": "^5.6.2", - "glob": "^9.3.5", + "glob": "^10.4.5", "yargs": "^17.7.2", "log-symbols": "^7.0.0" + }, + "resolutions": { + "lru-cache": "11.0.2" } } diff --git a/test/unit/history/local.test.ts b/test/unit/history/local.test.ts index 0b58df0..ac8c9dd 100644 --- a/test/unit/history/local.test.ts +++ b/test/unit/history/local.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import {expect} from "chai"; -import rimraf from "rimraf"; +import {rimrafSync} from "rimraf"; import {Benchmark} from "../../../src/types.js"; import {LocalHistoryProvider} from "../../../src/history/local.js"; @@ -15,7 +15,7 @@ describe("benchmark history local", () => { const historyProvider = new LocalHistoryProvider(testDir); after(() => { - rimraf.sync(testDir); + rimrafSync(testDir); }); it("Should write and read history", async () => { diff --git a/yarn.lock b/yarn.lock index 8c65b4c..3519a47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -256,6 +256,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@jridgewell/resolve-uri@^3.0.3": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" @@ -396,6 +408,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.0.0-rc.0.tgz#0c7c3f5e1285f99cedb563d74ad1adb9822b5144" integrity sha512-iXKByCMfrlO5S6Oh97BuM56tM2cIBB0XsL/vWF/AtJrJEKx4MC/Xdu0xDsGXMGcNWpqF7ujMsjjnp0+UHBwnDQ== +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@pkgr/core@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" @@ -436,14 +453,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== -"@types/glob@*": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" - integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== - dependencies: - "@types/minimatch" "^5.1.2" - "@types/node" "*" - "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -454,11 +463,6 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/minimatch@^5.1.2": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" - integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== - "@types/mocha@^10.0.9": version "10.0.9" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.9.tgz#101e9da88d2c02e5ac8952982c23b224524d662a" @@ -484,14 +488,6 @@ dependencies: undici-types "~5.26.4" -"@types/rimraf@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.2.tgz#a63d175b331748e5220ad48c901d7bbf1f44eef8" - integrity sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ== - dependencies: - "@types/glob" "*" - "@types/node" "*" - "@types/tunnel@^0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c" @@ -675,6 +671,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -682,6 +683,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -980,7 +986,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.5: +cross-spawn@^7.0.0, cross-spawn@^7.0.5: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -1114,11 +1120,21 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + enhanced-resolve@^5.15.0: version "5.17.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" @@ -1499,6 +1515,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -1612,17 +1636,29 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== +glob@^10.4.5: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" + integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" glob@^8.1.0: version "8.1.0" @@ -1635,16 +1671,6 @@ glob@^8.1.0: minimatch "^5.0.1" once "^1.3.0" -glob@^9.3.5: - version "9.3.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" - integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== - dependencies: - fs.realpath "^1.0.0" - minimatch "^8.0.2" - minipass "^4.2.4" - path-scurry "^1.6.1" - globals@^14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" @@ -2024,6 +2050,22 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jackspeak@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" + integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== + dependencies: + "@isaacs/cliui" "^8.0.2" + jmespath@0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" @@ -2118,10 +2160,10 @@ loupe@^3.1.2: resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== -lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@11.0.2, lru-cache@^10.2.0, lru-cache@^11.0.0: + version "11.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" + integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== make-error@^1.1.1: version "1.3.6" @@ -2153,6 +2195,13 @@ mime-types@^2.1.12: dependencies: mime-db "1.48.0" +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -2160,7 +2209,7 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -2174,13 +2223,6 @@ minimatch@^5.0.1, minimatch@^5.1.6: dependencies: brace-expansion "^2.0.1" -minimatch@^8.0.2: - version "8.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" - integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== - dependencies: - brace-expansion "^2.0.1" - minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -2198,12 +2240,7 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" - integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== @@ -2340,6 +2377,11 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -2352,11 +2394,6 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -2367,7 +2404,7 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.6.1: +path-scurry@^1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== @@ -2375,6 +2412,14 @@ path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" @@ -2518,12 +2563,13 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== +rimraf@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-6.0.1.tgz#ffb8ad8844dd60332ab15f52bc104bc3ed71ea4e" + integrity sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A== dependencies: - glob "^7.1.3" + glob "^11.0.0" + package-json-from-dist "^1.0.0" run-parallel@^1.1.9: version "1.2.0" @@ -2632,6 +2678,20 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" @@ -2650,6 +2710,15 @@ string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -2678,6 +2747,13 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -2692,6 +2768,13 @@ strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -3017,6 +3100,15 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -3026,6 +3118,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" From 1eba6cce404e7c5d30163a41b4e0da5c9135b988 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 4 Dec 2024 15:53:44 +0100 Subject: [PATCH 09/19] Skip lib check for the old version of a package --- package.json | 2 +- tsconfig.build.cjs.json | 1 + tsconfig.build.esm.json | 1 + yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a8273f3..1de2114 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,6 @@ "log-symbols": "^7.0.0" }, "resolutions": { - "lru-cache": "11.0.2" + "lru-cache": "10.4.3" } } diff --git a/tsconfig.build.cjs.json b/tsconfig.build.cjs.json index 5c299e9..dc8f108 100644 --- a/tsconfig.build.cjs.json +++ b/tsconfig.build.cjs.json @@ -5,5 +5,6 @@ "outDir": "./lib/cjs", "esModuleInterop": true, "module": "commonjs", + "skipLibCheck": true, } } diff --git a/tsconfig.build.esm.json b/tsconfig.build.esm.json index 4079686..5f24418 100644 --- a/tsconfig.build.esm.json +++ b/tsconfig.build.esm.json @@ -5,5 +5,6 @@ "outDir": "./lib/esm", "esModuleInterop": true, "module": "es2020", + "skipLibCheck": true, } } diff --git a/yarn.lock b/yarn.lock index 3519a47..f887433 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2160,10 +2160,10 @@ loupe@^3.1.2: resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== -lru-cache@11.0.2, lru-cache@^10.2.0, lru-cache@^11.0.0: - version "11.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" - integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== +lru-cache@10.4.3, lru-cache@^10.2.0, lru-cache@^11.0.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== make-error@^1.1.1: version "1.3.6" From d7399edd75e39caae93fabecaaeaf3166733ec2c Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 4 Dec 2024 15:54:31 +0100 Subject: [PATCH 10/19] Add comment --- tsconfig.build.cjs.json | 2 ++ tsconfig.build.esm.json | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tsconfig.build.cjs.json b/tsconfig.build.cjs.json index dc8f108..2946c10 100644 --- a/tsconfig.build.cjs.json +++ b/tsconfig.build.cjs.json @@ -5,6 +5,8 @@ "outDir": "./lib/cjs", "esModuleInterop": true, "module": "commonjs", + + // To build for Node 18 the lru-cache package has conflicting types "skipLibCheck": true, } } diff --git a/tsconfig.build.esm.json b/tsconfig.build.esm.json index 5f24418..913b549 100644 --- a/tsconfig.build.esm.json +++ b/tsconfig.build.esm.json @@ -5,6 +5,8 @@ "outDir": "./lib/esm", "esModuleInterop": true, "module": "es2020", + + // To build for Node 18 the lru-cache package has conflicting types "skipLibCheck": true, } } From 900d6d6b56a2b174919a6fa172008f928dbc256a Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 4 Dec 2024 15:57:04 +0100 Subject: [PATCH 11/19] Downgrade rimraf to support node 18 --- package.json | 2 +- yarn.lock | 49 +++++++------------------------------------------ 2 files changed, 8 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 1de2114..c4a623d 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "eslint-config-prettier": "^9.1.0", "mocha": "^10.8.2", "prettier": "^3.4.0", - "rimraf": "^6.0.1", + "rimraf": "^5.0.10", "ts-node": "^10.9.2", "typescript": "^5.6.3", "typescript-eslint": "^8.16.0" diff --git a/yarn.lock b/yarn.lock index f887433..470b505 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1636,7 +1636,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^10.4.5: +glob@^10.3.7, glob@^10.4.5: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -1648,18 +1648,6 @@ glob@^10.4.5: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" - integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== - dependencies: - foreground-child "^3.1.0" - jackspeak "^4.0.1" - minimatch "^10.0.0" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^2.0.0" - glob@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" @@ -2059,13 +2047,6 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jackspeak@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" - integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== - dependencies: - "@isaacs/cliui" "^8.0.2" - jmespath@0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" @@ -2160,7 +2141,7 @@ loupe@^3.1.2: resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== -lru-cache@10.4.3, lru-cache@^10.2.0, lru-cache@^11.0.0: +lru-cache@10.4.3, lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== @@ -2195,13 +2176,6 @@ mime-types@^2.1.12: dependencies: mime-db "1.48.0" -minimatch@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" - integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== - dependencies: - brace-expansion "^2.0.1" - minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -2412,14 +2386,6 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-scurry@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" - integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== - dependencies: - lru-cache "^11.0.0" - minipass "^7.1.2" - pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" @@ -2563,13 +2529,12 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-6.0.1.tgz#ffb8ad8844dd60332ab15f52bc104bc3ed71ea4e" - integrity sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A== +rimraf@^5.0.10: + version "5.0.10" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c" + integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ== dependencies: - glob "^11.0.0" - package-json-from-dist "^1.0.0" + glob "^10.3.7" run-parallel@^1.1.9: version "1.2.0" From 7a5af0cd5d7c56905a2be1ad9c475e3d903e5005 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 4 Dec 2024 16:00:48 +0100 Subject: [PATCH 12/19] Add ts as default extension list to not break compatibility --- src/cli/options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/options.ts b/src/cli/options.ts index 5821fdf..f51c27e 100644 --- a/src/cli/options.ts +++ b/src/cli/options.ts @@ -24,7 +24,7 @@ export const fileCollectionOptions: ICliCommandOptions description: "File extension(s) to load", type: "array", alias: "ext", - default: ["js", "cjs", "mjs"], + default: ["js", "cjs", "mjs", "ts"], group: fileGroup, }, ignore: { From 19411b6413188d920f6e1ad03f2ea180835314f2 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Fri, 6 Dec 2024 10:30:28 +0100 Subject: [PATCH 13/19] Update reame file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 943b42d..758adf8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This tooling provides both a easy to use runner for benchmarking and easy integr Create a test mocha test file but use `itBench` instead of `it` ```ts -import {itBench, setBenchOpts} from "../../src"; +import {itBench, setBenchOpts, describe} from "../../src"; describe("Sum array benchmark", () => { itBench("sum array with reduce", () => { From dad1312a235d00d36f9524727b4b9b06122d9ca7 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Mon, 9 Dec 2024 12:06:03 +0100 Subject: [PATCH 14/19] Update all mocha references --- README.md | 10 ++++------ src/benchmark/benchmarkFn.ts | 5 +++-- src/benchmark/globalState.ts | 1 - src/benchmark/reporter.ts | 1 - src/cli/cli.ts | 1 - src/types.ts | 4 +--- test/perf/iteration.test.ts | 2 -- 7 files changed, 8 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 758adf8..bb38113 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This tooling provides both a easy to use runner for benchmarking and easy integr ## Quick start -Create a test mocha test file but use `itBench` instead of `it` +Create a benchmark test file but use `itBench` instead of `it` ```ts import {itBench, setBenchOpts, describe} from "../../src"; @@ -21,7 +21,7 @@ describe("Sum array benchmark", () => { }); ``` -Then run the CLI, compatible with all mocha options. +Then run the CLI. ``` benchmark 'test/perf/**/*.perf.ts' --local @@ -36,7 +36,7 @@ Inspect benchmark results in the terminal ## How does it work? -This tool is a CLI wrapper around mocha, example usage: +This tool is a CLI tool, example usage: ``` benchmark 'test/perf/**/*.perf.ts' --s3 @@ -47,9 +47,7 @@ The above command will: - Read benchmark history from the specified provider (AWS S3) - Figure out the prev benchmark based on your option (defaults to latest commit in main branch) - Run benchmark comparing with previous - - Runs mocha programatically against the file globs - - Collect benchmark data in-memory while streaming results with a familiar mocha reporter - - Note: also runs any test that would regularly be run with mocha + - Collect benchmark data in-memory while streaming results - Add result to benchmark history and persist them to the specified provider (AWS S3) - If in CI, post a PR or commit comment with an expandable summary of the benchmark results comparision - If a performance regression was detected, exit 1 diff --git a/src/benchmark/benchmarkFn.ts b/src/benchmark/benchmarkFn.ts index 42e6d79..cf554f2 100644 --- a/src/benchmark/benchmarkFn.ts +++ b/src/benchmark/benchmarkFn.ts @@ -14,6 +14,7 @@ export type BenchmarkRunOptsWithFn = BenchmarkOpts & { beforeEach?: (arg: T2, i: number) => T | Promise; }; +// Create partial only for specific keys type PartialBy = Omit & Partial>; export function getRootSuite(suite: Suite | SuiteCollector): Suite { @@ -61,7 +62,7 @@ export const bench = createBenchmarkFunction(function ( concurrent: false, timeout, meta: { - "dapplion/benchmark": true, + "chainsafe/benchmark": true, }, async handler() { // Ensure bench id is unique @@ -141,7 +142,7 @@ function coerceToOptsObj( } /** - * Customize benchmark opts for a describe block. Affects only tests within that Mocha.Suite + * Customize benchmark opts for a describe block * ```ts * describe("suite A1", function () { * setBenchOpts({runs: 100}); diff --git a/src/benchmark/globalState.ts b/src/benchmark/globalState.ts index 53b2ea3..1443af9 100644 --- a/src/benchmark/globalState.ts +++ b/src/benchmark/globalState.ts @@ -3,7 +3,6 @@ import {BenchmarkResult, BenchmarkOpts, BenchmarkResults} from "../types.js"; /**t * Map of results by root suite. - * Before running mocha, you must register the root suite here */ const results = new Map(); diff --git a/src/benchmark/reporter.ts b/src/benchmark/reporter.ts index 94a854a..8388663 100644 --- a/src/benchmark/reporter.ts +++ b/src/benchmark/reporter.ts @@ -69,7 +69,6 @@ export class BenchmarkReporter { this.passed++; } catch (e) { this.failed++; - // Log error manually since mocha doesn't log errors thrown here consoleLog(e); process.exitCode = 1; throw e; diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 7c9e327..87e865b 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -22,7 +22,6 @@ void yargs(hideBin(process.argv)) // As of yargs v16.1.0 dot-notation breaks strictOptions() // Manually processing options is typesafe tho more verbose "dot-notation": false, - // From mocha "combine-arrays": true, "short-option-groups": false, "strip-aliased": true, diff --git a/src/types.ts b/src/types.ts index f5a10f8..557eed8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,8 +49,7 @@ export type BenchmarkOpts = { runsFactor?: number; /** Run `sleep(0)` after each fn() call. Use when the event loop needs to tick to free resources created by fn() */ yieldEventLoopAfterEach?: boolean; - /** Hard timeout, enforced by mocha. */ - // NOTE: Must not use `.timeout` or it collisions with mocha's .timeout option. It defaults to 2000 and messed up everything + /** Hard timeout */ timeoutBench?: number; // For reporter /** Customize the threshold for this specific benchmark. Set to Infinity to disable it */ @@ -58,7 +57,6 @@ export type BenchmarkOpts = { /** Equivalent to setting threshold = Infinity */ noThreshold?: boolean; - // For mocha only?: boolean; skip?: boolean; }; diff --git a/test/perf/iteration.test.ts b/test/perf/iteration.test.ts index 21ac852..33e8b01 100644 --- a/test/perf/iteration.test.ts +++ b/test/perf/iteration.test.ts @@ -62,8 +62,6 @@ describe("Array iteration", () => { }, }); - // Test mocha skip and only - itBench.skip("sum array with reduce", () => { arr.reduce((total, curr) => total + curr, 0); }); From a11ba755cd1da23fe47210ea7acd9d005cb08aa7 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Mon, 9 Dec 2024 12:11:47 +0100 Subject: [PATCH 15/19] Remove extra sanitization option --- src/cli/run.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cli/run.ts b/src/cli/run.ts index d0ecfbd..7a9ef35 100644 --- a/src/cli/run.ts +++ b/src/cli/run.ts @@ -15,9 +15,6 @@ import {consoleLog} from "../utils/output.js"; export async function run(opts_: FileCollectionOptions & StorageOptions & BenchmarkOpts): Promise { const opts = Object.assign({}, optionsDefault, opts_); - // Sanitize opts - if (isNaN(opts.threshold)) throw Error("opts.threshold is not a number"); - // Retrieve history const historyProvider = getHistoryProvider(opts); consoleLog(`Connected to historyProvider: ${historyProvider.providerInfo()}`); From 6654249249d5be07f61c55ed00b3cec2d440347c Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Mon, 9 Dec 2024 12:19:27 +0100 Subject: [PATCH 16/19] Update color keys --- src/utils/output.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/utils/output.ts b/src/utils/output.ts index c953b71..8ef9e97 100644 --- a/src/utils/output.ts +++ b/src/utils/output.ts @@ -3,25 +3,25 @@ import logSymbols from "log-symbols"; const colors = { pass: 90, fail: 31, - "bright pass": 92, - "bright fail": 91, - "bright yellow": 93, + brightPass: 92, + brightFail: 91, + brightYellow: 93, pending: 36, suite: 0, - "error title": 0, - "error message": 31, - "error stack": 90, + errorTitle: 0, + errorMessage: 31, + errorStack: 90, checkmark: 32, fast: 90, medium: 33, slow: 31, green: 32, light: 90, - "diff gutter": 90, - "diff added": 32, - "diff removed": 31, - "diff added inline": "30;42", - "diff removed inline": "30;41", + diffGutter: 90, + diffAdded: 32, + diffRemoved: 31, + diffAddedInline: "30;42", + diffRemovedInline: "30;41", }; export const symbols = { From 58b75137693b3fc097e3531040316ea0886a0a77 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 11 Dec 2024 10:11:25 +0100 Subject: [PATCH 17/19] Organize the exports --- src/benchmark/benchmarkFn.ts | 52 ++++------------------------------- src/compare/compute.ts | 6 ++-- src/github/comment.ts | 4 +-- src/index.ts | 37 +++++++++++++++++++++++-- src/types.ts | 53 ++++++++++++++++-------------------- src/utils/render.ts | 8 +++--- 6 files changed, 72 insertions(+), 88 deletions(-) diff --git a/src/benchmark/benchmarkFn.ts b/src/benchmark/benchmarkFn.ts index cf554f2..f6b5f53 100644 --- a/src/benchmark/benchmarkFn.ts +++ b/src/benchmark/benchmarkFn.ts @@ -1,39 +1,13 @@ import fs from "node:fs"; import path from "node:path"; -import {getCurrentSuite, Suite, SuiteCollector} from "@vitest/runner"; +import {getCurrentSuite} from "@vitest/runner"; import {createChainable} from "@vitest/runner/utils"; import {store} from "./globalState.js"; -import {BenchmarkOpts} from "../types.js"; +import {BenchApi, BenchmarkOpts, BenchmarkRunOptsWithFn, PartialBy} from "../types.js"; import {runBenchFn} from "./runBenchmarkFn.js"; import {optionsDefault} from "../cli/options.js"; -export type BenchmarkRunOptsWithFn = BenchmarkOpts & { - id: string; - fn: (arg: T) => void | Promise; - before?: () => T2 | Promise; - beforeEach?: (arg: T2, i: number) => T | Promise; -}; - -// Create partial only for specific keys -type PartialBy = Omit & Partial>; - -export function getRootSuite(suite: Suite | SuiteCollector): Suite { - if (suite.type === "collector") { - return getRootSuite(suite.tasks[0] as Suite); - } - - if (suite.name === "") { - return suite; - } else if (suite.suite) { - getRootSuite(suite.suite); - } else { - return suite; - } - - throw new Error("Can not find root suite"); -} - -export const bench = createBenchmarkFunction(function ( +export const bench: BenchApi = createBenchmarkFunction(function ( this: Record<"skip" | "only", boolean | undefined>, idOrOpts: string | PartialBy, "fn">, fn?: (arg: T) => void | Promise @@ -99,26 +73,10 @@ function createBenchmarkFunction( idOrOpts: string | PartialBy, "fn">, fn?: (arg: T) => void | Promise ) => void -): BenchFuncApi { - return createChainable(["skip", "only"], fn) as BenchFuncApi; -} - -interface BenchFuncApi { - (opts: BenchmarkRunOptsWithFn): void; - (idOrOpts: string | Omit, "fn">, fn: (arg: T) => void): void; - ( - idOrOpts: string | PartialBy, "fn">, - fn?: (arg: T) => void | Promise - ): void; -} - -interface BenchApi extends BenchFuncApi { - only: BenchFuncApi; - skip: BenchFuncApi; +): BenchApi { + return createChainable(["skip", "only"], fn) as BenchApi; } -export const itBench = bench as BenchApi; - function coerceToOptsObj( idOrOpts: string | PartialBy, "fn">, fn?: (arg: T) => void | Promise diff --git a/src/compare/compute.ts b/src/compare/compute.ts index 84e7ea1..32513ad 100644 --- a/src/compare/compute.ts +++ b/src/compare/compute.ts @@ -1,10 +1,10 @@ -import {ResultComparision, BenchmarkComparision, Benchmark, BenchmarkResult} from "../types.js"; +import {ResultComparison, BenchmarkComparison, Benchmark, BenchmarkResult} from "../types.js"; export function computeBenchComparision( currBench: Benchmark, prevBench: Benchmark | null, threshold: number -): BenchmarkComparision { +): BenchmarkComparison { const prevResults = new Map(); if (prevBench) { for (const bench of prevBench.results) { @@ -12,7 +12,7 @@ export function computeBenchComparision( } } - const results = currBench.results.map((currBench): ResultComparision => { + const results = currBench.results.map((currBench): ResultComparison => { const {id} = currBench; const prevBench = prevResults.get(id); const thresholdBench = currBench.threshold ?? threshold; diff --git a/src/github/comment.ts b/src/github/comment.ts index f668ad2..5eb04ad 100644 --- a/src/github/comment.ts +++ b/src/github/comment.ts @@ -1,9 +1,9 @@ import * as github from "@actions/github"; -import {BenchmarkComparision} from "../types.js"; +import {BenchmarkComparison} from "../types.js"; import {commetToPrUpdatable, commentToCommit} from "./octokit.js"; import {getGithubEventData, GithubActionsEventData, renderComment} from "../utils/index.js"; -export async function postGaComment(resultsComp: BenchmarkComparision): Promise { +export async function postGaComment(resultsComp: BenchmarkComparison): Promise { switch (github.context.eventName) { case "pull_request": { const eventData = getGithubEventData(); diff --git a/src/index.ts b/src/index.ts index 9a64862..1a85af6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,36 @@ -import {suite, test} from "@vitest/runner"; +export {suite as describe, beforeEach, beforeAll} from "@vitest/runner"; +export {bench, setBenchOpts, setBenchmarkOptions} from "./benchmark/index.js"; -export {bench, itBench, setBenchOpts, setBenchmarkOptions} from "./benchmark/index.js"; -export const describe = suite; +import {bench} from "./benchmark/index.js"; +import {test} from "@vitest/runner"; + +/** + * @deprecated We recommend to use `bench` instead. + */ +export const itBench = bench; + +/** + * Defines a test case with a given name and test function. The test function can optionally be configured with test options. + * + * * **Note:** We expose this utility for user flexibility but recommends users to not mix normal tests with benchmarks + * + * @param {string | Function} name - The name of the test or a function that will be used as a test name. + * @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided. + * @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters. + * @throws {Error} If called inside another test function. + * @example + * ```ts + * // Define a simple test + * it('should add two numbers', () => { + * expect(add(1, 2)).toBe(3); + * }); + * ``` + * @example + * ```ts + * // Define a test with options + * it('should subtract two numbers', { retry: 3 }, () => { + * expect(subtract(5, 2)).toBe(3); + * }); + * ``` + */ export const it = test; diff --git a/src/types.ts b/src/types.ts index 557eed8..7b35d8a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -61,33 +61,28 @@ export type BenchmarkOpts = { skip?: boolean; }; -/** Manual lodash.pick() function. Ensure no unwanted options end up in optsByRootSuite */ -export function onlyBenchmarkOpts(opts: BenchmarkOpts): BenchmarkOpts { - // Define in this way so Typescript guarantees all keys are considered - const keysObj: Record = { - maxRuns: true, - minRuns: true, - maxMs: true, - minMs: true, - maxWarmUpMs: true, - maxWarmUpRuns: true, - convergeFactor: true, - runsFactor: true, - yieldEventLoopAfterEach: true, - timeoutBench: true, - threshold: true, - noThreshold: true, - only: true, - skip: true, - }; +// Create partial only for specific keys +export type PartialBy = Omit & Partial>; + +export type BenchmarkRunOptsWithFn = BenchmarkOpts & { + id: string; + fn: (arg: T) => void | Promise; + before?: () => T2 | Promise; + beforeEach?: (arg: T2, i: number) => T | Promise; +}; + +export interface BenchFuncApi { + (opts: BenchmarkRunOptsWithFn): void; + (idOrOpts: string | Omit, "fn">, fn: (arg: T) => void): void; + ( + idOrOpts: string | PartialBy, "fn">, + fn?: (arg: T) => void | Promise + ): void; +} - const optsOut = {} as Record; - for (const key of Object.keys(keysObj) as (keyof BenchmarkOpts)[]) { - if (opts[key] !== undefined) { - optsOut[key] = opts[key]; - } - } - return optsOut as BenchmarkOpts; +export interface BenchApi extends BenchFuncApi { + only: BenchFuncApi; + skip: BenchFuncApi; } export type BenchmarkResults = BenchmarkResult[]; @@ -115,14 +110,14 @@ export type BenchmarkHistory = { }; }; -export type BenchmarkComparision = { +export type BenchmarkComparison = { currCommitSha: string; prevCommitSha: string | null; someFailed: boolean; - results: ResultComparision[]; + results: ResultComparison[]; }; -export type ResultComparision = { +export type ResultComparison = { id: string; currAverageNs: number; prevAverageNs: number | null; diff --git a/src/utils/render.ts b/src/utils/render.ts index 04b8b85..d287b5e 100644 --- a/src/utils/render.ts +++ b/src/utils/render.ts @@ -1,8 +1,8 @@ -import {BenchmarkComparision, ResultComparision} from "../types.js"; +import {BenchmarkComparison, ResultComparison} from "../types.js"; -type CommitsSha = Pick; +type CommitsSha = Pick; -export function renderComment(benchComp: BenchmarkComparision): string { +export function renderComment(benchComp: BenchmarkComparison): string { const isFailedResults = benchComp.results.filter((r) => r.isFailed); const isImprovedResults = benchComp.results.filter((r) => r.isImproved); @@ -42,7 +42,7 @@ ${renderBenchmarkTable(benchComp.results, benchComp)} `; } -function renderBenchmarkTable(benchComp: ResultComparision[], {currCommitSha, prevCommitSha}: CommitsSha): string { +function renderBenchmarkTable(benchComp: ResultComparison[], {currCommitSha, prevCommitSha}: CommitsSha): string { function toRow(arr: (number | string)[]): string { // Don't surround string items with \`, it doesn't look great rendered in Github comments const row = arr.map((e) => `${e}`).join(" | "); From 1f1c661efc524af7c2e7a0dee5dea7a2abbf2097 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 11 Dec 2024 10:12:49 +0100 Subject: [PATCH 18/19] Remove it from the exports --- src/index.ts | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1a85af6..ef216f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,35 +2,8 @@ export {suite as describe, beforeEach, beforeAll} from "@vitest/runner"; export {bench, setBenchOpts, setBenchmarkOptions} from "./benchmark/index.js"; import {bench} from "./benchmark/index.js"; -import {test} from "@vitest/runner"; /** * @deprecated We recommend to use `bench` instead. */ export const itBench = bench; - -/** - * Defines a test case with a given name and test function. The test function can optionally be configured with test options. - * - * * **Note:** We expose this utility for user flexibility but recommends users to not mix normal tests with benchmarks - * - * @param {string | Function} name - The name of the test or a function that will be used as a test name. - * @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided. - * @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters. - * @throws {Error} If called inside another test function. - * @example - * ```ts - * // Define a simple test - * it('should add two numbers', () => { - * expect(add(1, 2)).toBe(3); - * }); - * ``` - * @example - * ```ts - * // Define a test with options - * it('should subtract two numbers', { retry: 3 }, () => { - * expect(subtract(5, 2)).toBe(3); - * }); - * ``` - */ -export const it = test; From d88b852fd649a522589fa6f189664f754165c54c Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Wed, 11 Dec 2024 10:15:48 +0100 Subject: [PATCH 19/19] Update the benchmark to reflect the changes in export --- test/perf/iteration.test.ts | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/test/perf/iteration.test.ts b/test/perf/iteration.test.ts index 33e8b01..0ee1e6f 100644 --- a/test/perf/iteration.test.ts +++ b/test/perf/iteration.test.ts @@ -1,5 +1,4 @@ -import assert from "node:assert"; -import {itBench, describe, it, setBenchOpts} from "../../src/index.js"; +import {bench, describe, setBenchOpts} from "../../src/index.js"; // As of Jun 17 2021 // Compare state root @@ -11,15 +10,11 @@ import {itBench, describe, it, setBenchOpts} from "../../src/index.js"; describe("Array iteration", () => { setBenchOpts({maxMs: 60 * 1000, convergeFactor: 0.1 / 100}); - it("Regular test", () => { - assert.strictEqual(1 + 2, 3); - }); - // nonce = 5 const n = 1e6; const arr = Array.from({length: n}, (_, i) => i); - itBench("sum array with raw for loop", () => { + bench("sum array with raw for loop", () => { let sum = 0; for (let i = 0, len = arr.length; i < len; i++) { sum += i; @@ -27,7 +22,7 @@ describe("Array iteration", () => { return sum; }); - itBench("sum array with reduce", () => { + bench("sum array with reduce", () => { arr.reduce((total, curr) => total + curr, 0); // Uncomment below to cause a guaranteed performance regression @@ -37,7 +32,7 @@ describe("Array iteration", () => { // Test before and beforeEach hooks - itBench({ + bench({ id: "sum array with reduce beforeEach", beforeEach: () => Array.from({length: 1e4}, (_, i) => i), fn: () => { @@ -49,7 +44,7 @@ describe("Array iteration", () => { }, }); - itBench({ + bench({ id: "sum array with reduce before beforeEach", before: () => Array.from({length: 1e4}, (_, i) => i), beforeEach: (arr) => arr.slice(0), @@ -62,17 +57,17 @@ describe("Array iteration", () => { }, }); - itBench.skip("sum array with reduce", () => { + bench.skip("sum array with reduce", () => { arr.reduce((total, curr) => total + curr, 0); }); - // itBench.only("sum array with reduce", () => { + // bench.only("sum array with reduce", () => { // arr.reduce((total, curr) => total + curr, 0); // }); // Reporter options - itBench({ + bench({ id: "sum array with reduce high threshold", threshold: 5, fn: () => { @@ -80,7 +75,7 @@ describe("Array iteration", () => { }, }); - itBench({ + bench({ id: "sum array with reduce no threshold", threshold: Infinity, fn: () => {