diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d23e9a0..e69e211 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -13,5 +13,5 @@ Please include a summary of the change and which issue is fixed. Please also inc - [ ] All tests are passing - [ ] Type definitions, if updated, pass `types:check` and `pack:check` -- [ ] Benchmark performance has not significantly decreased +- [ ] Benchmark performance (`pnpm bench`, CI job summary) has not significantly decreased - [ ] Bundle size (`pnpm size`, CI job summary) has not been significantly impacted diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd501ab..4993130 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -126,3 +126,25 @@ jobs: node -e 'for (const [p, v] of [["react", "18.0.0"], ["react-dom", "18.0.0"], ["preact", "10."]]) { const got = require(`${p}/package.json`).version; console.log(`${p} ${got}`); if (!got.startsWith(v)) throw new Error(`${p} is ${got}, expected ${v}`); }' - name: Test run: pnpm test --project node + + # Report only: `pnpm bench` adds the benchmark tables to the job summary. + # Shared runners are too noisy to gate on the numbers; the job fails only + # if the benchmark itself breaks. + bench: + name: Benchmarks + runs-on: ubuntu-latest + permissions: + contents: read # to check out repo (actions/checkout) + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@v7 + with: + node-version-file: '.nvmrc' + cache: 'pnpm' + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Benchmarks + run: pnpm bench diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c9857e4..dc1b635 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,8 +13,8 @@ We encourage pull requests concerning: - Integrating updates from `fast-deep-equal` - This, unfortunately, now requires more manual work. Use the comment blocks in `index.js` to figure out what to paste and where. - Integrating tests from `fast-deep-equal` - Copy its `spec/` test files into `test/fixtures/fast-deep-equal/` - and update the version in that directory's README. The `fast-deep-equal` npm package in `devDependencies` is the - benchmark baseline. + and update the version in that directory's README. The `fast-deep-equal` npm package in `devDependencies` is one of + the [benchmark](#benchmarks) baselines. - Bugs in this library - New tests for React - Documentation @@ -55,7 +55,7 @@ included. For an urgent security fix that's newer than that, add the package to ```sh # Format, lint, types, all tests, then the packed package $ pnpm run check -$ pnpm run benchmark +$ pnpm run bench ``` ### Testing @@ -171,6 +171,45 @@ CI runs it against the base commit and adds the table to the job summary. It's a report, not a check: if the min+gz size grows significantly, flag it to your reviewers so you can discuss whether the addition is worth it. +### Benchmarks + +`pnpm bench` runs `bench/compare.bench.mjs` with Vitest's +[benchmarking](https://vitest.dev/guide/benchmarking) in Node. For each group +of test fixtures (`generic`, `es6`, `react`, `custom`), it compares: + +- `this tree`: `index.js` in your working tree, +- `published`: the last release (`react-fast-compare-published`, an aliased + devDependency), and +- `fast-deep-equal`: the upstream version we forked from. + +A candidate that throws on a group's cases (`fast-deep-equal` on +`Object.create(null)` objects) is left out of that group, and the test title +says so. + +```sh +$ pnpm bench +# One fixture group +$ pnpm bench -t react +# In a browser instead of Node (coarser timers) +$ pnpm exec vitest bench --run --project 'browser (chromium) (bench)' +``` + +CI runs it in its own job and adds the tables, with each candidate's +difference from `published`, to the job summary. It's a report, not a check: +shared runners are too noisy to gate on. If your change touches the comparison +code, run it locally before and after, and mention any difference beyond a few +percent in your PR. + +Differences under about 3–5% in a single run are noise. As a calibration: +`this tree` and `published` run the same code until a source change lands, yet +they differ by up to ~2% from run to run, more than the ±RME column suggests +(it only measures variation within a run). Re-run before reading anything into +a small gap. + +After a release, bump `react-fast-compare-published` to it +(`pnpm add -D react-fast-compare-published@npm:react-fast-compare@`) +once it's a week old (`minimumReleaseAge`). + ## Before submitting a PR... ... please make sure that you have done the following: @@ -179,13 +218,12 @@ reviewers so you can discuss whether the addition is worth it. ```sh $ pnpm run check - $ pnpm run benchmark + $ pnpm run bench ``` -2. Confirm we don't have any significant performance regressions (check out `master` for a baseline comparison on _your_ machine). +2. Confirm we don't have any significant performance regressions (see [benchmarks](#benchmarks)). 3. Confirm you aren't significantly impacting our bundle size (see [size](#size)). - - _Org members:_ Update the README's benchmark comparison png using this [internal Google Sheet template](https://docs.google.com/spreadsheets/d/1GuqpO0wgPjQ9usx6sR3t0Y_HTmAdRqjXkSjs3SBsmTc/edit?usp=sharing_eip&ts=5ed1642f). ## Releasing diff --git a/README.md b/README.md index 0cb4dc5..2855674 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,6 @@ This is a fork of the brilliant [fast-deep-equal](https://github.com/epoberezkin/fast-deep-equal) with some extra handling for React. -![benchmark chart](https://raw.githubusercontent.com/FormidableLabs/react-fast-compare/master/assets/benchmarking.png 'benchmarking chart') - -(Check out the [benchmarking details](#benchmarking-this-library).) - ## Install ```sh @@ -74,51 +70,21 @@ be for you! ## Benchmarking this Library -The absolute values are much less important than the relative differences -between packages. - -Benchmarking source can be found -[here](https://github.com/FormidableLabs/react-fast-compare/blob/master/benchmark/index.js). -Each "operation" consists of running all relevant tests. The React benchmark -uses both the generic tests and the react tests; these runs will be slower -simply because there are more tests in each operation. - -The results below are from a local test on a laptop _(stats last updated 6/2/2020)_: - -### Generic Data - -``` -react-fast-compare x 177,600 ops/sec ±1.73% (92 runs sampled) -fast-deep-equal x 184,211 ops/sec ±0.65% (87 runs sampled) -lodash.isEqual x 39,826 ops/sec ±1.32% (86 runs sampled) -nano-equal x 176,023 ops/sec ±0.89% (92 runs sampled) -shallow-equal-fuzzy x 146,355 ops/sec ±0.64% (89 runs sampled) - fastest: fast-deep-equal -``` - -`react-fast-compare` and `fast-deep-equal` should be the same speed for these -tests; any difference is just noise. `react-fast-compare` won't be faster than -`fast-deep-equal`, because it's based on it. - -### React and Generic Data - -``` -react-fast-compare x 86,392 ops/sec ±0.70% (93 runs sampled) -fast-deep-equal x 85,567 ops/sec ±0.95% (92 runs sampled) -lodash.isEqual x 7,369 ops/sec ±1.78% (84 runs sampled) - fastest: react-fast-compare,fast-deep-equal -``` - -Two of these packages cannot handle comparing React elements, because they -contain circular reference: `nano-equal` and `shallow-equal-fuzzy`. - -### Running Benchmarks +`react-fast-compare` is based on `fast-deep-equal`, and aims to be as fast on +generic data while also handling React elements. The benchmark +([`bench/`](./bench/compare.bench.mjs), Vitest's +[benchmarking](https://vitest.dev/guide/benchmarking)) compares this code with +the last published version and with `fast-deep-equal`, over the same test cases +the tests use: ```sh $ pnpm install -$ pnpm run benchmark +$ pnpm bench ``` +Absolute numbers depend on the machine; compare rows from the same run. CI adds +the results to the job summary of each run. + ## Differences between this library and `fast-deep-equal` `react-fast-compare` is based on `fast-deep-equal`, with some additions: diff --git a/assets/benchmarking.png b/assets/benchmarking.png deleted file mode 100644 index 95373b4..0000000 Binary files a/assets/benchmarking.png and /dev/null differ diff --git a/bench/compare.bench.mjs b/bench/compare.bench.mjs new file mode 100644 index 0000000..f14f5d4 --- /dev/null +++ b/bench/compare.bench.mjs @@ -0,0 +1,60 @@ +// Self-regression benchmark (`pnpm bench`): this tree vs. the last published +// version vs. fast-deep-equal, the upstream we forked from. One benchmark per +// fixture group; each operation compares every case in the group once. +// +// A candidate that throws on any case in a group is left out of that group +// (and named in the test title): timing exceptions would swamp the numbers. +// Correctness is the tests' job, not this file's. +import { test } from 'vitest'; + +import fastDeepEqual from 'fast-deep-equal/es6/react.js'; +import published from 'react-fast-compare-published'; + +import equal from '../index.js'; +import * as fixtures from '../test/fixtures/index.mjs'; + +// `published` is an aliased devDependency on the last release; bump it after +// each release (see CONTRIBUTING). +const CANDIDATES = { + 'this tree': equal, + published, + 'fast-deep-equal': fastDeepEqual, +}; + +const GROUPS = { + generic: fixtures.generic, + es6: fixtures.es6, + react: fixtures.react, + custom: fixtures.custom, +}; + +const throws = (fn, cases) => + cases.some(({ value1, value2 }) => { + try { + fn(value1, value2); + return false; + } catch { + return true; + } + }); + +for (const [group, suites] of Object.entries(GROUPS)) { + const cases = suites.flatMap((suite) => suite.tests); + const candidates = Object.entries(CANDIDATES); + const skipped = candidates.filter(([, fn]) => throws(fn, cases)); + const title = skipped.length + ? `${group} (${skipped.map(([name]) => name).join(', ')} skipped: throws)` + : group; + + test(title, async ({ bench }) => { + await bench.compare( + ...candidates + .filter((candidate) => !skipped.includes(candidate)) + .map(([name, fn]) => + bench(name, () => { + for (const { value1, value2 } of cases) fn(value1, value2); + }) + ) + ); + }); +} diff --git a/bench/summary-reporter.mjs b/bench/summary-reporter.mjs new file mode 100644 index 0000000..41acfe6 --- /dev/null +++ b/bench/summary-reporter.mjs @@ -0,0 +1,60 @@ +// Vitest reporter for `pnpm bench`: in GitHub Actions, appends the benchmark +// results to the job summary as Markdown tables. Elsewhere it does nothing; +// the default reporter already prints the tables. +import fs from 'node:fs'; +import { createRequire } from 'node:module'; + +const BASELINE = 'published'; +const { version } = createRequire(import.meta.url)( + 'react-fast-compare-published/package.json' +); + +const format = (n, digits = 0) => + n.toLocaleString('en-US', { + minimumFractionDigits: digits, + maximumFractionDigits: digits, + }); + +const versus = (task, baseline) => { + if (!baseline || task === baseline) return ''; + const diff = (task.throughput.mean / baseline.throughput.mean - 1) * 100; + return `${diff >= 0 ? '+' : ''}${diff.toFixed(1)}%`; +}; + +const table = (title, { tasks }) => { + const baseline = tasks.find((task) => task.name === BASELINE); + return [ + `#### ${title}`, + '', + `| | ops/s | Mean (µs) | ±RME | Samples | vs. ${BASELINE} |`, + '|---|--:|--:|--:|--:|--:|', + ...tasks.map( + (task) => + `| ${task.name} | ${format(task.throughput.mean)} | ${format(task.latency.mean * 1000, 3)} | ${task.latency.rme.toFixed(2)}% | ${format(task.latency.samplesCount)} | ${versus(task, baseline)} |` + ), + '', + ]; +}; + +export default class SummaryReporter { + sections = []; + + onTestCaseResult(testCase) { + const benchmarks = testCase.benchmarks().filter((b) => b.tasks.length); + for (const benchmark of benchmarks) + this.sections.push(table(testCase.name, benchmark)); + } + + onTestRunEnd() { + const file = process.env.GITHUB_STEP_SUMMARY; + if (!file || !this.sections.length) return; + const report = [ + '### Benchmarks', + '', + `\`${BASELINE}\` is react-fast-compare@${version}. Node ${process.version}; operations per second, higher is better. A report, not a check: CI runners are noisy, so compare locally before drawing conclusions.`, + '', + ...this.sections.flat(), + ].join('\n'); + fs.appendFileSync(file, report + '\n'); + } +} diff --git a/benchmark/index.js b/benchmark/index.js deleted file mode 100644 index d5a411d..0000000 --- a/benchmark/index.js +++ /dev/null @@ -1,115 +0,0 @@ -'use strict'; - -const tests = require('../test/fixtures/index.mjs'); -const Benchmark = require('benchmark'); - -const correctnessTests = []; -const genericSuite = new Benchmark.Suite(); -const advancedSuite = new Benchmark.Suite(); - -const equalPackages = { - 'react-fast-compare': require('../index'), - 'fast-deep-equal': require('fast-deep-equal/es6/react'), - 'lodash.isEqual': require('lodash').isEqual, - 'nano-equal': require('nano-equal'), - 'shallow-equal-fuzzy': require('shallow-equal-fuzzy'), -}; - -const advancedPkgs = new Set([ - 'react-fast-compare', - 'fast-deep-equal', - 'lodash.isEqual', -]); - -for (const equalName in equalPackages) { - const equalFunc = equalPackages[equalName]; - - genericSuite.add(equalName, function () { - for (const testSuite of tests.generic) { - for (const test of testSuite.tests) { - try { - equalFunc(test.value1, test.value2); - } catch { - // swallow errors during benchmarking. they are reported in the test section - } - } - } - }); - - if (advancedPkgs.has(equalName)) { - advancedSuite.add(equalName, function () { - for (const testSuite of tests.all) { - for (const test of testSuite.tests) { - try { - equalFunc(test.value1, test.value2); - } catch { - // swallow errors during benchmarking. they are reported in the test section - } - } - } - }); - - correctnessTests.push(() => console.log(equalName)); - for (const testSuite of tests.all) { - for (const test of testSuite.tests) { - correctnessTests.push(() => { - try { - if (equalFunc(test.value1, test.value2) !== test.equal) { - console.error( - '- different result:', - equalName, - testSuite.description, - test.description - ); - } - } catch (error) { - console.error( - '- error:', - testSuite.description, - test.description, - error.message - ); - } - }); - } - } - } -} - -const chartData = {}; - -console.log('\n--- speed tests: generic usage ---\n'); - -genericSuite - .on('cycle', (event) => console.log(String(event.target))) - .on('complete', function () { - console.log(' fastest: ' + this.filter('fastest').map('name')); - chartData.categories = this.map((test) => test.name); - chartData.genericTestData = this.map((test) => ({ - x: test.name, - y: test.hz, - })); - }) - .run({ async: false }); - -console.log('\n--- speed tests: generic and react ---\n'); - -advancedSuite - .on('cycle', (event) => console.log(String(event.target))) - .on('complete', function () { - console.log(' fastest: ' + this.filter('fastest').map('name')); - chartData.reactAndGenericTestData = this.map((test) => ({ - x: test.name, - y: test.hz, - })); - }) - .run({ async: false }); - -// **Note**: `lodash.isEqual` gets different results for Sets, Maps -// because it **is** correct and `fast-deep-equal` is not. -// See: https://github.com/FormidableLabs/react-fast-compare/issues/50 -console.log('\n--- correctness tests: generic and react ---\n'); - -correctnessTests.forEach((test) => test()); - -console.log(JSON.stringify(chartData, null, 2)); diff --git a/package.json b/package.json index 19412df..bcec8e8 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "typings": "index.d.ts", "scripts": { "check": "pnpm format && pnpm lint && pnpm lint:gha && pnpm types:check && pnpm test && pnpm pack:check", - "benchmark": "node benchmark", "format": "oxfmt --check", "format:fix": "oxfmt", "lint": "oxlint --report-unused-disable-directives-severity=error", @@ -16,7 +15,8 @@ "test": "vitest run", "changeset": "changeset", "pack:check": "node scripts/pack-check.mjs", - "size": "node scripts/size.mjs" + "size": "node scripts/size.mjs", + "bench": "vitest bench --run --project \"node (bench)\" --reporter=default --reporter=./bench/summary-reporter.mjs" }, "repository": { "type": "git", @@ -44,11 +44,8 @@ "@types/react": "^19.3.0", "@vitest/browser-playwright": "^5.0.5", "@vitest/coverage-v8": "^5.0.5", - "benchmark": "^2.1.4", "fast-deep-equal": "3.1.3", "jsdom": "^30.1.3", - "lodash": "^4.17.10", - "nano-equal": "^2.0.2", "oxfmt": "0.75.0", "oxlint": "1.90.0", "playwright": "^1.64.1", @@ -56,7 +53,7 @@ "publint": "0.3.25", "react": "^19.3.1", "react-dom": "^19.3.1", - "shallow-equal-fuzzy": "0.0.2", + "react-fast-compare-published": "npm:react-fast-compare@3.2.2", "terser": "5.51.2", "typescript": "7.0.2", "vitest": "^5.0.5" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c5eb1c..ca02420 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -184,21 +184,12 @@ importers: '@vitest/coverage-v8': specifier: ^5.0.5 version: 5.0.5(@vitest/browser@5.0.5)(vitest@5.0.5) - benchmark: - specifier: ^2.1.4 - version: 2.1.4 fast-deep-equal: specifier: 3.1.3 version: 3.1.3 jsdom: specifier: ^30.1.3 version: 30.1.3 - lodash: - specifier: ^4.17.10 - version: 4.17.21 - nano-equal: - specifier: ^2.0.2 - version: 2.0.2 oxfmt: specifier: 0.75.0 version: 0.75.0 @@ -220,9 +211,9 @@ importers: react-dom: specifier: ^19.3.1 version: 19.3.1(react@19.3.1) - shallow-equal-fuzzy: - specifier: 0.0.2 - version: 0.0.2 + react-fast-compare-published: + specifier: npm:react-fast-compare@3.2.2 + version: react-fast-compare@3.2.2 terser: specifier: 5.51.2 version: 5.51.2 @@ -1026,9 +1017,6 @@ packages: ast-v8-to-istanbul@1.0.7: resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==} - benchmark@2.1.4: - resolution: {integrity: sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==} - bidi-js@1.1.0: resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} @@ -1292,9 +1280,6 @@ packages: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - lru-cache@11.5.3: resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} engines: {node: 20 || >=22} @@ -1330,9 +1315,6 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nano-equal@2.0.2: - resolution: {integrity: sha512-RtAioZtSSppEWRruYU8oz2AKPJWTW74TpzcqP79n7Kh4nHW+gmrJGcTuELmid9tvGR4Xp2cCoTtYIIDy9iWO4g==} - nanoid@3.3.20: resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1404,9 +1386,6 @@ packages: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} - platform@1.3.5: - resolution: {integrity: sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==} - playwright-core@1.64.1: resolution: {integrity: sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==} engines: {node: '>=20'} @@ -1447,6 +1426,9 @@ packages: peerDependencies: react: ^19.3.1 + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + react@19.3.1: resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==} engines: {node: '>=0.10.0'} @@ -1480,10 +1462,6 @@ packages: engines: {node: '>=10'} hasBin: true - shallow-equal-fuzzy@0.0.2: - resolution: {integrity: sha512-HKGvWaQ6fuFhNH2k1krxxmhKYxyPSbPzwCkM94qYVoLKCGCqg0r9bdCoX4A37UJvAfyLje9zHS5F/SsrYgrqgA==} - engines: {node: '>=0.12.0'} - shell-quote@1.12.0: resolution: {integrity: sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==} engines: {node: '>= 0.4'} @@ -2359,11 +2337,6 @@ snapshots: estree-walker: 3.0.3 js-tokens: 10.0.0 - benchmark@2.1.4: - dependencies: - lodash: 4.17.21 - platform: 1.3.5 - bidi-js@1.1.0: dependencies: require-from-string: 2.0.2 @@ -2584,8 +2557,6 @@ snapshots: lightningcss-win32-arm64-msvc: 1.33.0 lightningcss-win32-x64-msvc: 1.33.0 - lodash@4.17.21: {} - lru-cache@11.5.3: {} magic-string@1.4.5: @@ -2623,8 +2594,6 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nano-equal@2.0.2: {} - nanoid@3.3.20: {} node-emoji@2.2.0: @@ -2704,8 +2673,6 @@ snapshots: picomatch@4.0.7: {} - platform@1.3.5: {} - playwright-core@1.64.1: {} playwright@1.64.1: @@ -2736,6 +2703,8 @@ snapshots: react: 19.3.1 scheduler: 0.28.0 + react-fast-compare@3.2.2: {} + react@19.3.1: {} require-directory@2.1.1: {} @@ -2775,8 +2744,6 @@ snapshots: semver@7.8.5: {} - shallow-equal-fuzzy@0.0.2: {} - shell-quote@1.12.0: {} sirv@3.0.2: diff --git a/vitest.config.mts b/vitest.config.mts index 5ab4128..e7a253a 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -61,6 +61,12 @@ export default defineConfig({ }, }, ], + // `pnpm bench`: `bench/*.bench.mjs` run only under `vitest bench`, which + // adds a " (bench)" project for each project above. The script + // picks `node (bench)`. + benchmark: { + include: ['bench/*.bench.mjs'], + }, // `pnpm test --project node --coverage` (CI's Coverage step). v8 coverage // only works in Node and Chromium, so the browser project doesn't run it. coverage: {