diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml new file mode 100644 index 0000000..7037817 --- /dev/null +++ b/.github/workflows/benchmarks.yml @@ -0,0 +1,102 @@ +name: benchmarks + +on: [pull_request] + +jobs: + size: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 50 + - uses: actions/setup-node@v4 + with: + node-version: '20.x' + - name: 'Setup temporary files' + run: | + echo "BASE_JSON=$(mktemp)" >> $GITHUB_ENV + echo "PATCH_JSON=$(mktemp)" >> $GITHUB_ENV + - name: 'Benchmark base' + run: | + git checkout -f ${{ github.event.pull_request.base.sha }} + npm install --loglevel error + if npm run benchmark:size -- -o ${{ env.BASE_JSON }}; then + echo "Ran successfully on base branch" + else + echo "{}" > ${{ env.BASE_JSON }} # Empty JSON as default + echo "Benchmark script not found on base branch, using default values" + fi + - name: 'Benchmark patch' + run: | + git checkout -f ${{ github.event.pull_request.head.sha }} + npm install --loglevel error + npm run benchmark:size -- -o ${{ env.PATCH_JSON }} + echo "Ran successfully on patch branch" + - name: 'Collect results' + id: collect + run: | + echo "table<> $GITHUB_OUTPUT + npm run benchmark:compare -- ${{ env.BASE_JSON }} ${{ env.PATCH_JSON }} >> markdown + cat markdown >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + - name: 'Post comment' + uses: edumserrano/find-create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + body-includes: '' + comment-author: 'github-actions[bot]' + body: | + + ### workflow: benchmarks/size + Comparison of minified (terser) and compressed (brotli) size results, measured in bytes. Smaller is better. + ${{ steps.collect.outputs.table }} + edit-mode: replace + + perf: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 50 + - uses: actions/setup-node@v4 + with: + node-version: '20.x' + - name: 'Setup temporary files' + run: | + echo "BASE_JSON=$(mktemp)" >> $GITHUB_ENV + echo "PATCH_JSON=$(mktemp)" >> $GITHUB_ENV + - name: 'Benchmark base' + run: | + git checkout -f ${{ github.event.pull_request.base.sha }} + npm install --loglevel error + if npm run benchmark:perf -- -o ${{ env.BASE_JSON }}; then + echo "Ran successfully on base branch" + else + echo "{}" > ${{ env.BASE_JSON }} # Empty JSON as default + echo "Benchmark script not found on base branch, using default values" + fi + - name: 'Benchmark patch' + run: | + git checkout -f ${{ github.event.pull_request.head.sha }} + npm install --loglevel error + npm run benchmark:perf -- -o ${{ env.PATCH_JSON }} + echo "Ran successfully on patch branch" + - name: 'Collect results' + id: collect + run: | + echo "table<> $GITHUB_OUTPUT + npm run benchmark:compare -- ${{ env.BASE_JSON }} ${{ env.PATCH_JSON }} >> markdown + cat markdown >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + - name: 'Post comment' + uses: edumserrano/find-create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + body-includes: '' + comment-author: 'github-actions[bot]' + body: | + + ### workflow: benchmarks/perf + Comparison of performance test results, measured in operations per second. Larger is better. + ${{ steps.collect.outputs.table }} + edit-mode: replace diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml deleted file mode 100644 index 07acf5d..0000000 --- a/.github/workflows/performance.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: performance - -on: [pull_request] - -jobs: - compressed-size: - name: compressed-size - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: necolas/compressed-size-action@master - with: - build-script: "build" - pattern: "./dist/**/*.js" - repo-token: "${{ secrets.GITHUB_TOKEN }}" - - benchmarks: - name: benchmarks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 50 - - uses: actions/setup-node@v4 - with: - node-version: '20.x' - - name: 'Setup temporary files' - run: | - echo "BASE_SHA=$(echo ${{ github.event.pull_request.base.sha }} | cut -c1-8)" >> $GITHUB_ENV - echo "PATCH_SHA=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-8)" >> $GITHUB_ENV - echo "BASELINE_JSON=$(mktemp)" >> $GITHUB_ENV - echo "PATCH_JSON=$(mktemp)" >> $GITHUB_ENV - echo "PR_COMMENT=$(mktemp)" >> $GITHUB_ENV - - name: 'Benchmark: baseline' - run: | - git checkout ${{ github.event.pull_request.base.sha }} - npm install - npm run build - npm run benchmark > ${{ env.BASELINE_JSON }} - - name: 'Benchmark: patch' - run: | - git checkout ${{ github.event.pull_request.head.sha }} - npm install - npm run build - npm run benchmark > ${{ env.PATCH_JSON }} - - name: 'Collect results' - run: | - echo "## Benchmarks" >> pr_comment - echo "### Base ${BASE_SHA}" >> pr_comment - tail -n +2 ${{ env.BASELINE_JSON }} >> pr_comment - echo "### Patch ${PATCH_SHA}" >> pr_comment - tail -n +2 ${{ env.PATCH_JSON }} >> pr_comment - cat pr_comment > ${{ env.PR_COMMENT }} - - name: 'Post comment' - uses: actions/github-script@v4.0.2 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: require('fs').readFileSync('${{ env.PR_COMMENT }}').toString() - }); diff --git a/.gitignore b/.gitignore index a8c5fb1..57f5e37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -*.log* coverage dist +logs node_modules diff --git a/benchmark/compare.js b/benchmark/compare.js new file mode 100644 index 0000000..0051540 --- /dev/null +++ b/benchmark/compare.js @@ -0,0 +1,103 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +const fs = require('fs'); + +function readJsonFile(filePath) { + try { + const fileContents = fs.readFileSync(filePath, 'utf8'); + const data = JSON.parse(fileContents); + return data; + } catch (error) { + console.error(`Error reading file ${filePath}:`, error); + return null; + } +} + +function mergeData(base, patch) { + const merged = {}; + function addToMerged(data, fileIndex) { + Object.keys(data).forEach((key) => { + if (merged[key] == null) { + merged[key] = {}; + } + Object.keys(data[key]).forEach((subKey) => { + if (merged[key][subKey] == null) { + merged[key][subKey] = {}; + } + merged[key][subKey][fileIndex] = data[key][subKey]; + }); + }); + } + if (base != null) { + addToMerged(base, 1); + } + if (patch != null) { + addToMerged(patch, 2); + } + return merged; +} + +function generateComparisonData(results) { + const baseResult = parseInt(results[1], 10); + const patchResult = parseInt(results[2], 10); + const isValidBase = !isNaN(baseResult); + const isValidPatch = !isNaN(patchResult); + let icon = '', + ratioFixed = ''; + + if (isValidBase && isValidPatch) { + const ratio = patchResult / baseResult; + ratioFixed = ratio.toFixed(2); + if (ratio < 0.95 || ratio > 1.05) { + icon = '**!!**'; + } else if (ratio < 1) { + icon = '-'; + } else if (ratio > 1) { + icon = '+'; + } + } + + return { + baseResult: isValidBase ? baseResult.toLocaleString() : '', + patchResult: isValidPatch ? patchResult.toLocaleString() : '', + ratio: ratioFixed, + icon, + }; +} + +function generateMarkdownTable(mergedData) { + const rows = []; + rows.push('| **Results** | **Base** | **Patch** | **Ratio** | |'); + rows.push('| :--- | ---: | ---: | ---: | ---: |'); + Object.keys(mergedData).forEach((suiteName) => { + rows.push('| | | | |'); + rows.push(`| **${suiteName}** | | | | |`); + Object.keys(mergedData[suiteName]).forEach((test) => { + const results = mergedData[suiteName][test]; + const { baseResult, patchResult, ratio, icon } = + generateComparisonData(results); + rows.push( + `| · ${test} | ${baseResult} | ${patchResult} | ${ratio} | ${icon} |` + ); + }); + }); + return rows.join('\n'); +} + +/** + * Compare up to 2 different benchmark runs + */ +const args = process.argv.slice(2); +const baseResults = args[0] ? readJsonFile(args[0]) : null; +const patchResults = args[1] ? readJsonFile(args[1]) : null; +const mergedData = mergeData(baseResults, patchResults); +const markdownTable = generateMarkdownTable(mergedData); + +console.log(markdownTable); diff --git a/test/benchmark.node.js b/benchmark/performance.js similarity index 73% rename from test/benchmark.node.js rename to benchmark/performance.js index cf37cec..4f4bce3 100644 --- a/test/benchmark.node.js +++ b/benchmark/performance.js @@ -9,78 +9,91 @@ const fs = require('fs'); const Benchmark = require('benchmark'); - +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); const { styleq } = require('../dist/styleq'); const { localizeStyle } = require('../dist/transform-localize-style'); +/** + * CLI + */ + +// run.js --outfile filename.js +const argv = yargs(hideBin(process.argv)).option('outfile', { + alias: 'o', + type: 'string', + description: 'Output file', + demandOption: false, +}).argv; +const outfile = argv.outfile; + /** * Test helpers */ -const suite = new Benchmark.Suite('styleq-benchmarks'); -const test = (...args) => suite.add(...args); - -function jsonReporter(suite) { - const benchmarks = []; - const config = { - folder: 'logs', - callback(results, name, folder) { - // Write the results log - const dirpath = `${process.cwd()}/${folder}`; - const filepath = `${dirpath}/${name}.log`; - if (!fs.existsSync(dirpath)) { - fs.mkdirSync(dirpath); - } - fs.writeFileSync(filepath, `${JSON.stringify(results, null, 2)}\n`); - - // Print the markdown table - let markdown = ''; - markdown += `| Benchmark | ops/sec | deviation (%) | samples |\n`; - markdown += `| :--- | ---: | ---: | ---: |\n`; - markdown += results - .map((data) => { - const { name, deviation, ops, samples } = data; - const prettyOps = parseInt(ops, 10).toLocaleString(); - return `| ${name} | ${prettyOps} | ${deviation} | ${samples} |`; - }) - .join('\n'); - console.log(markdown); - }, - }; +function createSuite(name, options) { + const suite = new Benchmark.Suite(name); + const test = (...args) => suite.add(...args); - suite.on('cycle', (event) => { - benchmarks.push(event.target); - }); + function jsonReporter(suite) { + const benchmarks = []; - suite.on('error', (event) => { - throw new Error(String(event.target.error)); - }); + suite.on('cycle', (event) => { + benchmarks.push(event.target); + }); + + suite.on('error', (event) => { + throw new Error(String(event.target.error)); + }); + + suite.on('complete', () => { + const timestamp = Date.now(); + const result = benchmarks.map((bench) => { + if (bench.error) { + return { + name: bench.name, + id: bench.id, + error: bench.error, + }; + } - suite.on('complete', () => { - const timestamp = Date.now(); - const result = benchmarks.map((bench) => { - if (bench.error) { return { name: bench.name, id: bench.id, - error: bench.error, + samples: bench.stats.sample.length, + deviation: bench.stats.rme.toFixed(2), + ops: bench.hz.toFixed(bench.hz < 100 ? 2 : 0), + timestamp, }; - } - - return { - name: bench.name, - id: bench.id, - samples: bench.stats.sample.length, - deviation: bench.stats.rme.toFixed(2), - ops: bench.hz.toFixed(bench.hz < 100 ? 2 : 0), - timestamp, - }; + }); + options.callback(result, suite.name); }); - config.callback(result, suite.name, config.folder); - }); + } + + jsonReporter(suite); + return { suite, test }; } -jsonReporter(suite); +/** + * Test setup + */ + +const aggregatedResults = {}; +const options = { + callback(data, suiteName) { + const testResults = data.reduce((acc, test) => { + const { name, ops } = test; + acc[name] = ops; + return acc; + }, {}); + + aggregatedResults[suiteName] = testResults; + }, +}; + +console.log('Running performance benchmark, please wait...'); + +const { suite, test } = createSuite('styleq', options); /** * Additional test subjects @@ -205,6 +218,10 @@ const complexNestedStyleFixture = [ ], ]; +/** + * Performance tests + */ + // SMALL OBJECT test('small object', () => { @@ -381,3 +398,29 @@ test('transform: localize-style', () => { }); suite.run(); + +/** + * Print results + */ + +const aggregatedResultsString = JSON.stringify(aggregatedResults, null, 2); + +// Print / Write results +const now = new Date(); +const year = now.getFullYear(); +const month = String(now.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed +const day = String(now.getDate()).padStart(2, '0'); +const hours = String(now.getHours()).padStart(2, '0'); +const minutes = String(now.getMinutes()).padStart(2, '0'); +const timestamp = `${year}${month}${day}-${hours}${minutes}`; + +const dirpath = `${process.cwd()}/logs`; +const filepath = `${dirpath}/perf-${timestamp}.json`; +if (!fs.existsSync(dirpath)) { + fs.mkdirSync(dirpath); +} +const outpath = outfile || filepath; +fs.writeFileSync(outpath, `${aggregatedResultsString}\n`); + +console.log(aggregatedResultsString); +console.log('Results written to', outpath); diff --git a/benchmark/size.js b/benchmark/size.js new file mode 100644 index 0000000..7cdd04c --- /dev/null +++ b/benchmark/size.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Nicolas Gallagher + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +const brotliSizePkg = require('brotli-size'); +const fs = require('fs'); +const path = require('path'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); +const { minify_sync } = require('terser'); + +// run.js --outfile filename.js +const argv = yargs(hideBin(process.argv)).option('outfile', { + alias: 'o', + type: 'string', + description: 'Output file', + demandOption: false, +}).argv; +const outfile = argv.outfile; + +const files = [ + path.join(__dirname, '../dist/styleq.js'), + path.join(__dirname, '../dist/transform-localize-style.js'), +]; + +console.log('Running benchmark-size, please wait...'); + +const sizes = files.map((file) => { + const code = fs.readFileSync(file, 'utf8'); + const result = minify_sync(code).code; + const minified = Buffer.byteLength(result, 'utf8'); + const compressed = brotliSizePkg.sync(result); + return { file, compressed, minified }; +}); + +const aggregatedResults = {}; +sizes.forEach((entry) => { + const { file, minified, compressed } = entry; + const filename = file.split('dist/')[1]; + aggregatedResults[filename] = { + compressed, + minified, + }; +}); + +const aggregatedResultsString = JSON.stringify(aggregatedResults, null, 2); + +// Print / Write results +const now = new Date(); +const year = now.getFullYear(); +const month = String(now.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed +const day = String(now.getDate()).padStart(2, '0'); +const hours = String(now.getHours()).padStart(2, '0'); +const minutes = String(now.getMinutes()).padStart(2, '0'); +const timestamp = `${year}${month}${day}-${hours}${minutes}`; + +const dirpath = `${process.cwd()}/logs`; +const filepath = `${dirpath}/size-${timestamp}.json`; +if (!fs.existsSync(dirpath)) { + fs.mkdirSync(dirpath); +} +const outpath = outfile || filepath; +fs.writeFileSync(outpath, `${aggregatedResultsString}\n`); + +console.log(aggregatedResultsString); +console.log('Results written to', outpath); diff --git a/configs/.eslintrc b/configs/.eslintrc index c51227d..6f86108 100644 --- a/configs/.eslintrc +++ b/configs/.eslintrc @@ -31,6 +31,7 @@ "ignorePatterns": [ "coverage/", "dist/", + "logs/", "node_modules/" ], "globals": {}, diff --git a/configs/.flowconfig b/configs/.flowconfig index 2bae171..4091d6d 100644 --- a/configs/.flowconfig +++ b/configs/.flowconfig @@ -5,6 +5,7 @@ .*/coverage/.* .*/dist/.* +.*/logs/.* .*/node_modules/.* [options] diff --git a/configs/.prettierignore b/configs/.prettierignore index 18f2b36..57f5e37 100644 --- a/configs/.prettierignore +++ b/configs/.prettierignore @@ -1,3 +1,4 @@ coverage dist +logs node_modules diff --git a/package-lock.json b/package-lock.json index 4a5954b..8a49219 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,12 +17,15 @@ "@babel/preset-flow": "^7.14.5", "@babel/types": "^7.15.6", "benchmark": "^2.1.4", + "brotli-size": "^4.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^7.2.0", "eslint-plugin-flowtype": "^7.0.0", "flow-bin": "^0.195.2", "jest": "^27.2.4", - "prettier": "^2.4.1" + "prettier": "^2.4.1", + "terser": "^5.3.0", + "yargs": "17.7.2" } }, "node_modules/@babel/cli": { @@ -2474,6 +2477,70 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", @@ -3054,6 +3121,19 @@ "node": ">=8" } }, + "node_modules/brotli-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz", + "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "0.1.1" + }, + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", @@ -3203,14 +3283,18 @@ "dev": true }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/co": { @@ -3471,6 +3555,12 @@ "node": ">=8" } }, + "node_modules/duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==", + "dev": true + }, "node_modules/electron-to-chromium": { "version": "1.4.30", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.30.tgz", @@ -4876,6 +4966,18 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/jest-cli/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4915,6 +5017,35 @@ "node": ">=8" } }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/jest-config": { "version": "27.4.5", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.4.5.tgz", @@ -5908,6 +6039,18 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-runtime/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/jest-runtime/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -5956,6 +6099,35 @@ "node": ">=8" } }, + "node_modules/jest-runtime/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-runtime/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/jest-serializer": { "version": "27.4.0", "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz", @@ -7593,6 +7765,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/terser": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -7898,6 +8109,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -7915,6 +8127,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -7930,6 +8143,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -7941,7 +8155,8 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wrappy": { "version": "1.0.2", @@ -8010,30 +8225,32 @@ "dev": true }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } } }, @@ -9777,6 +9994,55 @@ } } }, + "@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", @@ -10256,6 +10522,15 @@ "fill-range": "^7.0.1" } }, + "brotli-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz", + "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==", + "dev": true, + "requires": { + "duplexer": "0.1.1" + } + }, "browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", @@ -10365,13 +10640,13 @@ "dev": true }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, @@ -10581,6 +10856,12 @@ } } }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==", + "dev": true + }, "electron-to-chromium": { "version": "1.4.30", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.30.tgz", @@ -11605,6 +11886,17 @@ "supports-color": "^7.1.0" } }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -11634,6 +11926,27 @@ "requires": { "has-flag": "^4.0.0" } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true } } }, @@ -12383,6 +12696,17 @@ "supports-color": "^7.1.0" } }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -12418,6 +12742,27 @@ "requires": { "has-flag": "^4.0.0" } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true } } }, @@ -13671,6 +14016,32 @@ "supports-hyperlinks": "^2.0.0" } }, + "terser": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -13994,24 +14365,24 @@ "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true } } diff --git a/package.json b/package.json index 79d6383..d5612a0 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "*.ts" ], "scripts": { - "benchmark": "node test/benchmark.node.js", + "benchmark:compare": "node benchmark/compare.js", + "benchmark:perf": "node benchmark/performance.js", + "benchmark:size": "node benchmark/size.js", "build": "babel src --out-dir dist --config-file ./configs/.babelrc", "flow": "flow --flowconfig-name ./configs/.flowconfig", "jest": "jest ./test", @@ -34,12 +36,15 @@ "@babel/preset-flow": "^7.14.5", "@babel/types": "^7.15.6", "benchmark": "^2.1.4", + "brotli-size": "^4.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^7.2.0", "eslint-plugin-flowtype": "^7.0.0", "flow-bin": "^0.195.2", "jest": "^27.2.4", - "prettier": "^2.4.1" + "prettier": "^2.4.1", + "terser": "^5.3.0", + "yargs": "17.7.2" }, "jest": { "snapshotFormat": {