diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 42a52a6bc..000000000 --- a/.eslintrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "standard", - "rules": { - "no-var": "error" - } -} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..8cd04782b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.ts text eol=lf +*.js text eol=lf \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/ask-a-question.md b/.github/ISSUE_TEMPLATE/ask-a-question.md index 8085180be..3a6fa5fd6 100644 --- a/.github/ISSUE_TEMPLATE/ask-a-question.md +++ b/.github/ISSUE_TEMPLATE/ask-a-question.md @@ -1,10 +1,7 @@ --- name: Ask a Question -about: '"How can I X?" – ask a question about how to use standard-version.' -title: '' +about: '"How can I X?" – ask a question about how to use commit-and-tag-version.' +title: "" labels: question -assignees: '' - +assignees: "" --- - - diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 3c1b10dbc..0e7fb8f09 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,10 +1,9 @@ --- name: Bug Report about: Use this template if something isn't working as expected. -title: '' +title: "" labels: bug -assignees: '' - +assignees: "" --- **Describe the bug** @@ -13,18 +12,17 @@ A clear and concise description of what the bug is. **Current behavior** A clear and concise description of the behavior. - **Expected behavior** A clear and concise description of what you expected to happen. - - **Environment** -- `standard-version` version(s): [e.g. v6.0.0, v8.0.0, master] + +- `commit-and-tag-version` version(s): [e.g. v6.0.0, v8.0.0, master] - Node/npm version: [e.g. Node 10/npm 6] - OS: [e.g. OSX 10.13.4, Windows 10] **Possible Solution** + **Additional context** diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..236d4254a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + # Node / npm + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + versioning-strategy: increase + schedule: + interval: "weekly" + + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c5d3968b1..d17b4fcc5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,24 +10,30 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node: [10, 12, 14] - os: [ubuntu-latest, windows-latest] + node: [ + 18, + 20, + lts/* + ] + os: [ + ubuntu-latest, + windows-latest + ] env: OS: ${{ matrix.os }} NODE_VERSION: ${{ matrix.node }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4 - run: git fetch --prune --unshallow - run: git config --global user.name 'Actions' - run: git config --global user.email 'dummy@example.org' - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - run: node --version - run: npm install --engine-strict - run: npm test - - run: npm run coverage - name: Codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v5 with: env_vars: OS, NODE_VERSION diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 585889f86..c3ffddc2b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,4 +1,5 @@ on: + workflow_dispatch: push: branches: - master @@ -7,21 +8,21 @@ jobs: release-please: runs-on: ubuntu-latest steps: - - uses: GoogleCloudPlatform/release-please-action@v3 + - uses: GoogleCloudPlatform/release-please-action@v4 id: release with: token: ${{ secrets.GITHUB_TOKEN }} release-type: node - package-name: standard-version + package-name: commit-and-tag-version # The logic below handles the npm publication: - - uses: actions/checkout@v2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4 # these if statements ensure that a publication only occurs when # a new release is created: if: ${{ steps.release.outputs.release_created }} - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v4 with: - node-version: 12 - registry-url: 'https://external-dot-oss-automation.appspot.com' + node-version: 20 + registry-url: 'https://registry.npmjs.org' if: ${{ steps.release.outputs.release_created }} - run: npm ci if: ${{ steps.release.outputs.release_created }} diff --git a/.gitignore b/.gitignore index 7d21798c9..99126fce5 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,7 @@ npm-debug.log # coverage coverage -package-lock.json \ No newline at end of file + +# temp dirs +*-temp +tmp diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..8db60caac --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc545fa7..79efe5bc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,236 @@ # Changelog -All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. + +## [12.6.0](https://github.com/absolute-version/commit-and-tag-version/compare/v12.5.2...v12.6.0) (2025-08-27) + + +### Features + +* add --config option to allow custom config file path ([#237](https://github.com/absolute-version/commit-and-tag-version/issues/237)) ([3958e68](https://github.com/absolute-version/commit-and-tag-version/commit/3958e688a60df4f1ba46137d1f4147a65817c8d7)) + +## [12.5.2](https://github.com/absolute-version/commit-and-tag-version/compare/v12.5.1...v12.5.2) (2025-07-30) + + +### Bug Fixes + +* Correct use of fs, so we no longer emit a deprecation warning for `fs.F_OK` ([7fe66bb](https://github.com/absolute-version/commit-and-tag-version/commit/7fe66bb590103a593ecacabeb8effb8716862517)), closes [#240](https://github.com/absolute-version/commit-and-tag-version/issues/240) + +## [12.5.1](https://github.com/absolute-version/commit-and-tag-version/compare/v12.5.0...v12.5.1) (2025-04-09) + + +### Bug Fixes + +* ignore other prerelease tags when finding latest tag ([#211](https://github.com/absolute-version/commit-and-tag-version/issues/211)) ([#213](https://github.com/absolute-version/commit-and-tag-version/issues/213)) ([1bcdf40](https://github.com/absolute-version/commit-and-tag-version/commit/1bcdf408cac54aff37aa4d5611444f6877aad6f9)) + +## [12.5.0](https://github.com/absolute-version/commit-and-tag-version/compare/v12.4.4...v12.5.0) (2024-10-10) + + +### Features + +* **python:** add poetry support ([#188](https://github.com/absolute-version/commit-and-tag-version/issues/188)) ([01f08e9](https://github.com/absolute-version/commit-and-tag-version/commit/01f08e9093e255f81394c97bcac59e057f5717dc)) + +## [12.4.4](https://github.com/absolute-version/commit-and-tag-version/compare/v12.4.3...v12.4.4) (2024-09-15) + + +### Bug Fixes + +* sanitize double quotes result from stdout ([#186](https://github.com/absolute-version/commit-and-tag-version/issues/186)) ([8dbeb79](https://github.com/absolute-version/commit-and-tag-version/commit/8dbeb794b960c6ea27527353ad2c55884f48b469)) + +## [12.4.3](https://github.com/absolute-version/commit-and-tag-version/compare/v12.4.2...v12.4.3) (2024-09-09) + + +### Bug Fixes + +* Correct issue where downstream dependency would throw `options.debug is not a function` ([4280bcf](https://github.com/absolute-version/commit-and-tag-version/commit/4280bcf102b78d1995b24ec9238219db81730ebd)) + +## [12.4.2](https://github.com/absolute-version/commit-and-tag-version/compare/v12.4.1...v12.4.2) (2024-08-25) + + +### Bug Fixes + +* **deps:** update dependency conventional-changelog to v4 ([#176](https://github.com/absolute-version/commit-and-tag-version/issues/176)) ([8d15fc7](https://github.com/absolute-version/commit-and-tag-version/commit/8d15fc788edc2fd5d901ccbbbacb3452eabc3091)) + +## [12.4.1](https://github.com/absolute-version/commit-and-tag-version/compare/v12.4.0...v12.4.1) (2024-04-28) + + +### Bug Fixes + +* raise the 'openapi'-updater to a higher order of precedence above the 'yaml'-updater ([#143](https://github.com/absolute-version/commit-and-tag-version/issues/143)) ([37fe178](https://github.com/absolute-version/commit-and-tag-version/commit/37fe178fdba051c665414565fe4b0e61336aff18)) + +## [12.4.0](https://github.com/absolute-version/commit-and-tag-version/compare/v12.3.0...v12.4.0) (2024-04-19) + + +### Features + +* Add OpenAPI version support ([#136](https://github.com/absolute-version/commit-and-tag-version/issues/136)) ([007b1b0](https://github.com/absolute-version/commit-and-tag-version/commit/007b1b0386651f2fbd6e8a9f552a2a34702086ca)) + +## [12.3.0](https://github.com/absolute-version/commit-and-tag-version/compare/v12.2.0...v12.3.0) (2024-04-19) + + +### Features + +* **updater:** add YAML support ([#137](https://github.com/absolute-version/commit-and-tag-version/issues/137)) ([b9dccc2](https://github.com/absolute-version/commit-and-tag-version/commit/b9dccc23ec05e4026899c676f3275d4dedf8c686)) + + +### Bug Fixes + +* Add debug messages for exclusions during bump lifecycle ([#131](https://github.com/absolute-version/commit-and-tag-version/issues/131)) ([a9191f2](https://github.com/absolute-version/commit-and-tag-version/commit/a9191f293eb9302afb1093ad37e9fa076f6b37a2)) + +## [12.2.0](https://github.com/absolute-version/commit-and-tag-version/compare/v12.1.0...v12.2.0) (2024-01-15) + + +### Features + +* **updater:** add maven pom.xml file support ([#33](https://github.com/absolute-version/commit-and-tag-version/issues/33), [#109](https://github.com/absolute-version/commit-and-tag-version/issues/109)) ([#123](https://github.com/absolute-version/commit-and-tag-version/issues/123)) ([6466beb](https://github.com/absolute-version/commit-and-tag-version/commit/6466bebf849bbdcdaebf493a1ccce9670c469fde)) + +## [12.1.0](https://github.com/absolute-version/commit-and-tag-version/compare/v12.0.0...v12.1.0) (2024-01-06) + + +### Features + +* Add signoff option ([#120](https://github.com/absolute-version/commit-and-tag-version/issues/120)) ([d107e38](https://github.com/absolute-version/commit-and-tag-version/commit/d107e38eb906dfb21658d12803b7308f2e3dda7d)) + +## [12.0.0](https://github.com/absolute-version/commit-and-tag-version/compare/v11.3.0...v12.0.0) (2023-10-31) + + +### ⚠ BREAKING CHANGES + +* Drop support for node 14, 16. Now supports node 18 and 20. + +### Bug Fixes + +* Drop support for node 14, 16. Now supports node 18 and 20. ([b1a58bc](https://github.com/absolute-version/commit-and-tag-version/commit/b1a58bc2a786da48fbcec248204ff8631c79606e)) +* preserve frontmatter when updating changelog ([#108](https://github.com/absolute-version/commit-and-tag-version/issues/108)) ([abdcfe2](https://github.com/absolute-version/commit-and-tag-version/commit/abdcfe295023f46c8724463940fff6a220434fad)) + +## [11.3.0](https://github.com/absolute-version/commit-and-tag-version/compare/v11.2.4...v11.3.0) (2023-10-10) + + +### Features + +* **updater:** add .csproj file support ([#95](https://github.com/absolute-version/commit-and-tag-version/issues/95)) ([a96554c](https://github.com/absolute-version/commit-and-tag-version/commit/a96554c9467bacdf6c9d898b223883ee32f63c15)) + +## [11.2.4](https://github.com/absolute-version/commit-and-tag-version/compare/v11.2.3...v11.2.4) (2023-10-02) + + +### Bug Fixes + +* allow bump task to handle versions with build metadata ([33913ee](https://github.com/absolute-version/commit-and-tag-version/commit/33913ee03bff2dfc26c9ffc942e1191c1f767949)) +* handle invalid versions passed to releaseAs ([33913ee](https://github.com/absolute-version/commit-and-tag-version/commit/33913ee03bff2dfc26c9ffc942e1191c1f767949)) + +## [11.2.3](https://github.com/absolute-version/commit-and-tag-version/compare/v11.2.2...v11.2.3) (2023-08-22) + + +### Bug Fixes + +* **bump:** propagate the parserOpts from args to conventionalRecommendedBump, fixing an issue with custom headerPatterns ([#89](https://github.com/absolute-version/commit-and-tag-version/issues/89)) ([bc685be](https://github.com/absolute-version/commit-and-tag-version/commit/bc685be46ca90417a03867717393bb9018e6036c)) + +## [11.2.2](https://github.com/absolute-version/commit-and-tag-version/compare/v11.2.1...v11.2.2) (2023-06-18) + + +### Bug Fixes + +* **deps:** update dependency conventional-changelog-conventionalcommits to v6 ([285f5e7](https://github.com/absolute-version/commit-and-tag-version/commit/285f5e7c9d415c029fe6b6f4781f6ccfa71a0151)) +* **deps:** update dependency conventional-changelog-conventionalcommits to v6 ([#81](https://github.com/absolute-version/commit-and-tag-version/issues/81)) ([ab67fa4](https://github.com/absolute-version/commit-and-tag-version/commit/ab67fa43b644f8078530ca7927054cd8cf5add77)) +* **deps:** update dependency conventional-changelog-conventionalcommits to v6.1.0 ([39827d3](https://github.com/absolute-version/commit-and-tag-version/commit/39827d386ce8c3dbad7605ef872975ebe48db72a)) +* **deps:** update dependency conventional-changelog-conventionalcommits to v6.1.0 ([#86](https://github.com/absolute-version/commit-and-tag-version/issues/86)) ([a8580d5](https://github.com/absolute-version/commit-and-tag-version/commit/a8580d5859c6c44ed82ac244eabad967eca0d4b8)) +* **deps:** update dependency conventional-recommended-bump to v7 ([5978564](https://github.com/absolute-version/commit-and-tag-version/commit/59785644f2cd7980139c49086f3ba662f63a7179)) +* **deps:** update dependency conventional-recommended-bump to v7 ([#83](https://github.com/absolute-version/commit-and-tag-version/issues/83)) ([1c9f82e](https://github.com/absolute-version/commit-and-tag-version/commit/1c9f82eca486dceda244d37f5264f992b9a5c57e)) +* **deps:** update dependency git-semver-tags to v5 ([97e0237](https://github.com/absolute-version/commit-and-tag-version/commit/97e0237d645e1686f96418a1f2d8dbd6bfeca90b)) +* **deps:** update dependency git-semver-tags to v5 ([#80](https://github.com/absolute-version/commit-and-tag-version/issues/80)) ([46ea506](https://github.com/absolute-version/commit-and-tag-version/commit/46ea506c5f2e5300f195462a6c2339cbe4b98fcb)) + +## [11.2.1](https://github.com/absolute-version/commit-and-tag-version/compare/v11.2.0...v11.2.1) (2023-04-05) + + +### Bug Fixes + +* **dep:** add stringify-package to project source, removing the deprecation warning on npm install ([#65](https://github.com/absolute-version/commit-and-tag-version/issues/65)) ([3a959a7](https://github.com/absolute-version/commit-and-tag-version/commit/3a959a7eba86ad42b98592167df7c67f00b661a0)) + +## [11.2.0](https://github.com/absolute-version/commit-and-tag-version/compare/v11.1.0...v11.2.0) (2023-03-15) + + +### Features + +* implement detect pm name ([174a8bd](https://github.com/absolute-version/commit-and-tag-version/commit/174a8bd00106dcc2a6b1c5bcb75bf3fbfd0317da)) +* support config npmClient ([c33686a](https://github.com/absolute-version/commit-and-tag-version/commit/c33686aec080162645466ed962d64133f71e4214)) +* Support customizing the npm publish hint message with a new option: `npmPublishHint` ([1f77110](https://github.com/absolute-version/commit-and-tag-version/commit/1f77110deefa8a394a37c421eb3d313f6c87aea5)) + +## [11.1.0](https://github.com/absolute-version/commit-and-tag-version/compare/v11.0.0...v11.1.0) (2023-02-14) + + +### Features + +* Expose release count option ([40d27f8](https://github.com/absolute-version/commit-and-tag-version/commit/40d27f8782c0621f6ec0ab796b4ae674ec6d43c8)) +* replace the changelog if releaseCount = 0 ([d18af90](https://github.com/absolute-version/commit-and-tag-version/commit/d18af9040fdc2a5798681fa747a43a71cb75e47b)) + + +### Bug Fixes + +* ensure git signatures are not present ([268800b](https://github.com/absolute-version/commit-and-tag-version/commit/268800b3c5e01993902cb0df0d123ac8b3388359)) + +## [11.0.0](https://github.com/absolute-version/commit-and-tag-version/compare/v10.1.0...v11.0.0) (2023-01-17) + + +### ⚠ BREAKING CHANGES + +* **deps:** update dependency conventional-changelog-conventionalcommits to v5. This is technically a breaking change for anyone relying on the exact formatting of the changelog, as it ensures that versions are always written with H2 headers. + +### Bug Fixes + +* **deps:** update dependency conventional-changelog-conventionalcommits to v5 ([b38e900](https://github.com/absolute-version/commit-and-tag-version/commit/b38e900c2b8577b492b4bb42e88d327e80e663a4)) +* **deps:** update dependency conventional-changelog-conventionalcommits to v5. This is technically a breaking change for anyone relying on the exact formatting of the changelog, as it ensures that versions are always written with H2 headers. ([ffa799a](https://github.com/absolute-version/commit-and-tag-version/commit/ffa799aa335f9b912a224336cf2ea2537b8aa310)) + +## [10.1.0](https://github.com/absolute-version/commit-and-tag-version/compare/v10.0.1...v10.1.0) (2022-08-11) + + +### Features + +* **options:** Expose parser and writer options from `conventional-commits-parser` and `conventional-commits-writer` ([185a461](https://github.com/absolute-version/commit-and-tag-version/commit/185a461acc1caf947bf78dd2185f4687afad66fc)) +* **updater:** add Gradle support ([0cf439f](https://github.com/absolute-version/commit-and-tag-version/commit/0cf439fe4047ffa10d21196714c25517535b6302)) + + +### Bug Fixes + +* use correct param for dryRun check ([300b907](https://github.com/absolute-version/commit-and-tag-version/commit/300b90777c5c8c9cca15f69122af1d41981ca47d)) + +### [10.0.1](https://github.com/absolute-version/commit-and-tag-version/compare/v10.0.0...v10.0.1) (2022-05-28) + + +### Bug Fixes + +* No longer warn inappropriately when a custom updater is provided as an object ([5eb8886](https://github.com/absolute-version/commit-and-tag-version/commit/5eb8886a56c6b14c13544192edb3d0e18f91184a)) + +## [10.0.0](https://github.com/absolute-version/commit-and-tag-version/compare/v9.6.0...v10.0.0) (2022-05-25) + + +### ⚠ BREAKING CHANGES + +* Drop support for node 10 and 12, support node 16 and 18 + +### Bug Fixes + +* **deps:** update dependency yargs to v17 ([d190c51](https://github.com/absolute-version/commit-and-tag-version/commit/d190c51507026adefe640cdd75f0a643afd81b87)) + + +### Build System + +* Drop support for node 10 and 12, support node 16 and 18 ([0f75115](https://github.com/absolute-version/commit-and-tag-version/commit/0f751158c2df9cbf7a2c16bef55a5de084f0d17d)) + +## [9.6.0](https://github.com/absolute-version/commit-and-tag-version/compare/v9.5.0...v9.6.0) (2022-05-25) + + +### Features + +* **tag:** add an option to force tag replacement ([df5a94a](https://github.com/absolute-version/commit-and-tag-version/commit/df5a94a978c6966e334ec0e4c9f082fae8deb4f9)) + + +### Bug Fixes + +* Combining both release-as and prerelease now doesn't break package ([5ecfa2e](https://github.com/absolute-version/commit-and-tag-version/commit/5ecfa2e250e134dbfd3ce8d3c6e9d3be28f6f2b8)) +* Fallback to git tag if no version in package file ([57e7091](https://github.com/absolute-version/commit-and-tag-version/commit/57e70916c8afbce16347ed1f710984f5a483152a)) +* No longer skips the commit if changelog and bump are both skipped but `commitAll` is set ([08a0121](https://github.com/absolute-version/commit-and-tag-version/commit/08a01212f0eea7ee5e454adf560755df67234d2f)) +* Use relative path from .gitignore to avoid files matching inappropriately ([d2491bc](https://github.com/absolute-version/commit-and-tag-version/commit/d2491bc8b61a60cd438045ac409278f5b84621dd)) +* When a custom updater reports a version other than the new computed semver, that version is now correctly reported in log output ([f2e83bf](https://github.com/absolute-version/commit-and-tag-version/commit/f2e83bfac711ac5ba4de940d654269af69fc7312)) ## [9.5.0](https://github.com/conventional-changelog/standard-version/compare/v9.4.0...v9.5.0) (2022-05-15) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..d6bee96c9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,41 @@ +# Contributing to `commit-and-tag-version` + +Thank you for considering contributing to `commit-and-tag-version`! Your help is greatly appreciated. + +## How to Get Started + +1. **Fork** this repository to your GitHub account. +2. **Clone** the forked repository to your local machine. +3. **Create** a new branch for your contributions. +4. **Make** your changes or additions. +5. **Push** the branch to your forked repository. +6. **Submit** a pull request and describe the changes made. + +## Bug Reports + +If you encounter any bugs, please open an [issue](https://github.com/absolute-version/commit-and-tag-version/issues) and include: + +- A detailed description of the bug. +- Steps to reproduce the bug. +- Expected and actual behavior. +- Your environment (OS, browser, etc.). + +## Feature Requests + +To suggest new features or enhancements, please open an [issue](https://github.com/absolute-version/commit-and-tag-version/issues) with: + +- A clear and detailed description of the feature. +- The problem it would solve or the use case it would address. +- Any implementation suggestions. + +## Pull Request Guidelines + +- Ensure your code adheres to the project’s coding standards. +- Write tests for new features or bug fixes. +- Update documentation as necessary. If there are user facing changes / new features, please ensure the `Readme.md` is updated +- Provide a comprehensive description of your changes in the pull request. +- Do not include `package.lock.json` modifications; only include `package.json`. Maintainers will be responsible to generate it. +- Do not include multiple features in a single commit/PR +- Please title the PR with [conventional commits](https://www.conventionalcommits.org/), as PRs are squash merged + +We look forward to your contributions and value your time and effort! diff --git a/README.md b/README.md index 19e92dbb1..b466a5e13 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,80 @@ -# Standard Version +# Commit and Tag Version -> **`standard-version` is deprecated**. If you're a GitHub user, I recommend [release-please](https://github.com/googleapis/release-please) as an alternative. I encourage folks to fork this repository and, if a fork gets popular, I will link to it in this README. +> **`commit-and-tag-version` is a fork of `standard-version`**. Because of maintainer availability, `standard-version` was [deprecated on 15th May 2022](https://github.com/conventional-changelog/standard-version/pull/907). The previous maintainer recommends [release-please](https://github.com/googleapis/release-please) as an alternative for those who are using GitHub actions. This fork exists for those who can't switch to `release-please`, or who would like to continue using `standard-version`. + +> **`Can I simply swap the library to migrate?`** To migrate, you can drop in `commit-and-tag-version` in place of `standard-version`. There are no changes in 9.5.0, other than to add the package.json config key `commit-and-tag-version` (the previous configuration key `standard-version` will still work). 10.x drops support for deprecated node versions, 11.x is a formatting change if you're relying on the exact markdown format in the changelog, and 12.x drops support for node 14/16. + +> **`Why was it renamed commit-and-tag-version?`**. I didn't want to scope the package or name it `standard-version-fork`, and it was a good opportunity to make the purpose of the tool clearer. I also wanted to distinguish it from the other tool in this organisation, [`absolute-version`](https://github.com/absolute-version/absolute-version-js), which just prints version information for pre-releases. A utility for versioning using [semver](https://semver.org/) and CHANGELOG generation powered by [Conventional Commits](https://conventionalcommits.org). -![ci](https://github.com/conventional-changelog/standard-version/workflows/ci/badge.svg) -[![NPM version](https://img.shields.io/npm/v/standard-version.svg)](https://www.npmjs.com/package/standard-version) -[![codecov](https://codecov.io/gh/conventional-changelog/standard-version/branch/master/graph/badge.svg?token=J7zMN7vTTd)](https://codecov.io/gh/conventional-changelog/standard-version) +![ci](https://github.com/absolute-version/commit-and-tag-version/workflows/ci/badge.svg) +[![NPM version](https://img.shields.io/npm/v/commit-and-tag-version.svg)](https://www.npmjs.com/package/commit-and-tag-version) +[![codecov](https://codecov.io/gh/absolute-version/commit-and-tag-version/branch/master/graph/badge.svg?token=J7zMN7vTTd)](https://codecov.io/gh/absolute-version/commit-and-tag-version) [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) [![Community slack](http://devtoolscommunity.herokuapp.com/badge.svg)](http://devtoolscommunity.herokuapp.com) _Having problems? Want to contribute? Join us on the [node-tooling community Slack](http://devtoolscommunity.herokuapp.com)_. - -_How It Works:_ +- [Commit and Tag Version](#commit-and-tag-version) + - [How It Works:](#how-it-works) + - [`bumpFiles`, `packageFiles` and `updaters`](#bumpfiles-packagefiles-and-updaters) + - [Maven Support (Java/Kotlin)](#maven-support-javakotlin) + - [Gradle Support (Java/Kotlin)](#gradle-support-javakotlin) + - [.NET Support](#net-support) + - [YAML Support](#yaml-support) + - [OpenAPI Support](#openapi-support) + - [Python Support](#python-support) + - [Installing `commit-and-tag-version`](#installing-commit-and-tag-version) + - [As a local `npm run` script](#as-a-local-npm-run-script) + - [As global `bin`](#as-global-bin) + - [Using `npx`](#using-npx) + - [Configuration](#configuration) + - [Customizing CHANGELOG Generation](#customizing-changelog-generation) + - [Deeper customization](#deeper-customization) + - [CLI Usage](#cli-usage) + - [First Release](#first-release) + - [Cutting Releases](#cutting-releases) + - [Release as a Pre-Release](#release-as-a-pre-release) + - [Release as a Target Type Imperatively (`npm version`-like)](#release-as-a-target-type-imperatively-npm-version-like) + - [Prevent Git Hooks](#prevent-git-hooks) + - [Custom Config Path](#custom-config-path) + - [Signing Commits and Tags](#signing-commits-and-tags) + - [Lifecycle Scripts](#lifecycle-scripts) + - [Skipping Lifecycle Steps](#skipping-lifecycle-steps) + - [Committing Generated Artifacts in the Release Commit](#committing-generated-artifacts-in-the-release-commit) + - [Dry Run Mode](#dry-run-mode) + - [Prefix Tags](#prefix-tags) + - [Tag replacement](#tag-replacement) + - [Generate changelogs for old releases](#generate-changelogs-for-old-releases) + - [CLI Help](#cli-help) + - [Code Usage](#code-usage) + - [FAQ](#faq) + - [How is `commit-and-tag-version` different from `semantic-release`?](#how-is-commit-and-tag-version-different-from-semantic-release) + - [Should I always squash commits when merging PRs?](#should-i-always-squash-commits-when-merging-prs) + - [Can I use `commit-and-tag-version` for additional metadata files, languages or version files?](#can-i-use-commit-and-tag-version-for-additional-metadata-files-languages-or-version-files) + - [Custom `updater`s](#custom-updaters) + - [`readVersion(contents = string): string`](#readversioncontents--string-string) + - [`writeVersion(contents = string, version: string): string`](#writeversioncontents--string-version-string-string) + - [License](#license) + + +### How It Works 1. Follow the [Conventional Commits Specification](https://conventionalcommits.org) in your repository. -2. When you're ready to release, run `standard-version`. +2. When you're ready to release, run `commit-and-tag-version`. -`standard-version` will then do the following: +`commit-and-tag-version` will then do the following: 1. Retrieve the current version of your repository by looking at `packageFiles`[[1]](#bumpfiles-packagefiles-and-updaters), falling back to the last `git tag`. 2. `bump` the version in `bumpFiles`[[1]](#bumpfiles-packagefiles-and-updaters) based on your commits. -4. Generates a `changelog` based on your commits (uses [conventional-changelog](https://github.com/conventional-changelog/conventional-changelog) under the hood). -5. Creates a new `commit` including your `bumpFiles`[[1]](#bumpfiles-packagefiles-and-updaters) and updated CHANGELOG. -6. Creates a new `tag` with the new version number. - +3. Generates a `changelog` based on your commits (uses [conventional-changelog](https://github.com/conventional-changelog/conventional-changelog) under the hood). +4. Creates a new `commit` including your `bumpFiles`[[1]](#bumpfiles-packagefiles-and-updaters) and updated CHANGELOG. +5. Creates a new `tag` with the new version number. ### `bumpFiles`, `packageFiles` and `updaters` -`standard-version` uses a few key concepts for handling version bumping in your project. +`commit-and-tag-version` uses a few key concepts for handling version bumping in your project. - **`packageFiles`** – User-defined files where versions can be read from _and_ be "bumped". - Examples: `package.json`, `manifest.json` @@ -38,18 +83,68 @@ _How It Works:_ - Examples: `package-lock.json`, `npm-shrinkwrap.json` - **`updaters`** – Simple modules used for reading `packageFiles` and writing to `bumpFiles`. -By default, `standard-version` assumes you're working in a NodeJS based project... because of this, for the majority of projects you might never need to interact with these options. +By default, `commit-and-tag-version` assumes you're working in a NodeJS based project... because of this, for the majority of projects you might never need to interact with these options. + +That said, if you find your self asking [How can I use commit-and-tag-version for additional metadata files, languages or version files?](#can-i-use-commit-and-tag-version-for-additional-metadata-files-languages-or-version-files) – these configuration options will help! + +### Maven Support (Java/Kotlin) + +If you are using Maven, then just point to your `pom.xml` file. + +```sh +commit-and-tag-version --packageFiles pom.xml --bumpFiles pom.xml +``` + +### Gradle Support (Java/Kotlin) + +If you are using Gradle, then just point to your `build.gradle` file (or `build.gradle.kts` if using Kotlin DSL). + +```sh +commit-and-tag-version --packageFiles build.gradle --bumpFiles build.gradle +``` + +### .NET Support + +If you are using .NET with `.csproj` files. +This is going to read and update only the `` tag in the file. + +```sh +commit-and-tag-version --packageFiles .csproj --bumpFiles .csproj +``` + +### YAML Support + +If you are using YAML files. +This is going to read and update only the `version:` tag in the file. + +```sh +commit-and-tag-version --packageFiles file.yaml --bumpFiles file.yaml +``` + +### OpenAPI Support + +If you are using OpenAPI, then just point to your `openapi.yaml` file. + +```sh +commit-and-tag-version --packageFiles openapi.yaml --bumpFiles openapi.yaml +``` + +### Python Support + +If you are using Python ***with Poetry***, then point to your `pyproject.toml` file. -That said, if you find your self asking [How can I use standard-version for additional metadata files, languages or version files?](#can-i-use-standard-version-for-additional-metadata-files-languages-or-version-files) – these configuration options will help! +```sh +commit-and-tag-version --packageFiles pyproject.toml --bumpFiles pyproject.toml +``` -## Installing `standard-version` +## Installing `commit-and-tag-version` ### As a local `npm run` script Install and add to `devDependencies`: ``` -npm i --save-dev standard-version +npm i --save-dev commit-and-tag-version ``` Add an [`npm run` script](https://docs.npmjs.com/cli/run-script) to your `package.json`: @@ -57,67 +152,77 @@ Add an [`npm run` script](https://docs.npmjs.com/cli/run-script) to your `packag ```json { "scripts": { - "release": "standard-version" + "release": "commit-and-tag-version" } } ``` Now you can use `npm run release` in place of `npm version`. -This has the benefit of making your repo/package more portable, so that other developers can cut releases without having to globally install `standard-version` on their machine. +This has the benefit of making your repo/package more portable, so that other developers can cut releases without having to globally install `commit-and-tag-version` on their machine. ### As global `bin` Install globally (add to your `PATH`): ``` -npm i -g standard-version +npm i -g commit-and-tag-version ``` -Now you can use `standard-version` in place of `npm version`. +Now you can use `commit-and-tag-version` in place of `npm version`. -This has the benefit of allowing you to use `standard-version` on any repo/package without adding a dev dependency to each one. +This has the benefit of allowing you to use `commit-and-tag-version` on any repo/package without adding a dev dependency to each one. ### Using `npx` -As of `npm@5.2.0`, `npx` is installed alongside `npm`. Using `npx` you can use `standard-version` without having to keep a `package.json` file by running: `npx standard-version`. +As of `npm@5.2.0`, `npx` is installed alongside `npm`. Using `npx` you can use `commit-and-tag-version` without having to keep a `package.json` file by running: `npx commit-and-tag-version`. -This method is especially useful when using `standard-version` in non-JavaScript projects. +This method is especially useful when using `commit-and-tag-version` in non-JavaScript projects. ## Configuration -You can configure `standard-version` either by: +You can configure `commit-and-tag-version` either by: -1. Placing a `standard-version` stanza in your `package.json` (assuming +1. Placing a `commit-and-tag-version` stanza in your `package.json` (assuming your project is JavaScript). + + > Note for users who have migrated to + `commit-and-tag-version` from `standard-version`: the previous package.json configuration key of `standard-version` will still work. + 2. Creating a `.versionrc`, `.versionrc.json` or `.versionrc.js`. - - If you are using a `.versionrc.js` your default export must be a configuration object, or a function returning a configuration object. -Any of the command line parameters accepted by `standard-version` can instead -be provided via configuration. Please refer to the [conventional-changelog-config-spec](https://github.com/conventional-changelog/conventional-changelog-config-spec/) for details on available configuration options. +- If you are using a `.versionrc.js` your default export must be a configuration object, or a function returning a configuration object. +Any of the command line parameters accepted by `commit-and-tag-version` can instead +be provided via configuration. Please refer to the [conventional-changelog-config-spec](https://github.com/conventional-changelog/conventional-changelog-config-spec/) for details on available configuration options. ### Customizing CHANGELOG Generation -By default (as of `6.0.0`), `standard-version` uses the [conventionalcommits preset](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-conventionalcommits). +By default, `commit-and-tag-version` uses the [conventionalcommits preset](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-conventionalcommits). -This preset: +This preset adheres closely to the [conventionalcommits.org](https://www.conventionalcommits.org) specification. -* Adheres closely to the [conventionalcommits.org](https://www.conventionalcommits.org) - specification. -* Is highly configurable, following the configuration specification - [maintained here](https://github.com/conventional-changelog/conventional-changelog-config-spec). - * _We've documented these config settings as a recommendation to other tooling makers._ +Suppose you're using GitLab, rather than GitHub, you might modify the following variables: -There are a variety of dials and knobs you can turn related to CHANGELOG generation. +- `commitUrlFormat`: the URL format of commit SHAs detected in commit messages. +- `compareUrlFormat`: the URL format used to compare two tags. +- `issueUrlFormat`: the URL format used to link to issues. -As an example, suppose you're using GitLab, rather than GitHub, you might modify the following variables: +Making these URLs match GitLab's format, rather than GitHub's. -* `commitUrlFormat`: the URL format of commit SHAs detected in commit messages. -* `compareUrlFormat`: the URL format used to compare two tags. -* `issueUrlFormat`: the URL format used to link to issues. +### Deeper customization -Making these URLs match GitLab's format, rather than GitHub's. +You can override both [parser](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-commits-parser) and [writer](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-writer) options (they will be merged into the preset we just mentioned). As an example, to list commits in the order that they were committed: + +```json +{ + "commit-and-tag-version": { + "writerOpts": { + "commitsSort": false + } + } +} +``` ## CLI Usage @@ -131,9 +236,9 @@ To generate your changelog for your first release, simply do: # npm run script npm run release -- --first-release # global bin -standard-version --first-release +commit-and-tag-version --first-release # npx -npx standard-version --first-release +npx commit-and-tag-version --first-release ``` This will tag a release **without bumping the version `bumpFiles`[1]()**. @@ -148,7 +253,7 @@ If you typically use `npm version` to cut a new release, do this instead: # npm run script npm run release # or global bin -standard-version +commit-and-tag-version ``` As long as your git commit messages are conventional and accurate, you no longer need to specify the semver type - and you get CHANGELOG generation for free! \o/ @@ -165,6 +270,7 @@ Suppose the last version of your code is `1.0.0`, and your code to be committed # npm run script npm run release -- --prerelease ``` + This will tag your version as: `1.0.1-0`. If you want to name the pre-release, you specify the name via `--prerelease `. @@ -204,34 +310,55 @@ If you use git hooks, like pre-commit, to test your code before committing, you # npm run script npm run release -- --no-verify # or global bin -standard-version --no-verify +commit-and-tag-version --no-verify +``` + +### Custom Config Path + +Specify a custom path to the configuration file using the `--config` option + +```sh +commit-and-tag-version --config ./path/to/.versionrc.js + +# or using alias +commit-and-tag-version -c ./path/to/.versionrc.js +``` + +All config file formats can be used: + +```sh +commit-and-tag-version -c ./path/to/.versionrc[.js|.cjs|.json] ``` ### Signing Commits and Tags -If you have your GPG key set up, add the `--sign` or `-s` flag to your `standard-version` command. +If you have your GPG key set up, add the `--sign` or `-s` flag to your `commit-and-tag-version` command. + +### Signed-off-by trailer + +To add the "Signed-off-by" trailer to the commit message add the `--signoff` flag to your `commit-and-tag-version` command. ### Lifecycle Scripts -`standard-version` supports lifecycle scripts. These allow you to execute your +`commit-and-tag-version` supports lifecycle scripts. These allow you to execute your own supplementary commands during the release. The following hooks are available and execute in the order documented: -* `prerelease`: executed before anything happens. If the `prerelease` script returns a +- `prerelease`: executed before anything happens. If the `prerelease` script returns a non-zero exit code, versioning will be aborted, but it has no other effect on the process. -* `prebump`/`postbump`: executed before and after the version is bumped. If the `prebump` +- `prebump`/`postbump`: executed before and after the version is bumped. If the `prebump` script returns a version #, it will be used rather than - the version calculated by `standard-version`. -* `prechangelog`/`postchangelog`: executes before and after the CHANGELOG is generated. -* `precommit`/`postcommit`: called before and after the commit step. -* `pretag`/`posttag`: called before and after the tagging step. + the version calculated by `commit-and-tag-version`. +- `prechangelog`/`postchangelog`: executes before and after the CHANGELOG is generated. +- `precommit`/`postcommit`: called before and after the commit step. +- `pretag`/`posttag`: called before and after the tagging step. Simply add the following to your package.json to configure lifecycle scripts: ```json { - "standard-version": { + "commit-and-tag-version": { "scripts": { "prebump": "echo 9.9.9" } @@ -240,11 +367,12 @@ Simply add the following to your package.json to configure lifecycle scripts: ``` As an example to change from using GitHub to track your items to using your projects Jira use a -`postchangelog` script to replace the url fragment containing 'https://github.com/`myproject`/issues/' +`postchangelog` script to replace the url fragment containing '' with a link to your Jira - assuming you have already installed [replace](https://www.npmjs.com/package/replace) + ```json { - "standard-version": { + "commit-and-tag-version": { "scripts": { "postchangelog": "replace 'https://github.com/myproject/issues/' 'https://myjira/browse/' CHANGELOG.md" } @@ -259,7 +387,7 @@ by adding the following to your package.json: ```json { - "standard-version": { + "commit-and-tag-version": { "skip": { "changelog": true } @@ -273,7 +401,7 @@ If you want to commit generated artifacts in the release commit, you can use the ```json { - "standard-version": { + "commit-and-tag-version": { "scripts": { "prerelease": "webpack -p --bail && git add " } @@ -284,21 +412,21 @@ If you want to commit generated artifacts in the release commit, you can use the ```json { "scripts": { - "release": "standard-version -a" + "release": "commit-and-tag-version -a" } } ``` ### Dry Run Mode -running `standard-version` with the flag `--dry-run` allows you to see what +running `commit-and-tag-version` with the flag `--dry-run` allows you to see what commands would be run, without committing to git or updating files. ```sh # npm run script npm run release -- --dry-run # or global bin -standard-version --dry-run +commit-and-tag-version --dry-run ``` ### Prefix Tags @@ -306,7 +434,7 @@ standard-version --dry-run Tags are prefixed with `v` by default. If you would like to prefix your tags with something else, you can do so with the `-t` flag. ```sh -standard-version -t @scope/package\@ +commit-and-tag-version -t @scope/package\@ ``` This will prefix your tags to look something like `@scope/package@2.0.0` @@ -315,46 +443,75 @@ If you do not want to have any tag prefix you can use the `-t` flag and provide > Note: simply -t or --tag-prefix without any value will fallback to the default 'v' +### Tag replacement + +If you've already run `commit-and-tag-version` when creating your release, you may want to alter the release content and changelog without bumping +the version, by using `commit-and-tag-version --skip.bump`. By default, tagging with an already existing tag make `git` fails. +You can add the `--tag-force` flag to make use of `-f` option when calling `git tag`, then the existing version tag will be replaced. + +### Generate changelogs for old releases + +Normally only the changelog for the last release will be generated and prepended to the `changelog.md`. If you want to generate changelogs for previous releases you can do so by setting the `releaseCount` option like described [here](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-core#releasecount). + +When setting releaseCount=0 the whole changelog gets regenerated and replaced. + +You can set the option either in the`.versionrc` file or inside `package.json` like below + +```json +//.versionrc +{ + "releaseCount": 0 +} + +//package.json + +"commit-and-tag-version": { + "releaseCount": 0 +} +``` + ### CLI Help ```sh # npm run script npm run release -- --help # or global bin -standard-version --help +commit-and-tag-version --help ``` ## Code Usage ```js -const standardVersion = require('standard-version') +const commitAndTagVersion = require("commit-and-tag-version"); // Options are the same as command line, except camelCase -// standardVersion returns a Promise -standardVersion({ +// commitAndTagVersion returns a Promise +commitAndTagVersion({ noVerify: true, - infile: 'docs/CHANGELOG.md', - silent: true -}).then(() => { - // standard-version is done -}).catch(err => { - console.error(`standard-version failed with message: ${err.message}`) + infile: "docs/CHANGELOG.md", + silent: true, }) + .then(() => { + // commit-and-tag-version is done + }) + .catch((err) => { + console.error(`commit-and-tag-version failed with message: ${err.message}`); + }); ``` -_TIP: Use the `silent` option to prevent `standard-version` from printing to the `console`._ +_TIP: Use the `silent` option to prevent `commit-and-tag-version` from printing to the `console`._ ## FAQ -### How is `standard-version` different from `semantic-release`? +### How is `commit-and-tag-version` different from `semantic-release`? [`semantic-release`](https://github.com/semantic-release/semantic-release) is described as: > semantic-release automates the whole package release workflow including: determining the next version number, generating the release notes and publishing the package. -While both are based on the same foundation of structured commit messages, `standard-version` takes a different approach by handling versioning, changelog generation, and git tagging for you **without** automatic pushing (to GitHub) or publishing (to an npm registry). Use of `standard-version` only affects your local git repo - it doesn't affect remote resources at all. After you run `standard-version`, you can review your release state, correct mistakes and follow the release strategy that makes the most sense for your codebase. +While both are based on the same foundation of structured commit messages, `commit-and-tag-version` takes a different approach by handling versioning, changelog generation, and git tagging for you **without** automatic pushing (to GitHub) or publishing (to an npm registry). Use of `commit-and-tag-version` only affects your local git repo - it doesn't affect remote resources at all. After you run `commit-and-tag-version`, you can review your release state, correct mistakes and follow the release strategy that makes the most sense for your codebase. -We think they are both fantastic tools, and we encourage folks to use `semantic-release` instead of `standard-version` if it makes sense for their use-case. +We think they are both fantastic tools, and we encourage folks to use `semantic-release` instead of `commit-and-tag-version` if it makes sense for their use-case. ### Should I always squash commits when merging PRs? @@ -366,14 +523,14 @@ Although this will allow each commit to be included as separate entries in your For this reason, we recommend keeping the scope of each PR to one general feature or fix. In practice, this allows you to use unstructured commit messages when committing each little change and then squash them into a single commit with a structured message (referencing the PR number) once they have been reviewed and accepted. -### Can I use `standard-version` for additional metadata files, languages or version files? +### Can I use `commit-and-tag-version` for additional metadata files, languages or version files? -As of version `7.1.0` you can configure multiple `bumpFiles` and `packageFiles`. +You can configure multiple `bumpFiles` and `packageFiles`: 1. Specify a custom `bumpFile` "`filename`", this is the path to the file you want to "bump" 2. Specify the `bumpFile` "`updater`", this is _how_ the file will be bumped. - a. If you're using a common type, you can use one of `standard-version`'s built-in `updaters` by specifying a `type`. - b. If your using an less-common version file, you can create your own `updater`. + a. If you're using a common type, you can use one of `commit-and-tag-version`'s built-in `updaters` by specifying a `type`. + b. If your using an less-common version file, you can create your own `updater`. ```js // .versionrc @@ -392,7 +549,7 @@ As of version `7.1.0` you can configure multiple `bumpFiles` and `packageFiles`. { "filename": "VERSION_TRACKER.json", // See "Custom `updater`s" for more details. - "updater": "standard-version-updater.js" + "updater": "commit-and-tag-version-updater.js" } ] } @@ -403,14 +560,14 @@ If using `.versionrc.js` as your configuration file, the `updater` may also be s ```js // .versionrc.js const tracker = { - filename: 'VERSION_TRACKER.json', - updater: require('./path/to/custom-version-updater') -} + filename: "VERSION_TRACKER.json", + updater: require("./path/to/custom-version-updater"), +}; module.exports = { bumpFiles: [tracker], - packageFiles: [tracker] -} + packageFiles: [tracker], +}; ``` #### Custom `updater`s @@ -441,30 +598,63 @@ Let's assume our `VERSION_TRACKER.json` has the following contents: } } } - ``` -An acceptable `standard-version-updater.js` would be: +An acceptable `commit-and-tag-version-updater.js` would be: ```js -// standard-version-updater.js -const stringifyPackage = require('stringify-package') -const detectIndent = require('detect-indent') -const detectNewline = require('detect-newline') +// commit-and-tag-version-updater.js +const stringifyPackage = require("stringify-package"); +const detectIndent = require("detect-indent"); +const detectNewline = require("detect-newline"); module.exports.readVersion = function (contents) { return JSON.parse(contents).tracker.package.version; -} +}; module.exports.writeVersion = function (contents, version) { - const json = JSON.parse(contents) - let indent = detectIndent(contents).indent - let newline = detectNewline(contents) - json.tracker.package.version = version - return stringifyPackage(json, indent, newline) -} + const json = JSON.parse(contents); + let indent = detectIndent(contents).indent; + let newline = detectNewline(contents); + json.tracker.package.version = version; + return stringifyPackage(json, indent, newline); +}; ``` +### Why do breaking changes before 1.0.0 not trigger a 1.0.0 release? + +Below 1.0.0, the semver specification doesn't give any guarantees about the +meaning of version numbers. However, with npm there is a community convention, +and implementation-defined behaviour: If your version is between 0.1.0 and +1.0.0, npm treats an update to the minor version as a breaking change - that is +^0.1.0 will match 0.1.2 but not 0.2.0. Rust's cargo package manager also behaves +the same way. + +This tool (via conventional-commits) also follows that convention - breaking +changes below v1.0.0 are treated as a minor version bump. Here's an example +series of commits with tagged versions: + +``` +1017b00 chore: initial commit +9e2ba95 (tag: v0.0.2) chore(release): 0.0.2 +3598012 fix!: Example breaking change +1a4994a (tag: v0.1.0) chore(release): 0.1.0 +``` + +Semver's only guarantee is "all bets are off", but npm has made a choice about +what bets to make. `commit-and-tag-version` follows the same convention (along +with other package managers for other ecosystems). + +When you are ready to release v1.0.0, add `--release-as 1.0.0` to the options. + +### Why do my `refactor`, `chore` etc changes not appear in the changelog? + +By default, the conventional commits preset is used. This means that only `fix`, `feat` and anything marked as a breaking change will appear in the changelog. + +Conventional commits is meant to make it easy for machines to reason about the user-facing changes, +and the changelog generation makes it easy for humans to consume this information too. +Usually, you wouldn't want non-user facing changes like refactor in the changelog. + ## License ISC diff --git a/bin/cli.js b/bin/cli.js index d430c823b..4f7e08310 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -2,12 +2,13 @@ /* istanbul ignore if */ if (process.version.match(/v(\d+)\./)[1] < 6) { - console.error('standard-version: Node v6 or greater is required. `standard-version` did not run.') + console.error( + 'commit-and-tag-version: Node v6 or greater is required. `commit-and-tag-version` did not run.', + ); } else { - const standardVersion = require('../index') - const cmdParser = require('../command') - standardVersion(cmdParser.argv) - .catch(() => { - process.exit(1) - }) + const standardVersion = require('../index'); + const cmdParser = require('../command'); + standardVersion(cmdParser.argv).catch(() => { + process.exit(1); + }); } diff --git a/command.js b/command.js index d65d5ea06..8d04da3d6 100755 --- a/command.js +++ b/command.js @@ -1,133 +1,173 @@ -const spec = require('conventional-changelog-config-spec') -const { getConfiguration } = require('./lib/configuration') -const defaults = require('./defaults') +const spec = require('conventional-changelog-config-spec'); +const { getConfiguration } = require('./lib/configuration'); +const defaults = require('./defaults'); const yargs = require('yargs') .usage('Usage: $0 [options]') .option('packageFiles', { default: defaults.packageFiles, - array: true + array: true, }) .option('bumpFiles', { default: defaults.bumpFiles, - array: true + array: true, }) .option('release-as', { alias: 'r', - describe: 'Specify the release type manually (like npm version )', + describe: + 'Specify the release type manually (like npm version )', requiresArg: true, - string: true + string: true, }) .option('prerelease', { alias: 'p', - describe: 'make a pre-release with optional option value to specify a tag id', - string: true + describe: + 'make a pre-release with optional option value to specify a tag id', + string: true, }) .option('infile', { alias: 'i', describe: 'Read the CHANGELOG from this file', - default: defaults.infile + default: defaults.infile, }) .option('message', { alias: ['m'], - describe: '[DEPRECATED] Commit message, replaces %s with new version.\nThis option will be removed in the next major version, please use --releaseCommitMessageFormat.', - type: 'string' + describe: + '[DEPRECATED] Commit message, replaces %s with new version.\nThis option will be removed in the next major version, please use --releaseCommitMessageFormat.', + type: 'string', }) .option('first-release', { alias: 'f', describe: 'Is this the first release?', type: 'boolean', - default: defaults.firstRelease + default: defaults.firstRelease, }) .option('sign', { alias: 's', describe: 'Should the git commit and tag be signed?', type: 'boolean', - default: defaults.sign + default: defaults.sign, + }) + .option('signoff', { + describe: 'Should the git commit have a "Signed-off-by" trailer', + type: 'boolean', + default: defaults.signoff, }) .option('no-verify', { alias: 'n', - describe: 'Bypass pre-commit or commit-msg git hooks during the commit phase', + describe: + 'Bypass pre-commit or commit-msg git hooks during the commit phase', type: 'boolean', - default: defaults.noVerify + default: defaults.noVerify, }) .option('commit-all', { alias: 'a', - describe: 'Commit all staged changes, not just files affected by standard-version', + describe: + 'Commit all staged changes, not just files affected by commit-and-tag-version', type: 'boolean', - default: defaults.commitAll + default: defaults.commitAll, }) .option('silent', { - describe: 'Don\'t print logs and errors', + describe: "Don't print logs and errors", type: 'boolean', - default: defaults.silent + default: defaults.silent, }) .option('tag-prefix', { alias: 't', describe: 'Set a custom prefix for the git tag to be created', type: 'string', - default: defaults.tagPrefix + default: defaults.tagPrefix, + }) + .option('release-count', { + describe: + 'How many releases of changelog you want to generate. It counts from the upcoming release. Useful when you forgot to generate any previous changelog. Set to 0 to regenerate all.', + type: 'number', + default: defaults.releaseCount, + }) + .option('tag-force', { + describe: 'Allow tag replacement', + type: 'boolean', + default: defaults.tagForce, }) .option('scripts', { - describe: 'Provide scripts to execute for lifecycle events (prebump, precommit, etc.,)', - default: defaults.scripts + describe: + 'Provide scripts to execute for lifecycle events (prebump, precommit, etc.,)', + default: defaults.scripts, }) .option('skip', { describe: 'Map of steps in the release process that should be skipped', - default: defaults.skip + default: defaults.skip, }) .option('dry-run', { type: 'boolean', default: defaults.dryRun, - describe: 'See the commands that running standard-version would run' + describe: 'See the commands that running commit-and-tag-version would run', }) .option('git-tag-fallback', { type: 'boolean', default: defaults.gitTagFallback, - describe: 'fallback to git tags for version, if no meta-information file is found (e.g., package.json)' + describe: + 'fallback to git tags for version, if no meta-information file is found (e.g., package.json)', }) .option('path', { type: 'string', - describe: 'Only populate commits made under this path' + describe: 'Only populate commits made under this path', }) .option('changelogHeader', { type: 'string', - describe: '[DEPRECATED] Use a custom header when generating and updating changelog.\nThis option will be removed in the next major version, please use --header.' + describe: + '[DEPRECATED] Use a custom header when generating and updating changelog.\nThis option will be removed in the next major version, please use --header.', }) .option('preset', { type: 'string', default: defaults.preset, - describe: 'Commit message guideline preset' + describe: 'Commit message guideline preset', }) .option('lerna-package', { type: 'string', - describe: 'Name of the package from which the tags will be extracted' + describe: 'Name of the package from which the tags will be extracted', + }) + .option('npmPublishHint', { + type: 'string', + default: defaults.npmPublishHint, + describe: 'Customized publishing hint', + }) + .option('config', { + type: 'string', + default: defaults.config, + alias: 'c', + describe: 'Path to a custom configuration file', }) .check((argv) => { if (typeof argv.scripts !== 'object' || Array.isArray(argv.scripts)) { - throw Error('scripts must be an object') + throw Error('scripts must be an object'); } else if (typeof argv.skip !== 'object' || Array.isArray(argv.skip)) { - throw Error('skip must be an object') + throw Error('skip must be an object'); } else { - return true + return true; } }) .alias('version', 'v') .alias('help', 'h') .example('$0', 'Update changelog and tag release') - .example('$0 -m "%s: see changelog for details"', 'Update changelog and tag release with custom commit message') + .example( + '$0 -m "%s: see changelog for details"', + 'Update changelog and tag release with custom commit message', + ) .pkgConf('standard-version') - .config(getConfiguration()) - .wrap(97) + .pkgConf('commit-and-tag-version') + .wrap(97); + +yargs.config(getConfiguration(yargs.argv.config)); -Object.keys(spec.properties).forEach(propertyKey => { - const property = spec.properties[propertyKey] +Object.keys(spec.properties).forEach((propertyKey) => { + const property = spec.properties[propertyKey]; yargs.option(propertyKey, { type: property.type, describe: property.description, default: defaults[propertyKey] ? defaults[propertyKey] : property.default, - group: 'Preset Configuration:' - }) -}) + group: 'Preset Configuration:', + }); +}); -module.exports = yargs +module.exports = yargs; diff --git a/defaults.js b/defaults.js index 614c453e4..83d69d225 100644 --- a/defaults.js +++ b/defaults.js @@ -1,43 +1,45 @@ -const spec = require('conventional-changelog-config-spec') +const spec = require('conventional-changelog-config-spec'); const defaults = { infile: 'CHANGELOG.md', firstRelease: false, sign: false, + signoff: false, noVerify: false, commitAll: false, silent: false, tagPrefix: 'v', + releaseCount: 1, scripts: {}, skip: {}, dryRun: false, + tagForce: false, gitTagFallback: true, - preset: require.resolve('conventional-changelog-conventionalcommits') -} + preset: require.resolve('conventional-changelog-conventionalcommits'), + npmPublishHint: undefined, + config: undefined, +}; /** * Merge in defaults provided by the spec */ -Object.keys(spec.properties).forEach(propertyKey => { - const property = spec.properties[propertyKey] - defaults[propertyKey] = property.default -}) +Object.keys(spec.properties).forEach((propertyKey) => { + const property = spec.properties[propertyKey]; + defaults[propertyKey] = property.default; +}); /** * Sets the default for `header` (provided by the spec) for backwards * compatibility. This should be removed in the next major version. */ -defaults.header = '# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.\n' +defaults.header = + '# Changelog\n\nAll notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.\n'; -defaults.packageFiles = [ - 'package.json', - 'bower.json', - 'manifest.json' -] +defaults.packageFiles = ['package.json', 'bower.json', 'manifest.json']; defaults.bumpFiles = defaults.packageFiles.concat([ 'package-lock.json', - 'npm-shrinkwrap.json' -]) + 'npm-shrinkwrap.json', +]); -module.exports = defaults +module.exports = defaults; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..98c4b7da0 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,67 @@ +import globals from "globals"; +import js from "@eslint/js"; +import jest from "eslint-plugin-jest"; +import eslintConfigPrettier from "eslint-config-prettier"; + +/** + * @type {import("eslint").Linter.Config} + */ +export default [ + { + "ignores": [".git/", ".github/", ".husky/", ".scannerwork/", ".vscode/", "coverage/", "node_modules/"], + "name": "Files to ignore" + }, + { + ...eslintConfigPrettier, + "name": "Prettier" + }, + { + ...js.configs.recommended, + "files": ["**/*.{js,cjs,mjs}"], + "languageOptions": { + "ecmaVersion": 2023 + }, + "name": "JavaScript files", + "rules": { + ...js.configs.recommended.rules, + "no-var": "error", + "no-unused-vars": [ + "error", + { + "argsIgnorePattern": "_.*" + } + ] + } + }, + { + "files": ["**/*.mjs"], + "languageOptions": { + "sourceType": "module" + }, + "name": "JavaScript modules" + }, + { + "files": ["**/*.{js,cjs,mjs}"], + "languageOptions": { + "globals": { + ...globals.node + } + }, + "name": "Node.js files" + }, + { + ...jest.configs["flat/recommended"], + "files": ["test/**/*{spec,test}.{js,cjs,mjs}", "test/mocks/jest-mocks.js"], + "languageOptions": { + "globals": { + ...globals.jest + } + }, + "name": "Test files", + "rules": { + ...jest.configs["flat/recommended"].rules, + "jest/prefer-expect-assertions": "off", + "jest/expect-expect": "off" + } + } +]; diff --git a/index.js b/index.js index 56f3106b3..c1f208564 100755 --- a/index.js +++ b/index.js @@ -1,80 +1,95 @@ -const bump = require('./lib/lifecycles/bump') -const changelog = require('./lib/lifecycles/changelog') -const commit = require('./lib/lifecycles/commit') -const fs = require('fs') -const latestSemverTag = require('./lib/latest-semver-tag') -const path = require('path') -const printError = require('./lib/print-error') -const tag = require('./lib/lifecycles/tag') -const { resolveUpdaterObjectFromArgument } = require('./lib/updaters') +const bump = require('./lib/lifecycles/bump'); +const changelog = require('./lib/lifecycles/changelog'); +const commit = require('./lib/lifecycles/commit'); +const fs = require('fs'); +const latestSemverTag = require('./lib/latest-semver-tag'); +const path = require('path'); +const printError = require('./lib/print-error'); +const tag = require('./lib/lifecycles/tag'); +const { resolveUpdaterObjectFromArgument } = require('./lib/updaters'); -module.exports = async function standardVersion (argv) { - const defaults = require('./defaults') +module.exports = async function standardVersion(argv) { + const defaults = require('./defaults'); /** * `--message` (`-m`) support will be removed in the next major version. */ - const message = argv.m || argv.message + const message = argv.m || argv.message; if (message) { /** * The `--message` flag uses `%s` for version substitutions, we swap this * for the substitution defined in the config-spec for future-proofing upstream * handling. */ - argv.releaseCommitMessageFormat = message.replace(/%s/g, '{{currentTag}}') + argv.releaseCommitMessageFormat = message.replace(/%s/g, '{{currentTag}}'); if (!argv.silent) { - console.warn('[standard-version]: --message (-m) will be removed in the next major release. Use --releaseCommitMessageFormat.') + console.warn( + '[commit-and-tag-version]: --message (-m) will be removed in the next major release. Use --releaseCommitMessageFormat.', + ); } } if (argv.changelogHeader) { - argv.header = argv.changelogHeader + argv.header = argv.changelogHeader; if (!argv.silent) { - console.warn('[standard-version]: --changelogHeader will be removed in the next major release. Use --header.') + console.warn( + '[commit-and-tag-version]: --changelogHeader will be removed in the next major release. Use --header.', + ); } } - if (argv.header && argv.header.search(changelog.START_OF_LAST_RELEASE_PATTERN) !== -1) { - throw Error(`custom changelog header must not match ${changelog.START_OF_LAST_RELEASE_PATTERN}`) + if ( + argv.header && + argv.header.search(changelog.START_OF_LAST_RELEASE_PATTERN) !== -1 + ) { + throw Error( + `custom changelog header must not match ${changelog.START_OF_LAST_RELEASE_PATTERN}`, + ); } /** * If an argument for `packageFiles` provided, we include it as a "default" `bumpFile`. */ if (argv.packageFiles) { - defaults.bumpFiles = defaults.bumpFiles.concat(argv.packageFiles) + defaults.bumpFiles = defaults.bumpFiles.concat(argv.packageFiles); } - const args = Object.assign({}, defaults, argv) - let pkg + const args = Object.assign({}, defaults, argv); + let pkg; for (const packageFile of args.packageFiles) { - const updater = resolveUpdaterObjectFromArgument(packageFile) - if (!updater) return - const pkgPath = path.resolve(process.cwd(), updater.filename) + const updater = resolveUpdaterObjectFromArgument(packageFile); + if (!updater) return; + const pkgPath = path.resolve(process.cwd(), updater.filename); try { - const contents = fs.readFileSync(pkgPath, 'utf8') + const contents = fs.readFileSync(pkgPath, 'utf8'); pkg = { version: updater.updater.readVersion(contents), - private: typeof updater.updater.isPrivate === 'function' ? updater.updater.isPrivate(contents) : false - } - break - } catch (err) {} + private: + typeof updater.updater.isPrivate === 'function' + ? updater.updater.isPrivate(contents) + : false, + }; + break; + // eslint-disable-next-line no-unused-vars + } catch (err) { + /* This probably shouldn't be empty? */ + } } try { - let version - if (pkg) { - version = pkg.version + let version; + if (pkg && pkg.version) { + version = pkg.version; } else if (args.gitTagFallback) { - version = await latestSemverTag(args.tagPrefix) + version = await latestSemverTag(args); } else { - throw new Error('no package file found') + throw new Error('no package file found'); } - const newVersion = await bump(args, version) - await changelog(args, newVersion) - await commit(args, newVersion) - await tag(newVersion, pkg ? pkg.private : false, args) + const newVersion = await bump(args, version); + await changelog(args, newVersion); + await commit(args, newVersion); + await tag(newVersion, pkg ? pkg.private : false, args); } catch (err) { - printError(args, err.message) - throw err + printError(args, err.message); + throw err; } -} +}; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..0ec1fd842 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,28 @@ +const config = { + collectCoverage: true, + collectCoverageFrom: [ + '**/*.{js,jsx,ts}', + '!**/node_modules/**', + '!**/tmp/**', + '!**/test/**', + '!**/coverage/**', + '!**/bin/**', + ], + coverageReporters: ['lcov', 'text'], + projects: [ + { + displayName: 'Unit Test', + testMatch: ['**/test/*.spec.js'], + }, + { + displayName: 'Integration Test', + runner: 'jest-serial-runner', + testMatch: ['**/test/*.integration-test.js'], + }, + ], + silent: true, // Suppresses runtime console logs during test runs + testTimeout: 30000, + verbose: true, // Prints test describe/it names into console during test runs +}; + +module.exports = config; diff --git a/lib/checkpoint.js b/lib/checkpoint.js index 634c8b5f7..634d55534 100644 --- a/lib/checkpoint.js +++ b/lib/checkpoint.js @@ -1,12 +1,23 @@ -const chalk = require('chalk') -const figures = require('figures') -const util = require('util') +const chalk = require('chalk'); +const figures = require('figures'); +const util = require('util'); module.exports = function (argv, msg, args, figure) { - const defaultFigure = args.dryRun ? chalk.yellow(figures.tick) : chalk.green(figures.tick) + const defaultFigure = argv.dryRun + ? chalk.yellow(figures.tick) + : chalk.green(figures.tick); if (!argv.silent) { - console.info((figure || defaultFigure) + ' ' + util.format.apply(util, [msg].concat(args.map(function (arg) { - return chalk.bold(arg) - })))) + console.info( + (figure || defaultFigure) + + ' ' + + util.format.apply( + util, + [msg].concat( + args.map(function (arg) { + return chalk.bold(arg); + }), + ), + ), + ); } -} +}; diff --git a/lib/configuration.js b/lib/configuration.js index 5b748318c..be8c634ef 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -1,30 +1,34 @@ -const path = require('path') -const findUp = require('find-up') -const { readFileSync } = require('fs') +const path = require('path'); +const findUp = require('find-up'); +const { readFileSync } = require('fs'); const CONFIGURATION_FILES = [ '.versionrc', '.versionrc.cjs', '.versionrc.json', - '.versionrc.js' -] + '.versionrc.js', +]; -module.exports.getConfiguration = function () { - let config = {} - const configPath = findUp.sync(CONFIGURATION_FILES) +module.exports.getConfiguration = function (configFile) { + let config = {}; + + // If the user has provided a configuration file via the `--config` argument, we use that. + const configurationFiles = configFile ?? CONFIGURATION_FILES; + + const configPath = findUp.sync(configurationFiles); if (!configPath) { - return config + return config; } - const ext = path.extname(configPath) + const ext = path.extname(configPath); if (ext === '.js' || ext === '.cjs') { - const jsConfiguration = require(configPath) + const jsConfiguration = require(configPath); if (typeof jsConfiguration === 'function') { - config = jsConfiguration() + config = jsConfiguration(); } else { - config = jsConfiguration + config = jsConfiguration; } } else { - config = JSON.parse(readFileSync(configPath)) + config = JSON.parse(readFileSync(configPath)); } /** @@ -33,9 +37,9 @@ module.exports.getConfiguration = function () { */ if (typeof config !== 'object') { throw Error( - `[standard-version] Invalid configuration in ${configPath} provided. Expected an object but found ${typeof config}.` - ) + `[commit-and-tag-version] Invalid configuration in ${configPath} provided. Expected an object but found ${typeof config}.`, + ); } - return config -} + return config; +}; diff --git a/lib/detect-package-manager.js b/lib/detect-package-manager.js new file mode 100644 index 000000000..421da6ed0 --- /dev/null +++ b/lib/detect-package-manager.js @@ -0,0 +1,53 @@ +/** + * modified from + * the original code is licensed under MIT + * modified to support only detecting lock file and not detecting global package manager + */ + +const { promises: fs } = require('fs'); +const { resolve } = require('path'); + +/** + * Check if a path exists + */ +async function pathExists(p) { + try { + await fs.access(p); + return true; + } catch { + return false; + } +} + +function getTypeofLockFile(cwd = '.') { + return Promise.all([ + pathExists(resolve(cwd, 'yarn.lock')), + pathExists(resolve(cwd, 'package-lock.json')), + pathExists(resolve(cwd, 'pnpm-lock.yaml')), + ]).then(([isYarn, isNpm, isPnpm]) => { + let value = null; + + if (isYarn) { + value = 'yarn'; + } else if (isPnpm) { + value = 'pnpm'; + } else if (isNpm) { + value = 'npm'; + } + + return value; + }); +} + +const detectPMByLockFile = async (cwd) => { + const type = await getTypeofLockFile(cwd); + if (type) { + return type; + } + + return 'npm'; +}; + +module.exports = { + detectPMByLockFile, +}; diff --git a/lib/format-commit-message.js b/lib/format-commit-message.js index a597b5198..f174c66a7 100644 --- a/lib/format-commit-message.js +++ b/lib/format-commit-message.js @@ -1,4 +1,4 @@ module.exports = function (rawMsg, newVersion) { - const message = String(rawMsg) - return message.replace(/{{currentTag}}/g, newVersion) -} + const message = String(rawMsg); + return message.replace(/{{currentTag}}/g, newVersion); +}; diff --git a/lib/latest-semver-tag.js b/lib/latest-semver-tag.js index eec93af2b..76a9ced39 100644 --- a/lib/latest-semver-tag.js +++ b/lib/latest-semver-tag.js @@ -1,17 +1,34 @@ -const gitSemverTags = require('git-semver-tags') -const semver = require('semver') +const gitSemverTags = require('git-semver-tags'); +const semver = require('semver'); -module.exports = function (tagPrefix = undefined) { +module.exports = function ({ tagPrefix, prerelease }) { return new Promise((resolve, reject) => { gitSemverTags({ tagPrefix }, function (err, tags) { - if (err) return reject(err) - else if (!tags.length) return resolve('1.0.0') + if (err) return reject(err); + else if (!tags.length) return resolve('1.0.0'); // Respect tagPrefix - tags = tags.map(tag => tag.replace(new RegExp('^' + tagPrefix), '')) + tags = tags.map((tag) => tag.replace(new RegExp('^' + tagPrefix), '')); + if (prerelease) { + // ignore any other prelease tags + tags = tags.filter((tag) => { + if (!semver.valid(tag)) return false; + if (!semver.prerelease(tag)) { + // include all non-prerelease versions + return true; + } + // check if the name of the prerelease matches the one we are looking for + if (semver.prerelease(tag)[0] === prerelease) { + return true; + } + return false; + }); + } // ensure that the largest semver tag is at the head. - tags = tags.map(tag => { return semver.clean(tag) }) - tags.sort(semver.rcompare) - return resolve(tags[0]) - }) - }) -} + tags = tags.map((tag) => { + return semver.clean(tag); + }); + tags.sort(semver.rcompare); + return resolve(tags[0]); + }); + }); +}; diff --git a/lib/lifecycles/bump.js b/lib/lifecycles/bump.js index 370fbff03..7db4524e3 100644 --- a/lib/lifecycles/bump.js +++ b/lib/lifecycles/bump.js @@ -1,63 +1,135 @@ -'use strict' - -const chalk = require('chalk') -const checkpoint = require('../checkpoint') -const conventionalRecommendedBump = require('conventional-recommended-bump') -const figures = require('figures') -const fs = require('fs') -const DotGitignore = require('dotgitignore') -const path = require('path') -const presetLoader = require('../preset-loader') -const runLifecycleScript = require('../run-lifecycle-script') -const semver = require('semver') -const writeFile = require('../write-file') -const { resolveUpdaterObjectFromArgument } = require('../updaters') -let configsToUpdate = {} - -async function Bump (args, version) { +'use strict'; + +const chalk = require('chalk'); +const checkpoint = require('../checkpoint'); +const conventionalRecommendedBump = require('conventional-recommended-bump'); +const figures = require('figures'); +const fs = require('fs'); +const DotGitignore = require('dotgitignore'); +const path = require('path'); +const presetLoader = require('../preset-loader'); +const runLifecycleScript = require('../run-lifecycle-script'); +const semver = require('semver'); +const writeFile = require('../write-file'); +const { resolveUpdaterObjectFromArgument } = require('../updaters'); +let configsToUpdate = {}; +const sanitizeQuotesRegex = /['"]+/g; + +async function Bump(args, version) { // reset the cache of updated config files each // time we perform the version bump step. - configsToUpdate = {} - - if (args.skip.bump) return version - let newVersion = version - await runLifecycleScript(args, 'prerelease') - const stdout = await runLifecycleScript(args, 'prebump') - if (stdout && stdout.trim().length) args.releaseAs = stdout.trim() - const release = await bumpVersion(args.releaseAs, version, args) + configsToUpdate = {}; + + if (args.skip.bump) return version; + + if ( + args.releaseAs && + !( + ['major', 'minor', 'patch'].includes(args.releaseAs.toLowerCase()) || + semver.valid(args.releaseAs) + ) + ) { + throw new Error( + "releaseAs must be one of 'major', 'minor' or 'patch', or a valid semvar version.", + ); + } + + let newVersion = version; + await runLifecycleScript(args, 'prerelease'); + const stdout = await runLifecycleScript(args, 'prebump'); + if (stdout?.trim().length) { + const prebumpString = stdout.trim().replace(sanitizeQuotesRegex, ''); + if (semver.valid(prebumpString)) args.releaseAs = prebumpString; + } if (!args.firstRelease) { - const releaseType = getReleaseType(args.prerelease, release.releaseType, version) - newVersion = semver.valid(releaseType) || semver.inc(version, releaseType, args.prerelease) - updateConfigs(args, newVersion) + if (semver.valid(args.releaseAs)) { + const releaseAs = new semver.SemVer(args.releaseAs); + if ( + isString(args.prerelease) && + releaseAs.prerelease.length && + releaseAs.prerelease.slice(0, -1).join('.') !== args.prerelease + ) { + // If both releaseAs and the prerelease identifier are supplied, they must match. The behavior + // for a mismatch is undefined, so error out instead. + throw new Error( + 'releaseAs and prerelease have conflicting prerelease identifiers', + ); + } else if (isString(args.prerelease) && releaseAs.prerelease.length) { + newVersion = releaseAs.version; + } else if (isString(args.prerelease)) { + newVersion = `${releaseAs.major}.${releaseAs.minor}.${releaseAs.patch}-${args.prerelease}.0`; + } else { + newVersion = releaseAs.version; + } + + // Check if the previous version is the same version and prerelease, and increment if so + if ( + isString(args.prerelease) && + ['prerelease', null].includes(semver.diff(version, newVersion)) && + semver.lte(newVersion, version) + ) { + newVersion = semver.inc(version, 'prerelease', args.prerelease); + } + + // Append any build info from releaseAs + newVersion = semvarToVersionStr(newVersion, releaseAs.build); + } else { + const release = await bumpVersion(args.releaseAs, version, args); + const releaseType = getReleaseType( + args.prerelease, + release.releaseType, + version, + ); + + newVersion = semver.inc(version, releaseType, args.prerelease); + } + updateConfigs(args, newVersion); } else { - checkpoint(args, 'skip version bump on first release', [], chalk.red(figures.cross)) + checkpoint( + args, + 'skip version bump on first release', + [], + chalk.red(figures.cross), + ); } - await runLifecycleScript(args, 'postbump') - return newVersion + await runLifecycleScript(args, 'postbump'); + return newVersion; } Bump.getUpdatedConfigs = function () { - return configsToUpdate + return configsToUpdate; +}; + +/** + * Convert a semver object to a full version string including build metadata + * @param {string} semverVersion The semvar version string + * @param {string[]} semverBuild An array of the build metadata elements, to be joined with '.' + * @returns {string} + */ +function semvarToVersionStr(semverVersion, semverBuild) { + return [semverVersion, semverBuild.join('.')].filter(Boolean).join('+'); } -function getReleaseType (prerelease, expectedReleaseType, currentVersion) { +function getReleaseType(prerelease, expectedReleaseType, currentVersion) { if (isString(prerelease)) { if (isInPrerelease(currentVersion)) { - if (shouldContinuePrerelease(currentVersion, expectedReleaseType) || - getTypePriority(getCurrentActiveType(currentVersion)) > getTypePriority(expectedReleaseType) + if ( + shouldContinuePrerelease(currentVersion, expectedReleaseType) || + getTypePriority(getCurrentActiveType(currentVersion)) > + getTypePriority(expectedReleaseType) ) { - return 'prerelease' + return 'prerelease'; } } - return 'pre' + expectedReleaseType + return 'pre' + expectedReleaseType; } else { - return expectedReleaseType + return expectedReleaseType; } } -function isString (val) { - return typeof val === 'string' +function isString(val) { + return typeof val === 'string'; } /** @@ -69,15 +141,15 @@ function isString (val) { * @param expectType * @return {boolean} */ -function shouldContinuePrerelease (version, expectType) { - return getCurrentActiveType(version) === expectType +function shouldContinuePrerelease(version, expectType) { + return getCurrentActiveType(version) === expectType; } -function isInPrerelease (version) { - return Array.isArray(semver.prerelease(version)) +function isInPrerelease(version) { + return Array.isArray(semver.prerelease(version)); } -const TypeList = ['major', 'minor', 'patch'].reverse() +const TypeList = ['major', 'minor', 'patch'].reverse(); /** * extract the in-pre-release type in target version @@ -85,11 +157,11 @@ const TypeList = ['major', 'minor', 'patch'].reverse() * @param version * @return {string} */ -function getCurrentActiveType (version) { - const typelist = TypeList +function getCurrentActiveType(version) { + const typelist = TypeList; for (let i = 0; i < typelist.length; i++) { if (semver[typelist[i]](version)) { - return typelist[i] + return typelist[i]; } } } @@ -101,33 +173,44 @@ function getCurrentActiveType (version) { * @param type * @return {number} */ -function getTypePriority (type) { - return TypeList.indexOf(type) +function getTypePriority(type) { + return TypeList.indexOf(type); } -function bumpVersion (releaseAs, currentVersion, args) { +function bumpVersion(releaseAs, currentVersion, args) { return new Promise((resolve, reject) => { if (releaseAs) { return resolve({ - releaseType: releaseAs - }) + releaseType: releaseAs, + }); } else { - const presetOptions = presetLoader(args) + const presetOptions = presetLoader(args); if (typeof presetOptions === 'object') { - if (semver.lt(currentVersion, '1.0.0')) presetOptions.preMajor = true + if (semver.lt(currentVersion, '1.0.0')) presetOptions.preMajor = true; } - conventionalRecommendedBump({ - debug: args.verbose && console.info.bind(console, 'conventional-recommended-bump'), - preset: presetOptions, - path: args.path, - tagPrefix: args.tagPrefix, - lernaPackage: args.lernaPackage - }, function (err, release) { - if (err) return reject(err) - else return resolve(release) - }) + conventionalRecommendedBump( + { + preset: presetOptions, + path: args.path, + tagPrefix: args.tagPrefix, + lernaPackage: args.lernaPackage, + ...(args.verbose + ? { + debug: console.info.bind( + console, + 'conventional-recommended-bump', + ), + } + : {}), + }, + args.parserOpts, + function (err, release) { + if (err) return reject(err); + else return resolve(release); + }, + ); } - }) + }); } /** @@ -136,37 +219,45 @@ function bumpVersion (releaseAs, currentVersion, args) { * @param newVersion version number to update to. * @return void */ -function updateConfigs (args, newVersion) { - const dotgit = DotGitignore() +function updateConfigs(args, newVersion) { + const dotgit = DotGitignore(); args.bumpFiles.forEach(function (bumpFile) { - const updater = resolveUpdaterObjectFromArgument(bumpFile) + const updater = resolveUpdaterObjectFromArgument(bumpFile); if (!updater) { - return + return; } - const configPath = path.resolve(process.cwd(), updater.filename) + const configPath = path.resolve(process.cwd(), updater.filename); try { - if (dotgit.ignore(configPath)) return - const stat = fs.lstatSync(configPath) + if (dotgit.ignore(updater.filename)) { + console.debug( + `Not updating file '${updater.filename}', as it is ignored in Git`, + ); + return; + } + const stat = fs.lstatSync(configPath); - if (!stat.isFile()) return - const contents = fs.readFileSync(configPath, 'utf8') + if (!stat.isFile()) { + console.debug( + `Not updating '${updater.filename}', as it is not a file`, + ); + return; + } + const contents = fs.readFileSync(configPath, 'utf8'); + const newContents = updater.updater.writeVersion(contents, newVersion); + const realNewVersion = updater.updater.readVersion(newContents); checkpoint( args, 'bumping version in ' + updater.filename + ' from %s to %s', - [updater.updater.readVersion(contents), newVersion] - ) - writeFile( - args, - configPath, - updater.updater.writeVersion(contents, newVersion) - ) + [updater.updater.readVersion(contents), realNewVersion], + ); + writeFile(args, configPath, newContents); // flag any config files that we modify the version # for // as having been updated. - configsToUpdate[updater.filename] = true + configsToUpdate[updater.filename] = true; } catch (err) { - if (err.code !== 'ENOENT') console.warn(err.message) + if (err.code !== 'ENOENT') console.warn(err.message); } - }) + }); } -module.exports = Bump +module.exports = Bump; diff --git a/lib/lifecycles/changelog.js b/lib/lifecycles/changelog.js index f3977f9a8..62a12cddd 100644 --- a/lib/lifecycles/changelog.js +++ b/lib/lifecycles/changelog.js @@ -1,66 +1,113 @@ -const chalk = require('chalk') -const checkpoint = require('../checkpoint') -const conventionalChangelog = require('conventional-changelog') -const fs = require('fs') -const presetLoader = require('../preset-loader') -const runLifecycleScript = require('../run-lifecycle-script') -const writeFile = require('../write-file') -const START_OF_LAST_RELEASE_PATTERN = /(^#+ \[?[0-9]+\.[0-9]+\.[0-9]+| { - createIfMissing(args) - const header = args.header + createIfMissing(args); + const header = args.header; - let oldContent = args.dryRun ? '' : fs.readFileSync(args.infile, 'utf-8') - const oldContentStart = oldContent.search(START_OF_LAST_RELEASE_PATTERN) - // find the position of the last release and remove header: - if (oldContentStart !== -1) { - oldContent = oldContent.substring(oldContentStart) - } - let content = '' - const context = { version: newVersion } - const changelogStream = conventionalChangelog({ - debug: args.verbose && console.info.bind(console, 'conventional-changelog'), - preset: presetLoader(args), - tagPrefix: args.tagPrefix - }, context, { merges: null, path: args.path }) - .on('error', function (err) { - return reject(err) - }) + const oldContent = + args.dryRun || args.releaseCount === 0 + ? '' + : fs.readFileSync(args.infile, 'utf-8'); + + const oldContentBody = extractChangelogBody(oldContent); + + const changelogFrontMatter = extractFrontMatter(oldContent); + + let content = ''; + const context = { version: newVersion }; + const changelogStream = conventionalChangelog( + { + preset: presetLoader(args), + tagPrefix: args.tagPrefix, + releaseCount: args.releaseCount, + ...(args.verbose + ? { + debug: console.info.bind( + console, + 'conventional-recommended-bump', + ), + } + : {}), + }, + context, + { merges: null, path: args.path, showSignature: false }, + args.parserOpts, + args.writerOpts, + ).on('error', function (err) { + return reject(err); + }); changelogStream.on('data', function (buffer) { - content += buffer.toString() - }) + content += buffer.toString(); + }); changelogStream.on('end', function () { - checkpoint(args, 'outputting changes to %s', [args.infile]) - if (args.dryRun) console.info(`\n---\n${chalk.gray(content.trim())}\n---\n`) - else writeFile(args, args.infile, header + '\n' + (content + oldContent).replace(/\n+$/, '\n')) - return resolve() - }) - }) + checkpoint(args, 'outputting changes to %s', [args.infile]); + if (args.dryRun) + console.info(`\n---\n${chalk.gray(content.trim())}\n---\n`); + else + writeFile( + args, + args.infile, + changelogFrontMatter + + header + + '\n' + + (content + oldContentBody).replace(/\n+$/, '\n'), + ); + return resolve(); + }); + }); } -function createIfMissing (args) { +function createIfMissing(args) { try { - fs.accessSync(args.infile, fs.F_OK) + fs.accessSync(args.infile, fs.constants.F_OK); } catch (err) { if (err.code === 'ENOENT') { - checkpoint(args, 'created %s', [args.infile]) - args.outputUnreleased = true - writeFile(args, args.infile, '\n') + checkpoint(args, 'created %s', [args.infile]); + args.outputUnreleased = true; + writeFile(args, args.infile, '\n'); } } } diff --git a/lib/lifecycles/commit.js b/lib/lifecycles/commit.js index 7b90a8f4c..f7b996c96 100644 --- a/lib/lifecycles/commit.js +++ b/lib/lifecycles/commit.js @@ -1,69 +1,68 @@ -const bump = require('../lifecycles/bump') -const checkpoint = require('../checkpoint') -const formatCommitMessage = require('../format-commit-message') -const path = require('path') -const runExecFile = require('../run-execFile') -const runLifecycleScript = require('../run-lifecycle-script') +const bump = require('../lifecycles/bump'); +const checkpoint = require('../checkpoint'); +const formatCommitMessage = require('../format-commit-message'); +const path = require('path'); +const runExecFile = require('../run-execFile'); +const runLifecycleScript = require('../run-lifecycle-script'); module.exports = async function (args, newVersion) { - if (args.skip.commit) return - const message = await runLifecycleScript(args, 'precommit') - if (message && message.length) args.releaseCommitMessageFormat = message - await execCommit(args, newVersion) - await runLifecycleScript(args, 'postcommit') -} + if (args.skip.commit) return; + const message = await runLifecycleScript(args, 'precommit'); + if (message && message.length) args.releaseCommitMessageFormat = message; + await execCommit(args, newVersion); + await runLifecycleScript(args, 'postcommit'); +}; -async function execCommit (args, newVersion) { - let msg = 'committing %s' - let paths = [] - const verify = args.verify === false || args.n ? ['--no-verify'] : [] - const sign = args.sign ? ['-S'] : [] - const toAdd = [] +async function execCommit(args, newVersion) { + let msg = 'committing %s'; + let paths = []; + const verify = args.verify === false || args.n ? ['--no-verify'] : []; + const sign = args.sign ? ['-S'] : []; + const signoff = args.signoff ? ['--signoff'] : []; + const toAdd = []; // only start with a pre-populated paths list when CHANGELOG processing is not skipped if (!args.skip.changelog) { - paths = [args.infile] - toAdd.push(args.infile) + paths = [args.infile]; + toAdd.push(args.infile); } // commit any of the config files that we've updated // the version # for. Object.keys(bump.getUpdatedConfigs()).forEach(function (p) { - paths.unshift(p) - toAdd.push(path.relative(process.cwd(), p)) + paths.unshift(p); + toAdd.push(path.relative(process.cwd(), p)); // account for multiple files in the output message if (paths.length > 1) { - msg += ' and %s' + msg += ' and %s'; } - }) + }); if (args.commitAll) { - msg += ' and %s' - paths.push('all staged files') + msg += ' and %s'; + paths.push('all staged files'); } - checkpoint(args, msg, paths) + checkpoint(args, msg, paths); // nothing to do, exit without commit anything - if (args.skip.changelog && args.skip.bump && toAdd.length === 0) { - return + if ( + !args.commitAll && + args.skip.changelog && + args.skip.bump && + toAdd.length === 0 + ) { + return; } - await runExecFile(args, 'git', ['add'].concat(toAdd)) + await runExecFile(args, 'git', ['add'].concat(toAdd)); await runExecFile( args, 'git', - [ - 'commit' - ].concat( - verify, - sign, - args.commitAll ? [] : toAdd, - [ - '-m', - `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}` - ] - ) - ) + ['commit'].concat(verify, sign, signoff, args.commitAll ? [] : toAdd, [ + '-m', + `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}`, + ]), + ); } diff --git a/lib/lifecycles/tag.js b/lib/lifecycles/tag.js index ec1b88e53..8a0edab0a 100644 --- a/lib/lifecycles/tag.js +++ b/lib/lifecycles/tag.js @@ -1,39 +1,60 @@ -const bump = require('../lifecycles/bump') -const chalk = require('chalk') -const checkpoint = require('../checkpoint') -const figures = require('figures') -const formatCommitMessage = require('../format-commit-message') -const runExecFile = require('../run-execFile') -const runLifecycleScript = require('../run-lifecycle-script') +const bump = require('../lifecycles/bump'); +const chalk = require('chalk'); +const checkpoint = require('../checkpoint'); +const figures = require('figures'); +const formatCommitMessage = require('../format-commit-message'); +const runExecFile = require('../run-execFile'); +const runLifecycleScript = require('../run-lifecycle-script'); +const { detectPMByLockFile } = require('../detect-package-manager'); module.exports = async function (newVersion, pkgPrivate, args) { - if (args.skip.tag) return - await runLifecycleScript(args, 'pretag') - await execTag(newVersion, pkgPrivate, args) - await runLifecycleScript(args, 'posttag') + if (args.skip.tag) return; + await runLifecycleScript(args, 'pretag'); + await execTag(newVersion, pkgPrivate, args); + await runLifecycleScript(args, 'posttag'); +}; + +async function detectPublishHint() { + const npmClientName = await detectPMByLockFile(); + const publishCommand = 'publish'; + return `${npmClientName} ${publishCommand}`; } -async function execTag (newVersion, pkgPrivate, args) { - let tagOption +async function execTag(newVersion, pkgPrivate, args) { + const tagOption = []; if (args.sign) { - tagOption = '-s' + tagOption.push('-s'); } else { - tagOption = '-a' + tagOption.push('-a'); + } + if (args.tagForce) { + tagOption.push('-f'); } - checkpoint(args, 'tagging release %s%s', [args.tagPrefix, newVersion]) - await runExecFile(args, 'git', ['tag', tagOption, args.tagPrefix + newVersion, '-m', `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}`]) - const currentBranch = await runExecFile('', 'git', ['rev-parse', '--abbrev-ref', 'HEAD']) - let message = 'git push --follow-tags origin ' + currentBranch.trim() + checkpoint(args, 'tagging release %s%s', [args.tagPrefix, newVersion]); + await runExecFile(args, 'git', [ + 'tag', + ...tagOption, + args.tagPrefix + newVersion, + '-m', + `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}`, + ]); + const currentBranch = await runExecFile('', 'git', [ + 'rev-parse', + '--abbrev-ref', + 'HEAD', + ]); + let message = 'git push --follow-tags origin ' + currentBranch.trim(); if (pkgPrivate !== true && bump.getUpdatedConfigs()['package.json']) { - message += ' && npm publish' + const npmPublishHint = args.npmPublishHint || (await detectPublishHint()); + message += ` && ${npmPublishHint}`; if (args.prerelease !== undefined) { if (args.prerelease === '') { - message += ' --tag prerelease' + message += ' --tag prerelease'; } else { - message += ' --tag ' + args.prerelease + message += ' --tag ' + args.prerelease; } } } - checkpoint(args, 'Run `%s` to publish', [message], chalk.blue(figures.info)) + checkpoint(args, 'Run `%s` to publish', [message], chalk.blue(figures.info)); } diff --git a/lib/preset-loader.js b/lib/preset-loader.js index a7c077281..0d5bde9ab 100644 --- a/lib/preset-loader.js +++ b/lib/preset-loader.js @@ -1,17 +1,19 @@ // TODO: this should be replaced with an object we maintain and // describe in: https://github.com/conventional-changelog/conventional-changelog-config-spec -const spec = require('conventional-changelog-config-spec') +const spec = require('conventional-changelog-config-spec'); module.exports = (args) => { - const defaultPreset = require.resolve('conventional-changelog-conventionalcommits') - let preset = args.preset || defaultPreset + const defaultPreset = require.resolve( + 'conventional-changelog-conventionalcommits', + ); + let preset = args.preset || defaultPreset; if (preset === defaultPreset) { preset = { - name: defaultPreset - } - Object.keys(spec.properties).forEach(key => { - if (args[key] !== undefined) preset[key] = args[key] - }) + name: defaultPreset, + }; + Object.keys(spec.properties).forEach((key) => { + if (args[key] !== undefined) preset[key] = args[key]; + }); } - return preset -} + return preset; +}; diff --git a/lib/print-error.js b/lib/print-error.js index 84aaa8257..298964e8c 100644 --- a/lib/print-error.js +++ b/lib/print-error.js @@ -1,12 +1,15 @@ -const chalk = require('chalk') +const chalk = require('chalk'); module.exports = function (args, msg, opts) { if (!args.silent) { - opts = Object.assign({ - level: 'error', - color: 'red' - }, opts) + opts = Object.assign( + { + level: 'error', + color: 'red', + }, + opts, + ); - console[opts.level](chalk[opts.color](msg)) + console[opts.level](chalk[opts.color](msg)); } -} +}; diff --git a/lib/run-exec.js b/lib/run-exec.js index eec2feb3d..6d4441717 100644 --- a/lib/run-exec.js +++ b/lib/run-exec.js @@ -1,18 +1,18 @@ -const { promisify } = require('util') -const printError = require('./print-error') +const { promisify } = require('util'); +const printError = require('./print-error'); -const exec = promisify(require('child_process').exec) +const exec = promisify(require('child_process').exec); module.exports = async function (args, cmd) { - if (args.dryRun) return + if (args.dryRun) return; try { - const { stderr, stdout } = await exec(cmd) + const { stderr, stdout } = await exec(cmd); // If exec returns content in stderr, but no error, print it as a warning - if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' }) - return stdout + if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' }); + return stdout; } catch (error) { // If exec returns an error, print it and exit with return code 1 - printError(args, error.stderr || error.message) - throw error + printError(args, error.stderr || error.message); + throw error; } -} +}; diff --git a/lib/run-execFile.js b/lib/run-execFile.js index c0c814d45..e20488ad6 100644 --- a/lib/run-execFile.js +++ b/lib/run-execFile.js @@ -1,18 +1,18 @@ -const { promisify } = require('util') -const printError = require('./print-error') +const { promisify } = require('util'); +const printError = require('./print-error'); -const execFile = promisify(require('child_process').execFile) +const execFile = promisify(require('child_process').execFile); module.exports = async function (args, cmd, cmdArgs) { - if (args.dryRun) return + if (args.dryRun) return; try { - const { stderr, stdout } = await execFile(cmd, cmdArgs) + const { stderr, stdout } = await execFile(cmd, cmdArgs); // If execFile returns content in stderr, but no error, print it as a warning - if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' }) - return stdout + if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' }); + return stdout; } catch (error) { // If execFile returns an error, print it and exit with return code 1 - printError(args, error.stderr || error.message) - throw error + printError(args, error.stderr || error.message); + throw error; } -} +}; diff --git a/lib/run-lifecycle-script.js b/lib/run-lifecycle-script.js index a4c88c2e9..4e10193cd 100644 --- a/lib/run-lifecycle-script.js +++ b/lib/run-lifecycle-script.js @@ -1,13 +1,18 @@ -const chalk = require('chalk') -const checkpoint = require('./checkpoint') -const figures = require('figures') -const runExec = require('./run-exec') +const chalk = require('chalk'); +const checkpoint = require('./checkpoint'); +const figures = require('figures'); +const runExec = require('./run-exec'); module.exports = function (args, hookName) { - const scripts = args.scripts - if (!scripts || !scripts[hookName]) return Promise.resolve() - const command = scripts[hookName] - checkpoint(args, 'Running lifecycle script "%s"', [hookName]) - checkpoint(args, '- execute command: "%s"', [command], chalk.blue(figures.info)) - return runExec(args, command) -} + const scripts = args.scripts; + if (!scripts || !scripts[hookName]) return Promise.resolve(); + const command = scripts[hookName]; + checkpoint(args, 'Running lifecycle script "%s"', [hookName]); + checkpoint( + args, + '- execute command: "%s"', + [command], + chalk.blue(figures.info), + ); + return runExec(args, command); +}; diff --git a/lib/stringify-package.js b/lib/stringify-package.js new file mode 100644 index 000000000..8fd32a9ec --- /dev/null +++ b/lib/stringify-package.js @@ -0,0 +1,36 @@ +/* +Copyright npm, Inc + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +https://github.com/npm/stringify-package/blob/main/LICENSE +*/ + +'use strict'; + +module.exports = stringifyPackage; + +const DEFAULT_INDENT = 2; +const CRLF = '\r\n'; +const LF = '\n'; + +function stringifyPackage(data, indent, newline) { + indent = indent || (indent === 0 ? 0 : DEFAULT_INDENT); + const json = JSON.stringify(data, null, indent); + + if (newline === CRLF) { + return json.replace(/\n/g, CRLF) + CRLF; + } + + return json + LF; +} diff --git a/lib/updaters/index.js b/lib/updaters/index.js index ec561a81e..9eb6dd93f 100644 --- a/lib/updaters/index.js +++ b/lib/updaters/index.js @@ -1,52 +1,79 @@ -const path = require('path') -const JSON_BUMP_FILES = require('../../defaults').bumpFiles +const path = require('path'); +const JSON_BUMP_FILES = require('../../defaults').bumpFiles; const updatersByType = { json: require('./types/json'), - 'plain-text': require('./types/plain-text') -} -const PLAIN_TEXT_BUMP_FILES = ['VERSION.txt', 'version.txt'] + 'plain-text': require('./types/plain-text'), + maven: require('./types/maven'), + gradle: require('./types/gradle'), + csproj: require('./types/csproj'), + yaml: require('./types/yaml'), + openapi: require('./types/openapi'), + python: require('./types/python'), +}; +const PLAIN_TEXT_BUMP_FILES = ['VERSION.txt', 'version.txt']; -function getUpdaterByType (type) { - const updater = updatersByType[type] +function getUpdaterByType(type) { + const updater = updatersByType[type]; if (!updater) { - throw Error(`Unable to locate updater for provided type (${type}).`) + throw Error(`Unable to locate updater for provided type (${type}).`); } - return updater + return updater; } -function getUpdaterByFilename (filename) { +function getUpdaterByFilename(filename) { if (JSON_BUMP_FILES.includes(path.basename(filename))) { - return getUpdaterByType('json') + return getUpdaterByType('json'); } if (PLAIN_TEXT_BUMP_FILES.includes(filename)) { - return getUpdaterByType('plain-text') + return getUpdaterByType('plain-text'); + } + if (/pom.xml/.test(filename)) { + return getUpdaterByType('maven'); + } + if (/build.gradle/.test(filename)) { + return getUpdaterByType('gradle'); + } + if (filename.endsWith('.csproj')) { + return getUpdaterByType('csproj'); + } + if (/openapi.yaml/.test(filename)) { + return getUpdaterByType('openapi'); + } + if (/\.ya?ml$/.test(filename)) { + return getUpdaterByType('yaml'); + } + if (/pyproject.toml/.test(filename)) { + return getUpdaterByType('python'); } throw Error( - `Unsupported file (${filename}) provided for bumping.\n Please specify the updater \`type\` or use a custom \`updater\`.` - ) + `Unsupported file (${filename}) provided for bumping.\n Please specify the updater \`type\` or use a custom \`updater\`.`, + ); } -function getCustomUpdaterFromPath (updater) { +function getCustomUpdaterFromPath(updater) { if (typeof updater === 'string') { - return require(path.resolve(process.cwd(), updater)) + return require(path.resolve(process.cwd(), updater)); } if ( typeof updater.readVersion === 'function' && typeof updater.writeVersion === 'function' ) { - return updater + return updater; } - throw new Error('Updater must be a string path or an object with readVersion and writeVersion methods') + throw new Error( + 'Updater must be a string path or an object with readVersion and writeVersion methods', + ); } /** * Simple check to determine if the object provided is a compatible updater. */ -function isValidUpdater (obj) { +function isValidUpdater(obj) { return ( + obj && typeof obj.readVersion === 'function' && typeof obj.writeVersion === 'function' - ) + ); } module.exports.resolveUpdaterObjectFromArgument = function (arg) { @@ -54,32 +81,40 @@ module.exports.resolveUpdaterObjectFromArgument = function (arg) { * If an Object was not provided, we assume it's the path/filename * of the updater. */ - let updater = arg + let updater = arg; if (isValidUpdater(updater)) { - return updater + return updater; } if (typeof updater !== 'object') { updater = { - filename: arg - } + filename: arg, + }; } - try { - if (typeof updater.updater === 'string') { - updater.updater = getCustomUpdaterFromPath(updater.updater) - } else if (updater.type) { - updater.updater = getUpdaterByType(updater.type) - } else { - updater.updater = getUpdaterByFilename(updater.filename) + + if (!isValidUpdater(updater.updater)) { + try { + if (typeof updater.updater === 'string') { + updater.updater = getCustomUpdaterFromPath(updater.updater); + } else if (updater.type) { + updater.updater = getUpdaterByType(updater.type); + } else { + updater.updater = getUpdaterByFilename(updater.filename); + } + } catch (err) { + if (err.code !== 'ENOENT') + console.warn( + `Unable to obtain updater for: ${JSON.stringify(arg)}\n - Error: ${ + err.message + }\n - Skipping...`, + ); } - } catch (err) { - if (err.code !== 'ENOENT') console.warn(`Unable to obtain updater for: ${JSON.stringify(arg)}\n - Error: ${err.message}\n - Skipping...`) } /** * We weren't able to resolve an updater for the argument. */ if (!isValidUpdater(updater.updater)) { - return false + return false; } - return updater -} + return updater; +}; diff --git a/lib/updaters/types/csproj.js b/lib/updaters/types/csproj.js new file mode 100644 index 000000000..286ea5b7c --- /dev/null +++ b/lib/updaters/types/csproj.js @@ -0,0 +1,15 @@ +const versionRegex = /(.*)<\/Version>/; + +module.exports.readVersion = function (contents) { + const matches = versionRegex.exec(contents); + if (matches === null || matches.length !== 2) { + throw new Error( + 'Failed to read the Version field in your csproj file - is it present?', + ); + } + return matches[1]; +}; + +module.exports.writeVersion = function (contents, version) { + return contents.replace(versionRegex, `${version}`); +}; diff --git a/lib/updaters/types/gradle.js b/lib/updaters/types/gradle.js new file mode 100644 index 000000000..58d9dc879 --- /dev/null +++ b/lib/updaters/types/gradle.js @@ -0,0 +1,18 @@ +const versionRegex = /^version\s+=\s+['"]([\d.]+)['"]/m; + +module.exports.readVersion = function (contents) { + const matches = versionRegex.exec(contents); + if (matches === null) { + throw new Error( + 'Failed to read the version field in your gradle file - is it present?', + ); + } + + return matches[1]; +}; + +module.exports.writeVersion = function (contents, version) { + return contents.replace(versionRegex, () => { + return `version = "${version}"`; + }); +}; diff --git a/lib/updaters/types/json.js b/lib/updaters/types/json.js index fdc2a8907..73e7c7e58 100644 --- a/lib/updaters/types/json.js +++ b/lib/updaters/types/json.js @@ -1,25 +1,25 @@ -const stringifyPackage = require('stringify-package') -const detectIndent = require('detect-indent') -const detectNewline = require('detect-newline') +const stringifyPackage = require('../../stringify-package'); +const detectIndent = require('detect-indent'); +const detectNewline = require('detect-newline'); module.exports.readVersion = function (contents) { - return JSON.parse(contents).version -} + return JSON.parse(contents).version; +}; module.exports.writeVersion = function (contents, version) { - const json = JSON.parse(contents) - const indent = detectIndent(contents).indent - const newline = detectNewline(contents) - json.version = version + const json = JSON.parse(contents); + const indent = detectIndent(contents).indent; + const newline = detectNewline(contents); + json.version = version; if (json.packages && json.packages['']) { // package-lock v2 stores version there too - json.packages[''].version = version + json.packages[''].version = version; } - return stringifyPackage(json, indent, newline) -} + return stringifyPackage(json, indent, newline); +}; module.exports.isPrivate = function (contents) { - return JSON.parse(contents).private -} + return JSON.parse(contents).private; +}; diff --git a/lib/updaters/types/maven.js b/lib/updaters/types/maven.js new file mode 100644 index 000000000..37d81c5be --- /dev/null +++ b/lib/updaters/types/maven.js @@ -0,0 +1,44 @@ +const jsdom = require('jsdom'); +const serialize = require('w3c-xmlserializer'); +const detectNewline = require('detect-newline'); +const CRLF = '\r\n'; +const LF = '\n'; + +function pomDocument(contents) { + const dom = new jsdom.JSDOM(''); + const parser = new dom.window.DOMParser(); + return parser.parseFromString(contents, 'application/xml'); +} + +function pomVersionElement(document) { + const versionElement = document.querySelector('project > version'); + + if (!versionElement) { + throw new Error( + 'Failed to read the version field in your pom file - is it present?', + ); + } + + return versionElement; +} + +module.exports.readVersion = function (contents) { + const document = pomDocument(contents); + return pomVersionElement(document).textContent; +}; + +module.exports.writeVersion = function (contents, version) { + const newline = detectNewline(contents); + const document = pomDocument(contents); + const versionElement = pomVersionElement(document); + + versionElement.textContent = version; + + const xml = serialize(document); + + if (newline === CRLF) { + return xml.replace(/\n/g, CRLF) + CRLF; + } + + return xml + LF; +}; diff --git a/lib/updaters/types/openapi.js b/lib/updaters/types/openapi.js new file mode 100644 index 000000000..981386be8 --- /dev/null +++ b/lib/updaters/types/openapi.js @@ -0,0 +1,15 @@ +const yaml = require('yaml'); +const detectNewline = require('detect-newline'); + +module.exports.readVersion = function (contents) { + return yaml.parse(contents).info.version; +}; + +module.exports.writeVersion = function (contents, version) { + const newline = detectNewline(contents); + const document = yaml.parseDocument(contents); + + document.get('info').set('version', version); + + return document.toString().replace(/\r?\n/g, newline); +}; diff --git a/lib/updaters/types/plain-text.js b/lib/updaters/types/plain-text.js index 18bcabed2..ecc19d33b 100644 --- a/lib/updaters/types/plain-text.js +++ b/lib/updaters/types/plain-text.js @@ -1,7 +1,7 @@ module.exports.readVersion = function (contents) { - return contents -} + return contents; +}; module.exports.writeVersion = function (_contents, version) { - return version -} + return version; +}; diff --git a/lib/updaters/types/python.js b/lib/updaters/types/python.js new file mode 100644 index 000000000..afaca68a5 --- /dev/null +++ b/lib/updaters/types/python.js @@ -0,0 +1,30 @@ +const versionExtractRegex = /version[" ]*=[ ]*["'](.*)["']/i; + +const getVersionIndex = function (lines) { + let version; + const lineNumber = lines.findIndex((line) => { + const versionMatcher = line.match(versionExtractRegex); + // if version not found in lines provided, return false + if (versionMatcher == null) { + return false; + } + version = versionMatcher[1]; + return true; + }); + return { version, lineNumber }; +}; + +module.exports.readVersion = function (contents) { + const lines = contents.split('\n'); + const versionIndex = getVersionIndex(lines); + return versionIndex.version; +}; + +module.exports.writeVersion = function (contents, version) { + const lines = contents.split('\n'); + const versionIndex = getVersionIndex(lines); + const versionLine = lines[versionIndex.lineNumber]; + const newVersionLine = versionLine.replace(versionIndex.version, version); + lines[versionIndex.lineNumber] = newVersionLine; + return lines.join('\n'); +}; diff --git a/lib/updaters/types/yaml.js b/lib/updaters/types/yaml.js new file mode 100644 index 000000000..0812514ad --- /dev/null +++ b/lib/updaters/types/yaml.js @@ -0,0 +1,15 @@ +const yaml = require('yaml'); +const detectNewline = require('detect-newline'); + +module.exports.readVersion = function (contents) { + return yaml.parse(contents).version; +}; + +module.exports.writeVersion = function (contents, version) { + const newline = detectNewline(contents); + const document = yaml.parseDocument(contents); + + document.set('version', version); + + return document.toString().replace(/\r?\n/g, newline); +}; diff --git a/lib/write-file.js b/lib/write-file.js index b6aaa19f7..b50ad4222 100644 --- a/lib/write-file.js +++ b/lib/write-file.js @@ -1,6 +1,6 @@ -const fs = require('fs') +const fs = require('fs'); module.exports = function (args, filePath, content) { - if (args.dryRun) return - fs.writeFileSync(filePath, content, 'utf8') -} + if (args.dryRun) return; + fs.writeFileSync(filePath, content, 'utf8'); +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..90489549a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,8775 @@ +{ + "name": "commit-and-tag-version", + "version": "12.6.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "commit-and-tag-version", + "version": "12.6.0", + "license": "ISC", + "dependencies": { + "chalk": "^2.4.2", + "conventional-changelog": "4.0.0", + "conventional-changelog-config-spec": "2.1.0", + "conventional-changelog-conventionalcommits": "6.1.0", + "conventional-recommended-bump": "7.0.1", + "detect-indent": "^6.1.0", + "detect-newline": "^3.1.0", + "dotgitignore": "^2.1.0", + "figures": "^3.2.0", + "find-up": "^5.0.0", + "git-semver-tags": "^5.0.1", + "jsdom": "^25.0.1", + "semver": "^7.7.2", + "w3c-xmlserializer": "^5.0.0", + "yaml": "^2.6.0", + "yargs": "^17.7.2" + }, + "bin": { + "commit-and-tag-version": "bin/cli.js" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "eslint": "^9.13.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest": "^28.8.3", + "eslint-plugin-n": "^17.21.3", + "eslint-plugin-promise": "^7.1.0", + "jest": "^29.7.0", + "jest-serial-runner": "^1.2.1", + "prettier": "^3.3.3", + "shelljs": "^0.10.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.17.10", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.18.0", + "@babel/helper-compilation-targets": "^7.17.10", + "@babel/helper-module-transforms": "^7.18.0", + "@babel/helpers": "^7.18.0", + "@babel/parser": "^7.18.0", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.17.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.17.10", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.18.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.18.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz", + "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", + "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", + "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.0", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@hutson/parse-repository-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", + "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/console/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/core/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/reporters/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/write-file-atomic": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", + "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", + "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", + "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", + "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", + "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.10.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", + "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.20.3", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", + "escalade": "^3.1.1", + "node-releases": "^2.0.3", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001342", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "2.4.2", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "1.9.3", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/conventional-changelog": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-4.0.0.tgz", + "integrity": "sha512-JbZjwE1PzxQCvm+HUTIr+pbSekS8qdOZzMakdFyPtdkEWwFvwEJYONzjgMm0txCb2yBcIcfKDmg8xtCKTdecNQ==", + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^6.0.0", + "conventional-changelog-atom": "^3.0.0", + "conventional-changelog-codemirror": "^3.0.0", + "conventional-changelog-conventionalcommits": "^6.0.0", + "conventional-changelog-core": "^5.0.0", + "conventional-changelog-ember": "^3.0.0", + "conventional-changelog-eslint": "^4.0.0", + "conventional-changelog-express": "^3.0.0", + "conventional-changelog-jquery": "^4.0.0", + "conventional-changelog-jshint": "^3.0.0", + "conventional-changelog-preset-loader": "^3.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", + "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-atom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-3.0.0.tgz", + "integrity": "sha512-pnN5bWpH+iTUWU3FaYdw5lJmfWeqSyrUkG+wyHBI9tC1dLNnHkbAOg1SzTQ7zBqiFrfo55h40VsGXWMdopwc5g==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-codemirror": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-3.0.0.tgz", + "integrity": "sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-config-spec": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "6.1.0", + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-core": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-5.0.2.tgz", + "integrity": "sha512-RhQOcDweXNWvlRwUDCpaqXzbZemKPKncCWZG50Alth72WITVd6nhVk9MJ6w1k9PFNBcZ3YwkdkChE+8+ZwtUug==", + "license": "MIT", + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^6.0.0", + "conventional-commits-parser": "^4.0.0", + "dateformat": "^3.0.3", + "get-pkg-repo": "^4.2.1", + "git-raw-commits": "^3.0.0", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^5.0.0", + "normalize-package-data": "^3.0.3", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-ember": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-3.0.0.tgz", + "integrity": "sha512-7PYthCoSxIS98vWhVcSphMYM322OxptpKAuHYdVspryI0ooLDehRXWeRWgN+zWSBXKl/pwdgAg8IpLNSM1/61A==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-eslint": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-4.0.0.tgz", + "integrity": "sha512-nEZ9byP89hIU0dMx37JXQkE1IpMmqKtsaR24X7aM3L6Yy/uAtbb+ogqthuNYJkeO1HyvK7JsX84z8649hvp43Q==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-express": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-3.0.0.tgz", + "integrity": "sha512-HqxihpUMfIuxvlPvC6HltA4ZktQEUan/v3XQ77+/zbu8No/fqK3rxSZaYeHYant7zRxQNIIli7S+qLS9tX9zQA==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-jquery": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-4.0.0.tgz", + "integrity": "sha512-TTIN5CyzRMf8PUwyy4IOLmLV2DFmPtasKN+x7EQKzwSX8086XYwo+NeaeA3VUT8bvKaIy5z/JoWUvi7huUOgaw==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-jshint": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-3.0.0.tgz", + "integrity": "sha512-bQof4byF4q+n+dwFRkJ/jGf9dCNUv4/kCDcjeCizBvfF81TeimPZBB6fT4HYbXgxxfxWXNl/i+J6T0nI4by6DA==", + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-preset-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-3.0.0.tgz", + "integrity": "sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-6.0.1.tgz", + "integrity": "sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==", + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^3.0.0", + "dateformat": "^3.0.3", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^8.1.2", + "semver": "^7.0.0", + "split": "^1.0.1" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-commits-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", + "integrity": "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==", + "license": "MIT", + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-commits-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", + "license": "MIT", + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-recommended-bump": { + "version": "7.0.1", + "license": "MIT", + "dependencies": { + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^3.0.0", + "conventional-commits-filter": "^3.0.0", + "conventional-commits-parser": "^4.0.0", + "git-raw-commits": "^3.0.0", + "git-semver-tags": "^5.0.0", + "meow": "^8.1.2" + }, + "bin": { + "conventional-recommended-bump": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/create-jest/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/create-jest/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssstyle": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "dependencies": { + "rrweb-cssom": "^0.7.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/dargs": { + "version": "7.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "node_modules/dedent": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotgitignore": { + "version": "2.1.0", + "license": "ISC", + "dependencies": { + "find-up": "^3.0.0", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/find-up": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/locate-path": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/p-limit": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotgitignore/node_modules/p-locate": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/path-exists": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.138", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", + "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.13.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "28.8.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.3.tgz", + "integrity": "sha512-HIQ3t9hASLKm2IhIOqnu+ifw7uLZkIlR7RYNv7fMcEi/p0CIiJmfriStQS2LDkgtY4nyLbIZAD+JL347Yc2ETQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "engines": { + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-n": { + "version": "17.21.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.21.3.tgz", + "integrity": "sha512-MtxYjDZhMQgsWRm/4xYLL0i2EhusWT7itDxlJ80l1NND2AL2Vi5Mvneqv/ikG9+zpran0VsVRXTEHrpLmUZRNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.5.0", + "enhanced-resolve": "^5.17.1", + "eslint-plugin-es-x": "^7.8.0", + "get-tsconfig": "^4.8.1", + "globals": "^15.11.0", + "globrex": "^0.1.2", + "ignore": "^5.3.2", + "semver": "^7.6.3", + "ts-declaration-location": "^1.0.6" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", + "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.1.0.tgz", + "integrity": "sha512-8trNmPxdAy3W620WKDpaS65NlM5yAumod6XeC4LOb+jxlkG4IVcp68c6dXY2ev+uT4U1PtG57YDV6EGAXN0GbQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", + "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "dev": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.15.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-pkg-repo": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", + "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", + "license": "MIT", + "dependencies": { + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" + }, + "bin": { + "get-pkg-repo": "src/cli.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-pkg-repo/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "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/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/git-raw-commits": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-3.0.0.tgz", + "integrity": "sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==", + "license": "MIT", + "dependencies": { + "dargs": "^7.0.0", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "license": "MIT", + "dependencies": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-semver-tags": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-5.0.1.tgz", + "integrity": "sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==", + "dependencies": { + "meow": "^8.1.2", + "semver": "^7.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "license": "BSD", + "dependencies": { + "ini": "^1.3.2" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "dev": true, + "license": "ISC", + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.4", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-circus/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-cli/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-config/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-config/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-each/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-resolve/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-runner/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-serial-runner": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-serial-runner/-/jest-serial-runner-1.2.1.tgz", + "integrity": "sha512-d59fF+7HdjNvQEL7B4WyFE+f8q5tGzlNUqtOnxTrT1ofun7O6/Lgm/j255BBgCY2fmSue/34M7Xy9+VWRByP0Q==", + "dev": true, + "peerDependencies": { + "jest-runner": "24.x - 29.x" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-snapshot/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "8.1.2", + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/find-up": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "2.8.9", + "license": "ISC" + }, + "node_modules/meow/node_modules/locate-path": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/p-limit": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/p-locate": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/parse-json": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/read-pkg": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg-up": { + "version": "7.0.1", + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/semver": { + "version": "5.7.2", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "license": "MIT" + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz", + "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==" + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/prompts": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.4", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.10.0.tgz", + "integrity": "sha512-Jex+xw5Mg2qMZL3qnzXIfaxEtBaC4n7xifqaqtrZDdlheR70OGkydrPJWT0V1cA1k3nanC86x9FwAmQl6w3Klw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "execa": "^5.1.1", + "fast-glob": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/string-length": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-extensions": { + "version": "1.9.0", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/tldts": { + "version": "6.1.52", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.52.tgz", + "integrity": "sha512-fgrDJXDjbAverY6XnIt0lNfv8A0cf7maTEaZxNykLGsLG7XP+5xhjBTrt/ieAsFjAlZ+G5nmXomLcZDkxXnDzw==", + "dependencies": { + "tldts-core": "^6.1.52" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.52", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.52.tgz", + "integrity": "sha512-j4OxQI5rc1Ve/4m/9o2WhWSC4jGc4uVbCINdOEJRAraCi0YqTqgMcxUx7DbmuP0G3PCixoof/RZB0Q5Kh9tagw==" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-declaration-location": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz", + "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==", + "dev": true, + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/ts-declaration-location" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, + "node_modules/ts-declaration-location/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.2.tgz", + "integrity": "sha512-S8KA6DDI47nQXJSi2ctQ629YzwOVs+bQML6DAtvy0wgNdpi+0ySpQK0g2pxBq2xfF2z3YCscu7NNA8nXT9PlIQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "8.0.1", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index d1514dad5..ac7463ef2 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,23 @@ { - "name": "standard-version", - "version": "9.5.0", + "name": "commit-and-tag-version", + "version": "12.6.0", "description": "replacement for `npm version` with automatic CHANGELOG generation", "bin": "bin/cli.js", "scripts": { - "fix": "eslint . --fix", - "posttest": "eslint .", - "test": "nyc mocha --timeout=30000", - "test:unit": "mocha --exclude test/git.spec.js", - "coverage": "nyc report --reporter=lcov", + "lint": "eslint .", + "lint:fix": "npm run lint -- --fix", + "posttest": "npm run lint && npm run format:check", + "format:base": "prettier \"./**/*.{ts,js}\"", + "format:fix": "npm run format:base -- --write", + "format:check": "npm run format:base -- --check", + "test": "jest", + "test:unit": "jest --testPathIgnorePatterns test/git.integration-test.js", + "test:git-integration": "jest --testPathPattern test/git.integration-test.js", "release": "bin/cli.js" }, - "nyc": { - "exclude": [ - "tmp/**" - ] - }, - "repository": "conventional-changelog/standard-version", + "repository": "absolute-version/commit-and-tag-version", "engines": { - "node": ">=10" + "node": ">=18" }, "keywords": [ "conventional-changelog", @@ -31,39 +30,44 @@ "standard" ], "author": "Ben Coe ", + "contributors": [ + "Timothy Jones (https://github.com/TimothyJones)" + ], "license": "ISC", "bugs": { - "url": "https://github.com/conventional-changelog/standard-version/issues" + "url": "https://github.com/absolute-version/commit-and-tag-version/issues" }, - "homepage": "https://github.com/conventional-changelog/standard-version#readme", + "homepage": "https://github.com/absolute-version/commit-and-tag-version#readme", "dependencies": { "chalk": "^2.4.2", - "conventional-changelog": "3.1.25", + "conventional-changelog": "4.0.0", "conventional-changelog-config-spec": "2.1.0", - "conventional-changelog-conventionalcommits": "4.6.3", - "conventional-recommended-bump": "6.1.0", - "detect-indent": "^6.0.0", + "conventional-changelog-conventionalcommits": "6.1.0", + "conventional-recommended-bump": "7.0.1", + "detect-indent": "^6.1.0", "detect-newline": "^3.1.0", "dotgitignore": "^2.1.0", - "figures": "^3.1.0", + "figures": "^3.2.0", "find-up": "^5.0.0", - "git-semver-tags": "^4.0.0", - "semver": "^7.1.1", - "stringify-package": "^1.0.1", - "yargs": "^16.0.0" + "git-semver-tags": "^5.0.1", + "jsdom": "^25.0.1", + "semver": "^7.7.2", + "w3c-xmlserializer": "^5.0.0", + "yaml": "^2.6.0", + "yargs": "^17.7.2" }, "devDependencies": { - "chai": "^4.2.0", - "eslint": "^7.14.0", - "eslint-config-standard": "^16.0.2", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.0.0", - "mocha": "^8.2.1", - "mock-fs": "^4.13.0", - "mockery": "^2.1.0", - "nyc": "^15.1.0", - "shelljs": "^0.8.4", - "std-mocks": "^1.0.1" + "@eslint/js": "^9.36.0", + "eslint": "^9.13.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest": "^28.8.3", + "eslint-plugin-n": "^17.21.3", + "eslint-plugin-promise": "^7.1.0", + "jest": "^29.7.0", + "jest-serial-runner": "^1.2.1", + "prettier": "^3.3.3", + "shelljs": "^0.10.0", + "strip-ansi": "^6.0.1" } } diff --git a/renovate.json b/renovate.json deleted file mode 100644 index 5ec138e45..000000000 --- a/renovate.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": [ - "config:base" - ], - "pinVersions": false, - "rebaseStalePrs": true, - "gitAuthor": null, - "ignoreDeps": ["decamelize"] -} diff --git a/test/commit-message-config.integration-test.js b/test/commit-message-config.integration-test.js new file mode 100644 index 000000000..80fa6601a --- /dev/null +++ b/test/commit-message-config.integration-test.js @@ -0,0 +1,106 @@ +'use strict'; + +const shell = require('shelljs'); +const fs = require('fs'); + +const mockers = require('./mocks/jest-mocks'); + +function exec(opt = '') { + if (typeof opt === 'string') { + const cli = require('../command'); + opt = cli.parse(`commit-and-tag-version ${opt}`); + } + return require('../index')(opt); +} + +/** + * Mock external conventional-changelog modules + * + * bump: 'major' | 'minor' | 'patch' | Error | (opt, parserOpts, cb) => { cb(err) | cb(null, { releaseType }) } + * changelog?: string | Error | Array string | null> + * tags?: string[] | Error + */ +function mock({ bump, changelog, tags }) { + if (bump === undefined) throw new Error('bump must be defined for mock()'); + + mockers.mockRecommendedBump({ bump }); + + if (!Array.isArray(changelog)) changelog = [changelog]; + mockers.mockConventionalChangelog({ changelog }); + + mockers.mockGitSemverTags({ tags }); +} + +function writePackageJson(version, option) { + const pkg = Object.assign({}, option, { version }); + fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8'); +} + +function setupTempGitRepo() { + shell.rm('-rf', 'commit-message-config-temp'); + shell.config.silent = true; + shell.mkdir('commit-message-config-temp'); + shell.cd('commit-message-config-temp'); + shell.exec('git init'); + shell.exec('git config commit.gpgSign false'); + shell.exec('git config core.autocrlf false'); + shell.exec('git commit --allow-empty -m"root-commit"'); +} + +function setup() { + setupTempGitRepo(); + writePackageJson('1.0.0'); +} + +function reset() { + shell.cd('../'); + shell.rm('-rf', 'commit-message-config-temp'); +} + +describe('configuration', function () { + beforeEach(function () { + setup(); + }); + + afterEach(function () { + reset(); + }); + + it('.versionrc : releaseCommitMessageFormat', async function () { + fs.writeFileSync( + '.versionrc', + JSON.stringify({ + releaseCommitMessageFormat: + 'This commit represents release: {{currentTag}}', + }), + 'utf-8', + ); + mock({ bump: 'minor' }); + await exec(''); + expect(shell.exec('git log --oneline -n1').stdout).toMatch( + 'This commit represents release: 1.1.0', + ); + }); + + it('--releaseCommitMessageFormat', async function () { + mock({ bump: 'minor' }); + await exec('--releaseCommitMessageFormat="{{currentTag}} is the version."'); + expect(shell.exec('git log --oneline -n1').stdout).toContain( + '1.1.0 is the version.', + ); + }); + + it('[LEGACY] supports --message (and single %s replacement)', async function () { + mock({ bump: 'minor' }); + await exec('--message="V:%s"'); + expect(shell.exec('git log --oneline -n1').stdout).toContain('V:1.1.0'); + }); + + it('[LEGACY] supports -m (and multiple %s replacements)', async function () { + mock({ bump: 'minor' }); + await exec('--message="V:%s is the %s."'); + expect(shell.exec('git log --oneline -n1').stdout).toContain( + 'V:1.1.0 is the 1.1.0.', + ); + }); +}); diff --git a/test/config-files.integration-test.js b/test/config-files.integration-test.js new file mode 100644 index 000000000..dba07a780 --- /dev/null +++ b/test/config-files.integration-test.js @@ -0,0 +1,138 @@ +'use strict'; + +const shell = require('shelljs'); +const fs = require('fs'); + +const mockers = require('./mocks/jest-mocks'); + +function exec() { + const cli = require('../command'); + const opt = cli.parse('commit-and-tag-version'); + opt.skip = { commit: true, tag: true }; + return require('../index')(opt); +} + +/** + * Mock external conventional-changelog modules + * + * Mocks should be unregistered in test cleanup by calling unmock() + * + * bump?: 'major' | 'minor' | 'patch' | Error | (opt, parserOpts, cb) => { cb(err) | cb(null, { releaseType }) } + * changelog?: string | Error | Array string | null> + * tags?: string[] | Error + */ +function mock({ bump, changelog, tags } = {}) { + mockers.mockRecommendedBump({ bump }); + + if (!Array.isArray(changelog)) changelog = [changelog]; + + mockers.mockConventionalChangelog({ changelog }); + + mockers.mockGitSemverTags({ tags }); +} + +function setupTestDirectory() { + shell.rm('-rf', 'config-files-temp'); + shell.config.silent = true; + shell.mkdir('config-files-temp'); + shell.cd('config-files-temp'); +} + +function resetShell() { + shell.cd('../'); + shell.rm('-rf', 'config-files-temp'); +} + +describe('config files', function () { + beforeEach(function () { + setupTestDirectory(); + + fs.writeFileSync( + 'package.json', + JSON.stringify({ version: '1.0.0' }), + 'utf-8', + ); + }); + + afterEach(function () { + resetShell(); + 4; + }); + + it('reads config from .versionrc', async function () { + const issueUrlFormat = 'http://www.foo.com/{{id}}'; + const changelog = ({ preset }) => preset.issueUrlFormat; + + mock({ bump: 'minor', changelog }); + fs.writeFileSync('.versionrc', JSON.stringify({ issueUrlFormat }), 'utf-8'); + + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toContain(issueUrlFormat); + }); + + it('reads config from .versionrc.json', async function () { + const issueUrlFormat = 'http://www.foo.com/{{id}}'; + const changelog = ({ preset }) => preset.issueUrlFormat; + mock({ bump: 'minor', changelog }); + fs.writeFileSync( + '.versionrc.json', + JSON.stringify({ issueUrlFormat }), + 'utf-8', + ); + + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toContain(issueUrlFormat); + }); + + it('reads config from custom path', async function () { + const issueUrlFormat = 'http://www.foo.com/{{id}}'; + const changelog = ({ preset }) => preset.issueUrlFormat; + mock({ bump: 'minor', changelog }); + fs.mkdirSync('custom-folder'); + fs.writeFileSync( + 'custom-folder/.versionrc.json', + JSON.stringify({ issueUrlFormat }), + 'utf-8', + ); + + // Override process.argv to simulate CLI arguments before `exec`. + // This ensures yargs parses the custom config argument. + const originalArgv = process.argv; + process.argv = ['node', 'script.js', '-c', 'custom-folder/.versionrc.json']; + + await exec(['-c', 'custom-folder/.versionrc.json']); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toContain(issueUrlFormat); + + // Restore original process.argv + process.argv = originalArgv; + }); + + it('evaluates a config-function from .versionrc.js', async function () { + const issueUrlFormat = 'http://www.foo.com/{{id}}'; + const src = `module.exports = function() { return ${JSON.stringify({ + issueUrlFormat, + })} }`; + const changelog = ({ preset }) => preset.issueUrlFormat; + mock({ bump: 'minor', changelog }); + fs.writeFileSync('.versionrc.js', src, 'utf-8'); + + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toContain(issueUrlFormat); + }); + + it('evaluates a config-object from .versionrc.js', async function () { + const issueUrlFormat = 'http://www.foo.com/{{id}}'; + const src = `module.exports = ${JSON.stringify({ issueUrlFormat })}`; + const changelog = ({ preset }) => preset.issueUrlFormat; + mock({ bump: 'minor', changelog }); + fs.writeFileSync('.versionrc.js', src, 'utf-8'); + + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toContain(issueUrlFormat); + }); +}); diff --git a/test/config-files.spec.js b/test/config-files.spec.js deleted file mode 100644 index 68f718b7d..000000000 --- a/test/config-files.spec.js +++ /dev/null @@ -1,174 +0,0 @@ -/* global describe it beforeEach afterEach */ - -'use strict' - -const shell = require('shelljs') -const fs = require('fs') -const { Readable } = require('stream') -const mockery = require('mockery') -const stdMocks = require('std-mocks') - -require('chai').should() - -function exec () { - const cli = require('../command') - const opt = cli.parse('standard-version') - opt.skip = { commit: true, tag: true } - return require('../index')(opt) -} - -/** - * Mock external conventional-changelog modules - * - * Mocks should be unregistered in test cleanup by calling unmock() - * - * bump?: 'major' | 'minor' | 'patch' | Error | (opt, cb) => { cb(err) | cb(null, { releaseType }) } - * changelog?: string | Error | Array string | null> - * tags?: string[] | Error - */ -function mock ({ bump, changelog, tags } = {}) { - mockery.enable({ warnOnUnregistered: false, useCleanCache: true }) - - mockery.registerMock('conventional-recommended-bump', function (opt, cb) { - if (typeof bump === 'function') bump(opt, cb) - else if (bump instanceof Error) cb(bump) - else cb(null, bump ? { releaseType: bump } : {}) - }) - - if (!Array.isArray(changelog)) changelog = [changelog] - mockery.registerMock( - 'conventional-changelog', - (opt) => - new Readable({ - read (_size) { - const next = changelog.shift() - if (next instanceof Error) { - this.destroy(next) - } else if (typeof next === 'function') { - this.push(next(opt)) - } else { - this.push(next ? Buffer.from(next, 'utf8') : null) - } - } - }) - ) - - mockery.registerMock('git-semver-tags', function (cb) { - if (tags instanceof Error) cb(tags) - else cb(null, tags | []) - }) - - stdMocks.use() - return () => stdMocks.flush() -} - -describe('config files', () => { - beforeEach(function () { - shell.rm('-rf', 'tmp') - shell.config.silent = true - shell.mkdir('tmp') - shell.cd('tmp') - fs.writeFileSync( - 'package.json', - JSON.stringify({ version: '1.0.0' }), - 'utf-8' - ) - }) - - afterEach(function () { - shell.cd('../') - shell.rm('-rf', 'tmp') - - mockery.deregisterAll() - mockery.disable() - stdMocks.restore() - - // push out prints from the Mocha reporter - const { stdout } = stdMocks.flush() - for (const str of stdout) { - if (str.startsWith(' ')) process.stdout.write(str) - } - }) - - it('reads config from package.json', async function () { - const issueUrlFormat = 'https://standard-version.company.net/browse/{{id}}' - mock({ - bump: 'minor', - changelog: ({ preset }) => preset.issueUrlFormat - }) - const pkg = { - version: '1.0.0', - repository: { url: 'git+https://company@scm.org/office/app.git' }, - 'standard-version': { issueUrlFormat } - } - fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8') - - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) - - it('reads config from .versionrc', async function () { - const issueUrlFormat = 'http://www.foo.com/{{id}}' - const changelog = ({ preset }) => preset.issueUrlFormat - mock({ bump: 'minor', changelog }) - fs.writeFileSync('.versionrc', JSON.stringify({ issueUrlFormat }), 'utf-8') - - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) - - it('reads config from .versionrc.json', async function () { - const issueUrlFormat = 'http://www.foo.com/{{id}}' - const changelog = ({ preset }) => preset.issueUrlFormat - mock({ bump: 'minor', changelog }) - fs.writeFileSync( - '.versionrc.json', - JSON.stringify({ issueUrlFormat }), - 'utf-8' - ) - - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) - - it('evaluates a config-function from .versionrc.js', async function () { - const issueUrlFormat = 'http://www.foo.com/{{id}}' - const src = `module.exports = function() { return ${JSON.stringify({ - issueUrlFormat - })} }` - const changelog = ({ preset }) => preset.issueUrlFormat - mock({ bump: 'minor', changelog }) - fs.writeFileSync('.versionrc.js', src, 'utf-8') - - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) - - it('evaluates a config-object from .versionrc.js', async function () { - const issueUrlFormat = 'http://www.foo.com/{{id}}' - const src = `module.exports = ${JSON.stringify({ issueUrlFormat })}` - const changelog = ({ preset }) => preset.issueUrlFormat - mock({ bump: 'minor', changelog }) - fs.writeFileSync('.versionrc.js', src, 'utf-8') - - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) - - it('throws an error when a non-object is returned from .versionrc.js', async function () { - mock({ bump: 'minor' }) - fs.writeFileSync('.versionrc.js', 'module.exports = 3', 'utf-8') - try { - await exec() - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (error) { - error.message.should.match(/Invalid configuration/) - } - }) -}) diff --git a/test/config-keys.integration-test.js b/test/config-keys.integration-test.js new file mode 100644 index 000000000..62505342e --- /dev/null +++ b/test/config-keys.integration-test.js @@ -0,0 +1,83 @@ +'use strict'; + +const shell = require('shelljs'); +const fs = require('fs'); + +const mockers = require('./mocks/jest-mocks'); + +function exec() { + const cli = require('../command'); + const opt = cli.parse('commit-and-tag-version'); + opt.skip = { commit: true, tag: true }; + return require('../index')(opt); +} + +/** + * Mock external conventional-changelog modules + * + * Mocks should be unregistered in test cleanup by calling unmock() + * + * bump?: 'major' | 'minor' | 'patch' | Error | (opt, parserOpts, cb) => { cb(err) | cb(null, { releaseType }) } + * changelog?: string | Error | Array string | null> + * tags?: string[] | Error + */ +function mock({ bump, changelog, tags } = {}) { + mockers.mockRecommendedBump({ bump }); + + if (!Array.isArray(changelog)) changelog = [changelog]; + + mockers.mockConventionalChangelog({ changelog }); + + mockers.mockGitSemverTags({ tags }); +} + +function setupTempGitRepo() { + shell.rm('-rf', 'config-keys-temp'); + shell.config.silent = true; + shell.mkdir('config-keys-temp'); + shell.cd('config-keys-temp'); +} + +function resetShell() { + shell.cd('../'); + shell.rm('-rf', 'config-keys-temp'); +} + +describe('config files', function () { + beforeEach(function () { + setupTempGitRepo(); + + fs.writeFileSync( + 'package.json', + JSON.stringify({ version: '1.0.0' }), + 'utf-8', + ); + }); + + afterEach(function () { + resetShell(); + }); + + const configKeys = ['commit-and-tag-version', 'standard-version']; + + configKeys.forEach((configKey) => { + it(`reads config from package.json key '${configKey}'`, async function () { + const issueUrlFormat = + 'https://commit-and-tag-version.company.net/browse/{{id}}'; + mock({ + bump: 'minor', + changelog: ({ preset }) => preset.issueUrlFormat, + }); + const pkg = { + version: '1.0.0', + repository: { url: 'git+https://company@scm.org/office/app.git' }, + [configKey]: { issueUrlFormat }, + }; + fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8'); + + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toMatch(issueUrlFormat); + }); + }); +}); diff --git a/test/core.spec.js b/test/core.spec.js index 448e107ae..3a579d78a 100644 --- a/test/core.spec.js +++ b/test/core.spec.js @@ -1,829 +1,1995 @@ -/* global describe it afterEach */ +'use strict'; -'use strict' +const shell = require('shelljs'); +const stripAnsi = require('strip-ansi'); +const fs = require('fs'); -const shell = require('shelljs') -const fs = require('fs') -const { resolve } = require('path') -const { Readable } = require('stream') -const mockFS = require('mock-fs') -const mockery = require('mockery') -const stdMocks = require('std-mocks') +const mockers = require('./mocks/jest-mocks'); -const cli = require('../command') -const formatCommitMessage = require('../lib/format-commit-message') +const runExecFile = require('../lib/run-execFile'); -require('chai').should() +const cli = require('../command'); +const formatCommitMessage = require('../lib/format-commit-message'); // set by mock() -let standardVersion +let standardVersion; +let readFileSyncSpy; +let lstatSyncSpy; -function exec (opt = '', git) { +// Rather than trying to re-read something written out during tests, we can spy on writeFileSync +// we can trust fs is capable of writing the file +let writeFileSyncSpy; + +const consoleErrorSpy = jest.spyOn(console, 'warn').mockImplementation(); +const consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation(); + +jest.mock('../lib/run-execFile'); + +const { readFileSync: readFileSyncActual, lstatSync: lstatSyncActual } = fs; + +function exec(opt = '', git) { if (typeof opt === 'string') { - opt = cli.parse(`standard-version ${opt}`) + opt = cli.parse(`commit-and-tag-version ${opt}`); } - if (!git) opt.skip = Object.assign({}, opt.skip, { commit: true, tag: true }) - return standardVersion(opt) + if (!git) opt.skip = Object.assign({}, opt.skip, { commit: true, tag: true }); + return standardVersion(opt); } -function getPackageVersion () { - return JSON.parse(fs.readFileSync('package.json', 'utf-8')).version +function attemptingToReadPackageJson(path) { + return path.includes('package.json') || path.includes('package-lock.json'); } +/** + * @param fs - reference to 'fs' - needs to be defined in the root test class so that mocking works correctly + * @param readFileSyncActual - actual implementation of fs.readFileSync - reference should be defined as a variable in root test class so we can unset spy after + * @param existingChangelog ?: string - Existing CHANGELOG.md content + * @param testFiles ?: object[] - with Path and Value fields, for mocking readFileSynch on packageFiles such as package.json, bower.json, manifest.json + * @param realTestFiles ?: object[] - with Filename (e.g. mix.exs) and Path to real file in a directory + * @return Jest spy on readFileSync + */ +const mockReadFilesFromDisk = ({ + fs, + readFileSyncActual, + existingChangelog, + testFiles, + realTestFiles, +}) => + jest.spyOn(fs, 'readFileSync').mockImplementation((path, opts) => { + if (path === 'CHANGELOG.md') { + if (existingChangelog) { + return existingChangelog; + } + return ''; + } + + // If deliberately set to null when mocking, don't create a fake package.json + if (testFiles === null && attemptingToReadPackageJson(path)) { + return '{}'; + } + + if (testFiles) { + const file = testFiles.find((otherFile) => { + return path.includes(otherFile.path); + }); + + if (file) { + if (file.value instanceof String || typeof file.value === 'string') { + return file.value; + } + return JSON.stringify(file.value); + } + + // For scenarios where we have defined testFiles such as bower.json + // Do not create a fake package.json file + if (attemptingToReadPackageJson(path)) { + return '{}'; + } + } + + // If no package files defined and not explicitly set to null, create a fake package json + // otherwise fs will read the real package.json in the root of this project! + if (attemptingToReadPackageJson(path)) { + return JSON.stringify({ version: '1.0.0' }); + } + + if (realTestFiles) { + const testFile = realTestFiles.find((testFile) => { + return path.includes(testFile.filename); + }); + + if (testFile) { + return readFileSyncActual(testFile.path, opts); + } + } + + return readFileSyncActual(path, opts); + }); + +/** + * @param fs - reference to 'fs' - needs to be defined in the root test class so that mocking works correctly + * @param lstatSyncActual - actual implementation of fs.lstatSync + * @param testFiles ?: object[] - with Path and Value fields, for mocking lstatSync on packageFiles such as package.json, bower.json, manifest.json + * @param realTestFiles ?: object[] - with Filename (e.g. mix.exs) and Path to real file in a directory + * @return Jest spy on lstatSync + */ +const mockFsLStat = ({ fs, lstatSyncActual, testFiles, realTestFiles }) => + jest.spyOn(fs, 'lstatSync').mockImplementation((path) => { + if (testFiles) { + const file = testFiles.find((otherFile) => { + return path.includes(otherFile.path); + }); + + if (file) { + return { + isFile: () => true, + }; + } + } + + if (realTestFiles) { + const file = realTestFiles.find((otherFile) => { + return path.includes(otherFile.filename); + }); + + if (file) { + return { + isFile: () => true, + }; + } + } + + return lstatSyncActual(path); + }); + /** * Mock external conventional-changelog modules * * Mocks should be unregistered in test cleanup by calling unmock() * - * bump?: 'major' | 'minor' | 'patch' | Error | (opt, cb) => { cb(err) | cb(null, { releaseType }) } - * changelog?: string | Error | Array string | null> + * bump?: 'major' | 'minor' | 'patch' | Error | (opt, parserOpts, cb) => { cb(err) | cb(null, { releaseType }) } + * changelog?: string | Error | Array string | null> - Changelog to be "generated" by conventional-changelog when reading commit history * execFile?: ({ dryRun, silent }, cmd, cmdArgs) => Promise - * fs?: { [string]: string | Buffer | any } - * pkg?: { [string]: any } * tags?: string[] | Error + * existingChangelog?: string - Existing CHANGELOG.md content + * testFiles?: object[] - with Path and Value fields, for mocking readFileSynch on packageFiles such as package.json, bower.json, manifest.json + * realTestFiles?: object[] - with Filename (e.g. mix.exs) and Path to real file in test directory */ -function mock ({ bump, changelog, execFile, fs, pkg, tags } = {}) { - mockery.enable({ warnOnUnregistered: false, useCleanCache: true }) - - mockery.registerMock('conventional-recommended-bump', function (opt, cb) { - if (typeof bump === 'function') bump(opt, cb) - else if (bump instanceof Error) cb(bump) - else cb(null, bump ? { releaseType: bump } : {}) - }) - - if (!Array.isArray(changelog)) changelog = [changelog] - mockery.registerMock( - 'conventional-changelog', - (opt) => - new Readable({ - read (_size) { - const next = changelog.shift() - if (next instanceof Error) { - this.destroy(next) - } else if (typeof next === 'function') { - this.push(next(opt)) - } else { - this.push(next ? Buffer.from(next, 'utf8') : null) - } - } - }) - ) - - mockery.registerMock('git-semver-tags', function (cb) { - if (tags instanceof Error) cb(tags) - else cb(null, tags | []) - }) - - if (typeof execFile === 'function') { - // called from commit & tag lifecycle methods - mockery.registerMock('../run-execFile', execFile) - } +function mock({ + bump, + changelog, + tags, + existingChangelog, + testFiles, + realTestFiles, +} = {}) { + mockers.mockRecommendedBump({ bump }); + + if (!Array.isArray(changelog)) changelog = [changelog]; + + mockers.mockConventionalChangelog({ + changelog, + }); + + mockers.mockGitSemverTags({ + tags, + }); // needs to be set after mockery, but before mock-fs - standardVersion = require('../index') + standardVersion = require('../index'); + + // For fake and injected test files pretend they exist at root level when Fs queries lstat + // Package.json works without this as it'll check the one in this actual repo... + lstatSyncSpy = mockFsLStat({ + fs, + lstatSyncActual, + testFiles, + realTestFiles, + }); + + readFileSyncSpy = mockReadFilesFromDisk({ + fs, + readFileSyncActual, + existingChangelog, + testFiles, + realTestFiles, + }); + + // Spies on writeFileSync to capture calls and ensure we don't actually try write anything to disc + writeFileSyncSpy = jest.spyOn(fs, 'writeFileSync').mockImplementation(); +} - fs = Object.assign({}, fs) - if (pkg) { - fs['package.json'] = JSON.stringify(pkg) - } else if (pkg === undefined && !fs['package.json']) { - fs['package.json'] = JSON.stringify({ version: '1.0.0' }) - } - mockFS(fs) +function clearCapturedSpyCalls() { + consoleInfoSpy.mockClear(); + consoleErrorSpy.mockClear(); +} - stdMocks.use() - return () => stdMocks.flush() +function restoreMocksToRealImplementation() { + readFileSyncSpy.mockRestore(); + writeFileSyncSpy.mockRestore(); + lstatSyncSpy.mockRestore(); } -function unmock () { - mockery.deregisterAll() - mockery.disable() - mockFS.restore() - stdMocks.restore() - standardVersion = null - - // push out prints from the Mocha reporter - const { stdout } = stdMocks.flush() - for (const str of stdout) { - if (str.startsWith(' ')) process.stdout.write(str) - } +function unmock() { + clearCapturedSpyCalls(); + + restoreMocksToRealImplementation(); + + standardVersion = null; } describe('format-commit-message', function () { it('works for no {{currentTag}}', function () { - formatCommitMessage('chore(release): 1.0.0', '1.0.0').should.equal( - 'chore(release): 1.0.0' - ) - }) + expect(formatCommitMessage('chore(release): 1.0.0', '1.0.0')).toEqual( + 'chore(release): 1.0.0', + ); + }); it('works for one {{currentTag}}', function () { - formatCommitMessage('chore(release): {{currentTag}}', '1.0.0').should.equal( - 'chore(release): 1.0.0' - ) - }) + expect( + formatCommitMessage('chore(release): {{currentTag}}', '1.0.0'), + ).toEqual('chore(release): 1.0.0'); + }); it('works for two {{currentTag}}', function () { - formatCommitMessage( - 'chore(release): {{currentTag}} \n\n* CHANGELOG: https://github.com/conventional-changelog/standard-version/blob/v{{currentTag}}/CHANGELOG.md', - '1.0.0' - ).should.equal( - 'chore(release): 1.0.0 \n\n* CHANGELOG: https://github.com/conventional-changelog/standard-version/blob/v1.0.0/CHANGELOG.md' - ) - }) -}) + expect( + formatCommitMessage( + 'chore(release): {{currentTag}} \n\n* CHANGELOG: https://github.com/absolute-version/commit-and-tag-version/blob/v{{currentTag}}/CHANGELOG.md', + '1.0.0', + ), + ).toEqual( + 'chore(release): 1.0.0 \n\n* CHANGELOG: https://github.com/absolute-version/commit-and-tag-version/blob/v1.0.0/CHANGELOG.md', + ); + }); +}); describe('cli', function () { - afterEach(unmock) + afterEach(unmock); describe('CHANGELOG.md does not exist', function () { it('populates changelog with commits since last tag by default', async function () { - mock({ bump: 'patch', changelog: 'patch release\n', tags: ['v1.0.0'] }) - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/patch release/) - }) + mock({ bump: 'patch', changelog: 'patch release\n', tags: ['v1.0.0'] }); + await exec(); + verifyNewChangelogContentMatches({ + writeFileSyncSpy, + expectedContent: /patch release/, + }); + }); it('includes all commits if --first-release is true', async function () { mock({ bump: 'minor', changelog: 'first commit\npatch release\n', - pkg: { version: '1.0.1' } - }) - await exec('--first-release') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/patch release/) - content.should.match(/first commit/) - }) + testFiles: [{ path: 'package.json', value: { version: '1.0.1' } }], + }); + await exec('--first-release'); + verifyNewChangelogContentMatches({ + writeFileSyncSpy, + expectedContent: /patch release/, + }); + verifyNewChangelogContentMatches({ + writeFileSyncSpy, + expectedContent: /first commit/, + }); + }); it('skipping changelog will not create a changelog file', async function () { - mock({ bump: 'minor', changelog: 'foo\n' }) - await exec('--skip.changelog true') - getPackageVersion().should.equal('1.1.0') - try { - fs.readFileSync('CHANGELOG.md', 'utf-8') - throw new Error('File should not exist') - } catch (err) { - err.code.should.equal('ENOENT') - } - }) - }) + mock({ bump: 'minor', changelog: 'foo\n' }); + await exec('--skip.changelog true'); + + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.1.0' }); + expect(writeFileSyncSpy).not.toHaveBeenCalledWith('CHANGELOG.md'); + }); + }); describe('CHANGELOG.md exists', function () { - it('appends the new release above the last release, removing the old header (legacy format)', async function () { + afterEach(unmock); + + it('appends the new release above the last release, removing the old header (legacy format), and does not retain any front matter', async function () { + const frontMatter = '---\nstatus: new\n---\n'; mock({ bump: 'patch', changelog: 'release 1.0.1\n', - fs: { 'CHANGELOG.md': 'legacy header format\n' }, - tags: ['v1.0.0'] - }) - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/1\.0\.1/) - content.should.not.match(/legacy header format/) - }) + existingChangelog: + frontMatter + 'legacy header format\n', + tags: ['v1.0.0'], + }); + await exec(); + + verifyNewChangelogContentMatches({ + writeFileSyncSpy, + expectedContent: /1\.0\.1/, + }); + + verifyNewChangelogContentDoesNotMatch({ + writeFileSyncSpy, + expectedContent: /legacy header format/, + }); + verifyNewChangelogContentDoesNotMatch({ + writeFileSyncSpy, + expectedContent: /---status: new---/, + }); + }); + + it('appends the new release above the last release, replacing the old header (standard-version format) with header (new format), and retains any front matter', async function () { + const { header } = require('../defaults'); + + const standardVersionHeader = + '# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.'; + + const frontMatter = '---\nstatus: new\n---\n'; + + const changelog101 = + '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n'; + + const changelog100 = + '### [1.0.0](/compare/v0.0.1...v1.0.0) (YYYY-MM-DD)\n\n\n### Features\n\n* Version one feature set\n'; + + const initialChangelog = + frontMatter + '\n' + standardVersionHeader + '\n' + changelog100; + + mock({ + bump: 'patch', + changelog: changelog101, + existingChangelog: initialChangelog, + tags: ['v1.0.0'], + }); + await exec(); + + verifyNewChangelogContentEquals({ + writeFileSyncSpy, + expectedContent: + frontMatter + '\n' + header + '\n' + changelog101 + changelog100, + }); + }); + + it('appends the new release above the last release, removing the old header (new format), and retains any front matter', async function () { + const { header } = require('../defaults'); + const frontMatter = '---\nstatus: new\n---\n'; + + const changelog101 = + '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n'; + + const changelog100 = + '### [1.0.0](/compare/v0.0.1...v1.0.0) (YYYY-MM-DD)\n\n\n### Features\n\n* Version one feature set\n'; + + const initialChangelog = + frontMatter + '\n' + header + '\n' + changelog100; + + mock({ + bump: 'patch', + changelog: changelog101, + existingChangelog: initialChangelog, + tags: ['v1.0.0'], + }); + await exec(); + + verifyNewChangelogContentEquals({ + writeFileSyncSpy, + expectedContent: + frontMatter + '\n' + header + '\n' + changelog101 + changelog100, + }); + }); it('appends the new release above the last release, removing the old header (new format)', async function () { - const { header } = require('../defaults') + const { header } = require('../defaults'); const changelog1 = - '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n' - mock({ bump: 'patch', changelog: changelog1, tags: ['v1.0.0'] }) - await exec() - let content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.equal(header + '\n' + changelog1) + '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n'; + mock({ bump: 'patch', changelog: changelog1, tags: ['v1.0.0'] }); + await exec(); + const content = header + '\n' + changelog1; + verifyNewChangelogContentEquals({ + writeFileSyncSpy, + expectedContent: content, + }); const changelog2 = - '### [1.0.2](/compare/v1.0.1...v1.0.2) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* another patch release ABCDEFXY\n' - unmock() + '### [1.0.2](/compare/v1.0.1...v1.0.2) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* another patch release ABCDEFXY\n'; + unmock(); + mock({ bump: 'patch', changelog: changelog2, - fs: { 'CHANGELOG.md': content }, - tags: ['v1.0.0', 'v1.0.1'] - }) - await exec() - content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.equal(header + '\n' + changelog2 + changelog1) - }) + existingChangelog: content, + tags: ['v1.0.0', 'v1.0.1'], + }); + await exec(); + verifyNewChangelogContentEquals({ + writeFileSyncSpy, + expectedContent: header + '\n' + changelog2 + changelog1, + }); + }); it('[DEPRECATED] (--changelogHeader) allows for a custom changelog header', async function () { - const header = '# Pork Chop Log' + const header = '# Pork Chop Log'; mock({ bump: 'minor', changelog: header + '\n', - fs: { 'CHANGELOG.md': '' } - }) - await exec(`--changelogHeader="${header}"`) - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(new RegExp(header)) - }) + existingChangelog: '', + }); + await exec(`--changelogHeader="${header}"`); + verifyNewChangelogContentMatches({ + writeFileSyncSpy, + expectedContent: new RegExp(header), + }); + }); it('[DEPRECATED] (--changelogHeader) exits with error if changelog header matches last version search regex', async function () { - mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }) - try { - await exec('--changelogHeader="## 3.0.2"') - throw new Error('That should not have worked') - } catch (error) { - error.message.should.match(/custom changelog header must not match/) - } - }) - }) + mock({ bump: 'minor', existingChangelog: '' }); + await expect(exec('--changelogHeader="## 3.0.2"')).rejects.toThrow( + /custom changelog header must not match/, + ); + }); + }); + + describe('lifecycle scripts', function () { + afterEach(unmock); - describe('lifecycle scripts', () => { describe('prerelease hook', function () { it('should run the prerelease hook when provided', async function () { - const flush = mock({ + mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + existingChangelog: 'legacy header format\n', + }); await exec({ - scripts: { prerelease: 'node -e "console.error(\'prerelease\' + \' ran\')"' } - }) - const { stderr } = flush() - stderr.join('\n').should.match(/prerelease ran/) - }) + scripts: { + prerelease: "node -e \"console.error('prerelease' + ' ran')\"", + }, + }); + + const expectedLog = 'prerelease ran'; + verifyLogPrinted({ consoleInfoSpy: consoleErrorSpy, expectedLog }); + }); it('should abort if the hook returns a non-zero exit code', async function () { mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + existingChangelog: 'legacy header format\n', + }); - try { - await exec({ + await expect( + exec({ scripts: { - prerelease: 'node -e "throw new Error(\'prerelease\' + \' fail\')"' - } - }) - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (error) { - error.message.should.match(/prerelease fail/) - } - }) - }) + prerelease: "node -e \"throw new Error('prerelease' + ' fail')\"", + }, + }), + ).rejects.toThrow(/prerelease fail/); + }); + }); describe('prebump hook', function () { it('should allow prebump hook to return an alternate version #', async function () { - const flush = mock({ + mock({ + bump: 'minor', + existingChangelog: 'legacy header format\n', + }); + + await exec({ + scripts: { + prebump: 'node -e "console.log(Array.of(9, 9, 9).join(\'.\'))"', + }, + }); + verifyLogPrinted({ consoleInfoSpy, expectedLog: '9.9.9' }); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '9.9.9' }); + }); + + it('should not allow prebump hook to return a releaseAs command', async function () { + mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + existingChangelog: 'legacy header format\n', + }); - await exec({ scripts: { prebump: 'node -e "console.log(Array.of(9, 9, 9).join(\'.\'))"' } }) - const { stdout } = flush() - stdout.join('').should.match(/9\.9\.9/) - }) - }) + await exec({ + scripts: { + prebump: 'node -e "console.log(\'major\')"', + }, + }); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.1.0' }); + }); + + it('should allow prebump hook to return an arbitrary string', async function () { + mock({ + bump: 'minor', + existingChangelog: 'legacy header format\n', + }); + + await exec({ + scripts: { + prebump: 'node -e "console.log(\'Hello World\')"', + }, + }); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.1.0' }); + }); + + it('should allow prebump hook to return a version with build info', async function () { + mock({ + bump: 'minor', + existingChangelog: 'legacy header format\n', + }); + + await exec({ + scripts: { + prebump: 'node -e "console.log(\'9.9.9-test+build\')"', + }, + }); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '9.9.9-test+build', + }); + }); + }); describe('postbump hook', function () { it('should run the postbump hook when provided', async function () { - const flush = mock({ + mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + existingChangelog: 'legacy header format\n', + }); await exec({ - scripts: { postbump: 'node -e "console.error(\'postbump\' + \' ran\')"' } - }) - const { stderr } = flush() - stderr.join('\n').should.match(/postbump ran/) - }) + scripts: { + postbump: "node -e \"console.error('postbump' + ' ran')\"", + }, + }); + + const expectedLog = 'postbump ran'; + verifyLogPrinted({ consoleInfoSpy: consoleErrorSpy, expectedLog }); + }); it('should run the postbump and exit with error when postbump fails', async function () { mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + existingChangelog: 'legacy header format\n', + }); + + await expect( + exec({ + scripts: { + postbump: "node -e \"throw new Error('postbump' + ' fail')\"", + }, + }), + ).rejects.toThrow(/postbump fail/); + }); + }); + + describe('manual-release', function () { + describe('release-types', function () { + const regularTypes = ['major', 'minor', 'patch']; + const nextVersion = { major: '2.0.0', minor: '1.1.0', patch: '1.0.1' }; + + regularTypes.forEach(function (type) { + it('creates a ' + type + ' release', async function () { + mock({ + bump: 'patch', + existingChangelog: 'legacy header format\n', + }); + await exec('--release-as ' + type); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: nextVersion[type], + }); + }); + }); + + // this is for pre-releases + regularTypes.forEach(function (type) { + it('creates a pre' + type + ' release', async function () { + mock({ + bump: 'patch', + existingChangelog: 'legacy header format\n', + }); + await exec('--release-as ' + type + ' --prerelease ' + type); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: `${nextVersion[type]}-${type}.0`, + }); + }); + }); + + it('exits with error if an invalid release type is provided', async function () { + mock({ bump: 'minor', existingChangelog: '' }); + + await expect(exec('--release-as invalid')).rejects.toThrow( + /releaseAs must be one of/, + ); + }); + }); + + describe('release-as-exact', function () { + it('releases as v100.0.0', async function () { + mock({ + bump: 'patch', + existingChangelog: 'legacy header format\n', + }); + await exec('--release-as v100.0.0'); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '100.0.0', + }); + }); + + it('releases as 200.0.0-amazing', async function () { + mock({ + bump: 'patch', + existingChangelog: 'legacy header format\n', + }); + await exec('--release-as 200.0.0-amazing'); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '200.0.0-amazing', + }); + }); + + it('releases as 100.0.0 with prerelease amazing', async function () { + mock({ + bump: 'patch', + existingChangelog: 'legacy header format\n', + testFiles: [ + { + path: 'package.json', + value: { + version: '1.0.0', + }, + }, + ], + }); + await exec('--release-as 100.0.0 --prerelease amazing'); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '100.0.0-amazing.0', + }); + }); + + it('release 100.0.0 with prerelease amazing bumps build', async function () { + mock({ + bump: 'patch', + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, + testFiles: [ + { + path: 'package.json', + value: { + version: '100.0.0-amazing.0', + }, + }, + ], + }); + await exec('--release-as 100.0.0 --prerelease amazing'); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '100.0.0-amazing.1', + }); + }); + + it('release 100.0.0-amazing.0 with prerelease amazing bumps build', async function () { + mock({ + bump: 'patch', + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, + testFiles: [ + { + path: 'package.json', + value: { + version: '100.0.0-amazing.1', + }, + }, + ], + }); + await exec('--release-as 100.0.0-amazing.0 --prerelease amazing'); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '100.0.0-amazing.2', + }); + }); + + it('release 100.0.0 with prerelease amazing correctly sets version', async function () { + mock({ + bump: 'patch', + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, + testFiles: [ + { + path: 'package.json', + value: { + version: '99.0.0-amazing.0', + }, + }, + ], + }); + await exec('--release-as 100.0.0 --prerelease amazing'); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '100.0.0-amazing.0', + }); + }); + + it('release 100.0.0-amazing.0 with prerelease amazing correctly sets version', async function () { + mock({ + bump: 'patch', + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, + testFiles: [ + { + path: 'package.json', + value: { + version: '99.0.0-amazing.0', + }, + }, + ], + }); + await exec('--release-as 100.0.0-amazing.0 --prerelease amazing'); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '100.0.0-amazing.0', + }); + }); + + it('release 100.0.0-amazing.0 with prerelease amazing retains build metadata', async function () { + mock({ + bump: 'patch', + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, + testFiles: [ + { + path: 'package.json', + value: { + version: '100.0.0-amazing.0', + }, + }, + ], + }); + await exec( + '--release-as 100.0.0-amazing.0+build.1234 --prerelease amazing', + ); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '100.0.0-amazing.1+build.1234', + }); + }); + + it('release 100.0.0-amazing.3 with prerelease amazing correctly sets prerelease version', async function () { + mock({ + bump: 'patch', + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, + testFiles: [ + { + path: 'package.json', + value: { + version: '100.0.0-amazing.0', + }, + }, + ], + }); + await exec('--release-as 100.0.0-amazing.3 --prerelease amazing'); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '100.0.0-amazing.3', + }); + }); + }); + + it('creates a prerelease with a new minor version after two prerelease patches', async function () { + let releaseType = 'patch'; + mock({ + bump: (_, __, cb) => cb(null, { releaseType }), + existingChangelog: 'legacy header format\n', + }); + + let version = '1.0.1-dev.0'; + await exec('--release-as patch --prerelease dev'); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: version }); + + unmock(); + mock({ + bump: (_, __, cb) => cb(null, { releaseType }), + existingChangelog: 'legacy header format\n', + testFiles: [{ path: 'package.json', value: { version } }], + }); + + version = '1.0.1-dev.1'; + await exec('--prerelease dev'); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: version }); + + releaseType = 'minor'; + unmock(); + mock({ + bump: (_, __, cb) => cb(null, { releaseType }), + existingChangelog: 'legacy header format\n', + testFiles: [{ path: 'package.json', value: { version } }], + }); + + version = '1.1.0-dev.0'; + await exec('--release-as minor --prerelease dev'); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: version }); + + unmock(); + mock({ + bump: (_, __, cb) => cb(null, { releaseType }), + existingChangelog: 'legacy header format\n', + testFiles: [{ path: 'package.json', value: { version } }], + }); + + version = '1.1.0-dev.1'; + await exec('--release-as minor --prerelease dev'); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: version }); + + unmock(); + mock({ + bump: (_, __, cb) => cb(null, { releaseType }), + existingChangelog: 'legacy header format\n', + testFiles: [{ path: 'package.json', value: { version } }], + }); + + version = '1.1.0-dev.2'; + await exec('--prerelease dev'); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: version }); + }); + + it('exits with error if an invalid release version is provided', async function () { + mock({ bump: 'minor', existingChangelog: '' }); + + await expect(exec('--release-as 10.2')).rejects.toThrow( + /releaseAs must be one of/, + ); + }); + + it('exits with error if release version conflicts with prerelease', async function () { + mock({ bump: 'minor', existingChangelog: '' }); + + await expect( + exec('--release-as 1.2.3-amazing.2 --prerelease awesome'), + ).rejects.toThrow( + /releaseAs and prerelease have conflicting prerelease identifiers/, + ); + }); + }); + + it('appends line feed at end of package.json', async function () { + mock({ bump: 'patch' }); + await exec(); + verifyFileContentEquals({ + writeFileSyncSpy, + content: '{\n "version": "1.0.1"\n}\n', + }); + }); + + it('preserves indentation of tabs in package.json', async function () { + mock({ + bump: 'patch', + testFiles: [ + { path: 'package.json', value: '{\n\t"version": "1.0.0"\n}\n' }, + ], + }); + await exec(); + // TODO: a) not bumping to 1.0.1, b) need to check how jest might handle tabbing etc + verifyFileContentEquals({ + writeFileSyncSpy, + content: '{\n\t"version": "1.0.1"\n}\n', + }); + }); + + it('preserves indentation of spaces in package.json', async function () { + mock({ + bump: 'patch', + testFiles: [ + { path: 'package.json', value: '{\n "version": "1.0.0"\n}\n' }, + ], + }); + await exec(); + verifyFileContentEquals({ + writeFileSyncSpy, + content: '{\n "version": "1.0.1"\n}\n', + }); + }); + + it('preserves carriage return + line feed in package.json', async function () { + mock({ + bump: 'patch', + testFiles: [ + { path: 'package.json', value: '{\r\n "version": "1.0.0"\r\n}\r\n' }, + ], + }); + await exec(); + verifyFileContentEquals({ + writeFileSyncSpy, + content: '{\r\n "version": "1.0.1"\r\n}\r\n', + }); + }); + + it('does not print output when the --silent flag is passed', async function () { + mock(); + await exec('--silent'); + expect(consoleErrorSpy).not.toHaveBeenCalled(); + expect(consoleInfoSpy).not.toHaveBeenCalled(); + }); + }); + + describe('commit-and-tag-version', function () { + afterEach(unmock); + + it('should exit on bump error', async function () { + mock({ bump: new Error('bump err') }); + + await expect(exec()).rejects.toThrow(/bump err/); + }); + + it('should exit on changelog error', async function () { + mock({ bump: 'minor', changelog: new Error('changelog err') }); + + await expect(exec()).rejects.toThrow(/changelog err/); + }); + + it('should exit with error without a package file to bump', async function () { + mock({ bump: 'patch', testFiles: null }); + + await expect(exec({ gitTagFallback: false })).rejects.toThrow( + 'no package file found', + ); + }); + + it('bumps version # in bower.json', async function () { + mock({ + bump: 'minor', + testFiles: [{ path: 'bower.json', value: { version: '1.0.0' } }], + tags: ['v1.0.0'], + }); + await exec(); + + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '1.1.0', + filename: 'bower.json', + }); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.1.0' }); + }); + + it('bumps version # in manifest.json', async function () { + mock({ + bump: 'minor', + testFiles: [{ path: 'manifest.json', value: { version: '1.0.0' } }], + tags: ['v1.0.0'], + }); + await exec(); + + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '1.1.0', + filename: 'manifest.json', + }); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.1.0' }); + }); + + describe('custom `bumpFiles` support', function () { + afterEach(unmock); + + it('mix.exs + version.txt', async function () { + mock({ + bump: 'minor', + realTestFiles: [ + { filename: 'mix.exs', path: './test/mocks/mix.exs' }, + { filename: 'version.txt', path: './test/mocks/version.txt' }, + ], + tags: ['v1.0.0'], + }); + + await exec({ + bumpFiles: [ + 'version.txt', + { + filename: 'mix.exs', + updater: './test/mocks/updater/customer-updater', + }, + ], + }); + + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '1.1.0', + filename: 'mix.exs', + asString: true, + }); + + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '1.1.0', + filename: 'version.txt', + asString: true, + }); + }); + + it('bumps a custom `plain-text` file', async function () { + mock({ + bump: 'minor', + realTestFiles: [ + { + filename: 'VERSION_TRACKER.txt', + path: './test/mocks/VERSION-1.0.0.txt', + }, + ], + }); + await exec({ + bumpFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }], + }); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '1.1.0', + filename: 'VERSION_TRACKER.txt', + asString: true, + }); + }); + + it('displays the new version from custom bumper with --dry-run', async function () { + mock({ + bump: 'minor', + realTestFiles: [ + { + filename: 'increment-version.txt', + path: './test/mocks/increment-version.txt', + }, + ], + }); + + const origInfo = console.info; + const capturedOutput = []; + console.info = (...args) => { + capturedOutput.push(...args); + origInfo(...args); + }; try { await exec({ - scripts: { postbump: 'node -e "throw new Error(\'postbump\' + \' fail\')"' } - }) - await exec('--patch') - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (error) { - error.message.should.match(/postbump fail/) + bumpFiles: [ + { + filename: 'increment-version.txt', + updater: './test/mocks/updater/increment-updater', + }, + ], + dryRun: true, + }); + + const logOutput = capturedOutput.join(' '); + expect(stripAnsi(logOutput)).toContain( + 'bumping version in increment-version.txt from 1 to 2', + ); + } finally { + console.info = origInfo; } - }) - }) - }) + }); + }); - describe('manual-release', function () { - describe('release-types', function () { - const regularTypes = ['major', 'minor', 'patch'] - const nextVersion = { major: '2.0.0', minor: '1.1.0', patch: '1.0.1' } + describe('custom `packageFiles` support', function () { + afterEach(unmock); - regularTypes.forEach(function (type) { - it('creates a ' + type + ' release', async function () { - mock({ - bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - await exec('--release-as ' + type) - getPackageVersion().should.equal(nextVersion[type]) - }) - }) - - // this is for pre-releases - regularTypes.forEach(function (type) { - it('creates a pre' + type + ' release', async function () { - mock({ - bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - await exec('--release-as ' + type + ' --prerelease ' + type) - getPackageVersion().should.equal(`${nextVersion[type]}-${type}.0`) - }) - }) - }) - - describe('release-as-exact', function () { - it('releases as v100.0.0', async function () { + it('reads and writes to a custom `plain-text` file', async function () { + mock({ + bump: 'minor', + realTestFiles: [ + { + filename: 'VERSION_TRACKER.txt', + path: './test/mocks/VERSION-6.3.1.txt', + }, + ], + }); + + await exec({ + packageFiles: [ + { filename: 'VERSION_TRACKER.txt', type: 'plain-text' }, + ], + bumpFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }], + }); + + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '6.4.0', + filename: 'VERSION_TRACKER.txt', + asString: true, + }); + }); + + it('allows same object to be used in packageFiles and bumpFiles', async function () { mock({ - bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - await exec('--release-as v100.0.0') - getPackageVersion().should.equal('100.0.0') - }) - - it('releases as 200.0.0-amazing', async function () { + bump: 'minor', + realTestFiles: [ + { + filename: 'VERSION_TRACKER.txt', + path: './test/mocks/VERSION-6.3.1.txt', + }, + ], + }); + const origWarn = console.warn; + + console.warn = () => { + throw new Error('console.warn should not be called'); + }; + + const filedesc = { + filename: 'VERSION_TRACKER.txt', + type: 'plain-text', + }; + + try { + await exec({ packageFiles: [filedesc], bumpFiles: [filedesc] }); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '6.4.0', + filename: 'VERSION_TRACKER.txt', + asString: true, + }); + } finally { + console.warn = origWarn; + } + }); + + it('bumps version in Python `pyproject.toml` file', async function () { + const expected = fs.readFileSync( + './test/mocks/pyproject-1.1.0.toml', + 'utf-8', + ); + + const filename = 'python.toml'; mock({ - bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - await exec('--release-as 200.0.0-amazing') - getPackageVersion().should.equal('200.0.0-amazing') - }) - }) - - it('creates a prerelease with a new minor version after two prerelease patches', async function () { - let releaseType = 'patch' - const bump = (_, cb) => cb(null, { releaseType }) + bump: 'minor', + realTestFiles: [ + { + filename, + path: './test/mocks/pyproject-1.0.0.toml', + }, + ], + }); + + await exec({ + packageFiles: [{ filename, type: 'python' }], + bumpFiles: [{ filename, type: 'python' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); + } + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + }); + + it('`packageFiles` are bumped along with `bumpFiles` defaults [commit-and-tag-version#533]', async function () { mock({ - bump, - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - - await exec('--release-as patch --prerelease dev') - getPackageVersion().should.equal('1.0.1-dev.0') - - await exec('--prerelease dev') - getPackageVersion().should.equal('1.0.1-dev.1') - - releaseType = 'minor' - await exec('--release-as minor --prerelease dev') - getPackageVersion().should.equal('1.1.0-dev.0') - - await exec('--release-as minor --prerelease dev') - getPackageVersion().should.equal('1.1.0-dev.1') - - await exec('--prerelease dev') - getPackageVersion().should.equal('1.1.0-dev.2') - }) - }) - - it('appends line feed at end of package.json', async function () { - mock({ bump: 'patch' }) - await exec() - const pkgJson = fs.readFileSync('package.json', 'utf-8') - pkgJson.should.equal('{\n "version": "1.0.1"\n}\n') - }) - - it('preserves indentation of tabs in package.json', async function () { - mock({ - bump: 'patch', - fs: { 'package.json': '{\n\t"version": "1.0.0"\n}\n' } - }) - await exec() - const pkgJson = fs.readFileSync('package.json', 'utf-8') - pkgJson.should.equal('{\n\t"version": "1.0.1"\n}\n') - }) - - it('preserves indentation of spaces in package.json', async function () { - mock({ - bump: 'patch', - fs: { 'package.json': '{\n "version": "1.0.0"\n}\n' } - }) - await exec() - const pkgJson = fs.readFileSync('package.json', 'utf-8') - pkgJson.should.equal('{\n "version": "1.0.1"\n}\n') - }) - - it('preserves carriage return + line feed in package.json', async function () { - mock({ - bump: 'patch', - fs: { 'package.json': '{\r\n "version": "1.0.0"\r\n}\r\n' } - }) - await exec() - const pkgJson = fs.readFileSync('package.json', 'utf-8') - pkgJson.should.equal('{\r\n "version": "1.0.1"\r\n}\r\n') - }) - - it('does not print output when the --silent flag is passed', async function () { - const flush = mock() - await exec('--silent') - flush().should.eql({ stdout: [], stderr: [] }) - }) -}) - -describe('standard-version', function () { - afterEach(unmock) - - it('should exit on bump error', async function () { - mock({ bump: new Error('bump err') }) - try { - await exec() - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (err) { - err.message.should.match(/bump err/) - } - }) - - it('should exit on changelog error', async function () { - mock({ bump: 'minor', changelog: new Error('changelog err') }) - try { - await exec() - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (err) { - err.message.should.match(/changelog err/) - } - }) - - it('should exit with error without a package file to bump', async function () { - mock({ bump: 'patch', pkg: false }) - try { - await exec({ gitTagFallback: false }) - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (err) { - err.message.should.equal('no package file found') - } - }) - - it('bumps version # in bower.json', async function () { - mock({ - bump: 'minor', - fs: { 'bower.json': JSON.stringify({ version: '1.0.0' }) }, - tags: ['v1.0.0'] - }) - await exec() - JSON.parse(fs.readFileSync('bower.json', 'utf-8')).version.should.equal( - '1.1.0' - ) - getPackageVersion().should.equal('1.1.0') - }) - - it('bumps version # in manifest.json', async function () { - mock({ - bump: 'minor', - fs: { 'manifest.json': JSON.stringify({ version: '1.0.0' }) }, - tags: ['v1.0.0'] - }) - await exec() - JSON.parse(fs.readFileSync('manifest.json', 'utf-8')).version.should.equal( - '1.1.0' - ) - getPackageVersion().should.equal('1.1.0') - }) - - describe('custom `bumpFiles` support', function () { - it('mix.exs + version.txt', async function () { - const updater = 'custom-updater.js' - const updaterModule = require('./mocks/updater/customer-updater') + bump: 'minor', + testFiles: [ + { + path: '.gitignore', + value: '', + }, + { + path: 'package-lock.json', + value: { version: '1.0.0' }, + }, + ], + realTestFiles: [ + { + filename: 'manifest.json', + path: './test/mocks/manifest-6.3.1.json', + }, + ], + tags: ['v1.0.0'], + }); + + await exec({ + packageFiles: [ + { + filename: 'manifest.json', + type: 'json', + }, + ], + }); + + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '6.4.0', + filename: 'package.json', + }); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '6.4.0', + filename: 'package-lock.json', + }); + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '6.4.0', + filename: 'manifest.json', + }); + }); + + it('bumps version in OpenAPI `openapi.yaml` file with CRLF Line Endings', async function () { + const expected = fs.readFileSync( + './test/mocks/openapi-1.3.0-crlf.yaml', + 'utf-8', + ); + const filename = 'openapi.yaml'; mock({ bump: 'minor', - fs: { - 'mix.exs': fs.readFileSync('./test/mocks/mix.exs'), - 'version.txt': fs.readFileSync('./test/mocks/version.txt') - }, - tags: ['v1.0.0'] - }) - mockery.registerMock(resolve(process.cwd(), updater), updaterModule) + realTestFiles: [ + { + filename, + path: './test/mocks/openapi-1.2.3-crlf.yaml', + }, + ], + }); + await exec({ + packageFiles: [{ filename, type: 'openapi' }], + bumpFiles: [{ filename, type: 'openapi' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); + } + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + + it('bumps version in OpenAPI `openapi.yaml` file with LF Line Endings', async function () { + const expected = fs.readFileSync( + './test/mocks/openapi-1.3.0-lf.yaml', + 'utf-8', + ); + const filename = 'openapi.yaml'; + mock({ + bump: 'minor', + realTestFiles: [ + { + filename, + path: './test/mocks/openapi-1.2.3-lf.yaml', + }, + ], + }); await exec({ - bumpFiles: [ - 'version.txt', - { filename: 'mix.exs', updater: 'custom-updater.js' } - ] - }) - fs.readFileSync('mix.exs', 'utf-8').should.contain('version: "1.1.0"') - fs.readFileSync('version.txt', 'utf-8').should.equal('1.1.0') - }) - - it('bumps a custom `plain-text` file', async function () { + packageFiles: [{ filename, type: 'openapi' }], + bumpFiles: [{ filename, type: 'openapi' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); + } + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + + it('bumps version in Maven `pom.xml` file with CRLF Line Endings', async function () { + const expected = fs.readFileSync( + './test/mocks/pom-6.4.0-crlf.xml', + 'utf-8', + ); + const filename = 'pom.xml'; mock({ bump: 'minor', - fs: { - 'VERSION_TRACKER.txt': fs.readFileSync( - './test/mocks/VERSION-1.0.0.txt' - ) - } - }) + realTestFiles: [ + { + filename, + path: './test/mocks/pom-6.3.1-crlf.xml', + }, + ], + }); await exec({ - bumpFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }] - }) - fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('1.1.0') - }) - }) - - describe('custom `packageFiles` support', function () { - it('reads and writes to a custom `plain-text` file', async function () { + packageFiles: [{ filename, type: 'maven' }], + bumpFiles: [{ filename, type: 'maven' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); + } + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + + it('bumps version in Maven `pom.xml` file with LF Line Endings', async function () { + const expected = fs.readFileSync( + './test/mocks/pom-6.4.0-lf.xml', + 'utf-8', + ); + const filename = 'pom.xml'; mock({ bump: 'minor', - fs: { - 'VERSION_TRACKER.txt': fs.readFileSync( - './test/mocks/VERSION-6.3.1.txt' - ) - } - }) + realTestFiles: [ + { + filename, + path: './test/mocks/pom-6.3.1-lf.xml', + }, + ], + }); await exec({ - packageFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }], - bumpFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }] - }) - fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('6.4.0') - }) + packageFiles: [{ filename, type: 'maven' }], + bumpFiles: [{ filename, type: 'maven' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); + } + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + + it('bumps version in Gradle `build.gradle.kts` file', async function () { + const expected = fs.readFileSync( + './test/mocks/build-6.4.0.gradle.kts', + 'utf-8', + ); - it('allows same object to be used in packageFiles and bumpFiles', async function () { + const filename = 'build.gradle.kts'; mock({ bump: 'minor', - fs: { - 'VERSION_TRACKER.txt': fs.readFileSync( - './test/mocks/VERSION-6.3.1.txt' - ) - } - }) - const origWarn = console.warn - console.warn = () => { - throw new Error('console.warn should not be called') + realTestFiles: [ + { + filename, + path: './test/mocks/build-6.3.1.gradle.kts', + }, + ], + }); + + await exec({ + packageFiles: [{ filename, type: 'gradle' }], + bumpFiles: [{ filename, type: 'gradle' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); } - const filedesc = { filename: 'VERSION_TRACKER.txt', type: 'plain-text' } - try { - await exec({ packageFiles: [filedesc], bumpFiles: [filedesc] }) - fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('6.4.0') - } finally { - console.warn = origWarn + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + + it('bumps version in .NET `Project.csproj` file', async function () { + const expected = fs.readFileSync( + './test/mocks/Project-6.4.0.csproj', + 'utf-8', + ); + const filename = 'Project.csproj'; + mock({ + bump: 'minor', + realTestFiles: [ + { + filename, + path: './test/mocks/Project-6.3.1.csproj', + }, + ], + }); + await exec({ + packageFiles: [{ filename, type: 'csproj' }], + bumpFiles: [{ filename, type: 'csproj' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); } - }) - }) - - it('`packageFiles` are bumped along with `bumpFiles` defaults [standard-version#533]', async function () { - mock({ - bump: 'minor', - fs: { - '.gitignore': '', - 'package-lock.json': JSON.stringify({ version: '1.0.0' }), - 'manifest.json': fs.readFileSync('./test/mocks/manifest-6.3.1.json') - }, - tags: ['v1.0.0'] - }) - - await exec({ - silent: true, - packageFiles: [ - { - filename: 'manifest.json', - type: 'json' - } - ] - }) - - JSON.parse(fs.readFileSync('manifest.json', 'utf-8')).version.should.equal('6.4.0') - JSON.parse(fs.readFileSync('package.json', 'utf-8')).version.should.equal('6.4.0') - JSON.parse(fs.readFileSync('package-lock.json', 'utf-8')).version.should.equal('6.4.0') - }) - - it('bumps version # in npm-shrinkwrap.json', async function () { - mock({ - bump: 'minor', - fs: { - 'npm-shrinkwrap.json': JSON.stringify({ version: '1.0.0' }) - }, - tags: ['v1.0.0'] - }) - await exec() - JSON.parse( - fs.readFileSync('npm-shrinkwrap.json', 'utf-8') - ).version.should.equal('1.1.0') - getPackageVersion().should.equal('1.1.0') - }) - - it('bumps version # in package-lock.json', async function () { - mock({ - bump: 'minor', - fs: { - '.gitignore': '', - 'package-lock.json': JSON.stringify({ version: '1.0.0' }) - }, - tags: ['v1.0.0'] - }) - await exec() - JSON.parse( - fs.readFileSync('package-lock.json', 'utf-8') - ).version.should.equal('1.1.0') - getPackageVersion().should.equal('1.1.0') - }) - - describe('skip', () => { - it('allows bump and changelog generation to be skipped', async function () { - const changelogContent = 'legacy header format\n' + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + + it('bumps version # in npm-shrinkwrap.json', async function () { mock({ bump: 'minor', - changelog: 'foo\n', - fs: { 'CHANGELOG.md': changelogContent } - }) - - await exec('--skip.bump true --skip.changelog true') - getPackageVersion().should.equal('1.0.0') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.equal(changelogContent) - }) - }) - - it('does not update files present in .gitignore', async () => { - mock({ - bump: 'minor', - fs: { - '.gitignore': 'package-lock.json\nbower.json', - // test a defaults.packageFiles - 'bower.json': JSON.stringify({ version: '1.0.0' }), - // test a defaults.bumpFiles - 'package-lock.json': JSON.stringify({ - name: '@org/package', - version: '1.0.0', - lockfileVersion: 1 - }) - }, - tags: ['v1.0.0'] - }) - await exec() - JSON.parse(fs.readFileSync('package-lock.json', 'utf-8')).version.should.equal( - '1.0.0' - ) - JSON.parse(fs.readFileSync('bower.json', 'utf-8')).version.should.equal( - '1.0.0' - ) - getPackageVersion().should.equal('1.1.0') - }) - - describe('configuration', () => { - it('--header', async function () { - mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }) - await exec('--header="# Welcome to our CHANGELOG.md"') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/# Welcome to our CHANGELOG.md/) - }) - - it('--issuePrefixes and --issueUrlFormat', async function () { - const format = 'http://www.foo.com/{{prefix}}{{id}}' - const prefix = 'ABC-' - const changelog = ({ preset }) => - preset.issueUrlFormat + ':' + preset.issuePrefixes - mock({ bump: 'minor', changelog }) - await exec(`--issuePrefixes="${prefix}" --issueUrlFormat="${format}"`) - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(`${format}:${prefix}`) - }) - }) - - describe('pre-major', () => { - it('bumps the minor rather than major, if version < 1.0.0', async function () { + testFiles: [ + { + path: 'npm-shrinkwrap.json', + value: { version: '1.0.0' }, + }, + ], + tags: ['v1.0.0'], + }); + + await exec(); + + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '1.1.0', + filename: 'npm-shrinkwrap.json', + }); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.1.0' }); + }); + + it('bumps version # in package-lock.json', async function () { mock({ bump: 'minor', - pkg: { - version: '0.5.0', - repository: { url: 'https://github.com/yargs/yargs.git' } - } - }) - await exec() - getPackageVersion().should.equal('0.6.0') - }) + testFiles: [ + { + path: '.gitignore', + value: '', + }, + { + path: 'package-lock.json', + value: { version: '1.0.0' }, + }, + ], + tags: ['v1.0.0'], + }); + await exec(); + + verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion: '1.1.0', + filename: 'package-lock.json', + }); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.1.0' }); + }); + + it('bumps version in Dart `pubspec.yaml` file', async function () { + const expected = fs.readFileSync( + './test/mocks/pubspec-6.4.0.yaml', + 'utf-8', + ); + + const filename = 'pubspec.yaml'; + mock({ + bump: 'minor', + realTestFiles: [ + { + filename, + path: './test/mocks/pubspec-6.3.1.yaml', + }, + ], + }); + + await exec({ + packageFiles: [{ filename, type: 'yaml' }], + bumpFiles: [{ filename, type: 'yaml' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); + } + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + + it('bumps version in Dart `pubspec.yaml` file with CRLF line endings', async function () { + const expected = fs.readFileSync( + './test/mocks/pubspec-6.4.0-crlf.yaml', + 'utf-8', + ); + + const filename = 'pubspec.yaml'; + mock({ + bump: 'minor', + realTestFiles: [ + { + filename, + path: './test/mocks/pubspec-6.3.1-crlf.yaml', + }, + ], + }); + + await exec({ + packageFiles: [{ filename, type: 'yaml' }], + bumpFiles: [{ filename, type: 'yaml' }], + }); + + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); + } + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + expect(calledWithContentStr).toEqual(expected); + }); + + describe('skip', function () { + it('allows bump and changelog generation to be skipped', async function () { + const changelogContent = 'legacy header format\n'; + mock({ + bump: 'minor', + changelog: 'foo\n', + existingChangelog: changelogContent, + }); + + await exec('--skip.bump true --skip.changelog true'); + + expect(writeFileSyncSpy).not.toHaveBeenCalledWith('package.json'); + expect(writeFileSyncSpy).not.toHaveBeenCalledWith('CHANGELOG.md'); + }); + }); + + it('does not update files present in .gitignore', async function () { + const DotGitIgnore = require('dotgitignore'); + jest.mock('dotgitignore'); + + DotGitIgnore.mockImplementation(() => { + return { + ignore: (filename) => { + if (filename === 'package-lock.json' || filename === 'bower.json') { + return true; + } + + return false; + }, + }; + }); + + mock({ + bump: 'minor', + testFiles: [ + { + path: 'bower.json', + value: { version: '1.0.0' }, + }, + { + path: 'package-lock.json', + value: { + name: '@org/package', + version: '1.0.0', + lockfileVersion: 1, + }, + }, + ], + tags: ['v1.0.0'], + }); + await exec(); + + // does not bump these as in .gitignore + expect(writeFileSyncSpy).not.toHaveBeenCalledWith('package-lock.json'); + expect(writeFileSyncSpy).not.toHaveBeenCalledWith('bower.json'); + + // should still bump version in package.json + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.1.0' }); + + DotGitIgnore.mockRestore(); + }); + + describe('configuration', function () { + it('--header', async function () { + mock({ bump: 'minor', existingChangelog: '' }); + await exec('--header="# Welcome to our CHANGELOG.md"'); + + verifyNewChangelogContentMatches({ + writeFileSyncSpy, + expectedContent: /# Welcome to our CHANGELOG.md/, + }); + }); + + it('--issuePrefixes and --issueUrlFormat', async function () { + const format = 'http://www.foo.com/{{prefix}}{{id}}'; + const prefix = 'ABC-'; + const changelog = ({ preset }) => + preset.issueUrlFormat + ':' + preset.issuePrefixes; + mock({ bump: 'minor', changelog }); + await exec(`--issuePrefixes="${prefix}" --issueUrlFormat="${format}"`); + + verifyNewChangelogContentMatches({ + writeFileSyncSpy, + expectedContent: `${format}:${prefix}`, + }); + }); + }); + + describe('pre-major', function () { + it('bumps the minor rather than major, if version < 1.0.0', async function () { + mock({ + bump: 'minor', + testFiles: [ + { + path: 'package.json', + value: { + version: '0.5.0', + repository: { url: 'https://github.com/yargs/yargs.git' }, + }, + }, + ], + }); + await exec(); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '0.6.0' }); + }); + + it('bumps major if --release-as=major specified, if version < 1.0.0', async function () { + mock({ + bump: 'major', + testFiles: [ + { + path: 'package.json', + value: { + version: '0.5.0', + repository: { url: 'https://github.com/yargs/yargs.git' }, + }, + }, + ], + }); + await exec('-r major'); + verifyPackageVersion({ writeFileSyncSpy, expectedVersion: '1.0.0' }); + }); + }); + }); + + describe('GHSL-2020-111', function () { + afterEach(unmock); + + it('does not allow command injection via basic configuration', async function () { + mock({ bump: 'patch' }); + await exec({ + noVerify: true, + releaseCommitMessageFormat: 'bla `touch exploit`', + }); + const stat = shell.test('-f', './exploit'); + expect(stat).toEqual(false); + }); + }); + + describe('with mocked git', function () { + afterEach(unmock); + + it('--sign signs the commit and tag', async function () { + const gitArgs = [ + ['add', 'CHANGELOG.md', 'package.json', 'package-lock.json'], + [ + 'commit', + '-S', + 'CHANGELOG.md', + 'package.json', + 'package-lock.json', + '-m', + 'chore(release): 1.0.1', + ], + ['tag', '-s', 'v1.0.1', '-m', 'chore(release): 1.0.1'], + ['rev-parse', '--abbrev-ref', 'HEAD'], + ]; + + runExecFile.mockImplementation((_args, cmd, cmdArgs) => { + expect(cmd).toEqual('git'); + + const expected = gitArgs.shift(); + expect(cmdArgs).toEqual(expected); + + if (expected[0] === 'rev-parse') return Promise.resolve('master'); + + return Promise.resolve(''); + }); + + mock({ + bump: 'patch', + changelog: 'foo\n', + }); + + await exec('--sign', true); + expect(gitArgs).toHaveLength(0); + }); + + it('--signedoff adds signed-off-by to the commit message', async function () { + const gitArgs = [ + ['add', 'CHANGELOG.md', 'package.json', 'package-lock.json'], + [ + 'commit', + '--signoff', + 'CHANGELOG.md', + 'package.json', + 'package-lock.json', + '-m', + 'chore(release): 1.0.1', + ], + ['tag', '-a', 'v1.0.1', '-m', 'chore(release): 1.0.1'], + ['rev-parse', '--abbrev-ref', 'HEAD'], + ]; + + runExecFile.mockImplementation((_args, cmd, cmdArgs) => { + expect(cmd).toEqual('git'); + + const expected = gitArgs.shift(); + expect(cmdArgs).toEqual(expected); + + if (expected[0] === 'rev-parse') return Promise.resolve('master'); + + return Promise.resolve(''); + }); - it('bumps major if --release-as=major specified, if version < 1.0.0', async function () { mock({ - bump: 'major', - pkg: { - version: '0.5.0', - repository: { url: 'https://github.com/yargs/yargs.git' } + bump: 'patch', + changelog: 'foo\n', + }); + + await exec('--signoff', true); + expect(gitArgs).toHaveLength(0); + }); + + it('--tag-force forces tag replacement', async function () { + const gitArgs = [ + ['add', 'CHANGELOG.md', 'package.json', 'package-lock.json'], + [ + 'commit', + 'CHANGELOG.md', + 'package.json', + 'package-lock.json', + '-m', + 'chore(release): 1.0.1', + ], + ['tag', '-a', '-f', 'v1.0.1', '-m', 'chore(release): 1.0.1'], + ['rev-parse', '--abbrev-ref', 'HEAD'], + ]; + + runExecFile.mockImplementation((_args, cmd, cmdArgs) => { + expect(cmd).toEqual('git'); + + const expected = gitArgs.shift(); + expect(cmdArgs).toEqual(expected); + + if (expected[0] === 'rev-parse') return Promise.resolve('master'); + + return Promise.resolve(''); + }); + + mock({ bump: 'patch', changelog: 'foo\n' }); + + await exec('--tag-force', true); + expect(gitArgs).toHaveLength(0); + }); + + it('fails if git add fails', async function () { + const gitArgs = [ + ['add', 'CHANGELOG.md', 'package.json', 'package-lock.json'], + ]; + const gitError = new Error('Command failed: git\nfailed add'); + + runExecFile.mockImplementation((_args, cmd, cmdArgs) => { + expect(cmd).toEqual('git'); + + const expected = gitArgs.shift(); + expect(cmdArgs).toEqual(expected); + + if (expected[0] === 'add') { + return Promise.reject(gitError); + } + return Promise.resolve(''); + }); + + mock({ bump: 'patch', changelog: 'foo\n' }); + + await expect(exec({}, true)).rejects.toThrow(gitError); + }); + + it('fails if git commit fails', async function () { + const gitArgs = [ + ['add', 'CHANGELOG.md', 'package.json', 'package-lock.json'], + [ + 'commit', + 'CHANGELOG.md', + 'package.json', + 'package-lock.json', + '-m', + 'chore(release): 1.0.1', + ], + ]; + const gitError = new Error('Command failed: git\nfailed commit'); + + runExecFile.mockImplementation((_args, cmd, cmdArgs) => { + expect(cmd).toEqual('git'); + + const expected = gitArgs.shift(); + expect(cmdArgs).toEqual(expected); + + if (expected[0] === 'commit') { + return Promise.reject(gitError); } - }) - await exec('-r major') - getPackageVersion().should.equal('1.0.0') - }) - }) -}) - -describe('GHSL-2020-111', function () { - afterEach(unmock) - - it('does not allow command injection via basic configuration', async function () { - mock({ bump: 'patch' }) - await exec({ - noVerify: true, - infile: 'foo.txt', - releaseCommitMessageFormat: 'bla `touch exploit`' - }) - const stat = shell.test('-f', './exploit') - stat.should.equal(false) - }) -}) - -describe('with mocked git', function () { - afterEach(unmock) - - it('--sign signs the commit and tag', async function () { - const gitArgs = [ - ['add', 'CHANGELOG.md', 'package.json'], - ['commit', '-S', 'CHANGELOG.md', 'package.json', '-m', 'chore(release): 1.0.1'], - ['tag', '-s', 'v1.0.1', '-m', 'chore(release): 1.0.1'], - ['rev-parse', '--abbrev-ref', 'HEAD'] - ] - const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) - if (expected[0] === 'rev-parse') return Promise.resolve('master') - return Promise.resolve('') + return Promise.resolve(''); + }); + + mock({ bump: 'patch', changelog: 'foo\n' }); + + await expect(exec({}, true)).rejects.toThrow(gitError); + }); + + it('fails if git tag fails', async function () { + const gitArgs = [ + ['add', 'CHANGELOG.md', 'package.json', 'package-lock.json'], + [ + 'commit', + 'CHANGELOG.md', + 'package.json', + 'package-lock.json', + '-m', + 'chore(release): 1.0.1', + ], + ['tag', '-a', 'v1.0.1', '-m', 'chore(release): 1.0.1'], + ]; + const gitError = new Error('Command failed: git\nfailed tag'); + + runExecFile.mockImplementation((_args, cmd, cmdArgs) => { + expect(cmd).toEqual('git'); + + const expected = gitArgs.shift(); + expect(cmdArgs).toEqual(expected); + + if (expected[0] === 'tag') { + return Promise.reject(gitError); + } + return Promise.resolve(''); + }); + + mock({ bump: 'patch', changelog: 'foo\n' }); + + await expect(exec({}, true)).rejects.toThrow(gitError); + }); + }); + + // ------- Verifiers ------ + function findWriteFileCallForPath({ writeFileSyncSpy, filename }) { + // filePath is the first arg passed to writeFileSync + return writeFileSyncSpy.mock.calls.find((args) => + args[0].includes(filename), + ); + } + + function verifyPackageVersion({ + writeFileSyncSpy, + expectedVersion, + filename = 'package.json', + asString = false, + }) { + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error(`writeFileSynch not invoked with path ${filename}`); } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) - - await exec('--sign', true) - gitArgs.should.have.lengthOf(0) - }) - - it('fails if git add fails', async function () { - const gitArgs = [ - ['add', 'CHANGELOG.md', 'package.json'] - ] - const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) - if (expected[0] === 'add') { - return Promise.reject(new Error('Command failed: git\nfailed add')) + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + if (!asString) { + // parse to JSON and verify has property + const calledWithContent = JSON.parse(calledWithContentStr); + + expect(calledWithContent).toHaveProperty('version'); + expect(calledWithContent.version).toEqual(expectedVersion); + } else { + // for non-JSON files i.e. .exs and .txt just verify version exists + if (filename.includes('.exs')) { + expect(calledWithContentStr).toMatch(`version: "${expectedVersion}"`); + } else { + expect(calledWithContentStr).toMatch(expectedVersion); } - return Promise.resolve('') - } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) - - try { - await exec({}, true) - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (error) { - error.message.should.match(/failed add/) } - }) - - it('fails if git commit fails', async function () { - const gitArgs = [ - ['add', 'CHANGELOG.md', 'package.json'], - ['commit', 'CHANGELOG.md', 'package.json', '-m', 'chore(release): 1.0.1'] - ] - const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) - if (expected[0] === 'commit') { - return Promise.reject(new Error('Command failed: git\nfailed commit')) - } - return Promise.resolve('') + } + + function verifyFileContentEquals({ + writeFileSyncSpy, + content, + filename = 'package.json', + }) { + // filePath is the first arg passed to writeFileSync + const packageJsonWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename, + }); + + if (!packageJsonWriteFileSynchCall) { + throw new Error('writeFileSynch not invoked with path package.json'); } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) - - try { - await exec({}, true) - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (error) { - error.message.should.match(/failed commit/) + + const calledWithContentStr = packageJsonWriteFileSynchCall[1]; + + expect(calledWithContentStr).toEqual(content); + } + + function verifyNewChangelogContentMatches({ + writeFileSyncSpy, + expectedContent, + }) { + const changelogWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename: 'CHANGELOG.md', + }); + + if (!changelogWriteFileSynchCall) { + throw new Error('writeFileSynch not invoked with path CHANGELOG.md'); } - }) - - it('fails if git tag fails', async function () { - const gitArgs = [ - ['add', 'CHANGELOG.md', 'package.json'], - ['commit', 'CHANGELOG.md', 'package.json', '-m', 'chore(release): 1.0.1'], - ['tag', '-a', 'v1.0.1', '-m', 'chore(release): 1.0.1'] - ] - const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) - if (expected[0] === 'tag') { - return Promise.reject(new Error('Command failed: git\nfailed tag')) - } - return Promise.resolve('') + + const calledWithContent = changelogWriteFileSynchCall[1]; + expect(calledWithContent).toMatch(expectedContent); + } + + function verifyNewChangelogContentEquals({ + writeFileSyncSpy, + expectedContent, + }) { + const changelogWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename: 'CHANGELOG.md', + }); + + if (!changelogWriteFileSynchCall) { + throw new Error('writeFileSynch not invoked with path CHANGELOG.md'); } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) - - try { - await exec({}, true) - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (error) { - error.message.should.match(/failed tag/) + + const calledWithContent = changelogWriteFileSynchCall[1]; + expect(calledWithContent).toEqual(expectedContent); + } + + function verifyNewChangelogContentDoesNotMatch({ + writeFileSyncSpy, + expectedContent, + }) { + const changelogWriteFileSynchCall = findWriteFileCallForPath({ + writeFileSyncSpy, + filename: 'CHANGELOG.md', + }); + + if (!changelogWriteFileSynchCall) { + throw new Error('writeFileSynch not invoked with path CHANGELOG.md'); } - }) -}) + + const calledWithContent = changelogWriteFileSynchCall[1]; + expect(calledWithContent).not.toMatch(expectedContent); + } + + function verifyLogPrinted({ consoleInfoSpy, expectedLog }) { + const consoleInfoLogs = consoleInfoSpy.mock.calls.map((args) => args[0]); + const desiredLog = consoleInfoLogs.find((log) => log.includes(expectedLog)); + expect(desiredLog).not.toBeUndefined(); + expect(desiredLog).toMatch(expectedLog); + } +}); diff --git a/test/git.integration-test.js b/test/git.integration-test.js new file mode 100644 index 000000000..33945868f --- /dev/null +++ b/test/git.integration-test.js @@ -0,0 +1,371 @@ +'use strict'; + +const shell = require('shelljs'); +const fs = require('fs'); + +const mockers = require('./mocks/jest-mocks'); + +// Jest swallows most standard console logs not explicitly defined into a custom logger +// see: https://stackoverflow.com/questions/51555568/remove-logging-the-origin-line-in-jest +const consoleWarnSpy = jest.spyOn(global.console, 'warn').mockImplementation(); +const consoleInfoSpy = jest.spyOn(global.console, 'info').mockImplementation(); + +function exec(opt = '') { + if (typeof opt === 'string') { + const cli = require('../command'); + opt = cli.parse(`commit-and-tag-version ${opt}`); + } + return require('../index')(opt); +} + +function writePackageJson(version, option) { + const pkg = Object.assign({}, option, { version }); + fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8'); +} + +function getPackageVersion() { + return JSON.parse(fs.readFileSync('package.json', 'utf-8')).version; +} + +/** + * Mock external conventional-changelog modules + * + * bump: 'major' | 'minor' | 'patch' | Error | (opt, parserOpts, cb) => { cb(err) | cb(null, { releaseType }) } + * changelog?: string | Error | Array string | null> + * tags?: string[] | Error + */ +function mock({ bump, changelog, tags }) { + if (bump === undefined) throw new Error('bump must be defined for mock()'); + + mockers.mockRecommendedBump({ bump }); + + if (!Array.isArray(changelog)) changelog = [changelog]; + mockers.mockConventionalChangelog({ changelog }); + + mockers.mockGitSemverTags({ tags }); +} + +function getLog(expectedLog, spy = consoleInfoSpy) { + const consoleInfoLogs = spy.mock.calls.map((args) => args[0]); + return consoleInfoLogs.find((log) => log.includes(expectedLog)); +} + +function verifyLogPrinted(expectedLog, spy = consoleInfoSpy) { + const logType = spy === consoleInfoSpy ? 'info' : 'error'; + const desiredLog = getLog(expectedLog, spy); + if (desiredLog) { + expect(desiredLog).toMatch(expectedLog); + } else { + expect(`no ${logType} Log printed matching`).toMatch(expectedLog); + } +} + +function verifyLogNotPrinted(expectedLog, spy = consoleInfoSpy) { + const desiredLog = getLog(expectedLog, spy); + expect(desiredLog).toBeUndefined(); +} + +function clearCapturedSpyCalls() { + consoleInfoSpy.mockClear(); + consoleWarnSpy.mockClear(); +} + +function resetShell() { + shell.cd('../'); + shell.rm('-rf', 'git-repo-temp'); +} + +function setupTempGitRepo() { + shell.rm('-rf', 'git-repo-temp'); + shell.config.silent = true; + shell.mkdir('git-repo-temp'); + shell.cd('git-repo-temp'); + shell.exec('git init'); + shell.exec('git config commit.gpgSign false'); + shell.exec('git config core.autocrlf false'); + shell.exec('git commit --allow-empty -m"root-commit"'); +} + +describe('git', function () { + function setup() { + setupTempGitRepo(); + writePackageJson('1.0.0'); + } + + function reset() { + resetShell(); + + clearCapturedSpyCalls(); + } + + beforeEach(function () { + setup(); + }); + + afterEach(function () { + reset(); + }); + + describe('tagPrefix', function () { + beforeEach(function () { + setup(); + }); + + afterEach(function () { + reset(); + }); + + // TODO: Use unmocked git-semver-tags and stage a git environment + it('will add prefix onto tag based on version from package', async function () { + writePackageJson('1.2.0'); + mock({ bump: 'minor', tags: ['p-v1.2.0'] }); + await exec('--tag-prefix p-v'); + expect(shell.exec('git tag').stdout).toMatch(/p-v1\.3\.0/); + }); + + it('will add prefix onto tag via when gitTagFallback is true and no package [cli]', async function () { + shell.rm('package.json'); + mock({ + bump: 'minor', + tags: ['android/production/v1.2.0', 'android/production/v1.0.0'], + }); + await exec('--tag-prefix android/production/v'); + expect(shell.exec('git tag').stdout).toMatch( + /android\/production\/v1\.3\.0/, + ); + }); + + it('will add prefix onto tag via when gitTagFallback is true and no package [options]', async function () { + mock({ + bump: 'minor', + tags: ['android/production/v1.2.0', 'android/production/v1.0.0'], + }); + await exec({ tagPrefix: 'android/production/v', packageFiles: [] }); + expect(shell.exec('git tag').stdout).toMatch( + /android\/production\/v1\.3\.0/, + ); + }); + }); + + it('formats the commit and tag messages appropriately', async function () { + mock({ bump: 'minor', tags: ['v1.0.0'] }); + await exec({}); + // check last commit message + expect(shell.exec('git log --oneline -n1').stdout).toMatch( + /chore\(release\): 1\.1\.0/, + ); + // check annotated tag message + expect(shell.exec('git tag -l -n1 v1.1.0').stdout).toMatch( + /chore\(release\): 1\.1\.0/, + ); + }); + + it('formats the tag if --first-release is true', async function () { + writePackageJson('1.0.1'); + mock({ bump: 'minor' }); + await exec('--first-release'); + expect(shell.exec('git tag').stdout).toMatch(/1\.0\.1/); + }); + + it('commits all staged files', async function () { + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + fs.writeFileSync('STUFF.md', 'stuff\n', 'utf-8'); + shell.exec('git add STUFF.md'); + + mock({ bump: 'patch', changelog: 'release 1.0.1\n', tags: ['v1.0.0'] }); + await exec('--commit-all'); + const status = shell.exec('git status --porcelain').stdout; // see http://unix.stackexchange.com/questions/155046/determine-if-git-working-directory-is-clean-from-a-script + expect(status).toEqual(''); + expect(status).not.toMatch(/STUFF.md/); + + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toMatch(/1\.0\.1/); + expect(content).not.toMatch(/legacy header format/); + }); + + it('does not run git hooks if the --no-verify flag is passed', async function () { + fs.writeFileSync( + '.git/hooks/pre-commit', + '#!/bin/sh\necho "precommit ran"\nexit 1', + 'utf-8', + ); + fs.chmodSync('.git/hooks/pre-commit', '755'); + + mock({ bump: 'minor' }); + await exec('--no-verify'); + await exec('-n'); + }); + + it('replaces tags if version not bumped', async function () { + mock({ bump: 'minor', tags: ['v1.0.0'] }); + await exec({}); + expect(shell.exec('git describe').stdout).toMatch(/v1\.1\.0/); + await exec('--tag-force --skip.bump'); + expect(shell.exec('git describe').stdout).toMatch(/v1\.1\.0/); + }); + + it('allows the commit phase to be skipped', async function () { + const changelogContent = 'legacy header format\n'; + writePackageJson('1.0.0'); + fs.writeFileSync('CHANGELOG.md', changelogContent, 'utf-8'); + + mock({ bump: 'minor', changelog: 'new feature\n' }); + await exec('--skip.commit true'); + expect(getPackageVersion()).toEqual('1.1.0'); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toMatch(/new feature/); + expect(shell.exec('git log --oneline -n1').stdout).toMatch(/root-commit/); + }); + + it('dry-run skips all non-idempotent steps', async function () { + shell.exec('git tag -a v1.0.0 -m "my awesome first release"'); + mock({ + bump: 'minor', + changelog: '### Features\n', + tags: ['v1.0.0'], + }); + await exec('--dry-run'); + verifyLogPrinted('### Features'); + + expect(shell.exec('git log --oneline -n1').stdout).toMatch(/root-commit/); + expect(shell.exec('git tag').stdout).toMatch(/1\.0\.0/); + expect(getPackageVersion()).toEqual('1.0.0'); + }); + + it('works fine without specifying a tag id when prereleasing', async function () { + writePackageJson('1.0.0'); + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + mock({ bump: 'minor' }); + await exec('--prerelease'); + expect(getPackageVersion()).toEqual('1.1.0-0'); + }); + + describe('gitTagFallback', function () { + beforeEach(function () { + setup(); + }); + + afterEach(function () { + reset(); + }); + + it('defaults to 1.0.0 if no tags in git history', async function () { + shell.rm('package.json'); + mock({ bump: 'minor' }); + await exec({}); + const output = shell.exec('git tag'); + expect(output.stdout).toContain('v1.1.0'); + }); + + it('bases version on greatest version tag, if tags are found', async function () { + shell.rm('package.json'); + mock({ bump: 'minor', tags: ['v3.9.0', 'v5.0.0', 'v3.0.0'] }); + await exec({}); + const output = shell.exec('git tag'); + expect(output.stdout).toContain('v5.1.0'); + }); + + it('uses only relevant prerelease tags', async function () { + shell.rm('package.json'); + mock({ bump: 'minor', tags: ['v1.1.0-b.0', 'v1.1.0-a.0', 'v1.0.0-b.0'] }); + await exec('--prerelease a'); + const output = shell.exec('git tag'); + expect(output.stdout).toContain('1.1.0-a.1'); + }); + }); + + describe('Run ... to publish', function () { + beforeEach(function () { + setup(); + }); + + afterEach(function () { + reset(); + }); + + it('does normally display `npm publish`', async function () { + mock({ bump: 'patch' }); + await exec(''); + verifyLogPrinted('npm publish'); + }); + + it('can display publish hints with custom npm client name', async function () { + mock({ bump: 'patch' }); + await exec('--npmPublishHint "yarn publish"'); + verifyLogPrinted('yarn publish'); + }); + + it('does not display `npm publish` if the package is private', async function () { + writePackageJson('1.0.0', { private: true }); + mock({ bump: 'patch' }); + await exec(''); + verifyLogNotPrinted('npm publish'); + }); + + it('does not display `npm publish` if there is no package.json', async function () { + shell.rm('package.json'); + mock({ bump: 'patch' }); + await exec(''); + verifyLogNotPrinted('npm publish'); + }); + + it('does not display `all staged files` without the --commit-all flag', async function () { + mock({ bump: 'patch' }); + await exec(''); + verifyLogNotPrinted('all staged files'); + }); + + it('does display `all staged files` if the --commit-all flag is passed', async function () { + mock({ bump: 'patch' }); + await exec('--commit-all'); + verifyLogPrinted('all staged files'); + }); + + it('advises use of --tag prerelease for publishing to npm', async function () { + writePackageJson('1.0.0'); + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + + mock({ bump: 'patch' }); + await exec('--prerelease'); + verifyLogPrinted('--tag prerelease'); + }); + + it('advises use of --tag alpha for publishing to npm when tagging alpha', async function () { + writePackageJson('1.0.0'); + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + + mock({ bump: 'patch' }); + await exec('--prerelease alpha'); + verifyLogPrinted('--tag alpha'); + }); + + it('does not advise use of --tag prerelease for private modules', async function () { + writePackageJson('1.0.0', { private: true }); + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + + mock({ bump: 'minor' }); + await exec('--prerelease'); + verifyLogNotPrinted('--tag prerelease'); + }); + }); +}); diff --git a/test/git.spec.js b/test/git.spec.js deleted file mode 100644 index dfa9dbb11..000000000 --- a/test/git.spec.js +++ /dev/null @@ -1,375 +0,0 @@ -/* global describe it beforeEach afterEach */ - -'use strict' - -const shell = require('shelljs') -const fs = require('fs') -const { Readable } = require('stream') -const mockery = require('mockery') -const stdMocks = require('std-mocks') - -require('chai').should() - -function exec (opt = '') { - if (typeof opt === 'string') { - const cli = require('../command') - opt = cli.parse(`standard-version ${opt}`) - } - return require('../index')(opt) -} - -function writePackageJson (version, option) { - const pkg = Object.assign({}, option, { version }) - fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8') -} - -function writeHook (hookName, causeError, script) { - shell.mkdir('-p', 'scripts') - let content = script || 'console.error("' + hookName + ' ran")' - content += causeError ? '\nthrow new Error("' + hookName + '-failure")' : '' - fs.writeFileSync('scripts/' + hookName + '.js', content, 'utf-8') - fs.chmodSync('scripts/' + hookName + '.js', '755') -} - -function getPackageVersion () { - return JSON.parse(fs.readFileSync('package.json', 'utf-8')).version -} - -/** - * Mock external conventional-changelog modules - * - * bump: 'major' | 'minor' | 'patch' | Error | (opt, cb) => { cb(err) | cb(null, { releaseType }) } - * changelog?: string | Error | Array string | null> - * tags?: string[] | Error - */ -function mock ({ bump, changelog, tags }) { - if (bump === undefined) throw new Error('bump must be defined for mock()') - mockery.enable({ warnOnUnregistered: false, useCleanCache: true }) - - mockery.registerMock('conventional-recommended-bump', function (opt, cb) { - if (typeof bump === 'function') bump(opt, cb) - else if (bump instanceof Error) cb(bump) - else cb(null, { releaseType: bump }) - }) - - if (!Array.isArray(changelog)) changelog = [changelog] - mockery.registerMock('conventional-changelog', (opt) => new Readable({ - read (_size) { - const next = changelog.shift() - if (next instanceof Error) { - this.destroy(next) - } else if (typeof next === 'function') { - this.push(next(opt)) - } else { - this.push(next ? Buffer.from(next, 'utf8') : null) - } - } - })) - - mockery.registerMock('git-semver-tags', function (_, cb) { - if (tags instanceof Error) cb(tags) - else cb(null, tags || []) - }) - - stdMocks.use() - return () => stdMocks.flush() -} - -describe('git', function () { - beforeEach(function () { - shell.rm('-rf', 'tmp') - shell.config.silent = true - shell.mkdir('tmp') - shell.cd('tmp') - shell.exec('git init') - shell.exec('git config commit.gpgSign false') - shell.exec('git config core.autocrlf false') - shell.exec('git commit --allow-empty -m"root-commit"') - writePackageJson('1.0.0') - }) - - afterEach(function () { - shell.cd('../') - shell.rm('-rf', 'tmp') - - mockery.deregisterAll() - mockery.disable() - stdMocks.restore() - - // push out prints from the Mocha reporter - const { stdout } = stdMocks.flush() - for (const str of stdout) { - if (str.startsWith(' ')) process.stdout.write(str) - } - }) - - describe('tagPrefix', () => { - // TODO: Use unmocked git-semver-tags and stage a git environment - it('will add prefix onto tag based on version from package', async function () { - writePackageJson('1.2.0') - mock({ bump: 'minor', tags: ['p-v1.2.0'] }) - await exec('--tag-prefix p-v') - shell.exec('git tag').stdout.should.match(/p-v1\.3\.0/) - }) - - it('will add prefix onto tag via when gitTagFallback is true and no package [cli]', async function () { - shell.rm('package.json') - mock({ bump: 'minor', tags: ['android/production/v1.2.0', 'android/production/v1.0.0'] }) - await exec('--tag-prefix android/production/v') - shell.exec('git tag').stdout.should.match(/android\/production\/v1\.3\.0/) - }) - - it('will add prefix onto tag via when gitTagFallback is true and no package [options]', async function () { - mock({ bump: 'minor', tags: ['android/production/v1.2.0', 'android/production/v1.0.0'] }) - await exec({ tagPrefix: 'android/production/v', packageFiles: [] }) - shell.exec('git tag').stdout.should.match(/android\/production\/v1\.3\.0/) - }) - }) - - it('formats the commit and tag messages appropriately', async function () { - mock({ bump: 'minor', tags: ['v1.0.0'] }) - await exec({}) - // check last commit message - shell.exec('git log --oneline -n1').stdout.should.match(/chore\(release\): 1\.1\.0/) - // check annotated tag message - shell.exec('git tag -l -n1 v1.1.0').stdout.should.match(/chore\(release\): 1\.1\.0/) - }) - - it('formats the tag if --first-release is true', async function () { - writePackageJson('1.0.1') - mock({ bump: 'minor' }) - await exec('--first-release') - shell.exec('git tag').stdout.should.match(/1\.0\.1/) - }) - - it('commits all staged files', async function () { - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - fs.writeFileSync('STUFF.md', 'stuff\n', 'utf-8') - shell.exec('git add STUFF.md') - - mock({ bump: 'patch', changelog: 'release 1.0.1\n', tags: ['v1.0.0'] }) - await exec('--commit-all') - const status = shell.exec('git status --porcelain') // see http://unix.stackexchange.com/questions/155046/determine-if-git-working-directory-is-clean-from-a-script - status.should.equal('') - status.should.not.match(/STUFF.md/) - - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/1\.0\.1/) - content.should.not.match(/legacy header format/) - }) - - it('does not run git hooks if the --no-verify flag is passed', async function () { - fs.writeFileSync('.git/hooks/pre-commit', '#!/bin/sh\necho "precommit ran"\nexit 1', 'utf-8') - fs.chmodSync('.git/hooks/pre-commit', '755') - - mock({ bump: 'minor' }) - await exec('--no-verify') - await exec('-n') - }) - - it('allows the commit phase to be skipped', async function () { - const changelogContent = 'legacy header format\n' - writePackageJson('1.0.0') - fs.writeFileSync('CHANGELOG.md', changelogContent, 'utf-8') - - mock({ bump: 'minor', changelog: 'new feature\n' }) - await exec('--skip.commit true') - getPackageVersion().should.equal('1.1.0') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/new feature/) - shell.exec('git log --oneline -n1').stdout.should.match(/root-commit/) - }) - - it('dry-run skips all non-idempotent steps', async function () { - shell.exec('git tag -a v1.0.0 -m "my awesome first release"') - const flush = mock({ bump: 'minor', changelog: '### Features\n', tags: ['v1.0.0'] }) - await exec('--dry-run') - const { stdout } = flush() - stdout.join('').should.match(/### Features/) - shell.exec('git log --oneline -n1').stdout.should.match(/root-commit/) - shell.exec('git tag').stdout.should.match(/1\.0\.0/) - getPackageVersion().should.equal('1.0.0') - }) - - it('works fine without specifying a tag id when prereleasing', async function () { - writePackageJson('1.0.0') - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - mock({ bump: 'minor' }) - await exec('--prerelease') - getPackageVersion().should.equal('1.1.0-0') - }) - - describe('gitTagFallback', () => { - it('defaults to 1.0.0 if no tags in git history', async () => { - shell.rm('package.json') - mock({ bump: 'minor' }) - await exec({}) - const output = shell.exec('git tag') - output.stdout.should.include('v1.1.0') - }) - - it('bases version on greatest version tag, if tags are found', async () => { - shell.rm('package.json') - mock({ bump: 'minor', tags: ['v3.9.0', 'v5.0.0', 'v3.0.0'] }) - await exec({}) - const output = shell.exec('git tag') - output.stdout.should.include('v5.1.0') - }) - }) - - describe('configuration', () => { - it('.versionrc : releaseCommitMessageFormat', async function () { - fs.writeFileSync('.versionrc', JSON.stringify({ - releaseCommitMessageFormat: 'This commit represents release: {{currentTag}}' - }), 'utf-8') - mock({ bump: 'minor' }) - await exec('') - shell.exec('git log --oneline -n1').should.include('This commit represents release: 1.1.0') - }) - - it('--releaseCommitMessageFormat', async function () { - mock({ bump: 'minor' }) - await exec('--releaseCommitMessageFormat="{{currentTag}} is the version."') - shell.exec('git log --oneline -n1').should.include('1.1.0 is the version.') - }) - - it('[LEGACY] supports --message (and single %s replacement)', async function () { - mock({ bump: 'minor' }) - await exec('--message="V:%s"') - shell.exec('git log --oneline -n1').should.include('V:1.1.0') - }) - - it('[LEGACY] supports -m (and multiple %s replacements)', async function () { - mock({ bump: 'minor' }) - await exec('--message="V:%s is the %s."') - shell.exec('git log --oneline -n1').should.include('V:1.1.0 is the 1.1.0.') - }) - }) - - describe('precommit hook', function () { - it('should run the precommit hook when provided via .versionrc.json (#371)', async function () { - fs.writeFileSync('.versionrc.json', JSON.stringify({ - scripts: { precommit: 'node scripts/precommit' } - }), 'utf-8') - - writeHook('precommit') - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - const flush = mock({ bump: 'minor' }) - await exec('') - const { stderr } = flush() - stderr[0].should.match(/precommit ran/) - }) - - it('should run the precommit hook when provided', async function () { - writePackageJson('1.0.0', { - 'standard-version': { - scripts: { precommit: 'node scripts/precommit' } - } - }) - writeHook('precommit') - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - - const flush = mock({ bump: 'minor' }) - await exec('--patch') - const { stderr } = flush() - stderr[0].should.match(/precommit ran/) - }) - - it('should run the precommit hook and exit with error when precommit fails', async function () { - writePackageJson('1.0.0', { - 'standard-version': { - scripts: { precommit: 'node scripts/precommit' } - } - }) - writeHook('precommit', true) - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - - mock({ bump: 'minor' }) - try { - await exec('--patch') - /* istanbul ignore next */ - throw new Error('Unexpected success') - } catch (error) { - error.message.should.match(/precommit-failure/) - } - }) - - it('should allow an alternate commit message to be provided by precommit script', async function () { - writePackageJson('1.0.0', { - 'standard-version': { - scripts: { precommit: 'node scripts/precommit' } - } - }) - writeHook('precommit', false, 'console.log("releasing %s delivers #222")') - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - - mock({ bump: 'minor' }) - await exec('--patch') - shell.exec('git log --oneline -n1').should.match(/delivers #222/) - }) - }) - - describe('Run ... to publish', function () { - it('does normally display `npm publish`', async function () { - const flush = mock({ bump: 'patch' }) - await exec('') - flush().stdout.join('').should.match(/npm publish/) - }) - - it('does not display `npm publish` if the package is private', async function () { - writePackageJson('1.0.0', { private: true }) - const flush = mock({ bump: 'patch' }) - await exec('') - flush().stdout.join('').should.not.match(/npm publish/) - }) - - it('does not display `npm publish` if there is no package.json', async function () { - shell.rm('package.json') - const flush = mock({ bump: 'patch' }) - await exec('') - flush().stdout.join('').should.not.match(/npm publish/) - }) - - it('does not display `all staged files` without the --commit-all flag', async function () { - const flush = mock({ bump: 'patch' }) - await exec('') - flush().stdout.join('').should.not.match(/all staged files/) - }) - - it('does display `all staged files` if the --commit-all flag is passed', async function () { - const flush = mock({ bump: 'patch' }) - await exec('--commit-all') - flush().stdout.join('').should.match(/all staged files/) - }) - - it('advises use of --tag prerelease for publishing to npm', async function () { - writePackageJson('1.0.0') - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - - const flush = mock({ bump: 'patch' }) - await exec('--prerelease') - const { stdout } = flush() - stdout.join('').should.include('--tag prerelease') - }) - - it('advises use of --tag alpha for publishing to npm when tagging alpha', async function () { - writePackageJson('1.0.0') - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - - const flush = mock({ bump: 'patch' }) - await exec('--prerelease alpha') - const { stdout } = flush() - stdout.join('').should.include('--tag alpha') - }) - - it('does not advise use of --tag prerelease for private modules', async function () { - writePackageJson('1.0.0', { private: true }) - fs.writeFileSync('CHANGELOG.md', 'legacy header format\n', 'utf-8') - - const flush = mock({ bump: 'minor' }) - await exec('--prerelease') - const { stdout } = flush() - stdout.join('').should.not.include('--tag prerelease') - }) - }) -}) diff --git a/test/invalid-config.integration-test.js b/test/invalid-config.integration-test.js new file mode 100644 index 000000000..a46b0a4d4 --- /dev/null +++ b/test/invalid-config.integration-test.js @@ -0,0 +1,73 @@ +'use strict'; + +const shell = require('shelljs'); +const fs = require('fs'); + +const mockers = require('./mocks/jest-mocks'); + +function exec() { + const cli = require('../command'); + const opt = cli.parse('commit-and-tag-version'); + opt.skip = { commit: true, tag: true }; + return require('../index')(opt); +} + +/** + * Mock external conventional-changelog modules + * + * Mocks should be unregistered in test cleanup by calling unmock() + * + * bump?: 'major' | 'minor' | 'patch' | Error | (opt, parserOpts, cb) => { cb(err) | cb(null, { releaseType }) } + * changelog?: string | Error | Array string | null> + * tags?: string[] | Error + */ +function mock({ bump, changelog, tags } = {}) { + mockers.mockRecommendedBump({ bump }); + + if (!Array.isArray(changelog)) changelog = [changelog]; + + mockers.mockConventionalChangelog({ changelog }); + + mockers.mockGitSemverTags({ tags }); +} + +function setupTestDirectory() { + shell.rm('-rf', 'invalid-config-temp'); + shell.config.silent = true; + shell.mkdir('invalid-config-temp'); + shell.cd('invalid-config-temp'); +} + +function resetShell() { + shell.cd('../'); + shell.rm('-rf', 'invalid-config-temp'); +} + +/** + * This test is very sensitive to the setup of "tmp" directories and must currently be run in it's own "Jest" runner instance. + * By default Jest spawns a Runner per-test file even if running serially each test in a File + * When we refactored from Mocha -> Jest, if this test was run as part of a larger test-suite, it would always fail, due to presence of a valid .verisonrc + * somewhere in the "real" or "tmp" filesystem, despite the shell code seemingly setting up and tearing down correctly + */ +describe('invalid .versionrc', function () { + beforeEach(function () { + setupTestDirectory(); + + fs.writeFileSync( + 'package.json', + JSON.stringify({ version: '1.0.0' }), + 'utf-8', + ); + }); + + afterEach(function () { + resetShell(); + }); + + it('throws an error when a non-object is returned from .versionrc.js', async function () { + mock({ bump: 'minor' }); + fs.writeFileSync('.versionrc.js', 'module.exports = 3', 'utf-8'); + + expect(exec).toThrow(/Invalid configuration/); + }); +}); diff --git a/test/mocks/Project-6.3.1.csproj b/test/mocks/Project-6.3.1.csproj new file mode 100644 index 000000000..03d561986 --- /dev/null +++ b/test/mocks/Project-6.3.1.csproj @@ -0,0 +1,18 @@ + + + + net7.0 + enable + enable + 6.3.1 + + + + + + + + + + + diff --git a/test/mocks/Project-6.4.0.csproj b/test/mocks/Project-6.4.0.csproj new file mode 100644 index 000000000..c410dbdff --- /dev/null +++ b/test/mocks/Project-6.4.0.csproj @@ -0,0 +1,18 @@ + + + + net7.0 + enable + enable + 6.4.0 + + + + + + + + + + + diff --git a/test/mocks/build-6.3.1.gradle.kts b/test/mocks/build-6.3.1.gradle.kts new file mode 100644 index 000000000..5262c003f --- /dev/null +++ b/test/mocks/build-6.3.1.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("org.springframework.boot") version "2.4.6" + kotlin("jvm") version "1.4.31" + kotlin("plugin.spring") version "1.4.31" +} + +version = "6.3.1" +java.sourceCompatibility = JavaVersion.VERSION_1_8 + +repositories { + mavenLocal() + mavenCentral() +} + diff --git a/test/mocks/build-6.4.0.gradle.kts b/test/mocks/build-6.4.0.gradle.kts new file mode 100644 index 000000000..1b5592ff9 --- /dev/null +++ b/test/mocks/build-6.4.0.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("org.springframework.boot") version "2.4.6" + kotlin("jvm") version "1.4.31" + kotlin("plugin.spring") version "1.4.31" +} + +version = "6.4.0" +java.sourceCompatibility = JavaVersion.VERSION_1_8 + +repositories { + mavenLocal() + mavenCentral() +} + diff --git a/test/mocks/increment-version.txt b/test/mocks/increment-version.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/mocks/increment-version.txt @@ -0,0 +1 @@ +1 diff --git a/test/mocks/jest-mocks.js b/test/mocks/jest-mocks.js new file mode 100644 index 000000000..ef16da5d1 --- /dev/null +++ b/test/mocks/jest-mocks.js @@ -0,0 +1,48 @@ +const gitSemverTags = require('git-semver-tags'); +const conventionalChangelog = require('conventional-changelog'); +const conventionalRecommendedBump = require('conventional-recommended-bump'); + +const { Readable } = require('stream'); + +jest.mock('conventional-changelog'); +jest.mock('conventional-recommended-bump'); +jest.mock('git-semver-tags'); + +const mockGitSemverTags = ({ tags = [] }) => { + gitSemverTags.mockImplementation((opts, cb) => { + if (tags instanceof Error) cb(tags); + else cb(null, tags); + }); +}; + +const mockConventionalChangelog = ({ changelog }) => { + conventionalChangelog.mockImplementation( + (opt) => + new Readable({ + read(_size) { + const next = changelog.shift(); + if (next instanceof Error) { + this.destroy(next); + } else if (typeof next === 'function') { + this.push(next(opt)); + } else { + this.push(next ? Buffer.from(next, 'utf8') : null); + } + }, + }), + ); +}; + +const mockRecommendedBump = ({ bump }) => { + conventionalRecommendedBump.mockImplementation((opt, parserOpts, cb) => { + if (typeof bump === 'function') bump(opt, parserOpts, cb); + else if (bump instanceof Error) cb(bump); + else cb(null, bump ? { releaseType: bump } : {}); + }); +}; + +module.exports = { + mockGitSemverTags, + mockConventionalChangelog, + mockRecommendedBump, +}; diff --git a/test/mocks/openapi-1.2.3-crlf.yaml b/test/mocks/openapi-1.2.3-crlf.yaml new file mode 100644 index 000000000..b5e7741ea --- /dev/null +++ b/test/mocks/openapi-1.2.3-crlf.yaml @@ -0,0 +1,12 @@ +openapi: "3.0.2" +info: + title: Mock API + description: >- + Description of Mock API + version: "1.2.3" + termsOfService: http://swagger.io/terms/ +externalDocs: + description: Find out more + url: https://example.com/foo/bar +servers: + - url: http://example.com diff --git a/test/mocks/openapi-1.2.3-lf.yaml b/test/mocks/openapi-1.2.3-lf.yaml new file mode 100644 index 000000000..19ac91bbe --- /dev/null +++ b/test/mocks/openapi-1.2.3-lf.yaml @@ -0,0 +1,12 @@ +openapi: "3.0.2" +info: + title: Mock API + description: >- + Description of Mock API + version: "1.2.3" + termsOfService: http://swagger.io/terms/ +externalDocs: + description: Find out more + url: https://example.com/foo/bar +servers: + - url: http://example.com diff --git a/test/mocks/openapi-1.3.0-crlf.yaml b/test/mocks/openapi-1.3.0-crlf.yaml new file mode 100644 index 000000000..74551f047 --- /dev/null +++ b/test/mocks/openapi-1.3.0-crlf.yaml @@ -0,0 +1,12 @@ +openapi: "3.0.2" +info: + title: Mock API + description: >- + Description of Mock API + version: "1.3.0" + termsOfService: http://swagger.io/terms/ +externalDocs: + description: Find out more + url: https://example.com/foo/bar +servers: + - url: http://example.com diff --git a/test/mocks/openapi-1.3.0-lf.yaml b/test/mocks/openapi-1.3.0-lf.yaml new file mode 100644 index 000000000..21dbaeabd --- /dev/null +++ b/test/mocks/openapi-1.3.0-lf.yaml @@ -0,0 +1,12 @@ +openapi: "3.0.2" +info: + title: Mock API + description: >- + Description of Mock API + version: "1.3.0" + termsOfService: http://swagger.io/terms/ +externalDocs: + description: Find out more + url: https://example.com/foo/bar +servers: + - url: http://example.com diff --git a/test/mocks/pom-6.3.1-crlf.xml b/test/mocks/pom-6.3.1-crlf.xml new file mode 100644 index 000000000..bfc60b070 --- /dev/null +++ b/test/mocks/pom-6.3.1-crlf.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + + com.mycompany.app + my-app + 3.0.0 + + + com.mycompany.app + my-module + 6.3.1 + + + 2.0.0 + + + + + org.some.dependency + some-artifact + ${some.version} + + + org.another.dependency + another-artifact + 1.0.0 + + + diff --git a/test/mocks/pom-6.3.1-lf.xml b/test/mocks/pom-6.3.1-lf.xml new file mode 100644 index 000000000..194cd8bc3 --- /dev/null +++ b/test/mocks/pom-6.3.1-lf.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + + com.mycompany.app + my-app + 3.0.0 + + + com.mycompany.app + my-module + 6.3.1 + + + 2.0.0 + + + + + org.some.dependency + some-artifact + ${some.version} + + + org.another.dependency + another-artifact + 1.0.0 + + + diff --git a/test/mocks/pom-6.4.0-crlf.xml b/test/mocks/pom-6.4.0-crlf.xml new file mode 100644 index 000000000..90b1bfdb7 --- /dev/null +++ b/test/mocks/pom-6.4.0-crlf.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + + com.mycompany.app + my-app + 3.0.0 + + + com.mycompany.app + my-module + 6.4.0 + + + 2.0.0 + + + + + org.some.dependency + some-artifact + ${some.version} + + + org.another.dependency + another-artifact + 1.0.0 + + + diff --git a/test/mocks/pom-6.4.0-lf.xml b/test/mocks/pom-6.4.0-lf.xml new file mode 100644 index 000000000..99cb25900 --- /dev/null +++ b/test/mocks/pom-6.4.0-lf.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + + com.mycompany.app + my-app + 3.0.0 + + + com.mycompany.app + my-module + 6.4.0 + + + 2.0.0 + + + + + org.some.dependency + some-artifact + ${some.version} + + + org.another.dependency + another-artifact + 1.0.0 + + + diff --git a/test/mocks/pubspec-6.3.1-crlf.yaml b/test/mocks/pubspec-6.3.1-crlf.yaml new file mode 100644 index 000000000..20548f164 --- /dev/null +++ b/test/mocks/pubspec-6.3.1-crlf.yaml @@ -0,0 +1,33 @@ +# This is a comment that should be preserved +name: mock +description: "A mock YAML file" +version: 6.3.1 + +environment: + dart: ">=3.2.6 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + uses-material-design: true + generate: true + + # More comments here nested under one entry + # + # These comments should also be preserved. + + # Another comment block, separated from the first one + assets: + - assets/icons/ + - assets/images/ diff --git a/test/mocks/pubspec-6.3.1.yaml b/test/mocks/pubspec-6.3.1.yaml new file mode 100644 index 000000000..cfcdaf5d4 --- /dev/null +++ b/test/mocks/pubspec-6.3.1.yaml @@ -0,0 +1,33 @@ +# This is a comment that should be preserved +name: mock +description: "A mock YAML file" +version: 6.3.1 + +environment: + dart: ">=3.2.6 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + uses-material-design: true + generate: true + + # More comments here nested under one entry + # + # These comments should also be preserved. + + # Another comment block, separated from the first one + assets: + - assets/icons/ + - assets/images/ diff --git a/test/mocks/pubspec-6.4.0-crlf.yaml b/test/mocks/pubspec-6.4.0-crlf.yaml new file mode 100644 index 000000000..25a039bc7 --- /dev/null +++ b/test/mocks/pubspec-6.4.0-crlf.yaml @@ -0,0 +1,33 @@ +# This is a comment that should be preserved +name: mock +description: "A mock YAML file" +version: 6.4.0 + +environment: + dart: ">=3.2.6 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + uses-material-design: true + generate: true + + # More comments here nested under one entry + # + # These comments should also be preserved. + + # Another comment block, separated from the first one + assets: + - assets/icons/ + - assets/images/ diff --git a/test/mocks/pubspec-6.4.0.yaml b/test/mocks/pubspec-6.4.0.yaml new file mode 100644 index 000000000..44e27ed7a --- /dev/null +++ b/test/mocks/pubspec-6.4.0.yaml @@ -0,0 +1,33 @@ +# This is a comment that should be preserved +name: mock +description: "A mock YAML file" +version: 6.4.0 + +environment: + dart: ">=3.2.6 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + uses-material-design: true + generate: true + + # More comments here nested under one entry + # + # These comments should also be preserved. + + # Another comment block, separated from the first one + assets: + - assets/icons/ + - assets/images/ diff --git a/test/mocks/pyproject-1.0.0.toml b/test/mocks/pyproject-1.0.0.toml new file mode 100644 index 000000000..ed8392488 --- /dev/null +++ b/test/mocks/pyproject-1.0.0.toml @@ -0,0 +1,12 @@ +[tool.poetry] +name = "test" +version = "1.0.0" +description = "" +authors = [] + +[tool.poetry.dependencies] +python = "^3.8" + +[build-system] +requires = ["poetry>=1"] +build-backend = "poetry.masonry.api" diff --git a/test/mocks/pyproject-1.1.0.toml b/test/mocks/pyproject-1.1.0.toml new file mode 100644 index 000000000..03745363d --- /dev/null +++ b/test/mocks/pyproject-1.1.0.toml @@ -0,0 +1,12 @@ +[tool.poetry] +name = "test" +version = "1.1.0" +description = "" +authors = [] + +[tool.poetry.dependencies] +python = "^3.8" + +[build-system] +requires = ["poetry>=1"] +build-backend = "poetry.masonry.api" diff --git a/test/mocks/updater/customer-updater.js b/test/mocks/updater/customer-updater.js index 989aba3c0..320204acb 100644 --- a/test/mocks/updater/customer-updater.js +++ b/test/mocks/updater/customer-updater.js @@ -1,12 +1,9 @@ -const REPLACER = /version: "(.*)"/ +const REPLACER = /version: "(.*)"/; module.exports.readVersion = function (contents) { - return REPLACER.exec(contents)[1] -} + return REPLACER.exec(contents)[1]; +}; module.exports.writeVersion = function (contents, version) { - return contents.replace( - REPLACER.exec(contents)[0], - `version: "${version}"` - ) -} + return contents.replace(REPLACER.exec(contents)[0], `version: "${version}"`); +}; diff --git a/test/mocks/updater/increment-updater.js b/test/mocks/updater/increment-updater.js new file mode 100644 index 000000000..86f274fa7 --- /dev/null +++ b/test/mocks/updater/increment-updater.js @@ -0,0 +1,7 @@ +module.exports.readVersion = function (contents) { + return Number.parseInt(contents); +}; + +module.exports.writeVersion = function (contents) { + return this.readVersion(contents) + 1; +}; diff --git a/test/pre-commit-hook.integration-test.js b/test/pre-commit-hook.integration-test.js new file mode 100644 index 000000000..0a4dfb426 --- /dev/null +++ b/test/pre-commit-hook.integration-test.js @@ -0,0 +1,192 @@ +'use strict'; + +const shell = require('shelljs'); +const fs = require('fs'); + +const mockers = require('./mocks/jest-mocks'); + +// Jest swallows most standard console logs not explicitly defined into a custom logger +// see: https://stackoverflow.com/questions/51555568/remove-logging-the-origin-line-in-jest +const consoleWarnSpy = jest.spyOn(global.console, 'warn').mockImplementation(); + +const consoleInfoSpy = jest.spyOn(global.console, 'info').mockImplementation(); + +const consoleErrorSpy = jest + .spyOn(global.console, 'error') + .mockImplementation(); + +function exec(opt = '') { + if (typeof opt === 'string') { + const cli = require('../command'); + opt = cli.parse(`commit-and-tag-version ${opt}`); + } + return require('../index')(opt); +} + +function writePackageJson(version, option) { + const pkg = Object.assign({}, option, { version }); + fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8'); +} + +function writeHook(hookName, causeError, script) { + shell.mkdir('-p', 'scripts'); + let content = script || 'console.error("' + hookName + ' ran")'; + content += causeError ? '\nthrow new Error("' + hookName + '-failure")' : ''; + fs.writeFileSync('scripts/' + hookName + '.js', content, 'utf-8'); + fs.chmodSync('scripts/' + hookName + '.js', '755'); +} + +function setupTempGitRepo() { + shell.rm('-rf', 'pre-commit-hook-temp'); + shell.config.silent = true; + shell.mkdir('pre-commit-hook-temp'); + shell.cd('pre-commit-hook-temp'); + shell.exec('git init'); + shell.exec('git config commit.gpgSign false'); + shell.exec('git config core.autocrlf false'); + shell.exec('git commit --allow-empty -m"root-commit"'); +} + +function setup() { + setupTempGitRepo(); + writePackageJson('1.0.0'); +} + +function clearCapturedSpyCalls() { + consoleInfoSpy.mockClear(); + consoleWarnSpy.mockClear(); + consoleErrorSpy.mockClear(); +} + +function resetShell() { + shell.cd('../'); + shell.rm('-rf', 'pre-commit-hook-temp'); +} + +function reset() { + resetShell(); + + clearCapturedSpyCalls(); +} + +/** + * Mock external conventional-changelog modules + * + * bump: 'major' | 'minor' | 'patch' | Error | (opt, parserOpts, cb) => { cb(err) | cb(null, { releaseType }) } + * changelog?: string | Error | Array string | null> + * tags?: string[] | Error + */ +function mock({ bump, changelog, tags }) { + if (bump === undefined) throw new Error('bump must be defined for mock()'); + + mockers.mockRecommendedBump({ bump }); + + if (!Array.isArray(changelog)) changelog = [changelog]; + mockers.mockConventionalChangelog({ changelog }); + + mockers.mockGitSemverTags({ tags }); +} + +function getLog(expectedLog, spy = consoleInfoSpy) { + const consoleInfoLogs = spy.mock.calls.map((args) => args[0]); + return consoleInfoLogs.find((log) => log.includes(expectedLog)); +} + +function verifyLogPrinted(expectedLog, spy = consoleInfoSpy) { + const logType = spy === consoleInfoSpy ? 'info' : 'warn'; + const desiredLog = getLog(expectedLog, spy); + if (desiredLog) { + expect(desiredLog).toMatch(expectedLog); + } else { + expect(`no ${logType} Log printed matching`).toMatch(expectedLog); + } +} + +describe('precommit hook', function () { + beforeEach(function () { + setup(); + }); + + afterEach(function () { + reset(); + }); + + it('should run the precommit hook when provided via .versionrc.json (#371)', async function () { + fs.writeFileSync( + '.versionrc.json', + JSON.stringify({ + scripts: { precommit: 'node scripts/precommit' }, + }), + 'utf-8', + ); + + writeHook('precommit'); + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + mock({ bump: 'minor' }); + await exec(''); + verifyLogPrinted('precommit ran', consoleWarnSpy); + }); + + it('should run the precommit hook when provided', async function () { + writePackageJson('1.0.0', { + 'commit-and-tag-version': { + scripts: { precommit: 'node scripts/precommit' }, + }, + }); + writeHook('precommit'); + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + + mock({ bump: 'minor' }); + await exec('--patch'); + verifyLogPrinted('precommit ran', consoleWarnSpy); + }); + + it('should run the precommit hook and throw error when precommit fails', async function () { + writePackageJson('1.0.0', { + 'commit-and-tag-version': { + scripts: { precommit: 'node scripts/precommit' }, + }, + }); + writeHook('precommit', true); + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + + mock({ bump: 'minor' }); + let errorMessage = ''; + try { + await exec('--patch'); + } catch (e) { + errorMessage = e.message; + } + expect(errorMessage).toMatch('precommit-failure'); + }); + + it('should allow an alternate commit message to be provided by precommit script', async function () { + writePackageJson('1.0.0', { + 'commit-and-tag-version': { + scripts: { precommit: 'node scripts/precommit' }, + }, + }); + writeHook('precommit', false, 'console.log("releasing %s delivers #222")'); + fs.writeFileSync( + 'CHANGELOG.md', + 'legacy header format\n', + 'utf-8', + ); + + mock({ bump: 'minor' }); + await exec('--patch'); + expect(shell.exec('git log --oneline -n1').stdout).toMatch(/delivers #222/); + }); +}); diff --git a/test/preset.integration-test.js b/test/preset.integration-test.js new file mode 100644 index 000000000..ae3c27d28 --- /dev/null +++ b/test/preset.integration-test.js @@ -0,0 +1,56 @@ +const shell = require('shelljs'); +const fs = require('fs'); + +function exec(opt) { + const cli = require('../command'); + opt = cli.parse(`commit-and-tag-version ${opt} --silent`); + opt.skip = { commit: true, tag: true }; + return require('../index')(opt); +} + +function setupTempGitRepo() { + shell.rm('-rf', 'preset-temp'); + shell.config.silent = true; + shell.mkdir('preset-temp'); + shell.cd('preset-temp'); + shell.exec('git init'); + shell.exec('git config commit.gpgSign false'); + shell.exec('git config core.autocrlf false'); + shell.exec('git commit --allow-empty -m "initial commit"'); + shell.exec('git commit --allow-empty -m "feat: A feature commit."'); + shell.exec('git commit --allow-empty -m "perf: A performance change."'); + shell.exec('git commit --allow-empty -m "chore: A chore commit."'); + shell.exec('git commit --allow-empty -m "ci: A ci commit."'); + shell.exec('git commit --allow-empty -m "custom: A custom commit."'); +} + +function resetShell() { + shell.cd('../'); + shell.rm('-rf', 'preset-temp'); +} + +describe('presets', function () { + beforeEach(function () { + setupTempGitRepo(); + }); + + afterEach(function () { + resetShell(); + }); + + it('Conventional Commits (default)', async function () { + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toContain('### Features'); + expect(content).not.toContain('### Performance Improvements'); + expect(content).not.toContain('### Custom'); + }); + + it('Angular', async function () { + await exec('--preset angular'); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + expect(content).toContain('### Features'); + expect(content).toContain('### Performance Improvements'); + expect(content).not.toContain('### Custom'); + }); +}); diff --git a/test/preset.spec.js b/test/preset.spec.js deleted file mode 100644 index 509dec84d..000000000 --- a/test/preset.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* global describe it beforeEach, afterEach */ - -const shell = require('shelljs') -const fs = require('fs') - -require('chai').should() - -function exec (opt) { - const cli = require('../command') - opt = cli.parse(`standard-version ${opt} --silent`) - opt.skip = { commit: true, tag: true } - return require('../index')(opt) -} - -describe('presets', () => { - beforeEach(function () { - shell.rm('-rf', 'tmp') - shell.config.silent = true - shell.mkdir('tmp') - shell.cd('tmp') - shell.exec('git init') - shell.exec('git config commit.gpgSign false') - shell.exec('git config core.autocrlf false') - shell.exec('git commit --allow-empty -m "initial commit"') - shell.exec('git commit --allow-empty -m "feat: A feature commit."') - shell.exec('git commit --allow-empty -m "perf: A performance change."') - shell.exec('git commit --allow-empty -m "chore: A chore commit."') - shell.exec('git commit --allow-empty -m "ci: A ci commit."') - shell.exec('git commit --allow-empty -m "custom: A custom commit."') - }) - - afterEach(function () { - shell.cd('../') - shell.rm('-rf', 'tmp') - }) - - it('Conventional Commits (default)', async function () { - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.contain('### Features') - content.should.not.contain('### Performance Improvements') - content.should.not.contain('### Custom') - }) - - it('Angular', async function () { - await exec('--preset angular') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.contain('### Features') - content.should.contain('### Performance Improvements') - content.should.not.contain('### Custom') - }) -}) diff --git a/test/stringify-package.spec.js b/test/stringify-package.spec.js new file mode 100644 index 000000000..c0416f568 --- /dev/null +++ b/test/stringify-package.spec.js @@ -0,0 +1,35 @@ +'use strict'; + +const stringifyPackage = require('../lib/stringify-package'); + +describe('stringifyPackage()', function () { + const dummy = { name: 'dummy' }; + + it('with no params uses \\n', function () { + expect(stringifyPackage(dummy)).toMatch(/\n$/m); + }); + + it('uses \\n', function () { + expect(stringifyPackage(dummy, 2, '\n')).toMatch(/\n$/m); + }); + + it('uses \\r\\n', function () { + expect(stringifyPackage(dummy, 2, '\r\n')).toMatch(/\r\n$/m); + }); + + it('with no params uses 2-space indent', function () { + expect(stringifyPackage(dummy)).toMatch(/^ {2}"name": "dummy"/m); + }); + + it('uses 2-space indent', function () { + expect(stringifyPackage(dummy, 2, '\n')).toMatch(/^ {2}"name": "dummy"/m); + }); + + it('uses 4-space indent', function () { + expect(stringifyPackage(dummy, 4, '\n')).toMatch(/^ {4}"name": "dummy"/m); + }); + + it('0 works', function () { + expect(stringifyPackage(dummy, 0).split(/\r\n|\r|\n/).length).toEqual(2); + }); +}); diff --git a/test/utils.spec.js b/test/utils.spec.js new file mode 100644 index 000000000..9d3031873 --- /dev/null +++ b/test/utils.spec.js @@ -0,0 +1,36 @@ +const { promises: fsp } = require('fs'); + +let mockFs; + +const setLockFile = (lockFile) => { + if (mockFs) { + mockFs.mockRestore(); + } + mockFs = jest.spyOn(fsp, 'access').mockImplementation(async (path) => { + if (lockFile && path.endsWith(lockFile)) { + return Promise.resolve(); + } + return Promise.reject(new Error('Invalid lockfile')); + }); +}; + +describe('utils', function () { + it('detectPMByLockFile should work', async function () { + const { detectPMByLockFile } = require('../lib/detect-package-manager'); + + let pm = await detectPMByLockFile(); + expect(pm).toEqual('npm'); + + setLockFile('yarn.lock'); + pm = await detectPMByLockFile(); + expect(pm).toEqual('yarn'); + + setLockFile('package-lock.json'); + pm = await detectPMByLockFile(); + expect(pm).toEqual('npm'); + + setLockFile('pnpm-lock.yaml'); + pm = await detectPMByLockFile(); + expect(pm).toEqual('pnpm'); + }); +});