diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d421cfff --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +* text eol=lf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.gif binary +*.svg binary diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 911e54ef..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -## Bug description - - - -- Would you like to work on a fix? [y/n] - -## To Reproduce - -Steps to reproduce the behavior: - -1. ... -2. ... -3. ... -4. ... - - - -## Expected behavior - - - -## Screenshots - - - -## Environment - - - -- OS: [e.g. Ubuntu 20.04] -- workspace-node-tools version: [e.g. 0.1.0] - -## Additional context - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 0086358d..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 3b54884e..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -## Motivations - - - -- Would you like to implement this feature? [y/n] - -## Solution - - - -## Alternatives - - - -## Additional context - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 4adb89ec..00000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml new file mode 100644 index 00000000..08db0c8d --- /dev/null +++ b/.github/actions/setup-node/action.yml @@ -0,0 +1,40 @@ +name: 'Setup Node' + +description: 'Setup node and pnpm' + +runs: + using: 'composite' + steps: + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version-file: .node-version + cache: 'pnpm' + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + if: ${{ github.ref_name == 'main' }} + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - uses: actions/cache@v4 + if: ${{ github.ref != 'main' }} + with: + path: ${{ env.STORE_PATH }} + key: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + shell: bash + run: pnpm install diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml new file mode 100644 index 00000000..9484859c --- /dev/null +++ b/.github/actions/setup-rust/action.yml @@ -0,0 +1,45 @@ +name: 'Setup Rust' + +description: 'Setup rust' + +inputs: + # See https://rust-lang.github.io/rustup/concepts/components.html + components: + required: false + type: string + description: space separated Rust components, e.g. `clippy rustfmt rust-docs` + + tools: + required: false + type: string + description: comma separated Cargo tools + + restore-cache: + default: true + required: false + type: boolean + description: whether to restore cache + + save-cache: + default: false + required: false + type: boolean + description: whether to save cache, e.g. `github.ref_name == 'main'` + + cache-key: + default: 'main' + required: false + type: string + description: cache key prefix + +runs: + using: 'composite' + steps: + - name: Setup Rust + uses: Boshen/setup-rust@main + with: + components: ${{ inputs.components }} + tools: ${{ inputs.tools }} + restore-cache: ${{ inputs.restore-cache }} + save-cache: ${{ inputs.save-cache }} + cache-key: ${{ inputs.cache-key }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index e6698867..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "cargo" - # Look for `Cargo.toml` and `Cargo.lock` in the root directory - directory: "/" - # Check for updates every Monday - schedule: - interval: "weekly" - open-pull-requests-limit: 10 - - package-ecosystem: "github-actions" - directory: "/" - # Check for updates every Monday - schedule: - interval: "weekly" - open-pull-requests-limit: 10 diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml deleted file mode 100644 index f860d738..00000000 --- a/.github/workflows/audit.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Security audit - -on: - schedule: - # Runs at 00:00 UTC everyday - - cron: '0 0 * * *' - push: - paths: - - '**/Cargo.toml' - - '**/Cargo.lock' - pull_request: - -jobs: - audit: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Run security audit - uses: rustsec/audit-check@v1.4.1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml deleted file mode 100644 index 6065c3c8..00000000 --- a/.github/workflows/cd.yml +++ /dev/null @@ -1,124 +0,0 @@ -name: CD # Continuous Deployment - -on: - push: - tags: - - '[v]?[0-9]+.[0-9]+.[0-9]+' - -jobs: - - publish: - name: Publishing for ${{ matrix.job.os }} - runs-on: ${{ matrix.job.os }} - strategy: - matrix: - rust: [stable] - job: - - os: macos-latest - os-name: macos - target: x86_64-apple-darwin - architecture: x86_64 - binary-postfix: "" - use-cross: false - - os: macos-latest - os-name: macos - target: aarch64-apple-darwin - architecture: arm64 - binary-postfix: "" - use-cross: false - - os: ubuntu-latest - os-name: linux - target: x86_64-unknown-linux-gnu - architecture: x86_64 - binary-postfix: "" - use-cross: false - - os: windows-latest - os-name: windows - target: x86_64-pc-windows-msvc - architecture: x86_64 - binary-postfix: ".exe" - use-cross: false - - os: ubuntu-latest - os-name: linux - target: aarch64-unknown-linux-gnu - architecture: arm64 - binary-postfix: "" - use-cross: true - - os: ubuntu-latest - os-name: linux - target: i686-unknown-linux-gnu - architecture: i686 - binary-postfix: "" - use-cross: true - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - target: ${{ matrix.job.target }} - profile: minimal - override: true - - uses: Swatinem/rust-cache@v2 - - name: Cargo build - uses: actions-rs/cargo@v1 - with: - command: build - use-cross: ${{ matrix.job.use-cross }} - toolchain: ${{ matrix.rust }} - args: --release --target ${{ matrix.job.target }} - - - name: install strip command - shell: bash - run: | - if [[ ${{ matrix.job.target }} == aarch64-unknown-linux-gnu ]]; then - sudo apt update - sudo apt-get install -y binutils-aarch64-linux-gnu - fi - - name: Packaging final binary - shell: bash - run: | - cd target/${{ matrix.job.target }}/release - - ####### reduce binary size by removing debug symbols ####### - BINARY_NAME=workspace-node-tools${{ matrix.job.binary-postfix }} - if [[ ${{ matrix.job.target }} == aarch64-unknown-linux-gnu ]]; then - GCC_PREFIX="aarch64-linux-gnu-" - else - GCC_PREFIX="" - fi - "$GCC_PREFIX"strip $BINARY_NAME - - ########## create tar.gz ########## - RELEASE_NAME=workspace-node-tools-${GITHUB_REF/refs\/tags\//}-${{ matrix.job.os-name }}-${{ matrix.job.architecture }} - tar czvf $RELEASE_NAME.tar.gz $BINARY_NAME - - ########## create sha256 ########## - if [[ ${{ runner.os }} == 'Windows' ]]; then - certutil -hashfile $RELEASE_NAME.tar.gz sha256 | grep -E [A-Fa-f0-9]{64} > $RELEASE_NAME.sha256 - else - shasum -a 256 $RELEASE_NAME.tar.gz > $RELEASE_NAME.sha256 - fi - - name: Releasing assets - uses: softprops/action-gh-release@v2 - with: - files: | - target/${{ matrix.job.target }}/release/workspace-node-tools-*.tar.gz - target/${{ matrix.job.target }}/release/workspace-node-tools-*.sha256 - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - - publish-cargo: - name: Publishing to Cargo - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - run: cargo publish - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index e5327b92..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: CI # Continuous Integration - -on: - push: - branches: - - main - pull_request: - -jobs: - - test: - name: Test Suite - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20] - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Create pnpm-lock.yaml - run: | - echo "node-version: 20" > pnpm-lock.yaml - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 9 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - #cache: 'pnpm' - - name: Delete pnpm-lock.yaml - run: | - rm -f pnpm-lock.yaml - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - name: Run tests - run: cargo test --all-features --workspace -- --test-threads=1 --nocapture - - rustfmt: - name: Rustfmt - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - uses: Swatinem/rust-cache@v2 - - name: Check formatting - run: cargo fmt --all --check - - clippy: - name: Clippy - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - uses: Swatinem/rust-cache@v2 - - name: Clippy check - run: cargo clippy --all-targets --all-features --workspace -- -D warnings - - docs: - name: Docs - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - name: Check documentation - env: - RUSTDOCFLAGS: -D warnings - run: cargo doc --no-deps --document-private-items --all-features --workspace --examples - diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 00000000..535bb83a --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,77 @@ +name: Pull Request +# Jobs in this workflow are all required run successfully before the PR can be merged. This is enforced by using github status checks. +# What's github status check: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks + +on: + pull_request: + types: [opened, synchronize] + merge_group: + push: + branches: + - main + - 'renovate/**' # For renovate bot "automerge": true + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.ref != 'main' }} + +jobs: + # JOB to run change detection + changes: # https://github.com/dorny/paths-filter + name: Detect Changes + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + rust-changes: ${{ (steps.filter.outputs.rust-changes == 'true') || (github.ref_name == 'main') }} + node-changes: ${{ (steps.filter.outputs.node-changes == 'true') || (github.ref_name == 'main') }} + steps: + # For pull requests it's not necessary to checkout the code + - uses: actions/checkout@v4 #Fix https://github.com/dorny/paths-filter/issues/212 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + rust-changes: &rust-changes + - '.github/workflows/**' + - 'crates/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + - 'deny.toml' + - '.gitattributes' + node-changes: + - *rust-changes + - 'packages/**' + - 'examples/**' + - 'scripts/**' + - 'package.json' + - 'pnpm-lock.yaml' + - 'pnpm-workspace.yaml' + - name: Show outputs + run: | + echo "Rust changes: ${{ (steps.filter.outputs.rust-changes == 'true') || (github.ref_name == 'main') }}" + echo "Node changes: ${{ (steps.filter.outputs.node-changes == 'true') || (github.ref_name == 'main') }}" + + cargo-test: + needs: changes + strategy: + matrix: + target: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.target }} + steps: + - uses: actions/checkout@v4 + + - name: Set up Rust + uses: ./.github/actions/setup-rust + with: + tools: just + cache-key: debug-build + components: clippy rustfmt + + - name: Set Node + uses: ./.github/actions/setup-node + + - name: Run Tests + run: cargo test --workspace -- --test-threads=1 --nocapture diff --git a/.gitignore b/.gitignore index 7677c72b..3cffa686 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .DS_Store .idea temp/ +node_modules/ diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..8326e27f --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +22.3.0 diff --git a/.rustfmt.toml b/.rustfmt.toml deleted file mode 100644 index 518d20d2..00000000 --- a/.rustfmt.toml +++ /dev/null @@ -1,5 +0,0 @@ -# Keep in sync with .editorconfig -max_width = 100 - -tab_spaces = 4 -hard_tabs = false diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 00000000..5e68a39c --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,18 @@ +// Folder-specific settings +// +// For a full list of overridable settings, and general information on folder-specific settings, +// see the documentation: https://zed.dev/docs/configuring-zed#settings-files +{ + "lsp": { + "rust-analyzer": { + "procMacro": { + "ignored": { "napi-derive": ["napi"] } + }, + "initialization_options": { + "checkOnSave": { + "command": "clippy" // rust-analyzer.checkOnSave.command + } + } + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 731ea486..e69de29b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,484 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -## [2.0.2] - 2024-09-23 - -### ๐Ÿ› Bug Fixes - -- Filter unwanted commits not following conventional rules - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Commit parser fix -- Merge pull request #52 from websublime/fix/git-commits-process - -## [2.0.1] - 2024-09-19 - -### ๐Ÿ› Bug Fixes - -- Snapshot versions for branches - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Merge pull request #49 from websublime/fix/release-as - -## [2.0.0] - 2024-09-19 - -### ๐Ÿš€ Features - -- Bumps by changes -- Init bumps deps graph -- Dependency graph implementation in get_packages -- Implemented and tested get_bumps with changes -- Apply bumps - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Deps graph updates -- Deep tests and evaluate solution -- Filter deps with changed packages -- Deps evaluation -- Bump tom version -- Napi structure missing -- Merge pull request #48 from websublime/feature/bumps-by-changes - -## [1.0.19] - 2024-09-12 - -### ๐Ÿ› Bug Fixes - -- Bump loop skip if package already bumped - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Debugging duplicated bumps -- Maintenance format -- Merge pull request #45 from websublime/fix/duplicated-bumps -- Bump version - -## [1.0.18] - 2024-09-11 - -### ๐Ÿ› Bug Fixes - -- Changes diff exists for empty and new changed package - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Merge pull request #44 from websublime/fix/change-exist-diff - -## [1.0.17] - 2024-09-09 - -### ๐Ÿ› Bug Fixes - -- Duplicate changes entries and check if exist - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Update versions -- Update cliff signature - -## [1.0.16] - 2024-08-07 - -### ๐Ÿ› Bug Fixes - -- Change_exist iter was using any, changed to all - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Merge pull request #31 from websublime/fix/change-exist-any-to-all - -## [1.0.15] - 2024-08-07 - -### ๐Ÿš€ Features - -- Snapshot version unique - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Merge pull request #30 from websublime/feature/snapshot-unique - -## [1.0.14] - 2024-08-06 - -### ๐Ÿ› Bug Fixes - -- Bump snapshot for dependencies when parent is snapshot too - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Merge pull request #29 from websublime/feature/dependency-patch-fix - -## [1.0.13] - 2024-07-24 - -### ๐Ÿš€ Features - -- Remove PackageJsonSchema and safe parse only with serde - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Bump patch version -- Merge pull request #24 from websublime/feature/safe-parse-json - -## [1.0.12] - 2024-07-24 - -### ๐Ÿ› Bug Fixes - -- Change exist, should also check package is defined not only branch name - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Merge pull request #23 from websublime/fix/change-exist - -## [1.0.11] - 2024-07-24 - -### ๐Ÿš€ Features - -- Changes pretty json and remove root package for pnpm - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Merge pull request #22 from websublime/feature/pretty-json-pnpm - -## [1.0.10] - 2024-07-23 - -### ๐Ÿš€ Features - -- Apply bumps -- New git commands and sync dependencies implementation -- Apply_bumps ability to push changes - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Init sync dependencies -- Docs for get_bumps and apply_bumps -- Version maintenance -- Merge pull request #21 from websublime/feature/apply-changes - -## [1.0.9] - 2024-07-22 - -### ๐Ÿš€ Features - -- Change file exist check -- Prepend to changelog - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Version maintenance -- Merge pull request #20 from websublime/feature/docs-and-change-file-exist - -## [1.0.8] - 2024-07-19 - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Make changes type available -- Merge pull request #19 from websublime/feature/changes-type - -## [1.0.7] - 2024-07-19 - -### ๐Ÿš€ Features - -- Changes generation api -- Changes add exist function - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Merge pull request #18 from websublime/feature/changes -- Bump patch version - -## [1.0.6] - 2024-07-19 - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Change order for looking files -- Merge pull request #17 from websublime/feature/change - -## [1.0.5] - 2024-07-19 - -### ๐Ÿ› Bug Fixes - -- Avoid symbolic links and transforma paths to canonical usage - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Merge pull request #16 from websublime/fix/canonical-paths -- Bump version - -## [1.0.4] - 2024-07-19 - -### ๐Ÿ› Bug Fixes - -- Missing napi trait in bumps struct's - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog - -## [1.0.3] - 2024-07-18 - -### ๐Ÿ› Bug Fixes - -- Cfg for windows and exclude compile functions for testing - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance - -## [1.0.2] - 2024-07-18 - -### ๐Ÿ› Bug Fixes - -- Main to build correctly - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog - -## [1.0.1] - 2024-07-18 - -### โš™๏ธ Miscellaneous Tasks - -- Changelog maintenance -- Remove package random -- No verify enable -- Bump version - -## [1.0.0] - 2024-07-18 - -### ๐Ÿš€ Features - -- Package manager and root project path -- Some git commands functions -- More git commands -- Remove clone method in git commands -- Get_packages for pnpm -- Get_packages for npm and yarn -- Get_packages for npm and yarn -- Get_changed_packages initial implementation -- Get_last_known_publish_tag_info_for_package init implementation -- Get_last_known_publish_tag_info_for_all_packages implementation -- Get_conventional_for_package implementation -- Init test structure -- Bumps implementation initialization -- Ge_bumps implementation - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Commits since test -- More git commands -- More git commands -- Maintenance format -- Init fixing tests -- Packages fixing tests -- Packages fixing tests -- Packages fixing tests -- Packages fixing tests -- Packages fixing tests -- Packages fixing tests -- Packages fixing tests -- Packages fixing tests -- Paths tests -- Maintenance format -- Test structure with git config -- Clippy all -- Packages module tests -- Conventional tests -- Serialize and deserialize on bump -- No trait clone in bump -- Missing cwd property -- Output debbug -- Conventional test find package -- Maintenance format -- Maintenance format -- Init apply_bumps -- Merge pull request #15 from websublime/feature/next -- Bump version - -## [0.9.0] - 2024-07-12 - -### ๐Ÿš€ Features - -- Make napi and napi_derive optional features - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Maintenance format -- Documenting functions and methods -- Maintenance format -- Merge pull request #13 from websublime/feature/cfg-features - -## [0.8.1] - 2024-07-11 - -### ๐Ÿ› Bug Fixes - -- Package json validation changed to return boolean - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Maintenance version -- Merge pull request #12 from websublime/fix/package-validation - -## [0.8.0] - 2024-07-11 - -### ๐Ÿš€ Features - -- Method to format repo url - -### ๐Ÿ› Bug Fixes - -- Clippy suggestions and upgrade serde - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Maintenance format -- Add repo url to package info -- Append to url for creation and init package json validation -- More options in package info -- Merge pull request #11 from websublime/feature/repo-url -- Bump version - -## [0.7.1] - 2024-07-06 - -### ๐Ÿ› Bug Fixes - -- Duplicated keys in changed packages - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Merge pull request #9 from websublime/fix/duplicated-changed-packages -- Bump version - -## [0.7.0] - 2024-07-06 - -### ๐Ÿš€ Features - -- Monorepo method for getting changed packages - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Chang map tp flat_map -- Merge pull request #8 from websublime/feature/changed-packages -- Bump version - -## [0.6.0] - 2024-07-06 - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Add more git commands and fix git tag creation -- Maintenance format -- Fail safe for get_root_workspace by git -- Merge pull request #7 from websublime/feature/more-git -- Bump version - -## [0.5.0] - 2024-07-04 - -### ๐Ÿš€ Features - -- Git commands -- Package info as now property for relative path of the package -- Package info converts to json and conventional config present in struct as json value -- Conventional package with commits,config and changelog output -- More git commands - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Maintenance sync lock file -- Maintenance format -- Support for changelog title -- Use of relative path from package info -- Maintenance format -- Serialize and deserialize objects -- Merge pull request #6 from websublime/feature/git -- Bump to version 0.5.0 - -## [0.4.0] - 2024-07-03 - -### ๐Ÿš€ Features - -- Monorepo struct api -- Package info struct with json value and tests to monorepo npm,pnpm -- Action CI to support pnpm - -### ๐Ÿ› Bug Fixes - -- Clippy issues - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Maintenance format -- Maintenance clippy suggestions -- Pnpm-lock.yaml -- Github action pnpm-lock creation and deletion -- Node without cache -- Merge pull request #5 from websublime/feature/monorepo -- Bump to version 0.4.0 - -## [0.3.0] - 2024-07-03 - -### ๐Ÿš€ Features - -- Project root detection - -### ๐Ÿ› Bug Fixes - -- Clippy checks -- Fmt formatting rules - -### ๐Ÿงช Testing - -- Add tests for filesystem root detection - -### โš™๏ธ Miscellaneous Tasks - -- Maintenance changelog -- Disable clippy checks -- Simplify match to unwrap_or_default -- Merge pull request #4 from websublime/feature/filesystem -- Bump to version 0.3.0 - -## [0.2.0] - 2024-07-03 - -### ๐Ÿš€ Features - -- Initial dependencies and configs -- Node package manager detection -- Agent display implementation - -### โš™๏ธ Miscellaneous Tasks - -- Init project -- Update github secret -- Maintenance format -- Init tests for agent -- Tests for all enum agents -- Tests for panic -- Add git-cliff config -- Merge pull request #2 from websublime/feature/agent -- Bump to version 0.2.0 - - diff --git a/Cargo.lock b/Cargo.lock index 1f7d5103..c0b2193b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler32" version = "1.2.0" @@ -46,9 +31,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "android-tzdata" @@ -66,31 +51,16 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.86" +name = "arraydeque" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "backtrace" -version = "0.3.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" @@ -129,14 +99,14 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] name = "bstr" -version = "1.9.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", "serde", @@ -148,6 +118,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cacache" version = "13.0.0" @@ -174,54 +150,23 @@ dependencies = [ [[package]] name = "calendrical_calculations" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec493b209a1b81fa32312d7ceca1b547d341c7b5f16a3edbf32b1d8b455bbdf" +checksum = "f27ca2b6e2f7d75f43e001ded6f25e79b80bded5abbe764cbdf78c25a3051f4b" dependencies = [ "core_maths", "displaydoc", ] -[[package]] -name = "camino" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "cc" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -268,32 +213,31 @@ dependencies = [ [[package]] name = "config" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf" dependencies = [ - "lazy_static", "nom", "pathdiff", "serde", - "toml", - "yaml-rust", + "toml 0.8.19", + "yaml-rust2", ] [[package]] name = "const_format" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" dependencies = [ "proc-macro2", "quote", @@ -310,20 +254,11 @@ dependencies = [ "pest_derive", ] -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core2" @@ -345,9 +280,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -392,34 +327,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.7", + "generic-array", "typenum", ] -[[package]] -name = "ctor" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "dary_heap" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] +checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" [[package]] name = "deunicode" @@ -487,6 +403,15 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -503,48 +428,11 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "execute" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" -dependencies = [ - "execute-command-macro", - "execute-command-tokens", - "generic-array 1.1.0", -] - -[[package]] -name = "execute-command-macro" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" -dependencies = [ - "execute-command-macro-impl", -] - -[[package]] -name = "execute-command-macro-impl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" -dependencies = [ - "execute-command-tokens", - "quote", - "syn", -] - -[[package]] -name = "execute-command-tokens" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" - [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "fixed_decimal" @@ -582,15 +470,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "generic-array" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" -dependencies = [ - "typenum", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -602,17 +481,11 @@ dependencies = [ "wasi", ] -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - [[package]] name = "git-cliff-core" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b82ecf763c4f7f9564880776ec536417e3ecd7c7d773f1413091c42afcdf265" +checksum = "0d39cee786ceeaf20564d25899b831edaed11d2af4d4091d14c1366818ebfbec" dependencies = [ "bincode", "cacache", @@ -636,7 +509,7 @@ dependencies = [ "serde_regex", "tera", "thiserror", - "toml", + "toml 0.8.19", "url", "urlencoding", ] @@ -674,14 +547,14 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.7", + "regex-automata 0.4.9", "regex-syntax", ] @@ -707,10 +580,19 @@ dependencies = [ ] [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "hashbrown" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] [[package]] name = "hex" @@ -729,9 +611,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1145,25 +1027,36 @@ checksum = "c588878c508a3e2ace333b3c50296053e6483c6a7541251b546cc59dcd6ced8e" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -1194,12 +1087,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.1", ] [[package]] @@ -1211,6 +1104,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1219,18 +1121,18 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -1266,9 +1168,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libflate" @@ -1290,7 +1192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" dependencies = [ "core2", - "hashbrown", + "hashbrown 0.14.5", "rle-decode-fast", ] @@ -1306,21 +1208,11 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "libloading" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" -dependencies = [ - "cfg-if", - "windows-targets 0.52.6", -] - [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -1334,9 +1226,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.18" +version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "libc", @@ -1344,12 +1236,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1412,69 +1298,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "napi" -version = "2.16.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53575dfa17f208dd1ce3a2da2da4659aae393b256a472f2738a8586a6c4107fd" -dependencies = [ - "bitflags", - "ctor", - "napi-derive", - "napi-sys", - "once_cell", - "serde", - "serde_json", - "tokio", -] - -[[package]] -name = "napi-derive" -version = "2.16.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17435f7a00bfdab20b0c27d9c56f58f6499e418252253081bfff448099da31d1" -dependencies = [ - "cfg-if", - "convert_case", - "napi-derive-backend", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "napi-derive-backend" -version = "1.0.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "967c485e00f0bf3b1bdbe510a38a4606919cf1d34d9a37ad41f25a81aa077abe" -dependencies = [ - "convert_case", - "once_cell", - "proc-macro2", - "quote", - "regex", - "semver", - "syn", -] - -[[package]] -name = "napi-sys" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" -dependencies = [ - "libloading", -] - [[package]] name = "next_version" version = "0.2.19" @@ -1496,15 +1319,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -1515,12 +1329,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -1550,39 +1358,11 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "object" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "option-ext" @@ -1601,9 +1381,9 @@ dependencies = [ [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" [[package]] name = "percent-encoding" @@ -1613,9 +1393,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -1624,9 +1404,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" dependencies = [ "pest", "pest_generator", @@ -1634,9 +1414,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" dependencies = [ "pest", "pest_meta", @@ -1647,9 +1427,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" dependencies = [ "once_cell", "pest", @@ -1704,17 +1484,11 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "pori" @@ -1725,32 +1499,29 @@ dependencies = [ "nom", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1787,9 +1558,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -1798,24 +1569,24 @@ dependencies = [ [[package]] name = "reflink-copy" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc31414597d1cd7fdd2422798b7652a6329dda0fe0219e6335a13d5bcaa9aeb6" +checksum = "17400ed684c3a0615932f00c271ae3eea13e47056a1455821995122348ab6438" dependencies = [ "cfg-if", "rustix", - "windows 0.58.0", + "windows", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.9", "regex-syntax", ] @@ -1830,9 +1601,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1841,9 +1612,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rle-decode-fast" @@ -1886,26 +1657,11 @@ dependencies = [ "walkdir", ] -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags", "errno", @@ -1914,12 +1670,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - [[package]] name = "ryu" version = "1.0.18" @@ -1956,18 +1706,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -1976,9 +1726,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -1998,9 +1748,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2038,6 +1788,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "siphasher" version = "0.3.11" @@ -2046,9 +1802,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slug" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" dependencies = [ "deunicode", "wasm-bindgen", @@ -2085,9 +1841,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" -version = "2.0.71" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", @@ -2105,25 +1861,11 @@ dependencies = [ "syn", ] -[[package]] -name = "sysinfo" -version = "0.30.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "windows 0.52.0", -] - [[package]] name = "tempfile" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -2156,57 +1898,24 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.7.6" @@ -2218,29 +1927,12 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.38.1" +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "backtrace", - "num_cpus", - "pin-project-lite", + "serde", ] [[package]] @@ -2266,9 +1958,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -2285,9 +1977,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unic-char-property" @@ -2341,57 +2033,33 @@ dependencies = [ [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", @@ -2422,22 +2090,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vergen" -version = "8.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" -dependencies = [ - "anyhow", - "cargo_metadata", - "cfg-if", - "regex", - "rustc_version", - "rustversion", - "sysinfo", - "time", -] - [[package]] name = "version-compare" version = "0.2.0" @@ -2446,9 +2098,9 @@ checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "virtue" @@ -2474,19 +2126,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -2499,9 +2152,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2509,9 +2162,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -2522,9 +2175,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wax" @@ -2533,7 +2186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d12a78aa0bab22d2f26ed1a96df7ab58e8a93506a3e20adb47c51a93b4e1357" dependencies = [ "const_format", - "itertools", + "itertools 0.11.0", "nom", "pori", "regex", @@ -2541,45 +2194,13 @@ dependencies = [ "walkdir", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.52.0" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-core 0.52.0", - "windows-targets 0.52.6", + "windows-sys 0.59.0", ] [[package]] @@ -2805,47 +2426,78 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] -name = "workspace-node-tools" -version = "2.0.2" +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +dependencies = [ + "either", +] + +[[package]] +name = "ws-git" +version = "0.1.0" +dependencies = [ + "regex", + "serde", + "serde_json", + "thiserror", + "ws-std", +] + +[[package]] +name = "ws-monorepo" +version = "0.1.0" dependencies = [ "chrono", - "execute", "git-cliff-core", "icu", - "napi", - "napi-derive", - "petgraph", - "rand", + "itertools 0.13.0", "regex", "semver", "serde", "serde_json", - "vergen", + "thiserror", + "toml 0.5.11", "version-compare", "wax", + "ws-git", + "ws-pkg", + "ws-std", ] [[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +name = "ws-pkg" +version = "0.1.0" +dependencies = [ + "petgraph", + "regex", + "semver", + "serde", + "serde_json", + "thiserror", +] [[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +name = "ws-std" +version = "0.1.0" dependencies = [ - "either", + "serde", + "thiserror", ] [[package]] @@ -2855,12 +2507,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "yaml-rust2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8" dependencies = [ - "linked-hash-map", + "arraydeque", + "encoding_rs", + "hashlink", ] [[package]] @@ -2893,6 +2547,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] diff --git a/Cargo.toml b/Cargo.toml index af181e06..0acc654f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,46 +1,85 @@ -[package] -name = "workspace-node-tools" -version = "2.0.2" +[workspace] +members = ["crates/standard", "crates/git", "crates/pkg", "crates/monorepo"] +resolver = "2" + +[workspace.package] edition = "2021" -description = "Node workspace version tools" +homepage = "https://websublime.com/" +license = "MIT" repository = "https://github.com/websublime/workspace-node-tools" -license = "MIT OR Apache-2.0" -build = "build.rs" -resolver = "2" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -path = "src/lib.rs" - -[dependencies] -execute = "0.2.13" -serde = { version = "1.0.210", features = ["derive"] } -serde_json = "1.0.128" -regex = "1.10.6" -wax = { version = "0.6.0", features = ["walk"] } -napi-derive = { version = "2.16.12", optional = true } -napi = { version = "2.16.11", default-features = false, features = [ - "napi9", - "serde-json", - "tokio_rt", -], optional = true } -icu = "1.5.0" -version-compare = "0.2" -git-cliff-core = "2.6.0" -chrono = "0.4.38" -semver = "1.0.23" -rand = "0.8.5" -petgraph = "0.6.5" - -[build-dependencies] -vergen = { version = "8.3.2", features = [ - "build", - "cargo", - "git", - "gitcl", - "rustc", - "si", -] } - -[features] -customfeature = ["napi", "napi-derive"] +[profile.release-debug] +debug = true +inherits = "release" + +[workspace.lints.rust] + +[workspace.lints.clippy] +# Guidelines +# - We should only disable rules globally if they are either false positives, chaotic, or does not make sense. +# - Group are enabled with priority -1, so we could easily override some specific rules. +# - https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-lints-section + +# --- restriction https://doc.rust-lang.org/clippy/usage.html#clippyrestriction +dbg_macro = "deny" +print_stdout = "deny" +allow-dbg-in-tests = "allow" + +# I like the explicitness of this rule as it removes confusion around `clone`. +# This increases readability, avoids `clone` mindlessly and heap allocating on accident. +clone_on_ref_ptr = "deny" +empty_drop = "deny" +exit = "deny" +filetype_is_file = "deny" +get_unwrap = "deny" +rc_buffer = "deny" +rc_mutex = "deny" +rest_pat_in_fully_bound_structs = "deny" +unnecessary_safety_comment = "deny" + +# --- pedantic #https://doc.rust-lang.org/clippy/usage.html#clippypedantic +# To write the best rust code, pedantic group is enabled by default. +pedantic = { level = "deny", priority = -1 } + +# Wizards, naming is too hard. +module_inception = "allow" +module_name_repetitions = "allow" +similar_names = "allow" + +# Forwarding `Result` is a common pattern, this rule is too pedantic. +missing_errors_doc = "allow" + +# #[must_use] is creating too much noise for this codebase, it does not add much value except nagging +# the programmer to add a #[must_use] after clippy has been run. +# Having #[must_use] everywhere also hinders readability. +must_use_candidate = "allow" + +doc_markdown = "allow" +missing_const_for_fn = "allow" +needless_for_each = "allow" +new_without_default = "allow" +# TODO: should review this rule. +missing_panics_doc = "allow" + +# Order doesn't really matter https://rust-lang.github.io/rust-clippy/master/index.html#/inconsistent_struct_constructor +inconsistent_struct_constructor = "allow" + +# Single match is equally readable as if/else. https://rust-lang.github.io/rust-clippy/master/index.html#/single_match +single_match = "allow" +single_match_else = "allow" + +[workspace.dependencies] +#workspace_std = { version = "0.1.0", path = "./crates/workspace_std" } + +serde = "1.0.203" +serde_json = "1.0.117" +toml = "^0.5" +thiserror = "1.0.64" +regex = "1.11.0" + +[profile.release] +codegen-units = 1 +debug = false # Set to `true` for debug information +lto = "fat" +opt-level = 3 +strip = "symbols" # Set to `false` for debug information diff --git a/build.rs b/build.rs deleted file mode 100644 index 9a70373a..00000000 --- a/build.rs +++ /dev/null @@ -1,14 +0,0 @@ -use vergen::EmitBuilder; - -fn main() { - if let Err(error) = EmitBuilder::builder() - .all_build() - .all_git() - .all_cargo() - .all_rustc() - .all_sysinfo() - .emit() - { - println!("Error: {error:?}"); - } -} diff --git a/cliff-d.toml b/cliff-d.toml deleted file mode 100644 index 43b49c67..00000000 --- a/cliff-d.toml +++ /dev/null @@ -1,110 +0,0 @@ -# git-cliff ~ configuration file -# https://git-cliff.org/docs/configuration -# -# Lines starting with "#" are comments. -# Configuration options are organized into tables and keys. -# See documentation for more information on available options. - -[changelog] -# template for the changelog footer -header = """ -## What's Changed\n -""" -# template for the changelog body -# https://keats.github.io/tera/docs/#introduction -body = """ -{%- macro remote_url() -%} - -{%- endmacro -%} - -{% macro print_commit(commit) -%} - - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ - {% if commit.breaking %}[**breaking**] {% endif %}\ - {{ commit.message | upper_first }} - \ - ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\ -{% endmacro -%} - -{% if version %}\ - {% if previous.version %}\ - ## [{{ version | trim_start_matches(pat="v") }}]\ - ({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} - {% else %}\ - ## [{{ version | trim_start_matches(pat="v") }}] - {{ now() | date(format="%Y-%m-%d") }} - {% endif %}\ -{% else %}\ - ## [unreleased] -{% endif %}\ - -{% for group, commits in commits | group_by(attribute="group") %} - ### {{ group | striptags | trim | upper_first }} - {% for commit in commits - | filter(attribute="scope") - | sort(attribute="scope") %} - {{ self::print_commit(commit=commit) }} - {%- endfor -%} - {% raw %}\n{% endraw %}\ - {%- for commit in commits %} - {%- if not commit.scope -%} - {{ self::print_commit(commit=commit) }} - {% endif -%} - {% endfor -%} -{% endfor %}\n -""" -# template for the changelog footer -footer = """ --- Total Releases: {{ releases | length }} -- -""" -# remove the leading and trailing whitespace from the templates -trim = true -# postprocessors -postprocessors = [ - { pattern = '', replace = "https://github.com/websublime/workspace-node-tools" }, # replace repository URL -] - -[git] -# parse the commits based on https://www.conventionalcommits.org -conventional_commits = true -# filter out the commits that are not conventional -filter_unconventional = true -# process each line of a commit as an individual commit -split_commits = false -# regex for preprocessing the commit messages -commit_preprocessors = [ - { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))" }, - # Check spelling of the commit with https://github.com/crate-ci/typos - # If the spelling is incorrect, it will be automatically fixed. - { pattern = '.*', replace_command = 'typos --write-changes -' }, -] -# regex for parsing and grouping commits -commit_parsers = [ - { message = "^feat", group = "โ›ฐ๏ธ Features" }, - { message = "^fix", group = "๐Ÿ› Bug Fixes" }, - { message = "^doc", group = "๐Ÿ“š Documentation" }, - { message = "^perf", group = "โšก Performance" }, - { message = "^refactor\\(clippy\\)", skip = true }, - { message = "^refactor", group = "๐Ÿšœ Refactor" }, - { message = "^style", group = "๐ŸŽจ Styling" }, - { message = "^test", group = "๐Ÿงช Testing" }, - { message = "^chore\\(release\\): prepare for", skip = true }, - { message = "^chore\\(deps.*\\)", skip = true }, - { message = "^chore\\(pr\\)", skip = true }, - { message = "^chore\\(pull\\)", skip = true }, - { message = "^chore\\(npm\\).*yarn\\.lock", skip = true }, - { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, - { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, - { message = "^revert", group = "โ—€๏ธ Revert" }, -] -# protect breaking changes from being skipped due to matching a skipping commit_parser -protect_breaking_commits = false -# filter out the commits that are not matched by commit parsers -filter_commits = false -# regex for matching git tags -tag_pattern = "v[0-9].*" -# regex for skipping tags -skip_tags = "beta|alpha" -# regex for ignoring tags -ignore_tags = "rc|v2.1.0|v2.1.1" -# sort the tags topologically -topo_order = false -# sort the commits inside sections by oldest/newest order -sort_commits = "newest" diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml new file mode 100644 index 00000000..0237336e --- /dev/null +++ b/crates/git/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "ws-git" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lib] +doctest = false + +[lints] +#workspace = false +clippy.dbg_macro = "allow" + +[dependencies] +ws-std = { version = "~0.1.0", path = "../standard" } +thiserror = { workspace = true } +serde_json = { workspace = true } +serde = { workspace = true, features = ["derive"] } +regex = { workspace = true } diff --git a/crates/git/src/error.rs b/crates/git/src/error.rs new file mode 100644 index 00000000..37fd9fb3 --- /dev/null +++ b/crates/git/src/error.rs @@ -0,0 +1,27 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum RepositoryError { + #[error("Fail to initialize git repo")] + InitializeFailure, + #[error("Fail to execute command")] + CommandFailure(#[from] ws_std::error::CommandError), + #[error("Failure from IO entry")] + IoFailure(#[from] std::io::Error), + #[error("Fail to config git user name")] + ConfigUsernameFailure, + #[error("Fail to config git user email")] + ConfigEmailFailure, + #[error("Fail to create branch")] + BranchCreationFailure, + #[error("Fail to checkout branch")] + BranchCheckoutFailure, + #[error("Fail to merge branch")] + BranchMergeFailure, + #[error("Fail to add all files")] + AddAllFailure, + #[error("Fail to add file")] + AddFailure, + #[error("Fail to fetch remote")] + FetchFailure, +} diff --git a/crates/git/src/lib.rs b/crates/git/src/lib.rs new file mode 100644 index 00000000..8f9da32b --- /dev/null +++ b/crates/git/src/lib.rs @@ -0,0 +1,2 @@ +pub mod error; +pub mod repo; diff --git a/crates/git/src/repo.rs b/crates/git/src/repo.rs new file mode 100644 index 00000000..94299529 --- /dev/null +++ b/crates/git/src/repo.rs @@ -0,0 +1,663 @@ +use regex::Regex; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + env::temp_dir, + fs::{canonicalize, remove_file, File}, + io::Write, + path::{Path, PathBuf}, +}; +use ws_std::command::execute; + +use super::error::RepositoryError; + +#[allow(clippy::from_over_into)] +impl Into> for RepositoryTags { + fn into(self) -> HashMap { + let mut map = HashMap::new(); + map.insert("hash".to_string(), self.hash); + map.insert("tag".to_string(), self.tag); + map + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct RepositoryPublishTagInfo { + pub hash: String, + pub tag: String, + pub package: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct RepositoryTags { + pub hash: String, + pub tag: String, +} + +#[allow(clippy::from_over_into)] +impl Into> for RepositoryCommit { + fn into(self) -> HashMap { + let mut map = HashMap::new(); + map.insert("hash".to_string(), self.hash); + map.insert("author_name".to_string(), self.author_name); + map.insert("author_email".to_string(), self.author_email); + map.insert("author_date".to_string(), self.author_date); + map.insert("message".to_string(), self.message); + map + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct RepositoryCommit { + pub hash: String, + pub author_name: String, + pub author_email: String, + pub author_date: String, + pub message: String, +} + +impl From<&str> for Repository { + fn from(root: &str) -> Self { + let path_buff = PathBuf::from(root); + let repo_path = &canonicalize(Path::new(path_buff.as_os_str())).expect("Invalid path"); + + Repository { location: repo_path.clone() } + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Repository { + location: PathBuf, +} + +impl Repository { + pub fn new(location: &Path) -> Self { + let root = canonicalize(location.as_os_str()).expect("Invalid path"); + Self { location: root } + } + + pub fn get_repo_path(&self) -> &Path { + &self.location + } + + pub fn init( + &self, + initial_branch: &str, + username: &str, + email: &str, + ) -> Result { + let inited = execute( + "git", + self.location.as_path(), + ["init", "--initial-branch", initial_branch], + |_, output| Ok(output.status.success()), + )?; + + if !inited { + return Err(RepositoryError::InitializeFailure); + } + + let configed = self.config(username, email)?; + + Ok(inited && configed) + } + + pub fn config(&self, username: &str, email: &str) -> Result { + let user_config = execute( + "git", + self.location.as_path(), + ["config", "user.name", username], + |_, output| Ok(output.status.success()), + )?; + + if !user_config { + return Err(RepositoryError::ConfigUsernameFailure); + } + + let email_config = execute( + "git", + self.location.as_path(), + ["config", "user.email", email], + |_, output| Ok(output.status.success()), + )?; + + if !email_config { + return Err(RepositoryError::ConfigEmailFailure); + } + + let clrf_config = execute( + "git", + self.location.as_path(), + ["config", "core.safecrlf", "true"], + |_, output| Ok(output.status.success()), + )?; + + let autocrlf_config = execute( + "git", + self.location.as_path(), + ["config", "core.autocrlf", "input"], + |_, output| Ok(output.status.success()), + )?; + + let filemode_config = execute( + "git", + self.location.as_path(), + ["config", "core.filemode", "false"], + |_, output| Ok(output.status.success()), + )?; + + Ok(user_config && email_config && clrf_config && autocrlf_config && filemode_config) + } + + pub fn is_vcs(&self) -> Result { + Ok(execute( + "git", + self.location.as_path(), + ["rev-parse", "--is-inside-work-tree"], + |stdout, _| Ok(stdout.trim() == "true"), + )?) + } + + pub fn create_branch(&self, branch_name: &str) -> Result { + let branch_created = execute( + "git", + self.location.as_path(), + ["checkout", "-b", branch_name], + |_, output| Ok(output.status.success()), + )?; + + if !branch_created { + return Err(RepositoryError::BranchCreationFailure); + } + + Ok(branch_created) + } + + pub fn list_branches(&self) -> Result { + let branches = execute( + "git", + self.location.as_path(), + ["--no-pager", "branch", "-a"], + |message, _| Ok(message.to_string()), + )?; + + Ok(branches) + } + + pub fn list_config(&self, config_type: &str) -> Result { + let list = execute( + "git", + self.location.as_path(), + ["--no-pager", "config", "--list", format!("--{config_type}").as_str()], + |stdout, _| Ok(stdout.to_string()), + )?; + + Ok(list) + } + + pub fn checkout(&self, branch_name: &str) -> Result { + let branch_checkouted = + execute("git", self.location.as_path(), ["checkout", branch_name], |_, output| { + Ok(output.status.success()) + })?; + + if !branch_checkouted { + return Err(RepositoryError::BranchCheckoutFailure); + } + + Ok(branch_checkouted) + } + + pub fn log(&self, target: Option) -> Result { + let mut args: Vec = vec!["--no-pager".to_string(), "log".to_string()]; + + if let Some(target_branch) = target { + args.push(target_branch); + } + + let args_ref: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); + + let log = execute("git", self.location.as_path(), args_ref, |stdout, _| { + Ok(stdout.trim().to_string()) + })?; + + Ok(log) + } + + pub fn diff(&self, diff: Option) -> Result { + let diff = match diff { + Some(diff) => diff, + None => ".".to_string(), + }; + + let diff = execute( + "git", + self.location.as_path(), + ["--no-pager", "diff", diff.as_str()], + |stdout, _| Ok(stdout.to_string()), + )?; + + Ok(diff) + } + + pub fn merge(&self, branch_name: &str) -> Result { + let merged = + execute("git", self.location.as_path(), ["merge", branch_name], |_, output| { + Ok(output.status.success()) + })?; + + if !merged { + return Err(RepositoryError::BranchMergeFailure); + } + + Ok(merged) + } + + pub fn add_all(&self) -> Result { + let add_all = + execute("git", self.location.as_path(), ["add", "--all", "--verbose"], |_, output| { + Ok(output.status.success()) + })?; + let renormalize = execute( + "git", + self.location.as_path(), + ["add", "--all", "--renormalize"], + |_, output| Ok(output.status.success()), + )?; + + if !add_all || !renormalize { + return Err(RepositoryError::AddAllFailure); + } + + Ok(add_all && renormalize) + } + + pub fn add(&self, path: &Path) -> Result { + if path.exists() { + let add = execute( + "git", + self.location.as_path(), + ["add", path.to_str().expect("Failed to convert path to str"), "--verbose"], + |_, output| Ok(output.status.success()), + )?; + + let renormalize = execute( + "git", + self.location.as_path(), + ["add", path.to_str().expect("Failed to convert path to str"), "--renormalize"], + |_, output| Ok(output.status.success()), + )?; + + if !add || !renormalize { + return Err(RepositoryError::AddFailure); + } + + Ok(add && renormalize) + } else { + Ok(false) + } + } + + pub fn fetch_all(&self, fetch_tags: Option) -> Result { + let mut args = vec!["fetch", "origin"]; + + if fetch_tags.unwrap_or(false) { + args.push("--tags"); + args.push("--force"); + } + + let fetched = + execute("git", self.location.as_path(), args, |_, output| Ok(output.status.success()))?; + + if !fetched { + return Err(RepositoryError::FetchFailure); + } + + Ok(fetched) + } + + pub fn get_diverged_commit(&self, sha: &str) -> Result { + let commit = + execute("git", self.location.as_path(), ["merge-base", sha, "HEAD"], |stdout, _| { + Ok(stdout.to_string()) + })?; + + Ok(commit) + } + + pub fn get_current_sha(&self) -> Result { + let commit = execute( + "git", + self.location.as_path(), + ["rev-parse", "--short", "HEAD"], + |stdout, _| Ok(stdout.to_string()), + )?; + + Ok(commit) + } + + pub fn get_previous_sha(&self) -> Result { + let commit = execute( + "git", + self.location.as_path(), + ["rev-parse", "--short", "HEAD~1"], + |stdout, _| Ok(stdout.to_string()), + )?; + + Ok(commit) + } + + pub fn get_first_sha(&self, branch: Option) -> Result { + let branch = match branch { + Some(branch) => branch, + None => String::from("main"), + }; + + #[cfg(not(windows))] + let commit = execute( + "sh", + self.location.as_path(), + [ + "-c", + format!("git log --oneline {}..HEAD --pretty=format:%h | tail -1", branch).as_str(), + ], + |stdout, _| Ok(stdout.to_string()), + )?; + + #[cfg(windows)] + let commit = execute( + "cmd", + &self.location, + [ + "/C", + format!("git log --oneline {}..HEAD --pretty=format:%h | findstr /R /C:^^", branch) + .as_str(), + ], + |stdout, _| { + let output = stdout.to_string(); + let cmd_out = output + .lines() + .filter_map(|line| Some(line)) + .last() + .expect("Failed to get last line"); + Ok(cmd_out.to_string()) + }, + )?; + + Ok(commit) + } + + pub fn status(&self) -> Result, RepositoryError> { + let status = + execute("git", self.location.as_path(), ["status", "--porcelain"], |stdout, _| { + if stdout.is_empty() { + Ok(None) + } else { + Ok(Some(stdout.to_string())) + } + })?; + + Ok(status) + } + + pub fn get_current_branch(&self) -> Result, RepositoryError> { + let current_branch = execute( + "git", + self.location.as_path(), + ["rev-parse", "--abbrev-ref", "HEAD"], + |stdout, _| { + if stdout.is_empty() { + Ok(None) + } else { + Ok(Some(stdout.to_string())) + } + }, + )?; + + Ok(current_branch) + } + + pub fn get_branch_from_commit(&self, sha: &str) -> Result, RepositoryError> { + let branch = execute( + "git", + self.location.as_path(), + [ + "--no-pager", + "branch", + "--no-color", + "--no-column", + "--format", + r#"%(refname:lstrip=2)"#, + "--contains", + sha, + ], + |stdout, _| { + if stdout.is_empty() { + Ok(None) + } else { + Ok(Some(stdout.to_string())) + } + }, + )?; + + Ok(branch) + } + + pub fn tag(&self, tag: &str, message: Option) -> Result { + let msg = message.unwrap_or(tag.to_string()); + + Ok(execute( + "git", + self.location.as_path(), + ["tag", "-a", tag, "-m", msg.as_str()], + |_, output| Ok(output.status.success()), + )?) + } + + pub fn push(&self, follow_tags: Option) -> Result { + let mut args = vec!["push", "--no-verify"]; + + if follow_tags.unwrap_or(false) { + args.push("--follow-tags"); + } + + Ok(execute("git", self.location.as_path(), args, |_, output| Ok(output.status.success()))?) + } + + pub fn commit( + &self, + message: &str, + body: Option, + footer: Option, + ) -> Result { + let mut msg = message.to_string(); + let root = self.location.as_path(); + + if let Some(body) = body { + msg.push_str("\n\n"); + msg.push_str(body.as_str()); + } + + if let Some(footer) = footer { + msg.push_str("\n\n"); + msg.push_str(footer.as_str()); + } + + let temp_dir = temp_dir(); + let temp_file_path = temp_dir.join("commit_message.txt"); + + let mut file = File::create(temp_file_path.as_path())?; + file.write_all(message.as_bytes())?; + + let file_path = temp_file_path.as_path(); + + Ok(execute( + "git", + root, + [ + "commit", + "-F", + file_path.as_os_str().to_str().expect("Failed to convert path to string"), + "--no-verify", + ], + |_, output| { + remove_file(file_path).expect("Commit file not deleted"); + + Ok(output.status.success()) + }, + )?) + } + + pub fn get_all_files_changed_since_sha( + &self, + sha: &str, + ) -> Result, RepositoryError> { + Ok(execute( + "git", + self.location.as_path(), + ["--no-pager", "diff", "--name-only", sha, "HEAD"], + |stdout, output| { + if !output.status.success() { + return Ok(vec![]); + } + + Ok(stdout + .split('\n') + .filter(|item| !item.trim().is_empty()) + .map(|item| self.location.join(item)) + .filter(|item| item.exists()) + .map(|item| { + item.to_str().expect("Failed to convert path to string").to_string() + }) + .collect::>()) + }, + )?) + } + + #[allow(clippy::uninlined_format_args)] + pub fn get_commits_since( + &self, + since: Option, + relative: Option, + ) -> Result, RepositoryError> { + const DELIMITER: &str = r"#=#"; + const BREAK_LINE: &str = r"#+#"; + + let log_format = format!( + "--format={}%H{}%an{}%ae{}%ad{}%B{}", + DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER, BREAK_LINE + ); + + let mut args = vec!["--no-pager", "log", log_format.as_str(), "--date=rfc2822"]; + let mut owned_strings = Vec::new(); + + if let Some(since) = since.as_deref() { + owned_strings.push(format!("{}..", since)); + args.push(owned_strings.last().unwrap().as_str()); + } + + if let Some(relative) = relative.as_deref() { + args.push("--"); + args.push(relative); + } + + Ok(execute("git", self.location.as_path(), args, |stdout, output| { + if !output.status.success() { + return Ok(vec![]); + } + + Ok(stdout + .split(BREAK_LINE) + .filter(|item| !item.trim().is_empty()) + .map(|item| { + let item_trimmed = item.trim(); + let items = item_trimmed.split(DELIMITER).collect::>(); + + RepositoryCommit { + hash: items[1].to_string(), + author_name: items[2].to_string(), + author_email: items[3].to_string(), + author_date: items[4].to_string(), + message: items[5].to_string(), + } + }) + .collect::>()) + })?) + } + + #[allow(clippy::items_after_statements)] + pub fn get_remote_or_local_tags( + &self, + local: Option, + ) -> Result, RepositoryError> { + let mut args = vec![]; + let regex = Regex::new(r"\s+").expect("Failed to create regex"); + + match local { + Some(true) => { + args.push("show-ref"); + args.push("--tags"); + } + Some(false) | None => { + args.push("ls-remote"); + args.push("--tags"); + args.push("origin"); + } + } + + Ok(execute("git", self.location.as_path(), args, |stdout, output| { + if !output.status.success() { + return Ok(vec![]); + } + + #[cfg(windows)] + const LINE_ENDING: &str = "\r\n"; + #[cfg(not(windows))] + const LINE_ENDING: &str = "\n"; + + Ok(stdout + .trim() + .split(LINE_ENDING) + .filter(|tags| !tags.trim().is_empty()) + .map(|tags| { + let hash_tags = regex.split(tags).collect::>(); + + RepositoryTags { hash: hash_tags[0].to_string(), tag: hash_tags[1].to_string() } + }) + .collect::>()) + })?) + } + + pub fn get_all_files_changed_since_branch( + &self, + packages_paths: &[String], + branch: &str, + ) -> Result, RepositoryError> { + let mut all_files = vec![]; + + for item in packages_paths { + let files = self.get_all_files_changed_since_sha(branch)?; + + let pkg_files = files + .iter() + .filter(|file| { + let file_path_buf = PathBuf::from(file); + let file_canonic = &canonicalize(file_path_buf).expect("Invalid file path"); + let file = file_canonic.to_str().expect("Failed to convert path to string"); + + let item_path_buf = PathBuf::from(item); + let item_canonic = &canonicalize(item_path_buf).expect("Invalid item path"); + let item = item_canonic.to_str().expect("Failed to convert path to string"); + + file.starts_with(item) + }) + .collect::>(); + + all_files.append( + &mut pkg_files.iter().map(|file| (*file).to_string()).collect::>(), + ); + } + + Ok(all_files) + } +} diff --git a/crates/git/tests/git_repo.rs b/crates/git/tests/git_repo.rs new file mode 100644 index 00000000..38ac3a07 --- /dev/null +++ b/crates/git/tests/git_repo.rs @@ -0,0 +1,725 @@ +#[cfg(test)] +mod repo_tests { + #[cfg(not(windows))] + use std::fs::set_permissions; + #[cfg(not(windows))] + use std::os::unix::fs::PermissionsExt; + use std::{ + env::temp_dir, + fs::{canonicalize, create_dir, remove_dir_all, File}, + io::Write, + path::PathBuf, + }; + use ws_git::{error::RepositoryError, repo::Repository}; + use ws_std::command::execute; + + fn create_workspace() -> Result { + let temp_dir = temp_dir(); + let monorepo_root_dir = temp_dir.join("monorepo-workspace"); + + if monorepo_root_dir.exists() { + remove_dir_all(&monorepo_root_dir)?; + } + + create_dir(&monorepo_root_dir)?; + + let mut readme_file = File::create(monorepo_root_dir.join("README.md").as_path())?; + readme_file.write_all(b"HELLO WORLD")?; + + #[cfg(not(windows))] + set_permissions(&monorepo_root_dir, std::fs::Permissions::from_mode(0o777))?; + + Ok(monorepo_root_dir) + } + + fn create_monorepo() -> Result { + let monorepo_root_dir = create_workspace()?; + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.init("main", "Sublime Machine", "machine@websublime.dev")?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["remote", "add", "origin", monorepo_root_dir.to_str().unwrap()], + |_, stdout| Ok(stdout.status.success()), + )?; + + execute("git", monorepo_root_dir.as_path(), ["add", "."], |_, stdout| { + Ok(stdout.status.success()) + })?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: init project"], + |_, stdout| Ok(stdout.status.success()), + )?; + + Ok(monorepo_root_dir) + } + + #[test] + fn test_repo_path() -> Result<(), std::io::Error> { + let monorepo_root_dir = create_workspace()?; + let root_dir = canonicalize(monorepo_root_dir.as_os_str())?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + + assert_eq!(repo.get_repo_path(), root_dir.as_path()); + + remove_dir_all(&root_dir)?; + + Ok(()) + } + + #[test] + fn test_init_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_workspace()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + let inited = repo.init("main", "Sublime Machine", "machine@websublime.dev")?; + + assert!(inited); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_config_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_workspace()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + let inited = repo.init("main", "Sublime Machine", "machine@websublime.dev")?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["remote", "add", "origin", monorepo_root_dir.to_str().unwrap()], + |_, stdout| Ok(stdout.status.success()), + )?; + + let message = execute("git", repo.get_repo_path(), ["config", "--list"], |message, _| { + Ok(message.to_string()) + })?; + + let has_username = message.contains("user.name=Sublime Machine"); + let has_email = message.contains("user.email=machine@websublime.dev"); + + assert!(inited); + assert!(has_username); + assert!(has_email); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_vcs_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_workspace()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.init("main", "Sublime Machine", "machine@websublime.dev")?; + let is_vcs = repo.is_vcs()?; + + assert!(is_vcs); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_create_branch_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_workspace()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.init("main", "Sublime Machine", "machine@websublime.dev")?; + let branch_created = repo.create_branch("feature/awesome")?; + + assert!(branch_created); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_checkout_branch_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + let checkouted = repo.checkout("main")?; + + let branches = repo.list_branches()?; + + assert!(checkouted); + assert!(branches.contains("main")); + assert!(branches.contains("feature/awesome")); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_list_branch_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let branches = repo.list_branches()?; + + assert!(branches.contains("main")); + assert!(branches.contains("feature/awesome")); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_log_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let mut main_file = File::create(monorepo_root_dir.join("main.mjs").as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + execute("git", monorepo_root_dir.as_path(), ["add", "."], |_, stdout| { + Ok(stdout.status.success()) + })?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add main.js"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let logs = repo.log(Some(String::from("main..HEAD")))?; + + assert!(logs.contains("chore: add main.js")); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_diff_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + execute("git", monorepo_root_dir.as_path(), ["add", "."], |_, stdout| { + Ok(stdout.status.success()) + })?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add main.js"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let diff_branch = repo.diff(Some(main_file_path.to_str().unwrap().to_string()))?; + + #[cfg(not(windows))] + assert!(diff_branch.is_empty()); + + #[cfg(windows)] + assert!(!diff_branch.is_empty()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_list_config_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + let config = repo.list_config("local")?; + + assert!(!config.is_empty()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_merge_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + execute("git", monorepo_root_dir.as_path(), ["add", "."], |_, stdout| { + Ok(stdout.status.success()) + })?; + execute( + "git", + monorepo_root_dir.as_path(), + ["add", "--all", "--renormalize"], + |_, output| Ok(output.status.success()), + )?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add main.mjs"], + |_, stdout| Ok(stdout.status.success()), + )?; + + repo.checkout("main")?; + repo.merge("feature/awesome")?; + + let logs = repo.log(None)?; + + assert!(logs.contains("chore: add main.mjs")); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_add_all_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + let main_file_path = monorepo_root_dir.join("utils.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const add = (value) => value + value;")?; + + let added = repo.add_all()?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add all files"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let logs = repo.log(Some(String::from("main..HEAD")))?; + + assert!(logs.contains("chore: add all files")); + assert!(added); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_add_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + let added = repo.add(main_file_path.as_path())?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add single file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let logs = repo.log(Some(String::from("main..HEAD")))?; + + assert!(logs.contains("chore: add single file")); + assert!(added); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_current_sha_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add(main_file_path.as_path())?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add single file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let sha = repo.get_current_sha()?; + + assert!(!sha.is_empty()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_previous_sha_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add(main_file_path.as_path())?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add single file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let sha = repo.get_previous_sha()?; + + assert!(!sha.is_empty()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + #[cfg_attr(target_os = "windows", ignore)] + fn test_first_sha_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add(main_file_path.as_path())?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add single file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let sha = repo.get_first_sha(Some(String::from("main")))?; + + assert!(!sha.is_empty()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_diverged_commit_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add main.mjs file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let sha = repo.get_diverged_commit("main")?; + + assert!(!sha.is_empty()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_status_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + + let status = repo.status()?; + + assert!(status.is_some()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_current_branch_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add main.mjs file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let branch = repo.get_current_branch()?; + + assert!(branch.is_some()); + assert_eq!(branch.unwrap(), "feature/awesome"); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_branch_from_commit_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add main.mjs file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + let sha = repo.get_current_sha()?; + let branch = repo.get_branch_from_commit(sha.as_str())?; + + assert!(branch.is_some()); + assert_eq!(branch.unwrap(), "feature/awesome"); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_tag_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add main.mjs file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + repo.checkout("main")?; + repo.merge("feature/awesome")?; + + let tagged = repo.tag("@scope/awesome@1.0.0", Some(String::from("feat: 1.0.0 version")))?; + + assert!(tagged); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_commit_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + let commited = repo.commit("chore: add main.mjs file", None, None)?; + let logs = repo.log(Some(String::from("main..HEAD")))?; + + assert!(commited); + assert!(logs.contains("chore: add main.mjs file")); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_all_files_changed_since_sha_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + repo.commit("chore: add main.mjs file", None, None)?; + let changes = repo.get_all_files_changed_since_sha("main")?; + + assert!(!changes.is_empty()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + fn test_commits_since_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + repo.commit("chore: add main.mjs file", None, None)?; + let commits = repo.get_commits_since(Some("main".to_string()), None)?; + + assert!(!commits.is_empty()); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + #[allow(clippy::len_zero)] + fn test_local_remote_tag_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + + execute( + "git", + monorepo_root_dir.as_path(), + ["commit", "-m", "chore: add main.mjs file"], + |_, stdout| Ok(stdout.status.success()), + )?; + + repo.checkout("main")?; + repo.merge("feature/awesome")?; + + repo.tag("@scope/awesome@1.0.0", Some(String::from("feat: 1.0.0 version")))?; + let tags = repo.get_remote_or_local_tags(Some(true))?; + + assert!(tags.len() > 0); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } + + #[test] + #[allow(clippy::len_zero)] + fn test_all_files_changed_since_branch_repo() -> Result<(), RepositoryError> { + let monorepo_root_dir = create_monorepo()?; + let pkg_dir = monorepo_root_dir.display().to_string(); + + let repo = Repository::new(monorepo_root_dir.as_path()); + repo.create_branch("feature/awesome")?; + + let main_file_path = monorepo_root_dir.join("main.mjs"); + let mut main_file = File::create(main_file_path.as_path())?; + main_file.write_all(b"const msg = 'Hello';")?; + + repo.add_all()?; + repo.commit("chore: add main.mjs file", None, None)?; + + let files = repo.get_all_files_changed_since_branch(&[pkg_dir], "main")?; + + assert!(files.len() > 0); + + remove_dir_all(&monorepo_root_dir)?; + + Ok(()) + } +} diff --git a/crates/monorepo/Cargo.toml b/crates/monorepo/Cargo.toml new file mode 100644 index 00000000..d226a2d7 --- /dev/null +++ b/crates/monorepo/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "ws-monorepo" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lib] +doctest = false + +[lints] +workspace = true + +[dependencies] +ws-std = { version = "~0.1.0", path = "../standard" } +ws-git = { version = "~0.1.0", path = "../git" } +ws-pkg = { version = "~0.1.0", path = "../pkg" } +thiserror = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +semver = { version = "1.0.23", features = ["serde"] } +regex = { workspace = true } +git-cliff-core = "2.6.1" +toml = "^0.5" +itertools = "0.13.0" +wax = { version = "0.6.0", features = ["walk"] } +chrono = "0.4.38" +icu = "1.5.0" +version-compare = "0.2" diff --git a/crates/monorepo/src/changes.rs b/crates/monorepo/src/changes.rs new file mode 100644 index 00000000..d7753bcd --- /dev/null +++ b/crates/monorepo/src/changes.rs @@ -0,0 +1,248 @@ +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use std::{ + collections::BTreeMap, + fs::File, + io::{BufReader, BufWriter}, + path::{Path, PathBuf}, +}; +use ws_git::repo::Repository; + +use crate::config::{get_workspace_config, WorkspaceConfig}; + +type ChangesData = BTreeMap; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Change { + pub package: String, + pub release_as: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ChangeMeta { + pub deploy: Vec, + pub pkgs: Vec, +} + +#[derive(Debug, Clone)] +pub struct Changes { + root: PathBuf, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ChangesConfig { + pub message: Option, + pub git_user_name: Option, + pub git_user_email: Option, + pub changes: ChangesData, +} + +impl From<&WorkspaceConfig> for Changes { + fn from(config: &WorkspaceConfig) -> Self { + Changes { root: config.workspace_root.clone() } + } +} + +impl From<&PathBuf> for Changes { + fn from(root: &PathBuf) -> Self { + Changes { root: root.clone() } + } +} + +impl Changes { + pub fn new(root: &Path) -> Self { + Changes { root: root.to_path_buf() } + } + + pub fn file_exist(&self) -> bool { + let root_path = Path::new(self.root.as_os_str()); + let changes_path = &root_path.join(String::from(".changes.json")); + + changes_path.exists() + } + + pub fn init(&self) -> ChangesConfig { + if self.file_exist() { + let changes_config = self.read_changes().expect("Failed to read changes file"); + + return changes_config; + } + + let config = get_workspace_config(Some(self.root.clone())); + let message = config.changes_config.get("message").expect("Failed to get message changes"); + let git_user_name = config + .changes_config + .get("git_user_name") + .expect("Failed to get git_user_name changes"); + let git_user_email = config + .changes_config + .get("git_user_email") + .expect("Failed to get git_user_email changes"); + + let changes = ChangesConfig { + message: Some(message.to_string()), + git_user_name: Some(git_user_name.to_string()), + git_user_email: Some(git_user_email.to_string()), + changes: ChangesData::new(), + }; + + self.write_changes(&changes); + + changes + } + + fn read_changes(&self) -> Option { + let root_path = Path::new(self.root.as_os_str()); + let changes_path = &root_path.join(String::from(".changes.json")); + + if self.file_exist() { + let changes_file = File::open(changes_path).expect("Failed to open changes file"); + let changes_reader = BufReader::new(changes_file); + + let changes_config: ChangesConfig = + serde_json::from_reader(changes_reader).expect("Failed to parse changes json file"); + + return Some(changes_config); + } + + None + } + + fn write_changes(&self, changes: &ChangesConfig) { + let root_path = Path::new(self.root.as_os_str()); + let changes_path = &root_path.join(String::from(".changes.json")); + + let changes_file = File::create(changes_path).expect("Failed to create changes file"); + let changes_writer = BufWriter::new(changes_file); + + serde_json::to_writer_pretty(changes_writer, changes) + .expect("Failed to write changes file"); + } + + pub fn add(&self, change: &Change, deploy_envs: Option>) -> bool { + if self.file_exist() { + let mut changes_config = self.read_changes().expect("Failed to read changes file"); + let current_branch = Repository::new(&self.root) + .get_current_branch() + .expect("Failed to get current branch"); + + let branch = match current_branch { + Some(branch) => branch, + None => String::from("main"), + }; + + let envs = &deploy_envs.unwrap_or_default(); + + changes_config + .changes + .entry(branch) + .and_modify(|entry| { + let pkg_exist = entry.pkgs.iter().any(|pkg| pkg.package == change.package); + + if !pkg_exist { + entry.deploy.extend(envs.clone()); + entry.deploy = + entry.deploy.clone().into_iter().unique().collect::>(); + entry.pkgs.push(change.clone()); + } + }) + .or_insert(ChangeMeta { deploy: envs.clone(), pkgs: vec![change.clone()] }); + + self.write_changes(&changes_config); + + return true; + } + + false + } + + pub fn remove(&self, branch_name: &str) -> bool { + if self.file_exist() { + let mut changes_config = self.read_changes().expect("Failed to read changes file"); + + if changes_config.changes.contains_key(branch_name) { + changes_config.changes.remove(branch_name); + + self.write_changes(&changes_config); + + return true; + } + } + + false + } + + pub fn changes(&self) -> ChangesData { + if self.file_exist() { + let changes_config = self.read_changes().expect("Failed to read changes file"); + + return changes_config.changes; + } + + ChangesData::new() + } + + pub fn changes_by_branch(&self, branch: &str) -> Option { + if self.file_exist() { + let changes_config = self.read_changes().expect("Failed to read changes file"); + + if changes_config.changes.contains_key(branch) { + return changes_config.changes.get(branch).cloned(); + } + + return None; + } + + None + } + + pub fn changes_by_package(&self, package_name: &str, branch: &str) -> Option { + if self.file_exist() { + let changes_config = self.read_changes().expect("Failed to read changes file"); + + if changes_config.changes.contains_key(branch) { + let branch_changes = + changes_config.changes.get(branch).expect("Failed to get branch changes"); + + let package_change = branch_changes + .pkgs + .clone() + .into_iter() + .find(|change| change.package == package_name); + + if let Some(change) = package_change { + return Some(change); + } + + return None; + } + + return None; + } + + None + } + + pub fn exist(&self, branch: &str, package_name: &str) -> bool { + if self.file_exist() { + let changes_config = self.read_changes().expect("Failed to read changes file"); + + if changes_config.changes.contains_key(branch) { + let branch_changes = + changes_config.changes.get(branch).expect("Failed to get branch changes"); + + let existing_packages_changes = branch_changes + .pkgs + .iter() + .map(|change| change.package.to_string()) + .collect::>(); + + return existing_packages_changes.iter().any(|pkg| pkg == package_name); + } + + return false; + } + + false + } +} diff --git a/crates/monorepo/src/config.rs b/crates/monorepo/src/config.rs new file mode 100644 index 00000000..5070f9e8 --- /dev/null +++ b/crates/monorepo/src/config.rs @@ -0,0 +1,263 @@ +use super::changes::ChangesConfig; +use git_cliff_core::config::{ + Bump, ChangelogConfig, CommitParser, Config, GitConfig, RemoteConfig, TextProcessor, +}; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::fs::canonicalize; +use std::fs::File; +use std::io::BufReader; +use std::io::Read; +use std::path::{Path, PathBuf}; +use ws_std::manager::{detect_package_manager, CorePackageManager}; +use ws_std::paths::get_project_root_path; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ToolsConfig { + pub tools: ToolsConfigGroup, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ToolsConfigGroup { + pub bump_sync: Option, + pub repo_url: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct WorkspaceConfig { + pub package_manager: CorePackageManager, + pub workspace_root: PathBuf, + pub changes_config: HashMap, + pub cliff_config: Config, + pub tools_config: ToolsConfig, +} + +#[allow(clippy::too_many_lines)] +fn get_cliff_config(root: &PathBuf) -> Config { + let default_cliff_config = Config { + bump: Bump::default(), + remote: RemoteConfig { ..RemoteConfig::default() }, + changelog: ChangelogConfig { + header: Some(String::from("# What's Changed")), + body: Some(String::from( + r#" + {%- macro remote_url() -%} + + {%- endmacro -%} + + {% macro print_commit(commit) -%} + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}{% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }} - ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }})) + {% endmacro -%} + + {% if version %} + {% if previous.version %} + ## [{{ version | trim_start_matches(pat="v") }}] + ({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ now() | date(format="%Y-%m-%d") }} + {% else %} + ## [{{ version | trim_start_matches(pat="v") }}] - {{ now() | date(format="%Y-%m-%d") }} + {% endif %} + {% else %} + ## [unreleased] + {% endif %} + + {% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits + | filter(attribute="scope") + | sort(attribute="scope") %} + {{ self::print_commit(commit=commit) }} + {%- endfor -%} + {% raw %} + {% endraw %} + {%- for commit in commits %} + {%- if not commit.scope -%} + {{ self::print_commit(commit=commit) }} + {% endif -%} + {% endfor -%} + {% endfor %}"#, + )), + trim: Some(true), + postprocessors: Some(vec![TextProcessor { + pattern: Regex::new("").expect("failed to compile regex"), + replace: Some(String::from("https://github.com/org/repo")), + replace_command: None, + }]), + render_always: Some(false), + ..ChangelogConfig::default() + }, + git: GitConfig { + commit_parsers: Some(vec![ + CommitParser { + message: Regex::new("^feat").ok(), + group: Some(String::from("โ›ฐ๏ธ Features")), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^fix").ok(), + group: Some(String::from("๐Ÿ› Bug Fixes")), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^doc").ok(), + group: Some(String::from("๐Ÿ“š Documentation")), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^perf").ok(), + group: Some(String::from("โšก Performance")), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^refactor\\(clippy\\)").ok(), + skip: Some(true), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^refactor").ok(), + group: Some(String::from("๐Ÿšœ Refactor")), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^style").ok(), + group: Some(String::from("๐ŸŽจ Styling")), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^test").ok(), + group: Some(String::from("๐Ÿงช Testing")), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^chore|^ci").ok(), + group: Some(String::from("โš™๏ธ Miscellaneous Tasks")), + ..CommitParser::default() + }, + CommitParser { + body: Regex::new(".*security").ok(), + group: Some(String::from("๐Ÿ›ก๏ธ Security")), + ..CommitParser::default() + }, + CommitParser { + message: Regex::new("^revert").ok(), + group: Some(String::from("โ—€๏ธ Revert")), + ..CommitParser::default() + }, + ]), + protect_breaking_commits: Some(false), + filter_commits: Some(false), + filter_unconventional: Some(true), + conventional_commits: Some(true), + tag_pattern: Regex::new("^((?:@[^/@]+/)?[^/@]+)(?:@([^/]+))?$").ok(), + skip_tags: Regex::new("beta|alpha|snapshot").ok(), + ignore_tags: Regex::new("rc|beta|alpha|snapshot").ok(), + topo_order: Some(false), + sort_commits: Some(String::from("newest")), + ..GitConfig::default() + }, + }; + + let root_path = Path::new(root); + let config_path = &root_path.join(String::from(".config.toml")); + + if config_path.exists() { + let config_file = File::open(config_path).expect("Failed to open config file"); + let mut config_reader = BufReader::new(config_file); + let mut buffer = String::new(); + + config_reader.read_to_string(&mut buffer).expect("Failed to read confile file"); + let cliff_data = buffer.replace("cliff.", ""); + + Config::parse_from_str(cliff_data.as_str()).expect("Failed to parse config content") + } else { + default_cliff_config + } +} + +fn get_tools_config(root: &PathBuf) -> ToolsConfig { + let default_tools_config = + ToolsConfig { tools: ToolsConfigGroup { bump_sync: Some(true), repo_url: None } }; + + let root_path = Path::new(root); + let tools_path = &root_path.join(String::from(".config.toml")); + + if tools_path.exists() { + let config_file = File::open(tools_path).expect("Failed to open config file"); + let mut config_reader = BufReader::new(config_file); + let mut buffer = String::new(); + + config_reader.read_to_string(&mut buffer).expect("Failed to read confile file"); + + toml::from_str::(buffer.as_str()).expect("Failed to parse config content") + } else { + default_tools_config + } +} + +fn get_changes_config(root: &PathBuf) -> HashMap { + let default_changes_config = HashMap::from([ + ("message".to_string(), "chore(release): |---| release new version".to_string()), + ("git_user_name".to_string(), "github-actions[bot]".to_string()), + ("git_user_email".to_string(), "github-actions[bot]@users.noreply.git.com".to_string()), + ]); + + let root_path = Path::new(root); + let changes_path = &root_path.join(String::from(".changes.json")); + + if changes_path.exists() { + let changes_file = File::open(changes_path).expect("Failed to open changes file"); + let changes_reader = BufReader::new(changes_file); + + let changes_config: ChangesConfig = + serde_json::from_reader(changes_reader).expect("Failed to parse changes file"); + + HashMap::from([ + ( + "message".to_string(), + changes_config.message.expect("Failed to get message from changes file"), + ), + ( + "git_user_name".to_string(), + changes_config + .git_user_name + .expect("Failed to get git user name from changes file"), + ), + ( + "git_user_email".to_string(), + changes_config + .git_user_email + .expect("Failed to get git user email from changes file"), + ), + ]) + } else { + default_changes_config + } +} + +#[allow(clippy::needless_pass_by_value)] +fn get_workspace_root(cwd: Option) -> PathBuf { + let root = match cwd { + Some(ref dir) => { + get_project_root_path(Some(PathBuf::from(dir))).expect("Failed to get project root") + } + None => get_project_root_path(None).expect("Failed to get project root"), + }; + + canonicalize(root.as_path()).expect("Failed to canonic package path") +} + +pub fn get_workspace_config(cwd: Option) -> WorkspaceConfig { + let root = &get_workspace_root(cwd); + let changes = get_changes_config(root); + let cliff = get_cliff_config(root); + let tools = get_tools_config(root); + let manager = detect_package_manager(root); + + WorkspaceConfig { + changes_config: changes, + cliff_config: cliff, + tools_config: tools, + workspace_root: root.clone(), + package_manager: manager.unwrap_or(CorePackageManager::Npm), + } +} diff --git a/crates/monorepo/src/conventional.rs b/crates/monorepo/src/conventional.rs new file mode 100644 index 00000000..3c6eb2f8 --- /dev/null +++ b/crates/monorepo/src/conventional.rs @@ -0,0 +1,80 @@ +use git_cliff_core::{ + changelog::Changelog, + commit::{Commit as GitCommit, Signature}, + config::{Config, GitConfig}, + release::Release, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use ws_git::repo::RepositoryCommit; +use ws_pkg::package::PackageInfo; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ConventionalPackage { + pub package_info: PackageInfo, + pub conventional_config: Value, + pub conventional_commits: Value, + pub changelog_output: String, +} + +#[derive(Debug, Clone)] +pub struct ConventionalPackageOptions { + pub version: Option, + pub title: Option, +} + +fn generate_changelog(commits: &[GitCommit], config: &Config, version: Option) -> String { + let releases = Release { version, commits: commits.to_vec(), ..Release::default() }; + + let changelog = Changelog::new(vec![releases], config); + let mut changelog_output = Vec::new(); + + changelog + .expect("Failed to init changelog") + .generate(&mut changelog_output) + .expect("Failed to generate changelog"); + + String::from_utf8(changelog_output).unwrap_or_default() +} + +/// Prepend changelog output +fn prepend_generate_changelog( + commits: &[GitCommit], + config: &Config, + changelog_content: &String, + version: Option, +) -> String { + let releases = Release { version, commits: commits.to_vec(), ..Release::default() }; + + let changelog = Changelog::new(vec![releases], config); + let mut changelog_output = Vec::new(); + + changelog + .expect("Failed to init changelog") + .prepend(changelog_content.to_string(), &mut changelog_output) + .expect("Failed to prepend to changelog"); + + String::from_utf8(changelog_output).unwrap_or_default() +} + +fn process_commits<'a>(commits: &[RepositoryCommit], config: &GitConfig) -> Vec> { + commits + .iter() + .map(|commit| { + let timestamp = chrono::DateTime::parse_from_rfc2822(&commit.author_date).unwrap(); + + let git_commit = GitCommit { + id: commit.hash.to_string(), + message: commit.message.to_string(), + author: Signature { + name: Some(commit.author_name.to_string()), + email: Some(commit.author_email.to_string()), + timestamp: timestamp.timestamp(), + }, + ..GitCommit::default() + }; + + git_commit.process(config).unwrap() + }) + .collect::>() +} diff --git a/crates/monorepo/src/lib.rs b/crates/monorepo/src/lib.rs new file mode 100644 index 00000000..39ce10a3 --- /dev/null +++ b/crates/monorepo/src/lib.rs @@ -0,0 +1,5 @@ +pub mod changes; +pub mod config; +pub mod conventional; +pub mod test; +pub mod workspace; diff --git a/crates/monorepo/src/test.rs b/crates/monorepo/src/test.rs new file mode 100644 index 00000000..71c084b3 --- /dev/null +++ b/crates/monorepo/src/test.rs @@ -0,0 +1,880 @@ +use ws_git::repo::Repository; + +use ws_std::manager::CorePackageManager; + +use std::{ + env::temp_dir, + fs::{canonicalize, create_dir_all, remove_dir_all, File, OpenOptions}, + path::PathBuf, +}; + +use std::io::{BufWriter, Write}; + +#[cfg(not(windows))] +use std::os::unix::fs::PermissionsExt; + +#[derive(Debug, Clone)] +pub struct MonorepoWorkspace { + root: PathBuf, + repository: Repository, +} + +impl MonorepoWorkspace { + pub fn new() -> Self { + let temp_dir = temp_dir(); + let monorepo_root_dir = &temp_dir.join("monorepo-workspace"); + + if monorepo_root_dir.exists() { + remove_dir_all(monorepo_root_dir).expect("Unable to remove directory"); + } + + create_dir_all(monorepo_root_dir).expect("Unable to create monorepo directory"); + + let root = canonicalize(monorepo_root_dir.clone()).expect("Failed to canonic package path"); + Self { root, repository: Repository::new(monorepo_root_dir.as_path()) } + } + + #[allow(clippy::too_many_lines)] + pub fn create_repository( + &self, + package_manager: CorePackageManager, + ) -> Result<(), std::io::Error> { + let monorepo_package_json = &self.root.join("package.json"); + let monorepo_config_toml = &self.root.join(".config.toml"); + let monorepo_gitattributes = &self.root.join(".gitattributes"); + let monorepo_packages_dir = &self.root.join("packages"); + + create_dir_all(monorepo_packages_dir)?; + + #[cfg(not(windows))] + std::fs::set_permissions(&self.root, std::fs::Permissions::from_mode(0o777))?; + + let monorepo_root_json = r#" + { + "name": "root", + "version": "0.0.0", + "workspaces": [ + "packages/package-foo", + "packages/package-bar", + "packages/package-baz", + "packages/package-charlie", + "packages/package-major", + "packages/package-tom" + ] + }"#; + + let package_root_json = serde_json::from_str::(monorepo_root_json)?; + let monorepo_package_root_json_file = File::create(monorepo_package_json.as_path())?; + let monorepo_root_json_writer = BufWriter::new(monorepo_package_root_json_file); + serde_json::to_writer_pretty(monorepo_root_json_writer, &package_root_json)?; + + let monorepo_config_data = r#" +[tools] +bump_sync = true +repo_url = "https://github.com/websublime/workspace-node-tools" + +[cliff.changelog] +# template for the changelog footer +header = """ +## What's Changed\n +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{%- macro remote_url() -%} + +{%- endmacro -%} + +{% macro print_commit(commit) -%} + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}[**breaking**] {% endif %}\ + {{ commit.message | upper_first }} - \ + ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\ +{% endmacro -%} + +{% if version %}\ + {% if previous.version %}\ + ## [{{ version | trim_start_matches(pat="v") }}]\ + ({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} + {% else %}\ + ## [{{ version | trim_start_matches(pat="v") }}] - {{ now() | date(format="%Y-%m-%d") }} + {% endif %}\ +{% else %}\ + ## [unreleased] +{% endif %}\ + +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits + | filter(attribute="scope") + | sort(attribute="scope") %} + {{ self::print_commit(commit=commit) }} + {%- endfor -%} + {% raw %}\n{% endraw %}\ + {%- for commit in commits %} + {%- if not commit.scope -%} + {{ self::print_commit(commit=commit) }} + {% endif -%} + {% endfor -%} +{% endfor %}\n +""" +# template for the changelog footer +footer = """ +-- Total Releases: {{ releases | length }} -- +""" +# remove the leading and trailing whitespace from the templates +trim = true +# postprocessors +postprocessors = [ + { pattern = '', replace = "https://github.com/websublime/workspace-node-tools" }, # replace repository URL +] + +[cliff.git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = true +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [ + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))" }, + # Check spelling of the commit with https://github.com/crate-ci/typos + # If the spelling is incorrect, it will be automatically fixed. + { pattern = '.*', replace_command = 'typos --write-changes -' }, +] +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^feat", group = "โ›ฐ๏ธ Features" }, + { message = "^fix", group = "๐Ÿ› Bug Fixes" }, + { message = "^doc", group = "๐Ÿ“š Documentation" }, + { message = "^perf", group = "โšก Performance" }, + { message = "^refactor\\(clippy\\)", skip = true }, + { message = "^refactor", group = "๐Ÿšœ Refactor" }, + { message = "^style", group = "๐ŸŽจ Styling" }, + { message = "^test", group = "๐Ÿงช Testing" }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore\\(npm\\).*yarn\\.lock", skip = true }, + { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, + { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert", group = "โ—€๏ธ Revert" }, +] +# protect breaking changes from being skipped due to matching a skipping commit_parser +protect_breaking_commits = false +# filter out the commits that are not matched by commit parsers +filter_commits = false +# regex for matching git tags +tag_pattern = "v[0-9].*" +# regex for skipping tags +skip_tags = "beta|alpha" +# regex for ignoring tags +ignore_tags = "rc|v2.1.0|v2.1.1" +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "newest" +"#; + + let mut monorepo_package_config_toml_file = File::create(monorepo_config_toml.as_path())?; + monorepo_package_config_toml_file.write_all(monorepo_config_data.as_bytes())?; + + let monorepo_config_gitattributes = String::from("* text=auto"); + + let mut monorepo_package_config_gitattributes_file = + File::create(monorepo_gitattributes.as_path())?; + monorepo_package_config_gitattributes_file + .write_all(monorepo_config_gitattributes.as_bytes())?; + + match package_manager { + CorePackageManager::Yarn => { + let yarn_lock = &self.root.join("yarn.lock"); + File::create(yarn_lock)?; + } + CorePackageManager::Pnpm => { + let pnpm_lock = &self.root.join("pnpm-lock.yaml"); + let pnpm_workspace = &self.root.join("pnpm-workspace.yaml"); + + let mut lock_file = File::create(pnpm_lock)?; + lock_file.write_all(r"lockfileVersion: '9.0'".as_bytes())?; + + let mut workspace_file = File::create(pnpm_workspace)?; + workspace_file.write_all( + r#" + packages: + - "packages/*" + "# + .as_bytes(), + )?; + } + CorePackageManager::Bun => { + let bun_lock = &self.root.join("bun.lockb"); + File::create(bun_lock)?; + } + CorePackageManager::Npm => { + let npm_lock = &self.root.join("package-lock.json"); + File::create(npm_lock)?; + } + } + + self.repository + .init("main", "Websublime Machine", "machine@websublime.com") + .expect("Failed to initialize git repository"); + self.repository.add_all().expect("Failed to add all files"); + self.repository + .commit("chore: init monorepo workspace", None, None) + .expect("Failed to commit changes"); + + Ok(()) + } + + pub fn create_changes(&self) -> Result<(), std::io::Error> { + let monorepo_changes_json = &self.root.join(".changes.json"); + + self.repository + .create_branch("feature/changes") + .expect("Failed to create branch feature/changes"); + + let monorepo_changes_json_data = r#" + { + "message": "chore(release): release new version", + "git_user_name": "github-actions[bot]", + "git_user_email": "github-actions[bot]@users.noreply.git.com", + "changes": {} + }"#; + + let package_changes_json = + serde_json::from_str::(monorepo_changes_json_data)?; + let monorepo_package_changes_json_file = OpenOptions::new() + .write(true) + .append(false) + .truncate(true) + .create(true) + .open(monorepo_changes_json.as_path())?; + let monorepo_changes_json_writer = BufWriter::new(monorepo_package_changes_json_file); + serde_json::to_writer_pretty(monorepo_changes_json_writer, &package_changes_json)?; + + self.repository.add_all().expect("Failed to add all files"); + self.repository + .commit("feat: add changes file", None, None) + .expect("Failed to commit changes"); + self.repository.checkout("main").expect("Failed to checkout main branch"); + self.repository.merge("feature/changes").expect("Failed to merge branches"); + + Ok(()) + } + + pub fn create_package_foo(&self) -> Result<(), std::io::Error> { + self.repository + .create_branch("feature/package-foo") + .expect("Failed to create branch feature/package-foo"); + + let monorepo_packages_dir = &self.root.join("packages"); + let monorepo_package_foo_dir = &monorepo_packages_dir.join("package-foo"); + let js_path = &monorepo_package_foo_dir.join("index.mjs"); + + create_dir_all(monorepo_package_foo_dir)?; + + let package_foo_json = r#" + { + "name": "@scope/package-foo", + "version": "1.0.0", + "description": "Awesome package foo", + "main": "index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + }, + "typesVersions": { + "*": { + "index.d.ts": [ + "./dist/index.d.ts" + ] + } + }, + "repository": { + "url": "git+ssh://git@github.com:websublime/package-foo.git", + "type": "git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "node index.mjs" + }, + "dependencies": { + "@scope/package-bar": "1.0.0" + }, + "keywords": [], + "author": "Author", + "license": "ISC" + }"#; + let package_foo_json = serde_json::from_str::(package_foo_json)?; + let monorepo_package_foo_json_file = OpenOptions::new() + .write(true) + .append(false) + .truncate(true) + .create(true) + .open(monorepo_package_foo_dir.join("package.json").as_path())?; + let monorepo_package_foo_json_writer = BufWriter::new(monorepo_package_foo_json_file); + serde_json::to_writer_pretty(monorepo_package_foo_json_writer, &package_foo_json)?; + + let mut js_file = File::create(js_path)?; + js_file.write_all(r#"export const foo = "hello foo";"#.as_bytes())?; + + self.repository.add_all().expect("Failed to add all files"); + self.repository + .commit("feat: add package foo", None, None) + .expect("Failed to commit package foo"); + self.repository.checkout("main").expect("Failed to checkout main branch"); + self.repository.merge("feature/package-foo").expect("Failed to merge branches"); + self.repository + .tag("@scope/package-foo@1.0.0", Some("chore: release package-foo@1.0.0".to_string())) + .expect("Failed to create tag"); + + Ok(()) + } + + #[allow(clippy::items_after_statements)] + pub fn create_package_bar(&self) -> Result<(), std::io::Error> { + let monorepo_packages_dir = &self.root.join("packages"); + let monorepo_package_bar_dir = &monorepo_packages_dir.join("package-bar"); + let js_path = &monorepo_package_bar_dir.join("index.mjs"); + + self.repository + .create_branch("feature/package-bar") + .expect("Failet to create branch feature/package-bar"); + + create_dir_all(monorepo_package_bar_dir)?; + + let package_bar_json = r#" + { + "name": "@scope/package-bar", + "version": "1.0.0", + "description": "Awesome package bar", + "main": "index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + }, + "typesVersions": { + "*": { + "index.d.ts": [ + "./dist/index.d.ts" + ] + } + }, + "repository": { + "url": "git+ssh://git@github.com:websublime/package-bar.git", + "type": "git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "node index.mjs" + }, + "dependencies": { + "@scope/package-baz": "1.0.0" + }, + "keywords": [], + "author": "Author", + "license": "ISC" + }"#; + let package_bar_json = serde_json::from_str::(package_bar_json)?; + let monorepo_package_bar_json_file = + File::create(monorepo_package_bar_dir.join("package.json").as_path())?; + let monorepo_package_bar_json_writer = BufWriter::new(monorepo_package_bar_json_file); + serde_json::to_writer_pretty(monorepo_package_bar_json_writer, &package_bar_json)?; + + #[cfg(windows)] + const LINE_ENDING: &str = "\r\n"; + #[cfg(not(windows))] + const LINE_ENDING: &str = "\n"; + + let mut js_file = OpenOptions::new() + .write(true) + .append(false) + .truncate(true) + .create(true) + .open(js_path.as_path())?; + js_file.write_all(format!(r#"export const bar = "hello bar";{LINE_ENDING}"#).as_bytes())?; + + self.repository.add_all().expect("Failed to add all files"); + self.repository + .commit("feat: add package bar", None, None) + .expect("Failed to commit changes"); + + self.repository.checkout("main").expect("Failed to checkout main branch"); + self.repository.merge("feature/package-bar").expect("Failed to merge branches"); + self.repository + .tag("@scope/package-bar@1.0.0", Some("chore: release package-bar@1.0.0".to_string())) + .expect("Failed to create tag"); + + Ok(()) + } + + pub fn create_package_baz(&self) -> Result<(), std::io::Error> { + self.repository + .create_branch("feature/package-baz") + .expect("Failet to create branch feature/package-baz"); + + let monorepo_packages_dir = &self.root.join("packages"); + let monorepo_package_baz_dir = &monorepo_packages_dir.join("package-baz"); + let js_path = &monorepo_package_baz_dir.join("index.mjs"); + + create_dir_all(monorepo_package_baz_dir)?; + + let package_baz_json = r#" + { + "name": "@scope/package-baz", + "version": "1.0.0", + "description": "Awesome package baz", + "main": "index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + }, + "typesVersions": { + "*": { + "index.d.ts": [ + "./dist/index.d.ts" + ] + } + }, + "repository": { + "url": "git+ssh://git@github.com:websublime/package-baz.git", + "type": "git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "node index.mjs" + }, + "dependencies": { + "@scope/package-bar": "1.0.0" + }, + "keywords": [], + "author": "Author", + "license": "ISC" + }"#; + let package_baz_json = serde_json::from_str::(package_baz_json)?; + let monorepo_package_baz_json_file = OpenOptions::new() + .write(true) + .append(false) + .truncate(true) + .create(true) + .open(monorepo_package_baz_dir.join("package.json").as_path())?; + let monorepo_package_baz_json_writer = BufWriter::new(monorepo_package_baz_json_file); + serde_json::to_writer_pretty(monorepo_package_baz_json_writer, &package_baz_json)?; + + let mut js_file = File::create(js_path)?; + js_file.write_all(r#"export const baz = "hello baz";"#.as_bytes())?; + + self.repository.add_all().expect("Failed to add all files"); + self.repository + .commit("feat: add package baz", None, None) + .expect("Failed to commit changes"); + self.repository.checkout("main").expect("Failed to checkout main branch"); + self.repository.merge("feature/package-baz").expect("Failed to merge branches"); + self.repository + .tag("@scope/package-baz@1.0.0", Some("chore: release package-baz@1.0.0".to_string())) + .expect("Failed to create tag"); + + Ok(()) + } + + pub fn create_package_charlie(&self) -> Result<(), std::io::Error> { + self.repository + .create_branch("feature/package-charlie") + .expect("Failet to create branch feature/package-charlie"); + + let monorepo_packages_dir = &self.root.join("packages"); + let monorepo_package_charlie_dir = &monorepo_packages_dir.join("package-charlie"); + let js_path = &monorepo_package_charlie_dir.join("index.mjs"); + + create_dir_all(monorepo_package_charlie_dir)?; + + let package_charlie_json = r#" + { + "name": "@scope/package-charlie", + "version": "1.0.0", + "description": "Awesome package charlie", + "main": "index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + }, + "typesVersions": { + "*": { + "index.d.ts": [ + "./dist/index.d.ts" + ] + } + }, + "repository": { + "url": "git+ssh://git@github.com:websublime/package-charlie.git", + "type": "git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "node index.mjs" + }, + "dependencies": { + "@scope/package-foo": "1.0.0" + }, + "keywords": [], + "author": "Author", + "license": "ISC" + }"#; + let package_charlie_json = serde_json::from_str::(package_charlie_json)?; + let monorepo_package_charlie_json_file = OpenOptions::new() + .write(true) + .append(false) + .truncate(true) + .create(true) + .open(monorepo_package_charlie_dir.join("package.json").as_path())?; + let monorepo_package_charlie_json_writer = + BufWriter::new(monorepo_package_charlie_json_file); + serde_json::to_writer_pretty(monorepo_package_charlie_json_writer, &package_charlie_json)?; + + let mut js_file = File::create(js_path)?; + js_file.write_all(r#"export const charlie = "hello charlie";"#.as_bytes())?; + + self.repository.add_all().expect("Failed to add all files"); + self.repository + .commit("feat: add package charlie", None, None) + .expect("Failed to commit changes"); + self.repository.checkout("main").expect("Failed to checkout main branch"); + self.repository.merge("feature/package-charlie").expect("Failed to merge branches"); + self.repository + .tag( + "@scope/package-charlie@1.0.0", + Some("chore: release package-charlie@1.0.0".to_string()), + ) + .expect("Failed to create tag"); + + Ok(()) + } + + pub fn create_package_major(&self) -> Result<(), std::io::Error> { + self.repository + .create_branch("feature/package-major") + .expect("Failet to create branch feature/package-major"); + + let monorepo_packages_dir = &self.root.join("packages"); + let monorepo_package_major_dir = &monorepo_packages_dir.join("package-major"); + let js_path = &monorepo_package_major_dir.join("index.mjs"); + + create_dir_all(monorepo_package_major_dir)?; + + let package_major_json = r#" + { + "name": "@scope/package-major", + "version": "1.0.0", + "description": "Awesome package major", + "main": "index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + }, + "typesVersions": { + "*": { + "index.d.ts": [ + "./dist/index.d.ts" + ] + } + }, + "repository": { + "url": "git+ssh://git@github.com:websublime/package-major.git", + "type": "git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "node index.mjs" + }, + "dependencies": { + "@websublime/pulseio-core": "^0.4.0", + "@websublime/pulseio-style": "^1.0.0", + "lit": "^3.0.0", + "rollup-plugin-postcss-lit": "^2.1.0" + }, + "keywords": [], + "author": "Author", + "license": "ISC" + }"#; + let package_major_json = serde_json::from_str::(package_major_json)?; + let monorepo_package_major_json_file = OpenOptions::new() + .write(true) + .append(false) + .truncate(true) + .create(true) + .open(monorepo_package_major_dir.join("package.json").as_path())?; + let monorepo_package_major_json_writer = BufWriter::new(monorepo_package_major_json_file); + serde_json::to_writer_pretty(monorepo_package_major_json_writer, &package_major_json)?; + + let mut js_file = File::create(js_path)?; + js_file.write_all(r#"export const major = "hello major";"#.as_bytes())?; + + self.repository.add_all().expect("Failed to add all files"); + self.repository + .commit("feat: add package major", None, None) + .expect("Failed to commit changes"); + self.repository.checkout("main").expect("Failed to checkout main branch"); + self.repository.merge("feature/package-major").expect("Failed to merge branches"); + self.repository + .tag( + "@scope/package-major@1.0.0", + Some("chore: release package-major@1.0.0".to_string()), + ) + .expect("Failed to create tag"); + + Ok(()) + } + + pub fn create_package_tom(&self) -> Result<(), std::io::Error> { + self.repository + .create_branch("feature/package-tom") + .expect("Failet to create branch feature/package-tom"); + + let monorepo_packages_dir = &self.root.join("packages"); + let monorepo_package_tom_dir = &monorepo_packages_dir.join("package-tom"); + let js_path = &monorepo_package_tom_dir.join("index.mjs"); + + create_dir_all(monorepo_package_tom_dir)?; + + let package_tom_json = r#" + { + "name": "@scope/package-tom", + "version": "1.0.0", + "description": "Awesome package tom", + "main": "index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + }, + "typesVersions": { + "*": { + "index.d.ts": [ + "./dist/index.d.ts" + ] + } + }, + "repository": { + "url": "git+ssh://git@github.com:websublime/package-tom.git", + "type": "git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "node index.mjs" + }, + "dependencies": { + "@scope/package-bar": "1.0.0", + "open-props": "^1.6.19", + "postcss": "^8.4.35", + "postcss-cli": "^11.0.0", + "postcss-custom-media": "^10.0.3", + "postcss-import": "^16.0.1", + "postcss-jit-props": "^1.0.14", + "postcss-mixins": "^9.0.4", + "postcss-nested": "^6.0.1", + "postcss-preset-env": "^9.4.0", + "postcss-simple-vars": "^7.0.1", + "typescript": "^5.3.3", + "vite": "^5.1.4" + }, + "keywords": [], + "author": "Author", + "license": "ISC" + }"#; + let package_tom_json = serde_json::from_str::(package_tom_json)?; + let monorepo_package_tom_json_file = OpenOptions::new() + .write(true) + .append(false) + .truncate(true) + .create(true) + .open(monorepo_package_tom_dir.join("package.json").as_path())?; + let monorepo_package_tom_json_writer = BufWriter::new(monorepo_package_tom_json_file); + serde_json::to_writer_pretty(monorepo_package_tom_json_writer, &package_tom_json)?; + + let mut js_file = File::create(js_path)?; + js_file.write_all(r#"export const tom = "hello tom";"#.as_bytes())?; + + self.repository.add_all().expect("Failed to add all files"); + self.repository + .commit("feat: add package tom", None, None) + .expect("Failed to commit changes"); + self.repository.checkout("main").expect("Failed to checkout main branch"); + self.repository.merge("feature/package-tom").expect("Failed to merge branches"); + self.repository + .tag("@scope/package-tom@1.0.0", Some("chore: release package-tom@1.0.0".to_string())) + .expect("Failed to create tag"); + + Ok(()) + } + + pub fn create_workspace( + &self, + package_manager: CorePackageManager, + ) -> Result<(), std::io::Error> { + self.create_repository(package_manager)?; + self.create_changes()?; + self.create_package_bar()?; + self.create_package_foo()?; + self.create_package_baz()?; + self.create_package_charlie()?; + self.create_package_major()?; + self.create_package_tom()?; + Ok(()) + } + + pub fn delete_repository(&self) -> bool { + remove_dir_all(&self.root).is_ok() + } + + pub fn get_monorepo_root(&self) -> &PathBuf { + &self.root + } +} + +#[cfg(test)] +#[allow(clippy::uninlined_format_args)] +mod tests { + use super::*; + + #[test] + fn test_create_repository() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_repository(CorePackageManager::Npm)?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_create_changes() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_repository(CorePackageManager::Npm)?; + monorepo.create_changes()?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_create_package_bar() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_repository(CorePackageManager::Npm)?; + monorepo.create_changes()?; + monorepo.create_package_bar()?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_create_package_baz() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_repository(CorePackageManager::Npm)?; + monorepo.create_changes()?; + monorepo.create_package_baz()?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_create_package_foo() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_repository(CorePackageManager::Npm)?; + monorepo.create_changes()?; + monorepo.create_package_foo()?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_create_package_tom() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_repository(CorePackageManager::Npm)?; + monorepo.create_changes()?; + monorepo.create_package_tom()?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_create_package_major() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_repository(CorePackageManager::Npm)?; + monorepo.create_changes()?; + monorepo.create_package_major()?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_create_package_charlie() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_repository(CorePackageManager::Npm)?; + monorepo.create_changes()?; + monorepo.create_package_charlie()?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_create_workspace() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + monorepo.create_workspace(CorePackageManager::Npm)?; + + assert!(monorepo.get_monorepo_root().exists()); + + monorepo.delete_repository(); + + Ok(()) + } +} diff --git a/crates/monorepo/src/workspace.rs b/crates/monorepo/src/workspace.rs new file mode 100644 index 00000000..36380cac --- /dev/null +++ b/crates/monorepo/src/workspace.rs @@ -0,0 +1,408 @@ +use icu::collator::{Collator, CollatorOptions, Numeric, Strength}; +use serde::{Deserialize, Serialize}; +use std::fs::canonicalize; +use std::path::Path; +use std::process::{Command, Stdio}; +use std::{fs::File, io::BufReader, path::PathBuf}; +use version_compare::{Cmp, Version}; +use wax::{CandidatePath, Glob, Pattern}; +use ws_git::repo::{Repository, RepositoryPublishTagInfo}; +use ws_pkg::package::{package_scope_name_version, Dependency, Package, PackageInfo, PackageJson}; +use ws_std::manager::CorePackageManager; + +use crate::config::{get_workspace_config, WorkspaceConfig}; + +#[derive(Debug, Deserialize, Serialize)] +/// A struct that represents a pnpm workspace. +struct PnpmInfo { + pub name: String, + pub path: String, + pub private: bool, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct Workspace { + pub config: WorkspaceConfig, + pub repo: Repository, +} + +impl From<&str> for Workspace { + fn from(root: &str) -> Self { + let path_buff = PathBuf::from(root); + + #[cfg(not(windows))] + let canonic_path = canonicalize(Path::new(path_buff.as_os_str())).expect("Invalid path"); + + #[cfg(windows)] + let canonic_path = path_buff; + + let repo = Repository::new(&canonic_path); + let config = get_workspace_config(Some(canonic_path)); + + Workspace { config, repo } + } +} + +impl From for Workspace { + fn from(config: WorkspaceConfig) -> Self { + let repo = Repository::new(&config.workspace_root); + Workspace { config, repo } + } +} + +impl Workspace { + pub fn new(root: PathBuf) -> Self { + let config = get_workspace_config(Some(root)); + let repo = Repository::new(&config.workspace_root); + Workspace { config, repo } + } + + pub fn get_packages(&self) -> Vec { + let manager = self.config.package_manager; + + match manager { + CorePackageManager::Npm | CorePackageManager::Yarn => self.get_packages_from_npm(), + CorePackageManager::Bun => todo!("Bun is not yet supported"), + CorePackageManager::Pnpm => self.get_packages_from_pnpm(), + } + } + + pub fn get_package_info(&self, package_name: &str) -> Option { + let packages = self.get_packages(); + packages.into_iter().find(|p| p.package.name == package_name) + } + + pub fn get_changed_packages(&self, sha: Option) -> Vec { + let packages = &self.get_packages(); + let since = sha.unwrap_or(String::from("main")); + let packages_paths = + packages.iter().map(|pkg| pkg.package_path.to_string()).collect::>(); + + let changed_files = self + .repo + .get_all_files_changed_since_branch(&packages_paths, since.as_str()) + .expect("Fail to get changed files"); + + packages + .iter() + .flat_map(|pkg| { + let mut pkgs = changed_files + .iter() + .filter(|file| file.starts_with(pkg.package_path.as_str())) + .map(|_| pkg.to_owned()) + .collect::>(); + + pkgs.dedup_by(|a, b| a.package.name == b.package.name); + + pkgs + }) + .collect::>() + } + + #[allow(clippy::default_trait_access)] + pub fn get_last_known_publish_tag_info_for_package( + &self, + package_info: &PackageInfo, + ) -> Option { + let mut remote_tags = + self.repo.get_remote_or_local_tags(Some(false)).expect("Error getting remote tags"); + let mut local_tags = + self.repo.get_remote_or_local_tags(Some(true)).expect("Error getting local tags"); + + remote_tags.append(&mut local_tags); + + let mut options = CollatorOptions::new(); + options.strength = Some(Strength::Secondary); + options.numeric = Some(Numeric::On); + + let collator = Collator::try_new(&Default::default(), options).unwrap(); + + remote_tags.sort_by(|a, b| { + let tag_a = a.tag.replace("refs/tags/", ""); + let tag_b = b.tag.replace("refs/tags/", ""); + + collator.compare(&tag_b, &tag_a) + }); + + let package_tag = + format!("{}@{}", package_info.package.name, package_info.package.version.to_string()); + + let mut match_tag = remote_tags.iter().find(|item| { + let tag = item.tag.replace("refs/tags/", ""); + let matches: Vec<&str> = tag.matches(&package_tag).collect(); + + !matches.is_empty() + }); + + if match_tag.is_none() { + let mut highest_tag = None; + + remote_tags.iter().for_each(|item| { + let tag = &item.tag.replace("refs/tags/", ""); + + if tag.contains(&package_info.package.name) { + if highest_tag.is_none() { + highest_tag = Some(String::from(tag)); + } + + let high_tag = highest_tag.as_ref().unwrap(); + let current_tag_meta = package_scope_name_version(tag).unwrap(); + let highest_tag_meta = package_scope_name_version(high_tag).unwrap(); + + let current_version = Version::from(¤t_tag_meta.version).unwrap(); + let highest_version = Version::from(&highest_tag_meta.version).unwrap(); + + if current_version.compare_to(&highest_version, Cmp::Gt) { + highest_tag = Some(String::from(tag)); + } + } + }); + + if highest_tag.is_some() { + let highest_tag = highest_tag.unwrap(); + let highest_tag_meta = package_scope_name_version(&highest_tag).unwrap(); + + match_tag = remote_tags.iter().find(|item| { + let tag = item.tag.replace("refs/tags/", ""); + let matches: Vec<&str> = tag.matches(&highest_tag_meta.full).collect(); + + !matches.is_empty() + }); + } + } + + if match_tag.is_some() { + let hash = &match_tag.unwrap().hash; + let tag = &match_tag.unwrap().tag; + let package = &package_info.package.name; + + return Some(RepositoryPublishTagInfo { + hash: hash.to_string(), + tag: tag.to_string(), + package: package.to_string(), + }); + } + + None + } + + fn get_root_package_json(&self) -> PackageJson { + let package_json_path = self.config.workspace_root.join("package.json"); + + let package_json_file = File::open(package_json_path.as_path()).expect("File not found"); + let package_json_buffer = BufReader::new(package_json_file); + + serde_json::from_reader(package_json_buffer).expect("Error parsing package.json") + } + + #[allow(clippy::unused_self)] + fn aggregate_dependencies(&self, package_json: &PackageJson) -> Vec { + let mut package_dependencies = vec![]; + + let dependencies = package_json.dependencies.clone().unwrap_or_default(); + let dev_dependencies = package_json.dev_dependencies.clone().unwrap_or_default(); + let peer_dependencies = package_json.peer_dependencies.clone().unwrap_or_default(); + let optional_dependencies = package_json.optional_dependencies.clone().unwrap_or_default(); + + if dependencies.is_object() { + dependencies.as_object().iter().for_each(|dep| { + dep.keys().for_each(|key| { + let dependency = Dependency { + name: key.clone(), + version: dep + .get(key) + .unwrap() + .as_str() + .unwrap() + .to_string() + .parse() + .unwrap(), + }; + package_dependencies.push(dependency); + }); + }); + } + + if dev_dependencies.is_object() { + dev_dependencies.as_object().iter().for_each(|dep| { + dep.keys().for_each(|key| { + let dependency = Dependency { + name: key.clone(), + version: dep + .get(key) + .unwrap() + .as_str() + .unwrap() + .to_string() + .parse() + .unwrap(), + }; + package_dependencies.push(dependency); + }); + }); + } + + if peer_dependencies.is_object() { + peer_dependencies.as_object().iter().for_each(|dep| { + dep.keys().for_each(|key| { + let dependency = Dependency { + name: key.clone(), + version: dep + .get(key) + .unwrap() + .as_str() + .unwrap() + .to_string() + .parse() + .unwrap(), + }; + package_dependencies.push(dependency); + }); + }); + } + + if optional_dependencies.is_object() { + optional_dependencies.as_object().iter().for_each(|dep| { + dep.keys().for_each(|key| { + let dependency = Dependency { + name: key.clone(), + version: dep + .get(key) + .unwrap() + .as_str() + .unwrap() + .to_string() + .parse() + .unwrap(), + }; + package_dependencies.push(dependency); + }); + }); + } + + package_dependencies + } + + #[allow(clippy::needless_borrows_for_generic_args)] + fn get_packages_from_npm(&self) -> Vec { + let path = self.config.workspace_root.as_path(); + let PackageJson { workspaces, .. } = self.get_root_package_json(); + let mut workspaces = workspaces.unwrap_or_default(); + let mut packages = vec![]; + + let globs = workspaces + .iter_mut() + .map(|workspace| { + if workspace.ends_with("/*") { + workspace.push_str("*/package.json"); + Glob::new(workspace).expect("Error parsing glob") + } else { + workspace.push_str("/package.json"); + Glob::new(workspace).expect("Error parsing glob") + } + }) + .collect::>(); + + let patterns = wax::any(globs).expect("Error creating patterns"); + let glob = Glob::new("**/package.json").expect("Error parsing glob"); + + for entry in glob + .walk(self.config.workspace_root.as_path()) + .not([ + "**/node_modules/**", + "**/src/**", + "**/dist/**", + "**/tests/**", + "**/__tests__/**", + ]) + .expect("Error walking glob") + { + let entry = entry.expect("Error reading entry"); + let rel_path = entry + .path() + .strip_prefix(&path) + .expect("Error getting entry path") + .display() + .to_string(); + let entry_path = entry.path().strip_prefix(&path).expect("Error getting entry path"); + + if patterns.is_match(CandidatePath::from(entry_path)) { + let package_json_file = File::open(&entry.path()).expect("File not found"); + let package_json_reader = BufReader::new(package_json_file); + let pkg_json: PackageJson = serde_json::from_reader(package_json_reader) + .expect("Failed to parse package json file"); + + let package_dependencies = self.aggregate_dependencies(&pkg_json); + + let package = Package { + name: pkg_json.name.clone(), + version: pkg_json.version.parse().unwrap(), + dependencies: package_dependencies, + }; + + packages.push(PackageInfo { + package, + package_path: entry.path().to_str().unwrap().replace("/package.json", ""), + package_json_path: entry.path().to_str().unwrap().to_string(), + package_relative_path: rel_path.replace("/package.json", ""), + pkg_json: serde_json::to_value(pkg_json) + .expect("Error converting package json"), + }); + } + } + + packages + } + + fn get_packages_from_pnpm(&self) -> Vec { + let path = &self.config.workspace_root.as_path(); + + let mut command = Command::new("pnpm"); + command.current_dir(path).arg("list").arg("-r").arg("--depth").arg("-1").arg("--json"); + + command.stdout(Stdio::piped()); + command.stderr(Stdio::piped()); + + let output = command.output().expect("Failed to execute command"); + let output_slice = &output.stdout.as_slice(); + let pnpm_info = serde_json::from_slice::>(output_slice) + .expect("Failed to parse pnpm list"); + + pnpm_info + .iter() + .filter(|pkgs| pkgs.path != path.display().to_string()) + .map(|pkgs| { + let package_path = + canonicalize(pkgs.path.clone()).expect("Failed to canonic package path"); + //let package_path = PathBuf::from(pkgs.path.clone()); + let package_json_path = package_path.join("package.json"); + + let package_json_file = File::open(&package_json_path).expect("File not found"); + let package_json_reader = BufReader::new(package_json_file); + let pkg_json: PackageJson = serde_json::from_reader(package_json_reader) + .expect("Failed to parse package json file"); + + let package_dependencies = self.aggregate_dependencies(&pkg_json); + + let package = Package { + name: pkg_json.name.clone(), + version: pkg_json.version.parse().unwrap(), + dependencies: package_dependencies, + }; + + PackageInfo { + package, + package_path: package_path.to_str().unwrap().to_string(), + package_json_path: package_json_path.to_str().unwrap().to_string(), + package_relative_path: package_path + .strip_prefix(path) + .expect("Error getting entry path") + .display() + .to_string(), + pkg_json: serde_json::to_value(pkg_json) + .expect("Error converting package json"), + } + }) + .collect::>() + } +} diff --git a/crates/monorepo/tests/monorepo_changes.rs b/crates/monorepo/tests/monorepo_changes.rs new file mode 100644 index 00000000..3b8bc206 --- /dev/null +++ b/crates/monorepo/tests/monorepo_changes.rs @@ -0,0 +1,365 @@ +#[cfg(test)] +mod changes_tests { + + use std::{fs::File, io::BufReader}; + use ws_monorepo::{ + changes::{Change, Changes, ChangesConfig}, + test::MonorepoWorkspace, + }; + use ws_std::manager::CorePackageManager; + + #[test] + fn test_init_changes() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + + let changes = Changes::new(root.as_path()); + let changes_config = changes.init(); + + assert_eq!( + changes_config.message, + Some("chore(release): |---| release new version".to_string()) + ); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_changes_file_not_exist() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + + let changes = Changes::new(root.as_path()); + + assert!(!changes.file_exist()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_changes_file_exist() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + + assert!(changes.file_exist()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_add_new_change() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change = &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + + changes.add(change, Some(vec!["production".to_string()])); + + let changes_path = root.join(".changes.json"); + let changes_file = File::open(changes_path.as_path())?; + let changes_reader = BufReader::new(changes_file); + let changes_config: ChangesConfig = serde_json::from_reader(changes_reader)?; + let change_meta = changes_config.changes.get("main").expect("Failed to get main change"); + + assert!(change_meta.deploy.contains(&"production".to_string())); + assert_eq!(change_meta.pkgs.len(), 1); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_update_new_change_with_same_environment() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change_one = + &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + let change_two = + &Change { package: "@scope/foo".to_string(), release_as: "patch".to_string() }; + + changes.add(change_one, Some(vec!["production".to_string()])); + changes.add(change_two, Some(vec!["production".to_string()])); + + let changes_path = root.join(".changes.json"); + let changes_file = File::open(changes_path.as_path())?; + let changes_reader = BufReader::new(changes_file); + let changes_config: ChangesConfig = serde_json::from_reader(changes_reader)?; + let change_meta = changes_config.changes.get("main").expect("Failed to get main change"); + + assert!(change_meta.deploy.contains(&"production".to_string())); + assert_eq!(change_meta.pkgs.len(), 2); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_update_new_change_with_diff_environment() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change_one = + &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + let change_two = + &Change { package: "@scope/foo".to_string(), release_as: "patch".to_string() }; + + changes.add(change_one, Some(vec!["production".to_string()])); + changes.add(change_two, Some(vec!["development".to_string()])); + + let changes_path = root.join(".changes.json"); + let changes_file = File::open(changes_path.as_path())?; + let changes_reader = BufReader::new(changes_file); + let changes_config: ChangesConfig = serde_json::from_reader(changes_reader)?; + let change_meta = changes_config.changes.get("main").expect("Failed to get main change"); + + assert!(change_meta.deploy.contains(&"production".to_string())); + assert!(change_meta.deploy.contains(&"development".to_string())); + assert_eq!(change_meta.pkgs.len(), 2); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_avoid_duplicate_new_change() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change_one = + &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + let change_two = + &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + + changes.add(change_one, Some(vec!["production".to_string()])); + changes.add(change_two, Some(vec!["development".to_string()])); + + let changes_path = root.join(".changes.json"); + let changes_file = File::open(changes_path.as_path())?; + let changes_reader = BufReader::new(changes_file); + let changes_config: ChangesConfig = serde_json::from_reader(changes_reader)?; + let change_meta = changes_config.changes.get("main").expect("Failed to get main change"); + + assert!(change_meta.deploy.contains(&"production".to_string())); + assert_eq!(change_meta.pkgs.len(), 1); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_remove_change() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change = &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + + changes.add(change, Some(vec!["production".to_string()])); + changes.remove("main"); + + let changes_path = root.join(".changes.json"); + let changes_file = File::open(changes_path.as_path())?; + let changes_reader = BufReader::new(changes_file); + let changes_config: ChangesConfig = serde_json::from_reader(changes_reader)?; + + assert!(changes_config.changes.is_empty()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_get_empty_changes() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + + assert!(changes.changes().is_empty()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_get_current_changes() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change = &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + + changes.add(change, Some(vec!["production".to_string()])); + + assert!(!changes.changes().is_empty()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_get_empty_changes_by_branch() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + + assert!(changes.changes_by_branch("main").is_none()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_get_changes_by_branch() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change = &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + + changes.add(change, Some(vec!["production".to_string()])); + + assert!(changes.changes_by_branch("main").is_some()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_get_empty_changes_by_package() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change = &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + + changes.add(change, Some(vec!["production".to_string()])); + + assert!(changes.changes_by_package("@scope/foo", "main").is_none()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_get_changes_by_package() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change = &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + + changes.add(change, Some(vec!["production".to_string()])); + + let change_by_package = &changes.changes_by_package("@scope/bar", "main"); + let package_change = change_by_package.as_ref().unwrap(); + + assert!(change_by_package.is_some()); + assert_eq!(package_change.package, "@scope/bar".to_string()); + assert_eq!(package_change.release_as, "patch".to_string()); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_package_change_exist() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change_bar = + &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + let change_foo = + &Change { package: "@scope/foo".to_string(), release_as: "patch".to_string() }; + + changes.add(change_bar, Some(vec!["production".to_string()])); + changes.add(change_foo, Some(vec!["production".to_string()])); + + let package_change_exist = changes.exist("main", "@scope/bar"); + + assert!(package_change_exist); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_package_change_not_exist() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + monorepo.create_changes()?; + + let changes = Changes::new(root.as_path()); + let change_bar = + &Change { package: "@scope/bar".to_string(), release_as: "patch".to_string() }; + let change_foo = + &Change { package: "@scope/foo".to_string(), release_as: "patch".to_string() }; + + changes.add(change_bar, Some(vec!["production".to_string()])); + changes.add(change_foo, Some(vec!["production".to_string()])); + + let package_change_exist = changes.exist("main", "@scope/baz"); + + assert!(!package_change_exist); + + monorepo.delete_repository(); + + Ok(()) + } +} diff --git a/crates/monorepo/tests/monorepo_config.rs b/crates/monorepo/tests/monorepo_config.rs new file mode 100644 index 00000000..5f222810 --- /dev/null +++ b/crates/monorepo/tests/monorepo_config.rs @@ -0,0 +1,38 @@ +#[cfg(test)] +mod config_tests { + + use std::path::PathBuf; + + use ws_monorepo::{config::get_workspace_config, test::MonorepoWorkspace}; + use ws_std::manager::CorePackageManager; + + #[test] + fn test_workspace_config() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_repository(CorePackageManager::Pnpm)?; + + let config = get_workspace_config(Some(root.clone())); + + assert_eq!(config.package_manager, CorePackageManager::Pnpm); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_default_workspace_config() { + let current_dir = PathBuf::from("."); + let config = get_workspace_config(Some(current_dir.clone())); + + #[cfg(not(windows))] + let canonic_path = &std::fs::canonicalize(current_dir.as_path()).unwrap(); + #[cfg(not(windows))] + let root = canonic_path.as_path(); + #[cfg(windows)] + let root = current_dir.as_path(); + + assert_ne!(config.workspace_root.as_path(), root); + } +} diff --git a/crates/monorepo/tests/monorepo_workspace.rs b/crates/monorepo/tests/monorepo_workspace.rs new file mode 100644 index 00000000..b70162ee --- /dev/null +++ b/crates/monorepo/tests/monorepo_workspace.rs @@ -0,0 +1,91 @@ +#[cfg(test)] +mod workspace_tests { + + use std::fs::File; + use std::io::Write; + + use ws_git::repo::Repository; + use ws_monorepo::test::MonorepoWorkspace; + use ws_monorepo::workspace::Workspace; + use ws_std::manager::CorePackageManager; + + #[test] + fn test_get_npm_packages() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_workspace(CorePackageManager::Npm)?; + + let workspace = Workspace::new(root); + let packages = workspace.get_packages(); + + assert_eq!(packages.len(), 6); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + fn test_get_yarn_packages() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_workspace(CorePackageManager::Yarn)?; + + let workspace = Workspace::new(root); + let packages = workspace.get_packages(); + + assert_eq!(packages.len(), 6); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + #[cfg_attr(target_os = "windows", ignore)] + fn test_get_pnpm_packages() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + monorepo.create_workspace(CorePackageManager::Pnpm)?; + + let workspace = Workspace::new(root); + let packages = workspace.get_packages(); + + assert_eq!(packages.len(), 6); + + monorepo.delete_repository(); + + Ok(()) + } + + #[test] + #[cfg_attr(target_os = "windows", ignore)] + fn test_get_changed_packages() -> Result<(), std::io::Error> { + let monorepo = MonorepoWorkspace::new(); + let root = monorepo.get_monorepo_root().clone(); + let js_path = root.join("packages/package-foo/main.mjs"); + + monorepo.create_workspace(CorePackageManager::Npm)?; + + let workspace = Workspace::new(root.clone()); + let repo = Repository::new(root.as_path()); + + let _ = repo.create_branch("feat/message").expect("Failed to create branch"); + + let mut js_file = File::create(js_path.as_path()).expect("Failed to create main.js file"); + js_file.write_all(r#"export const message = "hello";"#.as_bytes())?; + + let _ = repo.add_all().expect("Failed to add files"); + let _ = repo.commit("feat: message to the world", None, None).expect("Failed to commit"); + + let packages = workspace.get_changed_packages(Some("main".to_string())); + let package = packages.as_slice().first().expect("No packages found"); + + assert_eq!(packages.len(), 1); + assert_eq!(package.package.name, "@scope/package-foo"); + + monorepo.delete_repository(); + + Ok(()) + } +} diff --git a/crates/pkg/Cargo.toml b/crates/pkg/Cargo.toml new file mode 100644 index 00000000..1b329b6b --- /dev/null +++ b/crates/pkg/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "ws-pkg" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lib] +doctest = false + +[lints] +workspace = true + +[dependencies] +thiserror = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +petgraph = "0.6.5" +semver = { version = "1.0.23", features = ["serde"] } +regex = { workspace = true } diff --git a/crates/pkg/src/dependency.rs b/crates/pkg/src/dependency.rs new file mode 100644 index 00000000..2a4afde3 --- /dev/null +++ b/crates/pkg/src/dependency.rs @@ -0,0 +1,145 @@ +use petgraph::{stable_graph::StableDiGraph, Direction}; +use std::fmt::Display; + +/// Must be implemented by the type you wish +pub trait Node { + /// Encodes a dependency relationship. In a Package Manager dependency graph for instance, this might be a (package name, version) tuple. + /// It might also just be the exact same type as the one that implements the Node trait, in which case `Node::matches` can be implemented through simple equality. + type DependencyType; + + /// Returns a slice of dependencies for this Node + fn dependencies(&self) -> &[Self::DependencyType]; + + /// Returns true if the `dependency` can be met by us. + fn matches(&self, dependency: &Self::DependencyType) -> bool; +} + +/// Wrapper around dependency graph nodes. +/// Since a graph might have dependencies that cannot be resolved internally, +/// this wrapper is necessary to differentiate between internally resolved and +/// externally (unresolved) dependencies. +/// An Unresolved dependency does not necessarily mean that it *cannot* be resolved, +/// only that no Node within the graph fulfills it. +#[derive(Debug, Clone)] +pub enum Step<'a, N: Node> { + Resolved(&'a N), + Unresolved(&'a N::DependencyType), +} + +impl<'a, N: Node> Step<'a, N> { + pub fn is_resolved(&self) -> bool { + match self { + Step::Resolved(_) => true, + Step::Unresolved(_) => false, + } + } + + pub fn as_resolved(&self) -> Option<&N> { + match self { + Step::Resolved(node) => Some(node), + Step::Unresolved(_) => None, + } + } + + pub fn as_unresolved(&self) -> Option<&N::DependencyType> { + match self { + Step::Resolved(_) => None, + Step::Unresolved(dependency) => Some(dependency), + } + } +} + +impl<'a, N: Node> Display for Step<'a, N> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Step::Resolved(_node) => write!(f, "Resolved"), + Step::Unresolved(_dependency) => write!(f, "Unresolved"), + } + } +} + +/// The [`DependencyGraph`] structure builds an internal [Directed Graph](`petgraph::stable_graph::StableDiGraph`), which can then be traversed +/// in an order which ensures that dependent Nodes are visited before their parents. +#[derive(Debug, Clone)] +pub struct DependencyGraph<'a, N: Node> { + pub graph: StableDiGraph, &'a N::DependencyType>, +} + +/// The only way to build a [`DependencyGraph`] is from a slice of objects implementing [`Node`]. +/// The graph references the original items, meaning the objects cannot be modified while +/// the [`DependencyGraph`] holds a reference to them. +impl<'a, N> From<&'a [N]> for DependencyGraph<'a, N> +where + N: Node, +{ + fn from(nodes: &'a [N]) -> Self { + let mut graph = StableDiGraph::, &'a N::DependencyType>::new(); + + // Insert the input nodes into the graph, and record their positions. + // We'll be adding the edges next, and filling in any unresolved + // steps we find along the way. + let nodes: Vec<(_, _)> = + nodes.iter().map(|node| (node, graph.add_node(Step::Resolved(node)))).collect(); + + for (node, index) in &nodes { + for dependency in node.dependencies() { + // Check to see if we can resolve this dependency internally. + if let Some((_, dependent)) = nodes.iter().find(|(dep, _)| dep.matches(dependency)) + { + // If we can, just add an edge between the two nodes. + graph.add_edge(*index, *dependent, dependency); + } else { + // If not, create a new "Unresolved" node, and create an edge to that. + let unresolved = graph.add_node(Step::Unresolved(dependency)); + graph.add_edge(*index, unresolved, dependency); + } + } + } + + Self { graph } + } +} + +impl<'a, N> DependencyGraph<'a, N> +where + N: Node, +{ + /// True if all graph [`Node`]s have only references to other internal [`Node`]s. + /// That is, there are no unresolved dependencies between nodes. + pub fn is_internally_resolvable(&self) -> bool { + self.graph.node_weights().all(Step::is_resolved) + } + + /// Get an iterator over unresolved dependencies, without traversing the whole graph. + /// Useful for doing pre-validation or pre-fetching of external dependencies before + /// starting to resolve internal dependencies. + pub fn unresolved_dependencies(&self) -> impl Iterator { + self.graph.node_weights().filter_map(Step::as_unresolved) + } + + pub fn resolved_dependencies(&self) -> impl Iterator { + self.graph.node_weights().filter_map(Step::as_resolved) + } +} + +/// Iterate over the DependencyGraph in an order which ensures dependencies are resolved before each Node is visited. +/// Note: If a `Step::Unresolved` node is returned, it is the caller's responsibility to ensure the dependency is resolved +/// before continuing. +impl<'a, N> Iterator for DependencyGraph<'a, N> +where + N: Node, +{ + type Item = Step<'a, N>; + + fn next(&mut self) -> Option { + // Returns the first node, which does not have any Outgoing + // edges, which means it is terminal. + for index in self.graph.node_indices().rev() { + if self.graph.neighbors_directed(index, Direction::Outgoing).count() == 0 { + return self.graph.remove_node(index); + } + } + + None + } +} diff --git a/crates/pkg/src/lib.rs b/crates/pkg/src/lib.rs new file mode 100644 index 00000000..eb0644cd --- /dev/null +++ b/crates/pkg/src/lib.rs @@ -0,0 +1,2 @@ +pub mod dependency; +pub mod package; diff --git a/crates/pkg/src/package.rs b/crates/pkg/src/package.rs new file mode 100644 index 00000000..15a6369b --- /dev/null +++ b/crates/pkg/src/package.rs @@ -0,0 +1,128 @@ +use regex::Regex; +use semver::{Version, VersionReq}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::fmt::Display; + +use super::dependency::Node; + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct Dependency { + pub name: String, + pub version: VersionReq, +} + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct Package { + pub name: String, + pub version: Version, + pub dependencies: Vec, +} + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct PackageInfo { + pub package: Package, + pub package_json_path: String, + pub package_path: String, + pub package_relative_path: String, + pub pkg_json: Value, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PackageJson { + #[serde(skip_serializing_if = "Option::is_none")] + pub workspaces: Option>, + pub name: String, + pub version: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub private: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub license: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub author: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub homepage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub repository: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub dependencies: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub dev_dependencies: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub peer_dependencies: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub optional_dependencies: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub engines: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub scripts: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub bin: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +/// Package scope metadata extracted from a package name. +pub struct PackageScopeMetadata { + pub full: String, + pub name: String, + pub version: String, + pub path: Option, +} + +/// Extracts the package scope name and version from a package name. +pub fn package_scope_name_version(pkg_name: &str) -> Option { + let regex = Regex::new("^((?:@[^/@]+/)?[^/@]+)(?:@([^/]+))?(/.*)?$").expect("Invalid regex"); + + let matches = regex.captures(pkg_name).expect("Invalid package name"); + + Some(PackageScopeMetadata { + full: matches.get(0).map_or("", |m| m.as_str()).to_string(), + name: matches.get(1).map_or("", |m| m.as_str()).to_string(), + version: matches.get(2).map_or("", |m| m.as_str()).to_string(), + path: matches.get(3).map(|m| m.as_str().to_string()), + }) +} + +impl Node for PackageInfo { + type DependencyType = Dependency; + + fn dependencies(&self) -> &[Self::DependencyType] { + &self.package.dependencies[..] + } + + fn matches(&self, dependency: &Self::DependencyType) -> bool { + let dependency_version = + semver::VersionReq::parse(&dependency.version.to_string()).unwrap(); + let self_version = semver::Version::parse(&self.package.version.to_string()).unwrap(); + + // Check that name is an exact match, and that the dependency + // requirements are fulfilled by our own version + self.package.name == dependency.name && dependency_version.matches(&self_version) + } +} + +impl Node for Package { + type DependencyType = Dependency; + + fn dependencies(&self) -> &[Self::DependencyType] { + &self.dependencies[..] + } + + fn matches(&self, dependency: &Self::DependencyType) -> bool { + let dependency_version = + semver::VersionReq::parse(&dependency.version.to_string()).unwrap(); + let self_version = semver::Version::parse(&self.version.to_string()).unwrap(); + + // Check that name is an exact match, and that the dependency + // requirements are fulfilled by our own version + self.name == dependency.name && dependency_version.matches(&self_version) + } +} + +impl Display for Package { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}@{}", self.name, self.version) + } +} diff --git a/crates/pkg/tests/pkg_dependency.rs b/crates/pkg/tests/pkg_dependency.rs new file mode 100644 index 00000000..5c9c23b6 --- /dev/null +++ b/crates/pkg/tests/pkg_dependency.rs @@ -0,0 +1,272 @@ +#[cfg(test)] +#[allow(clippy::print_stdout)] +mod dependency_tests { + + use std::fmt::Display; + + use petgraph::dot::Dot; + use semver::{BuildMetadata, Prerelease, Version, VersionReq}; + use ws_pkg::dependency::{DependencyGraph, Node, Step}; + + #[derive(Debug)] + struct Package { + name: &'static str, + version: Version, + dependencies: Vec, + } + + #[derive(Debug)] + struct Dependency { + name: &'static str, + version: VersionReq, + } + + impl Node for Package { + type DependencyType = Dependency; + + fn dependencies(&self) -> &[Self::DependencyType] { + &self.dependencies[..] + } + + fn matches(&self, dependency: &Self::DependencyType) -> bool { + self.name == dependency.name && dependency.version.matches(&self.version) + } + } + + impl Display for Package { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}@{}", self.name, self.version) + } + } + + impl Display for Dependency { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}@{}", self.name, self.version) + } + } + + fn build_test_graph() -> Vec { + vec![ + Package { + name: "@scope/bar", + version: semver::Version { + major: 1, + minor: 2, + patch: 3, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![], + }, + Package { + name: "@scope/foo", + version: semver::Version { + major: 1, + minor: 2, + patch: 3, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![Dependency { + name: "@scope/bar", + version: ">=1.0.0".parse().unwrap(), + }], + }, + Package { + name: "@scope/baz", + version: semver::Version { + major: 1, + minor: 2, + patch: 3, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![Dependency { + name: "@scope/foo", + version: ">=1.0.0".parse().unwrap(), + }], + }, + Package { + name: "@scope/charlie", + version: semver::Version { + major: 1, + minor: 2, + patch: 3, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![ + Dependency { name: "@scope/bar", version: ">=1.0.0".parse().unwrap() }, + Dependency { name: "@scope/foo", version: ">=1.0.0".parse().unwrap() }, + ], + }, + Package { + name: "@scope/major", + version: semver::Version { + major: 1, + minor: 2, + patch: 3, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![], + }, + Package { + name: "@scope/tom", + version: semver::Version { + major: 1, + minor: 2, + patch: 3, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![ + Dependency { name: "@scope/unknown", version: ">=1.0.0".parse().unwrap() }, + Dependency { name: "@scope/remote", version: "=3.0.0".parse().unwrap() }, + ], + }, + ] + } + + #[test] + fn test_graph() { + let build = build_test_graph(); + let dependency_graph = DependencyGraph::from(&build[..]); + + println!("{}", Dot::new(&dependency_graph.graph)); + } + + #[test] + fn test_dependencies_synchronous() { + let build = build_test_graph(); + let graph = DependencyGraph::from(&build[..]); + + assert!(!graph.is_internally_resolvable()); + + for node in graph { + match node { + Step::Resolved(build) => println!("build: {:?}", build.name), + Step::Unresolved(lookup) => println!("lookup: {:?}", lookup.name), + } + } + } + + #[test] + fn test_unresolved_dependencies() { + let build = build_test_graph(); + let graph = DependencyGraph::from(&build[..]); + + assert!(!graph.is_internally_resolvable()); + + let unresolved_dependencies: Vec<_> = + graph.unresolved_dependencies().map(|dep| dep.name).collect(); + + assert_eq!(unresolved_dependencies, vec!["@scope/unknown", "@scope/remote"]); + } + + #[test] + fn test_resolved_dependencies() { + let build = build_test_graph(); + let graph = DependencyGraph::from(&build[..]); + + assert!(!graph.is_internally_resolvable()); + + let resolved_dependencies: Vec<_> = + graph.resolved_dependencies().map(|package| package.name).collect(); + + assert_eq!( + resolved_dependencies, + vec![ + "@scope/bar", + "@scope/foo", + "@scope/baz", + "@scope/charlie", + "@scope/major", + "@scope/tom" + ] + ); + } + + #[test] + fn test_generate_dependency_graph() { + let _ = DependencyGraph::from(&build_test_graph()[..]); + } + + #[test] + fn test_internally_resolved() { + let packages = vec![ + Package { + name: "@scope/bar", + version: semver::Version { + major: 1, + minor: 2, + patch: 3, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![], + }, + Package { + name: "@scope/foo", + version: semver::Version { + major: 3, + minor: 2, + patch: 0, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![Dependency { + name: "@scope/bar", + version: "=1.2.3".parse().unwrap(), + }], + }, + Package { + name: "@scope/baz", + version: semver::Version { + major: 11, + minor: 2, + patch: 4, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![Dependency { + name: "@scope/foo", + version: ">=3.0.0".parse().unwrap(), + }], + }, + Package { + name: "@scope/tom", + version: semver::Version { + major: 1, + minor: 2, + patch: 3, + pre: Prerelease::new("").unwrap(), + build: BuildMetadata::EMPTY, + }, + dependencies: vec![ + Dependency { name: "@scope/unknown", version: ">=1.0.0".parse().unwrap() }, + Dependency { name: "@scope/remote", version: "=3.0.0".parse().unwrap() }, + ], + }, + ]; + + let graph = DependencyGraph::from(&packages[..]); + + for package in graph { + match package { + // Print out the package name so we can verify the order ourselves + Step::Resolved(package) => println!("Building {}!", package.name), + + // Since we know that all our Packages only have internal references to each other, + // we can safely ignore any Unresolved steps in the graph. + // + // If for example `second_order` required some unknown package `external_package`, + // iterating over our graph would yield that as a Step::Unresolved *before* + // the `second_order` package. + Step::Unresolved(dependency) => { + println!("External {}@{}!", dependency.name, dependency.version); + } + } + } + } +} diff --git a/crates/pkg/tests/pkg_package.rs b/crates/pkg/tests/pkg_package.rs new file mode 100644 index 00000000..e1787b34 --- /dev/null +++ b/crates/pkg/tests/pkg_package.rs @@ -0,0 +1,49 @@ +#[cfg(test)] +#[allow(clippy::print_stdout)] +#[allow(clippy::uninlined_format_args)] +mod package_tests { + use petgraph::dot::Dot; + use semver::Version; + use ws_pkg::dependency::DependencyGraph; + use ws_pkg::package::{Dependency, Package}; + + fn build_packages() -> Vec { + vec![ + Package { + name: "@scope/bar".to_string(), + version: Version::parse("1.0.0").unwrap(), + dependencies: vec![Dependency { + name: "@scope/foo".to_string(), + version: ">=2.0.0".parse().unwrap(), + }], + }, + Package { + name: "@scope/foo".to_string(), + version: Version::parse("2.0.0").unwrap(), + dependencies: vec![], + }, + Package { + name: "@scope/baz".to_string(), + version: Version::parse("3.0.0").unwrap(), + dependencies: vec![ + Dependency { + name: "@scope/bar".to_string(), + version: ">=1.0.0".parse().unwrap(), + }, + Dependency { + name: "@scope/foo".to_string(), + version: ">=2.0.0".parse().unwrap(), + }, + ], + }, + ] + } + + #[test] + fn test_display() { + let pkgs = build_packages(); + let dependency_graph = DependencyGraph::from(&pkgs[..]); + let dot = Dot::new(&dependency_graph.graph); + println!("{:?}", dot); + } +} diff --git a/crates/standard/Cargo.toml b/crates/standard/Cargo.toml new file mode 100644 index 00000000..59573e64 --- /dev/null +++ b/crates/standard/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "ws-std" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lib] +doctest = false + +[lints] +workspace = true + +[dependencies] +thiserror = { workspace = true } +serde = { workspace = true, features = ["derive"] } diff --git a/crates/standard/src/command.rs b/crates/standard/src/command.rs new file mode 100644 index 00000000..db30a353 --- /dev/null +++ b/crates/standard/src/command.rs @@ -0,0 +1,47 @@ +use std::{ + ffi::OsStr, + fs::canonicalize, + path::Path, + process::{Command, Output, Stdio}, + str::from_utf8, +}; + +use super::error::CommandError; +use super::utils::strip_trailing_newline; + +pub type ComandResult = Result; + +pub fn execute(cmd: S, path: P, args: I, process: F) -> Result +where + P: AsRef, + I: IntoIterator, + S: AsRef, + F: Fn(&str, &Output) -> ComandResult, +{ + let canonic_path = &canonicalize(&path)?; + let output = Command::new(cmd) + .current_dir(canonic_path.as_path()) + .args(args) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to execute command"); + + output.wait_with_output().map_err(CommandError::Run).and_then(|output| { + if output.status.success() { + if let Ok(message) = from_utf8(&output.stdout) { + process(strip_trailing_newline(&message.to_string()).as_str(), &output) + } else { + Err(CommandError::Execution) + } + } else if let Ok(message) = from_utf8(&output.stdout) { + if let Ok(err) = from_utf8(&output.stderr) { + Err(CommandError::Failure { stdout: message.to_string(), stderr: err.to_string() }) + } else { + Err(CommandError::Execution) + } + } else { + Err(CommandError::Execution) + } + }) +} diff --git a/crates/standard/src/error.rs b/crates/standard/src/error.rs new file mode 100644 index 00000000..740e93ce --- /dev/null +++ b/crates/standard/src/error.rs @@ -0,0 +1,11 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum CommandError { + #[error("Fail to run command")] + Run(#[from] std::io::Error), + #[error("Fail to execute command")] + Execution, + #[error("command failed with the following stdout: {stdout} stderr: {stderr}")] + Failure { stdout: String, stderr: String }, +} diff --git a/crates/standard/src/lib.rs b/crates/standard/src/lib.rs new file mode 100644 index 00000000..4cc43af8 --- /dev/null +++ b/crates/standard/src/lib.rs @@ -0,0 +1,5 @@ +pub mod command; +pub mod error; +pub mod manager; +pub mod paths; +pub mod utils; diff --git a/crates/standard/src/manager.rs b/crates/standard/src/manager.rs new file mode 100644 index 00000000..fff7b2a3 --- /dev/null +++ b/crates/standard/src/manager.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + fmt::{Display, Formatter, Result as StdResult}, + path::Path, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum CorePackageManager { + Npm, + Yarn, + Pnpm, + Bun, +} + +impl From for CorePackageManager { + fn from(manager: String) -> Self { + match manager.as_str() { + "npm" => Self::Npm, + "yarn" => Self::Yarn, + "pnpm" => Self::Pnpm, + "bun" => Self::Bun, + _ => panic!("Unable to identify package manager: {manager}"), + } + } +} + +impl Display for CorePackageManager { + fn fmt(&self, f: &mut Formatter) -> StdResult { + match self { + Self::Npm => write!(f, "npm"), + Self::Yarn => write!(f, "yarn"), + Self::Pnpm => write!(f, "pnpm"), + Self::Bun => write!(f, "bun"), + } + } +} + +/// Detects which package manager is available in the workspace. +pub fn detect_package_manager(path: &Path) -> Option { + let package_manager_files = HashMap::from([ + ("package-lock.json", CorePackageManager::Npm), + ("npm-shrinkwrap.json", CorePackageManager::Npm), + ("yarn.lock", CorePackageManager::Yarn), + ("pnpm-lock.yaml", CorePackageManager::Pnpm), + ("bun.lockb", CorePackageManager::Bun), + ]); + + for (file, package_manager) in package_manager_files { + let lock_file = path.join(file); + + if lock_file.exists() { + return Some(package_manager); + } + } + + if let Some(parent) = path.parent() { + return detect_package_manager(parent); + } + + None +} diff --git a/crates/standard/src/paths.rs b/crates/standard/src/paths.rs new file mode 100644 index 00000000..2f6a6b92 --- /dev/null +++ b/crates/standard/src/paths.rs @@ -0,0 +1,74 @@ +use super::command::execute; + +use std::{ + env, + path::{Path, PathBuf}, +}; + +/// Get the project root path. +pub fn get_project_root_path(root: Option) -> Option { + let env_dir = match root { + Some(dir) => Ok(dir), + None => env::current_dir(), + }; + + let current_dir = match env_dir { + Ok(dir) => dir, + _ => PathBuf::from("./"), + }; + let current_path = current_dir.as_path(); + let git_root_dir = walk_reverse_dir(current_path); + + let project_root = match git_root_dir { + Some(current) => current, + None => { + let search_root = get_git_root_dir(current_path); + search_root.unwrap_or(current_path.to_str().unwrap().to_string()) + } + }; + + Some(PathBuf::from(project_root)) +} + +/// Get the git root directory. +fn get_git_root_dir(dir: &Path) -> Option { + let top_level = + execute("git", dir, ["rev-parse", "--show-toplevel"], |stdout, _| Ok(stdout.to_string())); + + match top_level { + Ok(output) => { + if output.is_empty() { + return None; + } + + Some(output) + } + Err(_) => None, + } +} + +/// Walk reverse directory to find the root project. +fn walk_reverse_dir(path: &Path) -> Option { + let current_path = path.to_path_buf(); + let map_files = vec![ + ("package-lock.json", "npm"), + ("npm-shrinkwrap.json", "npm"), + ("yarn.lock", "yarn"), + ("pnpm-lock.yaml", "pnpm"), + ("bun.lockb", "bun"), + ]; + + for (file, _) in map_files { + let lock_file = current_path.join(file); + + if lock_file.exists() { + return Some(current_path.to_str().unwrap().to_string()); + } + } + + if let Some(parent) = path.parent() { + return walk_reverse_dir(parent); + } + + None +} diff --git a/crates/standard/src/utils.rs b/crates/standard/src/utils.rs new file mode 100644 index 00000000..fe20fb10 --- /dev/null +++ b/crates/standard/src/utils.rs @@ -0,0 +1,4 @@ +/// Strips the trailing newline from a string. +pub fn strip_trailing_newline(input: &String) -> String { + input.strip_suffix("\r\n").or(input.strip_suffix("\n")).unwrap_or(input).trim().to_string() +} diff --git a/crates/standard/tests/std_command.rs b/crates/standard/tests/std_command.rs new file mode 100644 index 00000000..bc7abd9e --- /dev/null +++ b/crates/standard/tests/std_command.rs @@ -0,0 +1,23 @@ +#[cfg(test)] +mod commands_tests { + use ws_std::{command::execute, error::CommandError}; + + #[test] + fn test_git_command() -> Result<(), CommandError> { + let result = execute("git", ".", ["--version"], |_message, output| { + Ok(output.status.code().unwrap()) + })?; + + assert!(result == 0); + Ok(()) + } + + #[test] + fn test_git_version() -> Result<(), CommandError> { + let result = + execute("git", ".", ["--version"], |message, _output| Ok(message.to_string()))?; + + assert!(result.contains("git version")); + Ok(()) + } +} diff --git a/crates/standard/tests/std_manager.rs b/crates/standard/tests/std_manager.rs new file mode 100644 index 00000000..e4be4e70 --- /dev/null +++ b/crates/standard/tests/std_manager.rs @@ -0,0 +1,111 @@ +#[cfg(test)] +mod manager_tests { + use std::{ + env::temp_dir, + fs::{create_dir, remove_dir_all, File}, + io::Write, + path::PathBuf, + }; + + #[cfg(not(windows))] + use std::os::unix::fs::PermissionsExt; + + #[cfg(not(windows))] + use std::fs::set_permissions; + + use ws_std::manager::{detect_package_manager, CorePackageManager}; + + fn create_workspace(manager_file: &str) -> Result { + let temp_dir = temp_dir(); + let monorepo_root_dir = temp_dir.join("monorepo-workspace"); + + if monorepo_root_dir.exists() { + remove_dir_all(&monorepo_root_dir)?; + } + + create_dir(&monorepo_root_dir)?; + + let mut readme_file = File::create(monorepo_root_dir.join(manager_file).as_path())?; + readme_file.write_all(b"{}")?; + + #[cfg(not(windows))] + set_permissions(&monorepo_root_dir, std::fs::Permissions::from_mode(0o777))?; + + Ok(monorepo_root_dir) + } + + #[test] + fn test_npm_manager() -> Result<(), std::io::Error> { + let root = &create_workspace("package-lock.json")?; + + let core_manager = detect_package_manager(root.as_path()); + let manager = core_manager.unwrap(); + + assert_eq!(manager, CorePackageManager::Npm); + + remove_dir_all(root)?; + + Ok(()) + } + + #[test] + fn test_pnpm_manager() -> Result<(), std::io::Error> { + let root = &create_workspace("pnpm-lock.yaml")?; + + let core_manager = detect_package_manager(root.as_path()); + let manager = core_manager.unwrap(); + + assert_eq!(manager, CorePackageManager::Pnpm); + + remove_dir_all(root)?; + + Ok(()) + } + + #[test] + fn test_yarn_manager() -> Result<(), std::io::Error> { + let root = &create_workspace("yarn.lock")?; + + let core_manager = detect_package_manager(root.as_path()); + let manager = core_manager.unwrap(); + + assert_eq!(manager, CorePackageManager::Yarn); + + remove_dir_all(root)?; + + Ok(()) + } + + #[test] + fn test_bun_manager() -> Result<(), std::io::Error> { + let root = &create_workspace("bun.lockb")?; + + let core_manager = detect_package_manager(root.as_path()); + let manager = core_manager.unwrap(); + + assert_eq!(manager, CorePackageManager::Bun); + + remove_dir_all(root)?; + + Ok(()) + } + + #[test] + fn test_from_manager() { + let npm_manager = CorePackageManager::from("npm".to_string()); + let yarn_manager = CorePackageManager::from("yarn".to_string()); + let pnpm_manager = CorePackageManager::from("pnpm".to_string()); + let bun_manager = CorePackageManager::from("bun".to_string()); + + assert_eq!(npm_manager, CorePackageManager::Npm); + assert_eq!(yarn_manager, CorePackageManager::Yarn); + assert_eq!(pnpm_manager, CorePackageManager::Pnpm); + assert_eq!(bun_manager, CorePackageManager::Bun); + } + + #[test] + #[should_panic(expected = "Unable to identify package manager: unknown")] + fn test_unknown_manager() { + let _ = CorePackageManager::from("unknown".to_string()); + } +} diff --git a/crates/standard/tests/std_paths.rs b/crates/standard/tests/std_paths.rs new file mode 100644 index 00000000..160e9145 --- /dev/null +++ b/crates/standard/tests/std_paths.rs @@ -0,0 +1,53 @@ +#[cfg(test)] +mod paths_tests { + use std::{ + env::temp_dir, + fs::{create_dir, remove_dir_all, File}, + io::Write, + ops::Deref, + path::PathBuf, + }; + + #[cfg(not(windows))] + use std::os::unix::fs::PermissionsExt; + + #[cfg(not(windows))] + use std::fs::set_permissions; + + use ws_std::paths::get_project_root_path; + + fn create_workspace() -> Result { + let temp_dir = temp_dir(); + let monorepo_root_dir = temp_dir.join("monorepo-workspace"); + + if monorepo_root_dir.exists() { + remove_dir_all(&monorepo_root_dir)?; + } + + create_dir(&monorepo_root_dir)?; + + let mut readme_file = File::create(monorepo_root_dir.join("package-lock.json").as_path())?; + readme_file.write_all(b"{}")?; + + #[cfg(not(windows))] + set_permissions(&monorepo_root_dir, std::fs::Permissions::from_mode(0o777))?; + + Ok(monorepo_root_dir) + } + + #[test] + fn test_project_root_path() -> Result<(), std::io::Error> { + let root = &create_workspace()?; + + let result = get_project_root_path(Some(root.deref().to_path_buf())); + + remove_dir_all(root)?; + + let root = root.display().to_string(); + let project_root = result.unwrap().display().to_string(); + + assert_eq!(root, project_root); + + Ok(()) + } +} diff --git a/justfile b/justfile new file mode 100644 index 00000000..3045199d --- /dev/null +++ b/justfile @@ -0,0 +1,59 @@ +set windows-shell := ["powershell"] +set shell := ["bash", "-cu"] + +_default: + just --list -u + +setup: + # Rust related setup + cargo install cargo-binstall + cargo binstall taplo-cli cargo-insta cargo-deny cargo-shear -y + # Node.js related setup + corepack enable pnpm + pnpm install + @echo "โœ…โœ…โœ… Setup complete!" + +roll: + just roll-rust + just roll-node + # just roll-repo + # just ued + +roll-rust: + just check-rust + just test-rust + just lint-rust + +roll-node: + just test-node + just check-node + just lint-node + +# run tests for both Rust and Node.js +test: test-rust + +test-rust: + cargo test --workspace --exclude workspace_binding -- --test-threads=1 --nocapture + +# Fix formatting issues both for Rust, Node.js and all files in the repository +fmt: fmt-rust + +fmt-rust: + cargo fmt --all -- --emit=files + taplo fmt + +# Lint the codebase +lint: lint-rust + +lint-rust: + cargo fmt --all -- --check + cargo clippy --workspace --all-targets -- --deny warnings + cargo shear + +# Fix formatting and some linting issues +fix: fix-rust + +fix-rust: + just fmt-rust + cargo fix --allow-dirty --allow-staged + cargo shear --fix diff --git a/package.json b/package.json new file mode 100644 index 00000000..3bedb2ea --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "workspace-tools-monorepo", + "description": "Tools for monorepos", + "private": true, + "packageManager": "pnpm@9.4.0", + "engines": { + "node": ">=18.20.3" + }, + "workspaces": [ + "packages/*" + ], + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "scripts": { + "prepare": "husky install", + "ci:build-release-binding": "pnpm --filter @websublime/workspace-tools run build-binding:release" + }, + "devDependencies": { + "@types/node": "22.5.2", + "@taplo/cli": "^0.7.0", + "lint-staged": "^15.2.2", + "npm-run-all2": "^6.1.2", + "oxlint": "^0.9.2", + "husky": "^9.0.11", + "prettier": "^3.2.5", + "typescript": "^5.4.5" + }, + "prettier": { + "printWidth": 120, + "semi": false, + "trailingComma": "all", + "singleQuote": true, + "arrowParens": "always" + }, + "lint-staged": { + "*.@(js|ts|tsx)": [ + "oxlint --fix" + ], + "*.@(js|ts|tsx|yml|yaml|md|json)": [ + "prettier --write" + ], + "*.toml": [ + "taplo format" + ] + } +} diff --git a/packages/workspace-tools/.github/renovate.json b/packages/workspace-tools/.github/renovate.json new file mode 100644 index 00000000..cc26fc16 --- /dev/null +++ b/packages/workspace-tools/.github/renovate.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:base", "group:allNonMajor", ":preserveSemverRanges", ":disablePeerDependencies"], + "labels": ["dependencies"], + "packageRules": [ + { + "matchPackageNames": ["@napi/cli", "napi", "napi-build", "napi-derive"], + "addLabels": ["napi-rs"], + "groupName": "napi-rs" + }, + { + "matchPackagePatterns": ["^eslint", "^@typescript-eslint"], + "groupName": "linter" + } + ], + "commitMessagePrefix": "chore: ", + "commitMessageAction": "bump up", + "commitMessageTopic": "{{depName}} version", + "ignoreDeps": [] +} diff --git a/packages/workspace-tools/.github/workflows/CI.yml b/packages/workspace-tools/.github/workflows/CI.yml new file mode 100644 index 00000000..ef68eae4 --- /dev/null +++ b/packages/workspace-tools/.github/workflows/CI.yml @@ -0,0 +1,364 @@ +name: CI +env: + DEBUG: napi:* + APP_NAME: workspace-tools + MACOSX_DEPLOYMENT_TARGET: '10.13' +permissions: + contents: write + id-token: write +'on': + push: + branches: + - main + tags-ignore: + - '**' + paths-ignore: + - '**/*.md' + - LICENSE + - '**/*.gitignore' + - .editorconfig + - docs/** + pull_request: null +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + build: + strategy: + fail-fast: false + matrix: + settings: + - host: macos-latest + target: x86_64-apple-darwin + build: pnpm build --target x86_64-apple-darwin + - host: windows-latest + build: pnpm build --target x86_64-pc-windows-msvc + target: x86_64-pc-windows-msvc + - host: windows-latest + build: pnpm build --target i686-pc-windows-msvc + target: i686-pc-windows-msvc + - host: ubuntu-latest + target: x86_64-unknown-linux-gnu + build: pnpm build --target x86_64-unknown-linux-gnu --use-napi-cross + - host: ubuntu-latest + target: x86_64-unknown-linux-musl + build: pnpm build --target x86_64-unknown-linux-musl -x + - host: macos-14 + target: aarch64-apple-darwin + build: pnpm build --target aarch64-apple-darwin + - host: ubuntu-latest + target: aarch64-unknown-linux-gnu + build: pnpm build --target aarch64-unknown-linux-gnu --use-napi-cross + - host: ubuntu-latest + target: armv7-unknown-linux-gnueabihf + build: pnpm build --target armv7-unknown-linux-gnueabihf --use-napi-cross + #- host: ubuntu-latest + # target: aarch64-linux-android + # build: pnpm build --target aarch64-linux-android + #- host: ubuntu-latest + # target: armv7-linux-androideabi + # build: pnpm build --target armv7-linux-androideabi + - host: ubuntu-latest + target: aarch64-unknown-linux-musl + build: pnpm build --target aarch64-unknown-linux-musl -x + - host: windows-latest + target: aarch64-pc-windows-msvc + build: pnpm build --target aarch64-pc-windows-msvc + #- host: ubuntu-latest + # target: wasm32-wasip1-threads + # build: pnpm build --target wasm32-wasip1-threads + name: stable - ${{ matrix.settings.target }} - node@20 + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v4 + - name: setup pnpm + uses: pnpm/action-setup@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + - name: Install + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + targets: ${{ matrix.settings.target }} + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + ~/.napi-rs + .cargo-cache + target/ + key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} + - uses: goto-bus-stop/setup-zig@v2 + if: ${{ contains(matrix.settings.target, 'musl') }} + with: + version: 0.12.0 + - name: Setup toolchain + run: ${{ matrix.settings.setup }} + if: ${{ matrix.settings.setup }} + shell: bash + - name: Install dependencies + run: pnpm install + - name: Setup node x86 + uses: actions/setup-node@v4 + if: matrix.settings.target == 'i686-pc-windows-msvc' + with: + node-version: 20 + cache: pnpm + architecture: x86 + - name: Build + run: ${{ matrix.settings.build }} + shell: bash + - name: Upload artifact + uses: actions/upload-artifact@v4 + if: matrix.settings.target != 'wasm32-wasip1-threads' + with: + name: bindings-${{ matrix.settings.target }} + path: "*.node" + if-no-files-found: error + + - name: Upload artifact + uses: actions/upload-artifact@v4 + if: matrix.settings.target == 'wasm32-wasip1-threads' + with: + name: bindings-${{ matrix.settings.target }} + path: "*.wasm" + if-no-files-found: error + #build-freebsd: + # runs-on: macos-13 + # name: Build FreeBSD + # steps: + # - uses: actions/checkout@v4 + # - name: Build + # id: build + # uses: cross-platform-actions/action@v0.24.0 + # env: + # DEBUG: napi:* + # RUSTUP_IO_THREADS: 1 + # with: + # operating_system: freebsd + # version: '13.2' + # memory: 8G + # cpu_count: 3 + # environment_variables: 'DEBUG RUSTUP_IO_THREADS' + # shell: bash + # run: | + # sudo pkg install -y -f curl node libnghttp2 npm + # sudo npm install -g corepack + # curl https://sh.rustup.rs -sSf --output rustup.sh + # sh rustup.sh -y --profile minimal --default-toolchain beta + # corepack prepare + # corepack enable + # source "$HOME/.cargo/env" + # echo "~~~~ rustc --version ~~~~" + # rustc --version + # echo "~~~~ node -v ~~~~" + # node -v + # echo "~~~~ pnpm --version ~~~~" + # pnpm --version + # pwd + # ls -lah + # whoami + # env + # freebsd-version + # pnpm install + # pnpm build + # rm -rf node_modules + # rm -rf target + # - name: Upload artifact + # uses: actions/upload-artifact@v4 + # with: + # name: bindings-freebsd + # path: ${{ env.APP_NAME }}.*.node + # if-no-files-found: error + test-macOS-windows-binding: + name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + settings: + - host: windows-latest + target: x86_64-pc-windows-msvc + architecture: x64 + - host: macos-latest + target: x86_64-apple-darwin + architecture: x64 + - host: macos-latest + target: aarch64-apple-darwin + architecture: arm64 + node: + - '18' + - '20' + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v4 + - name: setup pnpm + uses: pnpm/action-setup@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: pnpm + architecture: ${{ matrix.settings.architecture }} + - name: Install dependencies + run: pnpm install + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-${{ matrix.settings.target }} + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Test bindings + run: pnpm test + test-linux-binding: + name: Test ${{ matrix.target }} - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + - aarch64-unknown-linux-gnu + - aarch64-unknown-linux-musl + - armv7-unknown-linux-gnueabihf + node: + - '18' + - '20' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: setup pnpm + uses: pnpm/action-setup@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: pnpm + - name: Output docker params + id: docker + run: | + node -e " + if ('${{ matrix.target }}'.startsWith('aarch64')) { + console.log('PLATFORM=linux/arm64') + } else if ('${{ matrix.target }}'.startsWith('armv7')) { + console.log('PLATFORM=linux/arm/v7') + } else { + console.log('PLATFORM=linux/amd64') + } + " >> $GITHUB_OUTPUT + node -e " + if ('${{ matrix.target }}'.endsWith('-musl')) { + console.log('IMAGE=node:${{ matrix.node }}-alpine') + } else { + console.log('IMAGE=node:${{ matrix.node }}-slim') + } + " >> $GITHUB_OUTPUT + echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT + # use --force to download the all platform/arch dependencies + - name: Install dependencies + run: pnpm install --force + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-${{ matrix.target }} + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: all + - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + - name: Test bindings + uses: addnab/docker-run-action@v3 + with: + image: ${{ steps.docker.outputs.IMAGE }} + options: -v ${{ steps.docker.outputs.PNPM_STORE_PATH }}:${{ steps.docker.outputs.PNPM_STORE_PATH }} -v ${{ github.workspace }}:${{ github.workspace }} -w ${{ github.workspace }} --platform ${{ steps.docker.outputs.PLATFORM }} + run: npm run test + #test-wasi: + # name: Test WASI target + # needs: + # - build + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v4 + # - name: setup pnpm + # uses: pnpm/action-setup@v4 + # - name: Setup node + # uses: actions/setup-node@v4 + # with: + # node-version: 20 + # cache: pnpm + # - name: Install dependencies + # run: pnpm install + # - name: Download artifacts + # uses: actions/download-artifact@v4 + # with: + # name: bindings-wasm32-wasip1-threads + # path: . + # - name: List packages + # run: ls -R . + # shell: bash + # - name: Test bindings + # run: pnpm test + # env: + # NAPI_RS_FORCE_WASI: 1 + publish: + name: Publish + runs-on: ubuntu-latest + needs: + #- build-freebsd + - test-macOS-windows-binding + - test-linux-binding + #- test-wasi + steps: + - uses: actions/checkout@v4 + - name: setup pnpm + uses: pnpm/action-setup@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + - name: Install dependencies + run: pnpm install + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + - name: create npm dirs + run: pnpm napi create-npm-dirs + - name: Move artifacts + run: pnpm artifacts + - name: List packages + run: ls -R ./npm + shell: bash + - name: Publish + run: | + npm config set provenance true + if git log -1 --pretty=%B | grep "^v\?[0-9]\+\.[0-9]\+\.[0-9]\+$"; + then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --access public + elif git log -1 --pretty=%B | grep "^v\?[0-9]\+\.[0-9]\+\.[0-9]\+"; + then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --tag next --access public + else + echo "Not a release, skipping publish" + fi + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/packages/workspace-tools/.github/workflows/lint.yml b/packages/workspace-tools/.github/workflows/lint.yml new file mode 100644 index 00000000..b1d65718 --- /dev/null +++ b/packages/workspace-tools/.github/workflows/lint.yml @@ -0,0 +1,42 @@ +name: Lint + +on: + push: + branches: + - main + tags-ignore: + - '**' + pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: setup pnpm + uses: pnpm/action-setup@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install + uses: dtolnay/rust-toolchain@stable + with: + components: clippy, rustfmt + + - name: Install dependencies + run: pnpm install + + - name: ESLint + run: pnpm lint + + - name: Cargo fmt + run: cargo fmt -- --check + + - name: Clippy + run: cargo clippy diff --git a/packages/workspace-tools/.gitignore b/packages/workspace-tools/.gitignore new file mode 100644 index 00000000..71e2ee39 --- /dev/null +++ b/packages/workspace-tools/.gitignore @@ -0,0 +1,195 @@ +### Created by https://www.gitignore.io +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### Created by https://www.gitignore.io +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +*.node +*.wasm + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache +.stylelintcache + +# SvelteKit build / generate output +.svelte-kit + +### Created by https://www.gitignore.io +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud diff --git a/packages/workspace-tools/.npmrc b/packages/workspace-tools/.npmrc new file mode 100644 index 00000000..581701ce --- /dev/null +++ b/packages/workspace-tools/.npmrc @@ -0,0 +1 @@ +package-manager-strict=false \ No newline at end of file diff --git a/packages/workspace-tools/.prettierignore b/packages/workspace-tools/.prettierignore new file mode 100644 index 00000000..89883365 --- /dev/null +++ b/packages/workspace-tools/.prettierignore @@ -0,0 +1,5 @@ +target +.yarn +index.js +index.d.ts +pnpm-lock.yaml \ No newline at end of file diff --git a/packages/workspace-tools/CHANGELOG.md b/packages/workspace-tools/CHANGELOG.md new file mode 100644 index 00000000..d7d7d020 --- /dev/null +++ b/packages/workspace-tools/CHANGELOG.md @@ -0,0 +1,277 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [1.0.2] - 2024-09-23 + +### ๐Ÿ› Bug Fixes + +- Skipping non conventional commits + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #21 from websublime/fix/conventional-commits + +## [1.0.1] - 2024-09-19 + +### ๐Ÿ› Bug Fixes + +- Release as snapshot for any branch except main + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #20 from websublime/fix/release-as + +## [1.0.0] - 2024-09-19 + +### ๐Ÿš€ Features + +- Bumps by changes + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #19 from websublime/feature/bumps-by-changes + +## [0.7.14] - 2024-09-12 + +### ๐Ÿ› Bug Fixes + +- Fix bump duplicate entries + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #18 from websublime/fix/bump-duplication + +## [0.7.13] - 2024-09-11 + +### ๐Ÿ› Bug Fixes + +- Changes exist diff + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #17 from websublime/fix/change-exist-diff + +## [0.7.12] - 2024-09-09 + +### ๐Ÿ› Bug Fixes + +- Workspace node tools update to fix check and duplicated entries + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #16 from websublime/fix/change-exist-duplicate + +## [0.7.11] - 2024-08-07 + +### ๐Ÿ› Bug Fixes + +- Update workspace node tools to fix issue in change exist + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #15 from websublime/fix/change-exist-any-to-all + +## [0.7.10] - 2024-08-07 + +### ๐Ÿš€ Features + +- Snapshot unique version generation + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #14 from websublime/feature/snapshot-unique + +## [0.7.9] - 2024-08-06 + +### โš™๏ธ Miscellaneous Tasks + +- Update workspace-node-tools to fix the issue +- Merge pull request #13 from websublime/fix/dependency-release + +## [0.7.8] - 2024-07-24 + +### โš™๏ธ Miscellaneous Tasks + +- Update workspace-node-tools crate +- Merge pull request #12 from websublime/feature/json-parse + +## [0.7.7] - 2024-07-24 + +### ๐Ÿ› Bug Fixes + +- Change exist signature + +### โš™๏ธ Miscellaneous Tasks + +- Update workspace-node-tools crate +- Update readme api +- Merge pull request #11 from websublime/feature/pretty-json-pnpm + +## [0.7.6] - 2024-07-23 + +### ๐Ÿš€ Features + +- Apply bumps and new git commands + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #10 from websublime/feature/apply-bumps + +## [0.7.5] - 2024-07-22 + +### ๐Ÿš€ Features + +- Add changes file exist to api + +### โš™๏ธ Miscellaneous Tasks + +- Merge pull request #9 from websublime/feature/changes-file-exist + +## [0.7.4] - 2024-07-19 + +### ๐Ÿš€ Features + +- Changes implementation +- GetChanges and types + +### โš™๏ธ Miscellaneous Tasks + +- Bump patch version +- Update readme api documentation +- Merge pull request #8 from websublime/feature/changes + +## [0.7.3] - 2024-07-19 + +### ๐Ÿ› Bug Fixes + +- Canonical paths from tools crate + +### โš™๏ธ Miscellaneous Tasks + +- Fix readme file +- Update workspace-tools crate +- Jsdoc generation for functions +- Tests +- Tests +- Merge pull request #7 from websublime/fix/canonical-paths + +## [0.7.2] - 2024-07-19 + +### ๐Ÿ› Bug Fixes + +- Binding tests + +### โš™๏ธ Miscellaneous Tasks + +- Bump patch version + +## [0.7.1] - 2024-07-19 + +### ๐Ÿ› Bug Fixes + +- Binding test + +## [0.7.0] - 2024-07-19 + +### ๐Ÿš€ Features + +- GetBumps and update to new tools lib + +## [0.6.0] - 2024-07-12 + +### ๐Ÿš€ Features + +- Get branch name for a commit id + +### โš™๏ธ Miscellaneous Tasks + +- Fix typos in readme file +- Merge pull request #6 from websublime/feature/git-commit-branch +- Bump version + +## [0.5.0] - 2024-07-11 + +### โš™๏ธ Miscellaneous Tasks + +- Update readme +- Validation for package json +- Merge pull request #5 from websublime/feature/package-info-updates +- Maintenance build + +## [0.4.2] - 2024-07-08 + +### โš™๏ธ Miscellaneous Tasks + +- Update builded files + +## [0.4.1] - 2024-07-08 + +### โš™๏ธ Miscellaneous Tasks + +- Bump version +- Maintenance changelog + +## [0.4.0] - 2024-07-08 + +### ๐Ÿš€ Features + +- Retrive changed packages + +### โš™๏ธ Miscellaneous Tasks + +- Bump version +- Maintenance changelog +- Maintenance patch update +- Disable freebsd +- Merge pull request #4 from websublime/feature/changed-packages + +## [0.3.0] - 2024-07-06 + +### ๐Ÿš€ Features + +- Git multiple commands + +### ๐Ÿ› Bug Fixes + +- Useless conversion into + +### โš™๏ธ Miscellaneous Tasks + +- Bump version +- Maintenance changelog +- Maintenance format +- Merge pull request #3 from websublime/feature/git + +## [0.2.0] - 2024-07-04 + +### ๐Ÿš€ Features + +- Git fetch commands + +### ๐Ÿ› Bug Fixes + +- Redundant match + +### โš™๏ธ Miscellaneous Tasks + +- Maintenance changelog and tag +- Merge pull request #2 from websublime/feature/git-fetch + +## [0.1.0] - 2024-07-04 + +### ๐Ÿš€ Features + +- Initial integration with workspace-node-tools crate + +### โš™๏ธ Miscellaneous Tasks + +- Rename secret in action +- Maintenance format and reduce os support +- Rename npm bindings +- Rename wasm bindings +- Enable wasm32-wasi build +- Disable wasm32-wasi build +- Merge pull request #1 from websublime/feature/integrations + + diff --git a/packages/workspace-tools/LICENSE b/packages/workspace-tools/LICENSE new file mode 100644 index 00000000..6c7f5625 --- /dev/null +++ b/packages/workspace-tools/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 N-API for Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/workspace-tools/README.md b/packages/workspace-tools/README.md new file mode 100644 index 00000000..fe6c1de6 --- /dev/null +++ b/packages/workspace-tools/README.md @@ -0,0 +1,97 @@ +# `@websublime/workspace-tools` + +![https://github.com/websublime/workspace-node-tools/actions](https://github.com/websublime/workspace-node-tools/workflows/CI/badge.svg) + +> Tools to use on github actions for bumping version, changelogs on a monorepo. + +## Install this package + +``` +pnpm add @websublime/workspace-tools +``` + +## Usage + +This package offer a set of functions to retrieve information about the monorepo and the packages that contain. It support all package managers including Bun (WIP). + +## API + +| Function | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| `getProjectRootPath(root?: string): string or undefined` | Get the root path of the project. | +| `getDefinedPackageManager(root?: string): string or undefined` | Get the package manager defined in the project. | +| `detectPackageManager(root: string): PackageManager or undefined` | Detect the package manager defined in the project. | +| `getPackages(cwd?: string): Array` | Get the list of packages in the monorepo. | +| `getPackageInfo(package_name: string, cwd?: string): PackageInfo` | Get PackageInfo for a package. | +| `getChangedPackages(sha?: string, cwd: string): Array` | Get the list of packages that have changed since the given sha ('main'). | +| `git_add(file: string, cwd?: string): boolean` | Stage a file. | +| `git_add_all(cwd?: string): boolean` | Stage all files. | +| `git_config(name: string, email: string, cwd?: string): boolean` | Git config user name and email. | +| `gitFetchAll(cwd?: string, fetch_tags?: boolean): boolean` | Execute a `fetch` command to get the latest changes from the remote repository. You can also retrieve tags | +| `gitCommit(message: string, body?: string, footer?: string cwd?: string): boolean` | Commit the changes. | +| `gitTag(tag: string, message?: string, cwd?: string): boolean` | Tag the repository with the given tag. | +| `gitPush(cwd?: string, follow_tags?: boolean): boolean` | Push the changes to the remote repository, including optional tags. | +| `gitCurrentBranch(cwd?: string): string or undefined` | Get the current branch name. | +| `gitCurrentSha(cwd?: string): string` | Get's the current commit id. | +| `gitPreviousSha(cwd?: string): string or undefined` | Get's the previous commit id. | +| `gitFirstSha(cwd?: string, branch?: string): string or undefined` | Get's the first commit id in a branch. Compare is done between branch..Head, and it should be used as main..HEAD | +| `isWorkdirUnclean(cwd?: string): boolean` | Check if the workdir is unclean (uncommited changes). | +| `gitCommitBranchName(sha: string, cwd?: string): string or undefined` | Get the branch name for the commit id. | +| `gitAllFilesChangedSinceSha(sha: string, cwd?: string): Array` | Get all files changed sinc branch, commit id etc. | +| `getDivergedCommit(sha: string, cwd?: string): string or undefined` | Get the diverged commit from the given sha (main). | +| `getCommitsSince(cwd?: string, since?: string, relative?: string): Array` | Get the commits since the given sha (main) for a particular package. | +| `getAllFilesChangedSinceBranch(package_info: Array, branch: string, cwd?: string): Array` | Get all the files changed for a branch (main). | +| `getLastKnownPublishTagInfoForPackage(package_info: PackageInfo, cwd?: string): Array` | Get the last known publish tag info for a particular package. | +| `getLastKnownPublishTagInfoForAllPackages(package_info: Array, cwd?: string): Array` | Get the last known publish tag info for all packages. | +| `getRemoteOrLocalTags(cwd?: string, local?: boolean): Array` | Get all the tags in the remote or local repository. | +| `getConventionalForPackage(package_info: PackageInfo, no_fetch_all?: boolean cwd?: string, conventional_options?: ConventionalPackageOptions): ConventionalPackage` | Get the conventional commits for a particular package, changelog output and package info. | +| `getBumps(options: BumpOptions): Array` | Output bumps version for packages and it's dependencies | +| `initChanges(cwd?: string, change_options?: ChangesOptions): ChangesFileData` | Creat changes file or retrieve is data if already exist | +| `addChange(change: Change, cwd?: string): boolean` | Adds a new change to the change file | +| `removeChange(branch_name: String, cwd?: string): boolean` | Removes the change from the changes files. | +| `changeExist(branch_name: string, packages_name: Array, cwd?: string): boolean` | Check if change already exist. | +| `getChange(branch_name: string, cwd?: string): Array` | Get the list of changes for the branch. | +| `getChanges(cwd?: string): Changes` | Get all changes. | +| `getPackageChange(package_name: string, branch: string, cwd?: string): Changes` | Get a change by package name. | +| `changesFileExist(cwd?: string): boolean` | Check if `.changes.json` file exist | + +## Develop requirements + +- Install the latest `Rust` +- Install `Node.js@16+` which fully supported `Node-API` +- Run `corepack enable` + +## Test in local + +- pnpm +- pnpm build +- pnpm test + +And you will see: + +```bash +$ ava --verbose + + โœ” get defined package manager + โ”€ + + 2 tests passed +โœจ Done in 1.12s. +``` + +## Release package + +Ensure you have set your **NPM_TOKEN** in the `GitHub` project setting. + +In `Settings -> Secrets`, add **NPM_TOKEN** into it. + +When you want to release the package: + +``` +npm run build +npm version [ | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=] | from-git] + +git push +``` + +GitHub actions will do the rest job for you. diff --git a/packages/workspace-tools/__test__/index.spec.ts b/packages/workspace-tools/__test__/index.spec.ts new file mode 100644 index 00000000..4a852a02 --- /dev/null +++ b/packages/workspace-tools/__test__/index.spec.ts @@ -0,0 +1,10 @@ +import test from 'ava'; + +import { + getDefinedPackageManager, + PackageManager +} from '../index'; + +test('get defined package manager', (t) => { + t.is(getDefinedPackageManager(), PackageManager.Pnpm); +}); diff --git a/packages/workspace-tools/benchmark/bench.ts b/packages/workspace-tools/benchmark/bench.ts new file mode 100644 index 00000000..210d5252 --- /dev/null +++ b/packages/workspace-tools/benchmark/bench.ts @@ -0,0 +1,28 @@ +import b from 'benny' + +import { plus100 } from '../index' + +function add(a: number) { + return a + 100 +} + +async function run() { + await b.suite( + 'Add 100', + + b.add('Native a + 100', () => { + plus100(10) + }), + + b.add('JavaScript a + 100', () => { + add(10) + }), + + b.cycle(), + b.complete(), + ) +} + +run().catch((e) => { + console.error(e) +}) diff --git a/packages/workspace-tools/build.config.ts b/packages/workspace-tools/build.config.ts new file mode 100644 index 00000000..e8e33810 --- /dev/null +++ b/packages/workspace-tools/build.config.ts @@ -0,0 +1,58 @@ +import { globSync } from 'glob' +import nodeFs from 'node:fs' +import nodeUrl from 'node:url' +import nodePath from 'node:path' +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + entries: ['src/index.ts'], + alias: { + '@src': nodePath.resolve(nodePath.dirname(nodeUrl.fileURLToPath(import.meta.url)), 'src'), + }, + sourcemap: true, + clean: true, + declaration: true, // generate .d.ts files + externals: [ + /workspace-tools\..*\.node/, + /workspace-tools\..*\.wasm/, + /@websublime\/workspace-tools.*/, + /\.\/workspace-tools\.wasi\.cjs/, + ], + rollup: { + emitCJS: true, + cjsBridge: true, + inlineDependencies: true, + resolve: { + exportConditions: ['node'], + }, + }, + hooks: { + 'build:done': async () => { + const binaryFiles = globSync(['./src/workspace-tools.*.node', './src/workspace-tools.*.wasm'], { + absolute: true, + }) + + const wasiShims = globSync(['./src/*.wasi.js', './src/*.wasi.cjs', './src/*.mjs', './src/browser.js'], { + absolute: true, + ignore: ['./src/main.mjs'], + }) + + nodeFs.mkdirSync('./dist/shared', { recursive: true }) + + // Move the binary file to dist + for (const file of binaryFiles) { + const fileName = nodePath.basename(file) + console.log('[build:done] Copying', file, 'to ./dist/shared') + nodeFs.copyFileSync(file, `./dist/shared/${fileName}`) + console.log(`[build:done] Cleaning ${file}`) + nodeFs.rmSync(file) + } + + for (const file of wasiShims) { + const fileName = nodePath.basename(file) + console.log('[build:done] Copying', file, 'to ./dist/shared') + nodeFs.copyFileSync(file, `./dist/shared/${fileName}`) + } + }, + }, +}) diff --git a/packages/workspace-tools/npm/darwin-arm64/README.md b/packages/workspace-tools/npm/darwin-arm64/README.md new file mode 100644 index 00000000..403a07c9 --- /dev/null +++ b/packages/workspace-tools/npm/darwin-arm64/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-darwin-arm64` + +This is the **aarch64-apple-darwin** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/darwin-arm64/package.json b/packages/workspace-tools/npm/darwin-arm64/package.json new file mode 100644 index 00000000..9bf92db8 --- /dev/null +++ b/packages/workspace-tools/npm/darwin-arm64/package.json @@ -0,0 +1,35 @@ +{ + "name": "@websublime/workspace-tools-darwin-arm64", + "version": "1.0.2", + "cpu": [ + "arm64" + ], + "main": "workspace-tools.darwin-arm64.node", + "files": [ + "workspace-tools.darwin-arm64.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "darwin" + ] +} diff --git a/packages/workspace-tools/npm/darwin-x64/README.md b/packages/workspace-tools/npm/darwin-x64/README.md new file mode 100644 index 00000000..c6c5de38 --- /dev/null +++ b/packages/workspace-tools/npm/darwin-x64/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-darwin-x64` + +This is the **x86_64-apple-darwin** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/darwin-x64/package.json b/packages/workspace-tools/npm/darwin-x64/package.json new file mode 100644 index 00000000..89f3e64b --- /dev/null +++ b/packages/workspace-tools/npm/darwin-x64/package.json @@ -0,0 +1,35 @@ +{ + "name": "@websublime/workspace-tools-darwin-x64", + "version": "1.0.2", + "cpu": [ + "x64" + ], + "main": "workspace-tools.darwin-x64.node", + "files": [ + "workspace-tools.darwin-x64.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "darwin" + ] +} diff --git a/packages/workspace-tools/npm/linux-arm-gnueabihf/README.md b/packages/workspace-tools/npm/linux-arm-gnueabihf/README.md new file mode 100644 index 00000000..ecfd702c --- /dev/null +++ b/packages/workspace-tools/npm/linux-arm-gnueabihf/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-linux-arm-gnueabihf` + +This is the **armv7-unknown-linux-gnueabihf** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/linux-arm-gnueabihf/package.json b/packages/workspace-tools/npm/linux-arm-gnueabihf/package.json new file mode 100644 index 00000000..6988b3d6 --- /dev/null +++ b/packages/workspace-tools/npm/linux-arm-gnueabihf/package.json @@ -0,0 +1,35 @@ +{ + "name": "@websublime/workspace-tools-linux-arm-gnueabihf", + "version": "1.0.2", + "cpu": [ + "arm" + ], + "main": "workspace-tools.linux-arm-gnueabihf.node", + "files": [ + "workspace-tools.linux-arm-gnueabihf.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "linux" + ] +} diff --git a/packages/workspace-tools/npm/linux-arm64-gnu/README.md b/packages/workspace-tools/npm/linux-arm64-gnu/README.md new file mode 100644 index 00000000..56868cba --- /dev/null +++ b/packages/workspace-tools/npm/linux-arm64-gnu/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-linux-arm64-gnu` + +This is the **aarch64-unknown-linux-gnu** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/linux-arm64-gnu/package.json b/packages/workspace-tools/npm/linux-arm64-gnu/package.json new file mode 100644 index 00000000..b3b8c55b --- /dev/null +++ b/packages/workspace-tools/npm/linux-arm64-gnu/package.json @@ -0,0 +1,38 @@ +{ + "name": "@websublime/workspace-tools-linux-arm64-gnu", + "version": "1.0.2", + "cpu": [ + "arm64" + ], + "main": "workspace-tools.linux-arm64-gnu.node", + "files": [ + "workspace-tools.linux-arm64-gnu.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "linux" + ], + "libc": [ + "glibc" + ] +} diff --git a/packages/workspace-tools/npm/linux-arm64-musl/README.md b/packages/workspace-tools/npm/linux-arm64-musl/README.md new file mode 100644 index 00000000..52b3a490 --- /dev/null +++ b/packages/workspace-tools/npm/linux-arm64-musl/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-linux-arm64-musl` + +This is the **aarch64-unknown-linux-musl** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/linux-arm64-musl/package.json b/packages/workspace-tools/npm/linux-arm64-musl/package.json new file mode 100644 index 00000000..422b1653 --- /dev/null +++ b/packages/workspace-tools/npm/linux-arm64-musl/package.json @@ -0,0 +1,38 @@ +{ + "name": "@websublime/workspace-tools-linux-arm64-musl", + "version": "1.0.2", + "cpu": [ + "arm64" + ], + "main": "workspace-tools.linux-arm64-musl.node", + "files": [ + "workspace-tools.linux-arm64-musl.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "linux" + ], + "libc": [ + "musl" + ] +} diff --git a/packages/workspace-tools/npm/linux-x64-gnu/README.md b/packages/workspace-tools/npm/linux-x64-gnu/README.md new file mode 100644 index 00000000..24dc32cc --- /dev/null +++ b/packages/workspace-tools/npm/linux-x64-gnu/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-linux-x64-gnu` + +This is the **x86_64-unknown-linux-gnu** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/linux-x64-gnu/package.json b/packages/workspace-tools/npm/linux-x64-gnu/package.json new file mode 100644 index 00000000..f85c113f --- /dev/null +++ b/packages/workspace-tools/npm/linux-x64-gnu/package.json @@ -0,0 +1,38 @@ +{ + "name": "@websublime/workspace-tools-linux-x64-gnu", + "version": "1.0.2", + "cpu": [ + "x64" + ], + "main": "workspace-tools.linux-x64-gnu.node", + "files": [ + "workspace-tools.linux-x64-gnu.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "linux" + ], + "libc": [ + "glibc" + ] +} diff --git a/packages/workspace-tools/npm/linux-x64-musl/README.md b/packages/workspace-tools/npm/linux-x64-musl/README.md new file mode 100644 index 00000000..0567acbf --- /dev/null +++ b/packages/workspace-tools/npm/linux-x64-musl/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-linux-x64-musl` + +This is the **x86_64-unknown-linux-musl** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/linux-x64-musl/package.json b/packages/workspace-tools/npm/linux-x64-musl/package.json new file mode 100644 index 00000000..81f0d078 --- /dev/null +++ b/packages/workspace-tools/npm/linux-x64-musl/package.json @@ -0,0 +1,38 @@ +{ + "name": "@websublime/workspace-tools-linux-x64-musl", + "version": "1.0.2", + "cpu": [ + "x64" + ], + "main": "workspace-tools.linux-x64-musl.node", + "files": [ + "workspace-tools.linux-x64-musl.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "linux" + ], + "libc": [ + "musl" + ] +} diff --git a/packages/workspace-tools/npm/wasm32-wasi/README.md b/packages/workspace-tools/npm/wasm32-wasi/README.md new file mode 100644 index 00000000..154df23b --- /dev/null +++ b/packages/workspace-tools/npm/wasm32-wasi/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-wasm32-wasi` + +This is the **wasm32-wasi-preview1-threads** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/wasm32-wasi/package.json b/packages/workspace-tools/npm/wasm32-wasi/package.json new file mode 100644 index 00000000..f95d3574 --- /dev/null +++ b/packages/workspace-tools/npm/wasm32-wasi/package.json @@ -0,0 +1,40 @@ +{ + "name": "@websublime/workspace-tools-wasm32-wasi", + "version": "0.0.1", + "cpu": [ + "wasm32" + ], + "main": "workspace-tools.wasi.cjs", + "files": [ + "workspace-tools.wasm32-wasi.wasm", + "workspace-tools.wasi.cjs", + "workspace-tools.wasi-browser.js", + "wasi-worker.mjs", + "wasi-worker-browser.mjs" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "browser": "workspace-tools.wasi-browser.js", + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.4" + } +} diff --git a/packages/workspace-tools/npm/win32-arm64-msvc/README.md b/packages/workspace-tools/npm/win32-arm64-msvc/README.md new file mode 100644 index 00000000..efb7fc36 --- /dev/null +++ b/packages/workspace-tools/npm/win32-arm64-msvc/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-win32-arm64-msvc` + +This is the **aarch64-pc-windows-msvc** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/win32-arm64-msvc/package.json b/packages/workspace-tools/npm/win32-arm64-msvc/package.json new file mode 100644 index 00000000..df4675a7 --- /dev/null +++ b/packages/workspace-tools/npm/win32-arm64-msvc/package.json @@ -0,0 +1,35 @@ +{ + "name": "@websublime/workspace-tools-win32-arm64-msvc", + "version": "1.0.2", + "cpu": [ + "arm64" + ], + "main": "workspace-tools.win32-arm64-msvc.node", + "files": [ + "workspace-tools.win32-arm64-msvc.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "win32" + ] +} diff --git a/packages/workspace-tools/npm/win32-ia32-msvc/README.md b/packages/workspace-tools/npm/win32-ia32-msvc/README.md new file mode 100644 index 00000000..514186ed --- /dev/null +++ b/packages/workspace-tools/npm/win32-ia32-msvc/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-win32-ia32-msvc` + +This is the **i686-pc-windows-msvc** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/win32-ia32-msvc/package.json b/packages/workspace-tools/npm/win32-ia32-msvc/package.json new file mode 100644 index 00000000..9b3b4714 --- /dev/null +++ b/packages/workspace-tools/npm/win32-ia32-msvc/package.json @@ -0,0 +1,35 @@ +{ + "name": "@websublime/workspace-tools-win32-ia32-msvc", + "version": "1.0.2", + "cpu": [ + "ia32" + ], + "main": "workspace-tools.win32-ia32-msvc.node", + "files": [ + "workspace-tools.win32-ia32-msvc.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "win32" + ] +} diff --git a/packages/workspace-tools/npm/win32-x64-msvc/README.md b/packages/workspace-tools/npm/win32-x64-msvc/README.md new file mode 100644 index 00000000..e939a09d --- /dev/null +++ b/packages/workspace-tools/npm/win32-x64-msvc/README.md @@ -0,0 +1,3 @@ +# `@websublime/workspace-tools-win32-x64-msvc` + +This is the **x86_64-pc-windows-msvc** binary for `@websublime/workspace-tools` diff --git a/packages/workspace-tools/npm/win32-x64-msvc/package.json b/packages/workspace-tools/npm/win32-x64-msvc/package.json new file mode 100644 index 00000000..63a82f4b --- /dev/null +++ b/packages/workspace-tools/npm/win32-x64-msvc/package.json @@ -0,0 +1,35 @@ +{ + "name": "@websublime/workspace-tools-win32-x64-msvc", + "version": "1.0.2", + "cpu": [ + "x64" + ], + "main": "workspace-tools.win32-x64-msvc.node", + "files": [ + "workspace-tools.win32-x64-msvc.node" + ], + "description": "Template project for writing node package with napi-rs", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "os": [ + "win32" + ] +} diff --git a/packages/workspace-tools/package.json b/packages/workspace-tools/package.json new file mode 100644 index 00000000..90d5f135 --- /dev/null +++ b/packages/workspace-tools/package.json @@ -0,0 +1,113 @@ +{ + "name": "@websublime/workspace-tools", + "version": "1.0.2", + "description": "Bindings for node from crate workspace-node-tools", + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + }, + "./package.json": "./package.json" + }, + "browser": "browser.js", + "repository": { + "url": "git+ssh://git@github.com/websublime/workspace-node-tools.git", + "type": "git" + }, + "license": "MIT", + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "files": [ + "dist", + "!dist/*.node" + ], + "napi": { + "binaryName": "workspace-tools", + "packageName": "@websublime/workspace-tools", + "targets": [ + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "i686-pc-windows-msvc", + "armv7-unknown-linux-gnueabihf", + "aarch64-unknown-linux-gnu", + "aarch64-apple-darwin", + "aarch64-unknown-linux-musl", + "aarch64-pc-windows-msvc" + ], + "wasm": { + "initialMemory": 16384, + "browser": { + "fs": true + } + }, + "dtsHeader": "import type { RepositoryCommit, RepositoryRemoteTags } from './types/repo';\n\nimport type { PackageManager } from './types/enums';\n\nimport type { WorkspaceConfig } from './types/config';\n\nimport type { Result } from './types/general';\n\nimport type { Changes, Change } from './types/changes';\n\n" + }, + "engines": { + "node": ">= 10" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "scripts": { + "artifacts": "napi artifacts --cwd ./src --package-json-path ../package.json -o=../artifacts --npm-dir ../npm", + "bench": "node -r @swc-node/register benchmark/bench.ts", + "build-binding": "napi build -o=./src --manifest-path ../../crates/workspace_binding/Cargo.toml --platform -p workspace_binding --js binding.js --dts binding.d.ts --no-const-enum", + "build-binding:release": "pnpm build-binding --release", + "build-binding:wasi": "pnpm build-binding --target wasm32-wasip1-threads", + "build-types": "tsc -p ./tsconfig.dts.json", + "build:debug": "napi build --platform", + "build:node": "unbuild", + "format": "run-p format:prettier format:rs format:toml", + "format:prettier": "prettier . -w", + "format:toml": "taplo format", + "format:rs": "cargo fmt", + "lint": "oxlint", + "prepublishOnly": "napi prepublish -t npm", + "test": "ava", + "version": "napi version" + }, + "devDependencies": { + "@emnapi/core": "^1.1.1", + "@emnapi/runtime": "^1.1.1", + "@napi-rs/cli": "^3.0.0-alpha.60", + "@napi-rs/wasm-runtime": "^0.2.4", + "@swc-node/register": "^1.9.0", + "@swc/core": "^1.4.13", + "@tybys/wasm-util": "^0.9.0", + "ava": "^6.1.2", + "benny": "^3.7.1", + "chalk": "^5.3.0", + "emnapi": "^1.2.0", + "unbuild": "^2.0.0", + "fs-extra": "^11.2.0", + "glob": "^11.0.0", + "typescript": "^5.4.5", + "vite": "^5.2.13", + "vitest": "^2.0.0" + }, + "ava": { + "require": [ + "@swc-node/register" + ], + "extensions": [ + "ts" + ], + "timeout": "2m", + "workerThreads": false, + "environmentVariables": { + "TS_NODE_PROJECT": "./tsconfig.json" + } + } +} diff --git a/packages/workspace-tools/simple-test.js b/packages/workspace-tools/simple-test.js new file mode 100644 index 00000000..54f132d2 --- /dev/null +++ b/packages/workspace-tools/simple-test.js @@ -0,0 +1,7 @@ +const { getDefinedPackageManager } = require('./index') + +const agent = getDefinedPackageManager(); + +console.assert(agent === 'pnpm', 'Simple test failed') + +console.info(`Simple test passed: ${agent}`) diff --git a/packages/workspace-tools/src/binding.d.ts b/packages/workspace-tools/src/binding.d.ts new file mode 100644 index 00000000..2b460175 --- /dev/null +++ b/packages/workspace-tools/src/binding.d.ts @@ -0,0 +1,76 @@ +import type { RepositoryCommit, RepositoryRemoteTags } from './types/repo'; + +import type { PackageManager } from './types/enums'; + +import type { WorkspaceConfig } from './types/config'; + +import type { Result } from './types/general'; + +import type { Changes, Change } from './types/changes'; + +export declare function addChange(change: Change, deploy_envs?: string[], cwd?: string): Result + +export declare function changeExists(branch: string, package: string, cwd?: string): boolean + +export declare function detectPackageManager(cwd: string): Result + +export declare function getChanges(cwd?: string): Result + +export declare function getChangesByBranch(branch: string, cwd?: string): Result<{deploy: string[]; pkgs: Changes[]}|null> + +export declare function getChangesByPackage(package: string, branch: string, cwd?: string): Result + +export declare function getConfig(cwd?: string): Result + +export declare function getProjectRootPath(cwd?: string | undefined | null): string | null + +export declare function initChanges(cwd?: string | undefined | null): Result + +export declare function isVcsRepository(cwd: string): Result + +export declare function removeChange(branch: string, cwd?: string): boolean + +export declare function repoAdd(filepath: string, cwd: string): Result + +export declare function repoAddAll(cwd: string): Result + +export declare function repoCheckout(branch: string, cwd: string): Result + +export declare function repoCommit(cwd: string, message: string, body?: string | undefined | null, footer?: string | undefined | null): Result + +export declare function repoConfig(username: string, email: string, cwd: string): Result + +export declare function repoCreateBranch(branch: string, cwd: string): Result + +export declare function repoCreateTag(tag: string, cwd: string, message?: string | undefined | null): Result + +export declare function repoFetchAll(cwd: string, fetchTags?: boolean | undefined | null): Result + +export declare function repoGetAllFilesChangedSinceBranch(cwd: string, packages: Array, branch: string): Result + +export declare function repoGetAllFilesChangedSinceSha(cwd: string, sha: string): Result + +export declare function repoGetBranchFromCommit(sha: string, cwd: string): Result + +export declare function repoGetCommitsSince(cwd: string, since?: string | undefined | null, relative?: string | undefined | null): Result + +export declare function repoGetCurrentBranch(cwd: string): Result + +export declare function repoGetCurrentSha(cwd: string): Result + +export declare function repoGetDivergedCommit(sha: string, cwd: string): Result + +export declare function repoGetFirstSha(cwd: string, branch?: string | undefined | null): Result + +export declare function repoGetPreviousSha(cwd: string): Result + +export declare function repoGetTags(cwd: string, local?: boolean | undefined | null): Result + +export declare function repoInit(initialBranch: string, username: string, email: string, cwd: string): Result + +export declare function repoIsUnclean(cwd: string): Result + +export declare function repoMerge(branch: string, cwd: string): Result + +export declare function repoPush(cwd: string, followTags?: boolean | undefined | null): Result + diff --git a/packages/workspace-tools/src/binding.js b/packages/workspace-tools/src/binding.js new file mode 100644 index 00000000..63a99f68 --- /dev/null +++ b/packages/workspace-tools/src/binding.js @@ -0,0 +1,396 @@ +// prettier-ignore +/* eslint-disable */ +/* auto-generated by NAPI-RS */ + +const { readFileSync } = require('fs') + +let nativeBinding = null +const loadErrors = [] + +const isMusl = () => { + let musl = false + if (process.platform === 'linux') { + musl = isMuslFromFilesystem() + if (musl === null) { + musl = isMuslFromReport() + } + if (musl === null) { + musl = isMuslFromChildProcess() + } + } + return musl +} + +const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-') + +const isMuslFromFilesystem = () => { + try { + return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl') + } catch { + return null + } +} + +const isMuslFromReport = () => { + const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null + if (!report) { + return null + } + if (report.header && report.header.glibcVersionRuntime) { + return false + } + if (Array.isArray(report.sharedObjects)) { + if (report.sharedObjects.some(isFileMusl)) { + return true + } + } + return false +} + +const isMuslFromChildProcess = () => { + try { + return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl') + } catch (e) { + // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false + return false + } +} + +function requireNative() { + if (process.platform === 'android') { + if (process.arch === 'arm64') { + try { + return require('./workspace-tools.android-arm64.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-android-arm64') + } catch (e) { + loadErrors.push(e) + } + + } else if (process.arch === 'arm') { + try { + return require('./workspace-tools.android-arm-eabi.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-android-arm-eabi') + } catch (e) { + loadErrors.push(e) + } + + } else { + loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`)) + } + } else if (process.platform === 'win32') { + if (process.arch === 'x64') { + try { + return require('./workspace-tools.win32-x64-msvc.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-win32-x64-msvc') + } catch (e) { + loadErrors.push(e) + } + + } else if (process.arch === 'ia32') { + try { + return require('./workspace-tools.win32-ia32-msvc.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-win32-ia32-msvc') + } catch (e) { + loadErrors.push(e) + } + + } else if (process.arch === 'arm64') { + try { + return require('./workspace-tools.win32-arm64-msvc.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-win32-arm64-msvc') + } catch (e) { + loadErrors.push(e) + } + + } else { + loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`)) + } + } else if (process.platform === 'darwin') { + try { + return require('./workspace-tools.darwin-universal.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-darwin-universal') + } catch (e) { + loadErrors.push(e) + } + + if (process.arch === 'x64') { + try { + return require('./workspace-tools.darwin-x64.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-darwin-x64') + } catch (e) { + loadErrors.push(e) + } + + } else if (process.arch === 'arm64') { + try { + return require('./workspace-tools.darwin-arm64.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-darwin-arm64') + } catch (e) { + loadErrors.push(e) + } + + } else { + loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`)) + } + } else if (process.platform === 'freebsd') { + if (process.arch === 'x64') { + try { + return require('./workspace-tools.freebsd-x64.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-freebsd-x64') + } catch (e) { + loadErrors.push(e) + } + + } else if (process.arch === 'arm64') { + try { + return require('./workspace-tools.freebsd-arm64.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-freebsd-arm64') + } catch (e) { + loadErrors.push(e) + } + + } else { + loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`)) + } + } else if (process.platform === 'linux') { + if (process.arch === 'x64') { + if (isMusl()) { + try { + return require('./workspace-tools.linux-x64-musl.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-x64-musl') + } catch (e) { + loadErrors.push(e) + } + + } else { + try { + return require('./workspace-tools.linux-x64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-x64-gnu') + } catch (e) { + loadErrors.push(e) + } + + } + } else if (process.arch === 'arm64') { + if (isMusl()) { + try { + return require('./workspace-tools.linux-arm64-musl.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-arm64-musl') + } catch (e) { + loadErrors.push(e) + } + + } else { + try { + return require('./workspace-tools.linux-arm64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-arm64-gnu') + } catch (e) { + loadErrors.push(e) + } + + } + } else if (process.arch === 'arm') { + if (isMusl()) { + try { + return require('./workspace-tools.linux-arm-musleabihf.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-arm-musleabihf') + } catch (e) { + loadErrors.push(e) + } + + } else { + try { + return require('./workspace-tools.linux-arm-gnueabihf.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-arm-gnueabihf') + } catch (e) { + loadErrors.push(e) + } + + } + } else if (process.arch === 'riscv64') { + if (isMusl()) { + try { + return require('./workspace-tools.linux-riscv64-musl.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-riscv64-musl') + } catch (e) { + loadErrors.push(e) + } + + } else { + try { + return require('./workspace-tools.linux-riscv64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-riscv64-gnu') + } catch (e) { + loadErrors.push(e) + } + + } + } else if (process.arch === 'ppc64') { + try { + return require('./workspace-tools.linux-ppc64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-ppc64-gnu') + } catch (e) { + loadErrors.push(e) + } + + } else if (process.arch === 's390x') { + try { + return require('./workspace-tools.linux-s390x-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + return require('@websublime/workspace-tools-linux-s390x-gnu') + } catch (e) { + loadErrors.push(e) + } + + } else { + loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`)) + } + } else { + loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`)) + } +} + +nativeBinding = requireNative() + +if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) { + try { + nativeBinding = require('./workspace-tools.wasi.cjs') + } catch (err) { + if (process.env.NAPI_RS_FORCE_WASI) { + loadErrors.push(err) + } + } + if (!nativeBinding) { + try { + nativeBinding = require('@websublime/workspace-tools-wasm32-wasi') + } catch (err) { + if (process.env.NAPI_RS_FORCE_WASI) { + loadErrors.push(err) + } + } + } +} + +if (!nativeBinding) { + if (loadErrors.length > 0) { + // TODO Link to documentation with potential fixes + // - The package owner could build/publish bindings for this arch + // - The user may need to bundle the correct files + // - The user may need to re-install node_modules to get new packages + throw new Error('Failed to load native binding', { cause: loadErrors }) + } + throw new Error(`Failed to load native binding`) +} + +module.exports.addChange = nativeBinding.addChange +module.exports.changeExists = nativeBinding.changeExists +module.exports.detectPackageManager = nativeBinding.detectPackageManager +module.exports.getChanges = nativeBinding.getChanges +module.exports.getChangesByBranch = nativeBinding.getChangesByBranch +module.exports.getChangesByPackage = nativeBinding.getChangesByPackage +module.exports.getConfig = nativeBinding.getConfig +module.exports.getProjectRootPath = nativeBinding.getProjectRootPath +module.exports.initChanges = nativeBinding.initChanges +module.exports.isVcsRepository = nativeBinding.isVcsRepository +module.exports.removeChange = nativeBinding.removeChange +module.exports.repoAdd = nativeBinding.repoAdd +module.exports.repoAddAll = nativeBinding.repoAddAll +module.exports.repoCheckout = nativeBinding.repoCheckout +module.exports.repoCommit = nativeBinding.repoCommit +module.exports.repoConfig = nativeBinding.repoConfig +module.exports.repoCreateBranch = nativeBinding.repoCreateBranch +module.exports.repoCreateTag = nativeBinding.repoCreateTag +module.exports.repoFetchAll = nativeBinding.repoFetchAll +module.exports.repoGetAllFilesChangedSinceBranch = nativeBinding.repoGetAllFilesChangedSinceBranch +module.exports.repoGetAllFilesChangedSinceSha = nativeBinding.repoGetAllFilesChangedSinceSha +module.exports.repoGetBranchFromCommit = nativeBinding.repoGetBranchFromCommit +module.exports.repoGetCommitsSince = nativeBinding.repoGetCommitsSince +module.exports.repoGetCurrentBranch = nativeBinding.repoGetCurrentBranch +module.exports.repoGetCurrentSha = nativeBinding.repoGetCurrentSha +module.exports.repoGetDivergedCommit = nativeBinding.repoGetDivergedCommit +module.exports.repoGetFirstSha = nativeBinding.repoGetFirstSha +module.exports.repoGetPreviousSha = nativeBinding.repoGetPreviousSha +module.exports.repoGetTags = nativeBinding.repoGetTags +module.exports.repoInit = nativeBinding.repoInit +module.exports.repoIsUnclean = nativeBinding.repoIsUnclean +module.exports.repoMerge = nativeBinding.repoMerge +module.exports.repoPush = nativeBinding.repoPush diff --git a/packages/workspace-tools/src/browser.js b/packages/workspace-tools/src/browser.js new file mode 100644 index 00000000..9f3ba63a --- /dev/null +++ b/packages/workspace-tools/src/browser.js @@ -0,0 +1 @@ +export * from '@websublime/workspace-tools-wasm32-wasi' diff --git a/packages/workspace-tools/src/index.ts b/packages/workspace-tools/src/index.ts new file mode 100644 index 00000000..84a9847b --- /dev/null +++ b/packages/workspace-tools/src/index.ts @@ -0,0 +1,6 @@ +export * from './types/changes' +export * from './types/general' +export * from './types/config' +export * from './types/enums' +export * from './types/repo' +export * from './binding.js' diff --git a/packages/workspace-tools/src/main.mjs b/packages/workspace-tools/src/main.mjs new file mode 100644 index 00000000..31664940 --- /dev/null +++ b/packages/workspace-tools/src/main.mjs @@ -0,0 +1,49 @@ +import { + initChanges, + addChange, + removeChange, + getChanges, + getChangesByBranch, + getChangesByPackage, + getConfig, + detectPackageManager, +} from './binding.js' +import util from 'node:util' + +const log = (() => { + const log = (...values) => { + console.log( + ...values.map((value) => + util.inspect(value, { + colors: true, + depth: null, + getters: true, + showHidden: false, + ...log.options, + }), + ), + ) + } + log.options = {} + return log +})() + +const root = process.cwd() + +log(initChanges(root)) + +log(addChange({ package: '@scope/foo', releaseAs: 'patch' }, ['int'], root)) + +log(getChanges(root)) + +log(getChangesByBranch('feature/next', root)) + +log(getChangesByBranch('unknown', root)) + +log(getChangesByPackage('@scope/foo', 'feature/next', root)) + +log(detectPackageManager(root)) + +log(getConfig(root)) + +log(removeChange('feature/next', root)) diff --git a/packages/workspace-tools/src/types/changes.ts b/packages/workspace-tools/src/types/changes.ts new file mode 100644 index 00000000..0c62e736 --- /dev/null +++ b/packages/workspace-tools/src/types/changes.ts @@ -0,0 +1,13 @@ +export type BumpType = 'major' | 'minor' | 'patch' | 'snapshot' | 'beta' | 'alpha' | 'rc' + +export interface Change { + package: string + releaseAs: BumpType +} + +export interface Changes { + [key: string]: { + deploy: string[] + pkgs: Change[] + } +} diff --git a/packages/workspace-tools/src/types/config.ts b/packages/workspace-tools/src/types/config.ts new file mode 100644 index 00000000..9604f93d --- /dev/null +++ b/packages/workspace-tools/src/types/config.ts @@ -0,0 +1,94 @@ +import type { PackageManager } from './enums' + +export type CliffBump = 'major' | 'minor' | 'patch' + +export interface CliffRemoteConfig { + github: CliffRemote + gitlab: CliffRemote + bitbucket: CliffRemote + gitea: CliffRemote +} + +export interface CliffRemote { + owner: string + repo: string + token?: string + isCustom: boolean +} + +export interface CliffConfig { + changelog: CliffChangelogConfig + git: CliffGitConfig + remote: CliffRemoteConfig + bump: CliffBump +} + +export interface CliffLinkParser { + pattern: string + href: string + text?: string +} + +export interface CliffCommitParser { + sha?: string + message?: string + body?: string + footer?: string + group?: string + defaultScope?: string + scope?: string + skip?: boolean + field?: string + pattern?: string +} + +export interface CliffTextProcessor { + pattern: string + replace?: string + replaceCommand?: string +} + +export interface CliffGitConfig { + conventionCommits?: boolean + filterUnconventional?: boolean + splitCommits?: boolean + commitPreprocessors?: CliffTextProcessor[] + commitParsers?: CliffCommitParser[] + protectBreakingCommits?: boolean + linkParsers?: CliffLinkParser[] + filterCommits?: boolean + tagPattern?: string + skipTags?: string + ignoreTags?: string + countTags?: string + useBranchTags?: boolean + topoOrder?: boolean + sortCommits?: string + limitCommits?: number +} + +export interface CliffChangelogConfig { + header?: string + body?: string + footer?: string + trim?: boolean + renderAlways?: boolean + postprocessors: CliffTextProcessor[] + output?: string +} + +export interface ToolsConfig { + tools: ToolsConfigGroup +} + +export interface ToolsConfigGroup { + bumpSync: boolean +} + +export interface WorkspaceConfig { + workspaceRoot: string + toolsConfig: ToolsConfig + packageManager: PackageManager + cliffConfig: CliffConfig + changesConfig: Record +} diff --git a/packages/workspace-tools/src/types/enums.ts b/packages/workspace-tools/src/types/enums.ts new file mode 100644 index 00000000..964cae91 --- /dev/null +++ b/packages/workspace-tools/src/types/enums.ts @@ -0,0 +1,6 @@ +export enum PackageManager { + Npm = 'npm', + Yarn = 'yarn', + Pnpm = 'pnpm', + Bun = 'bun', +} diff --git a/packages/workspace-tools/src/types/general.ts b/packages/workspace-tools/src/types/general.ts new file mode 100644 index 00000000..abf729cb --- /dev/null +++ b/packages/workspace-tools/src/types/general.ts @@ -0,0 +1 @@ +export type Result = OK | Error diff --git a/packages/workspace-tools/src/types/repo.ts b/packages/workspace-tools/src/types/repo.ts new file mode 100644 index 00000000..15bec920 --- /dev/null +++ b/packages/workspace-tools/src/types/repo.ts @@ -0,0 +1,12 @@ +export interface RepositoryCommit { + hash: string + authorName: string + authorEmail: string + authorDate: string + message: string +} + +export interface RepositoryRemoteTags { + hash: string + tag: string +} diff --git a/packages/workspace-tools/src/wasi-worker-browser.mjs b/packages/workspace-tools/src/wasi-worker-browser.mjs new file mode 100644 index 00000000..cc4469bb --- /dev/null +++ b/packages/workspace-tools/src/wasi-worker-browser.mjs @@ -0,0 +1,39 @@ +import { instantiateNapiModuleSync, MessageHandler, WASI, createFsProxy } from '@napi-rs/wasm-runtime' +import { memfsExported as __memfsExported } from '@napi-rs/wasm-runtime/fs' + +const fs = createFsProxy(__memfsExported) + +const handler = new MessageHandler({ + onLoad({ wasmModule, wasmMemory }) { + const wasi = new WASI({ + fs, + preopens: { + '/': '/', + }, + print: function () { + // eslint-disable-next-line no-console + console.log.apply(console, arguments) + }, + printErr: function() { + // eslint-disable-next-line no-console + console.error.apply(console, arguments) + }, + }) + return instantiateNapiModuleSync(wasmModule, { + childThread: true, + wasi, + overwriteImports(importObject) { + importObject.env = { + ...importObject.env, + ...importObject.napi, + ...importObject.emnapi, + memory: wasmMemory, + } + }, + }) + }, +}) + +globalThis.onmessage = function (e) { + handler.handle(e) +} diff --git a/packages/workspace-tools/src/wasi-worker.mjs b/packages/workspace-tools/src/wasi-worker.mjs new file mode 100644 index 00000000..84b448fc --- /dev/null +++ b/packages/workspace-tools/src/wasi-worker.mjs @@ -0,0 +1,63 @@ +import fs from "node:fs"; +import { createRequire } from "node:module"; +import { parse } from "node:path"; +import { WASI } from "node:wasi"; +import { parentPort, Worker } from "node:worker_threads"; + +const require = createRequire(import.meta.url); + +const { instantiateNapiModuleSync, MessageHandler, getDefaultContext } = require("@napi-rs/wasm-runtime"); + +if (parentPort) { + parentPort.on("message", (data) => { + globalThis.onmessage({ data }); + }); +} + +Object.assign(globalThis, { + self: globalThis, + require, + Worker, + importScripts: function (f) { + ;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f); + }, + postMessage: function (msg) { + if (parentPort) { + parentPort.postMessage(msg); + } + }, +}); + +const emnapiContext = getDefaultContext(); + +const __rootDir = parse(process.cwd()).root; + +const handler = new MessageHandler({ + onLoad({ wasmModule, wasmMemory }) { + const wasi = new WASI({ + version: 'preview1', + env: process.env, + preopens: { + [__rootDir]: __rootDir, + }, + }); + + return instantiateNapiModuleSync(wasmModule, { + childThread: true, + wasi, + context: emnapiContext, + overwriteImports(importObject) { + importObject.env = { + ...importObject.env, + ...importObject.napi, + ...importObject.emnapi, + memory: wasmMemory + }; + }, + }); + }, +}); + +globalThis.onmessage = function (e) { + handler.handle(e); +}; diff --git a/packages/workspace-tools/src/workspace-tools.wasi-browser.js b/packages/workspace-tools/src/workspace-tools.wasi-browser.js new file mode 100644 index 00000000..0a91caaf --- /dev/null +++ b/packages/workspace-tools/src/workspace-tools.wasi-browser.js @@ -0,0 +1,63 @@ +import { + instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync, + getDefaultContext as __emnapiGetDefaultContext, + WASI as __WASI, + createOnMessage as __wasmCreateOnMessageForFsProxy, +} from '@napi-rs/wasm-runtime' +import { memfs } from '@napi-rs/wasm-runtime/fs' +import __wasmUrl from './workspace-tools.wasm32-wasi.wasm?url' + +export const { fs: __fs, vol: __volume } = memfs() + +const __wasi = new __WASI({ + version: 'preview1', + fs: __fs, + preopens: { + '/': '/', + } +}) + +const __emnapiContext = __emnapiGetDefaultContext() + +const __sharedMemory = new WebAssembly.Memory({ + initial: 16384, + maximum: 65536, + shared: true, +}) + +const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer()) + +const { + instance: __napiInstance, + module: __wasiModule, + napiModule: __napiModule, +} = __emnapiInstantiateNapiModuleSync(__wasmFile, { + context: __emnapiContext, + asyncWorkPoolSize: 4, + wasi: __wasi, + onCreateWorker() { + const worker = new Worker(new URL('./wasi-worker-browser.mjs', import.meta.url), { + type: 'module', + }) + worker.addEventListener('message', __wasmCreateOnMessageForFsProxy(__fs)) + + return worker + }, + overwriteImports(importObject) { + importObject.env = { + ...importObject.env, + ...importObject.napi, + ...importObject.emnapi, + memory: __sharedMemory, + } + return importObject + }, + beforeInit({ instance }) { + __napi_rs_initialize_modules(instance) + }, +}) + +function __napi_rs_initialize_modules(__napiInstance) { + __napiInstance.exports['__napi_register__js_init_changes_0']?.() +} +export const initChanges = __napiModule.exports.initChanges diff --git a/packages/workspace-tools/src/workspace-tools.wasi.cjs b/packages/workspace-tools/src/workspace-tools.wasi.cjs new file mode 100644 index 00000000..8d7d7f66 --- /dev/null +++ b/packages/workspace-tools/src/workspace-tools.wasi.cjs @@ -0,0 +1,87 @@ +/* eslint-disable */ +/* prettier-ignore */ + +/* auto-generated by NAPI-RS */ + +const __nodeFs = require('node:fs') +const __nodePath = require('node:path') +const { WASI: __nodeWASI } = require('node:wasi') +const { Worker } = require('node:worker_threads') + +const { + instantiateNapiModuleSync: __emnapiInstantiateNapiModuleSync, + getDefaultContext: __emnapiGetDefaultContext, + createOnMessage: __wasmCreateOnMessageForFsProxy, +} = require('@napi-rs/wasm-runtime') + +const __rootDir = __nodePath.parse(process.cwd()).root + +const __wasi = new __nodeWASI({ + version: 'preview1', + env: process.env, + preopens: { + [__rootDir]: __rootDir, + } +}) + +const __emnapiContext = __emnapiGetDefaultContext() + +const __sharedMemory = new WebAssembly.Memory({ + initial: 16384, + maximum: 65536, + shared: true, +}) + +let __wasmFilePath = __nodePath.join(__dirname, 'workspace-tools.wasm32-wasi.wasm') +const __wasmDebugFilePath = __nodePath.join(__dirname, 'workspace-tools.wasm32-wasi.debug.wasm') + +if (__nodeFs.existsSync(__wasmDebugFilePath)) { + __wasmFilePath = __wasmDebugFilePath +} else if (!__nodeFs.existsSync(__wasmFilePath)) { + try { + __wasmFilePath = __nodePath.resolve('@websublime/workspace-tools-wasm32-wasi') + } catch { + throw new Error('Cannot find workspace-tools.wasm32-wasi.wasm file, and @websublime/workspace-tools-wasm32-wasi package is not installed.') + } +} + +const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = __emnapiInstantiateNapiModuleSync(__nodeFs.readFileSync(__wasmFilePath), { + context: __emnapiContext, + asyncWorkPoolSize: (function() { + const threadsSizeFromEnv = Number(process.env.NAPI_RS_ASYNC_WORK_POOL_SIZE ?? process.env.UV_THREADPOOL_SIZE) + // NaN > 0 is false + if (threadsSizeFromEnv > 0) { + return threadsSizeFromEnv + } else { + return 4 + } + })(), + wasi: __wasi, + onCreateWorker() { + const worker = new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), { + env: process.env, + execArgv: ['--experimental-wasi-unstable-preview1'], + }) + worker.onmessage = ({ data }) => { + __wasmCreateOnMessageForFsProxy(__nodeFs)(data) + } + return worker + }, + overwriteImports(importObject) { + importObject.env = { + ...importObject.env, + ...importObject.napi, + ...importObject.emnapi, + memory: __sharedMemory, + } + return importObject + }, + beforeInit({ instance }) { + __napi_rs_initialize_modules(instance) + } +}) + +function __napi_rs_initialize_modules(__napiInstance) { + __napiInstance.exports['__napi_register__js_init_changes_0']?.() +} +module.exports.initChanges = __napiModule.exports.initChanges diff --git a/packages/workspace-tools/tsconfig.dts.json b/packages/workspace-tools/tsconfig.dts.json new file mode 100644 index 00000000..41c17ff1 --- /dev/null +++ b/packages/workspace-tools/tsconfig.dts.json @@ -0,0 +1,38 @@ +{ + "include": [ + "src/**/*" + ], + "exclude": [ + "src/*.js", + "src/*.mjs", + "src/*.cjs" + ], + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src", + "moduleResolution": "Bundler", + "baseUrl": "./", + "paths": { + "@tests/*": [ + "tests/src/*" + ], + "@tests": [ + "tests/src/index.ts" + ] + }, + "types": [ + "node", + "vite/client" + ], + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "./lib/types", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/packages/workspace-tools/tsconfig.json b/packages/workspace-tools/tsconfig.json new file mode 100644 index 00000000..ad1c21d5 --- /dev/null +++ b/packages/workspace-tools/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "strict": true, + "allowJs": true, + "checkJs": false, + "noEmit": true, + "moduleResolution": "Bundler", + "module": "ESNext", + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "baseUrl": "./", + "types": ["node", "vite/client"] + }, + "include": ["."], + "exclude": ["src/*.js", "src/*.mjs", "src/*.cjs"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..50ab20bc --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,6200 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@taplo/cli': + specifier: ^0.7.0 + version: 0.7.0 + '@types/node': + specifier: 22.5.2 + version: 22.5.2 + husky: + specifier: ^9.0.11 + version: 9.1.6 + lint-staged: + specifier: ^15.2.2 + version: 15.2.10 + npm-run-all2: + specifier: ^6.1.2 + version: 6.2.3 + oxlint: + specifier: ^0.9.2 + version: 0.9.9 + prettier: + specifier: ^3.2.5 + version: 3.3.3 + typescript: + specifier: ^5.4.5 + version: 5.6.2 + + packages/workspace-tools: + devDependencies: + '@emnapi/core': + specifier: ^1.1.1 + version: 1.2.0 + '@emnapi/runtime': + specifier: ^1.1.1 + version: 1.2.0 + '@napi-rs/cli': + specifier: ^3.0.0-alpha.60 + version: 3.0.0-alpha.62(@emnapi/runtime@1.2.0)(emnapi@1.2.0) + '@napi-rs/wasm-runtime': + specifier: ^0.2.4 + version: 0.2.4 + '@swc-node/register': + specifier: ^1.9.0 + version: 1.10.9(@swc/core@1.7.28)(@swc/types@0.1.12)(typescript@5.6.2) + '@swc/core': + specifier: ^1.4.13 + version: 1.7.28 + '@tybys/wasm-util': + specifier: ^0.9.0 + version: 0.9.0 + ava: + specifier: ^6.1.2 + version: 6.1.3 + benny: + specifier: ^3.7.1 + version: 3.7.1 + chalk: + specifier: ^5.3.0 + version: 5.3.0 + emnapi: + specifier: ^1.2.0 + version: 1.2.0 + fs-extra: + specifier: ^11.2.0 + version: 11.2.0 + glob: + specifier: ^11.0.0 + version: 11.0.0 + typescript: + specifier: ^5.4.5 + version: 5.6.2 + unbuild: + specifier: ^2.0.0 + version: 2.0.0(typescript@5.6.2) + vite: + specifier: ^5.2.13 + version: 5.4.8(@types/node@22.7.3) + vitest: + specifier: ^2.0.0 + version: 2.1.1(@types/node@22.7.3) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@arrows/array@1.4.1': + resolution: {integrity: sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==} + + '@arrows/composition@1.2.2': + resolution: {integrity: sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==} + + '@arrows/dispatch@1.0.3': + resolution: {integrity: sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==} + + '@arrows/error@1.0.2': + resolution: {integrity: sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==} + + '@arrows/multimethod@1.4.1': + resolution: {integrity: sha512-AZnAay0dgPnCJxn3We5uKiB88VL+1ZIF2SjZohLj6vqY2UyvB/sKdDnFP+LZNVsTC5lcnGPmLlRRkAh4sXkXsQ==} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.25.4': + resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.25.2': + resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.25.6': + resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.2': + resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.25.2': + resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.25.6': + resolution: {integrity: sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.25.6': + resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/standalone@7.25.6': + resolution: {integrity: sha512-Kf2ZcZVqsKbtYhlA7sP0z5A3q5hmCVYMKMWRWNK/5OVwHIve3JY1djVRmIVAx8FMueLIfZGKQDIILK2w8zO4mg==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.0': + resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.6': + resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.25.6': + resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.2.0': + resolution: {integrity: sha512-E7Vgw78I93we4ZWdYCb4DGAwRROGkMIXk7/y87UmANR+J6qsWusmC3gLt0H+O0KOt5e6O38U8oJamgbudrES/w==} + + '@emnapi/runtime@1.2.0': + resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==} + + '@emnapi/wasi-threads@1.0.1': + resolution: {integrity: sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==} + + '@esbuild/aix-ppc64@0.19.12': + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.19.12': + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.19.12': + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.19.12': + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.19.12': + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.19.12': + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.19.12': + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.19.12': + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.19.12': + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.19.12': + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.19.12': + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.19.12': + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.19.12': + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.19.12': + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.19.12': + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.19.12': + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.19.12': + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.19.12': + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.19.12': + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.19.12': + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.19.12': + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.19.12': + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.19.12': + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@inquirer/checkbox@2.5.0': + resolution: {integrity: sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==} + engines: {node: '>=18'} + + '@inquirer/confirm@3.2.0': + resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==} + engines: {node: '>=18'} + + '@inquirer/core@9.2.1': + resolution: {integrity: sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==} + engines: {node: '>=18'} + + '@inquirer/editor@2.2.0': + resolution: {integrity: sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==} + engines: {node: '>=18'} + + '@inquirer/expand@2.3.0': + resolution: {integrity: sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==} + engines: {node: '>=18'} + + '@inquirer/figures@1.0.6': + resolution: {integrity: sha512-yfZzps3Cso2UbM7WlxKwZQh2Hs6plrbjs1QnzQDZhK2DgyCo6D8AaHps9olkNcUFlcYERMqU3uJSp1gmy3s/qQ==} + engines: {node: '>=18'} + + '@inquirer/input@2.3.0': + resolution: {integrity: sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==} + engines: {node: '>=18'} + + '@inquirer/number@1.1.0': + resolution: {integrity: sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==} + engines: {node: '>=18'} + + '@inquirer/password@2.2.0': + resolution: {integrity: sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==} + engines: {node: '>=18'} + + '@inquirer/prompts@5.5.0': + resolution: {integrity: sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==} + engines: {node: '>=18'} + + '@inquirer/rawlist@2.3.0': + resolution: {integrity: sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==} + engines: {node: '>=18'} + + '@inquirer/search@1.1.0': + resolution: {integrity: sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==} + engines: {node: '>=18'} + + '@inquirer/select@2.5.0': + resolution: {integrity: sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==} + engines: {node: '>=18'} + + '@inquirer/type@1.5.5': + resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==} + engines: {node: '>=18'} + + '@inquirer/type@2.0.0': + resolution: {integrity: sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==} + engines: {node: '>=18'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + + '@napi-rs/cli@3.0.0-alpha.62': + resolution: {integrity: sha512-IDUwHAEJZ9ad/s5oyhznrz5ZDcU+SJ6GdP5nb++Wx5MkS4FD9MeS3HfNZdsxkf10pOUPnmvCVFuG4xnLBQYmjw==} + engines: {node: '>= 16'} + hasBin: true + peerDependencies: + '@emnapi/runtime': ^1.1.0 + emnapi: ^1.1.0 + peerDependenciesMeta: + '@emnapi/runtime': + optional: true + emnapi: + optional: true + + '@napi-rs/cross-toolchain@0.0.16': + resolution: {integrity: sha512-jwdjHT5L0m9MH0CmzDwPp0ckn/UO7afHCsPeo7NugHUvYgvlgS7SWhdMVgIgJW2HHqhcW/2nhaLLGpAU1c7QRQ==} + peerDependencies: + '@napi-rs/cross-toolchain-arm64-target-aarch64': ^0.0.16 + '@napi-rs/cross-toolchain-arm64-target-armv7': ^0.0.16 + '@napi-rs/cross-toolchain-arm64-target-x86_64': ^0.0.16 + '@napi-rs/cross-toolchain-x64-target-aarch64': ^0.0.16 + '@napi-rs/cross-toolchain-x64-target-armv7': ^0.0.16 + '@napi-rs/cross-toolchain-x64-target-x86_64': ^0.0.16 + peerDependenciesMeta: + '@napi-rs/cross-toolchain-arm64-target-aarch64': + optional: true + '@napi-rs/cross-toolchain-arm64-target-armv7': + optional: true + '@napi-rs/cross-toolchain-arm64-target-x86_64': + optional: true + '@napi-rs/cross-toolchain-x64-target-aarch64': + optional: true + '@napi-rs/cross-toolchain-x64-target-armv7': + optional: true + '@napi-rs/cross-toolchain-x64-target-x86_64': + optional: true + + '@napi-rs/lzma-android-arm-eabi@1.4.1': + resolution: {integrity: sha512-yenreSpZ9IrqppJOiWDqWMmja7XtSgio9LhtxYwgdILmy/OJTe/mlTYv+FhJBf7hIV9Razu5eBuEa3zKri81IA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@napi-rs/lzma-android-arm64@1.4.1': + resolution: {integrity: sha512-piutVBz5B1TNxXeEjub0n/IKI6dMaXPPRbVSXuc4gnZgzcihNDUh68vcLZgYd+IMiACZvBxvx2O3t5nthtph3A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/lzma-darwin-arm64@1.4.1': + resolution: {integrity: sha512-sDfOhQQFqV8lGbpgJN9DqNLBPR7QOfYjcWUv8FOGPaVP1LPJDnrc5uCpRWWEa2zIKmTiO8P9xzIl0TDzrYmghg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/lzma-darwin-x64@1.4.1': + resolution: {integrity: sha512-S5/RbC6EP4QkYy2xhxbfm48ZD9FkysfpWY4Slve0nj5RGGsHvcJBg2Pi69jrTPB/zLKz2SUa0i+RfUt9zvZNaw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/lzma-freebsd-x64@1.4.1': + resolution: {integrity: sha512-4AFnq6aZnclwameSBkDWu5Ftb8y4GwvVXeQXJKbN7hf7O5GG/8QpQB1R1NJw2QORUhpKwjAQUpbkTyhL2GFWWw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/lzma-linux-arm-gnueabihf@1.4.1': + resolution: {integrity: sha512-j5rL1YRIm6rWmmGAvN6DPX6QuRjvFGB93xJ7DTRB47GXW4zHekXae6ivowjJ95vT4Iz4hSWkZbuwAy95eFrWRA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/lzma-linux-arm64-gnu@1.4.1': + resolution: {integrity: sha512-1XdFGKyTS9m+VrRQYs9uz+ToHf4Jwm0ejHU48k9lT9MPl8jSqzKdVtFzZBPzronHteSynBfKmUq0+HeWmjrsOQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/lzma-linux-arm64-musl@1.4.1': + resolution: {integrity: sha512-9d09tYS0/rBwIk1QTcO2hMZEB/ZpsG2+uFW5am1RHElSWMclObirB1An7b6AMDJcRvcomkOg2GZ9COzrvHKwEA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/lzma-linux-ppc64-gnu@1.4.1': + resolution: {integrity: sha512-UzEkmsgoJ3IOGIRb6kBzNiw+ThUpiighop7dVYfSqlF5juGzwf7YewC57RGn4FoJCvadOCrSm5VikAcgrwVgAw==} + engines: {node: '>= 10'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@napi-rs/lzma-linux-riscv64-gnu@1.4.1': + resolution: {integrity: sha512-9dUKlZ1PdwxTaFF+j3oc+xjlk9nqFwo1NWWOH30uwjl4Rm5Gkv+Fx0pHrzu4kR/iVA+oyQqa9/2uDYnGSTijBA==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/lzma-linux-s390x-gnu@1.4.1': + resolution: {integrity: sha512-MOVXUWJSLLCJDCCAlGa39sh7nv9XjvXzCf7QJus7rD8Ciz0mpXNXF9mg0ji7/MZ7pZlKPlXjXDnpVCfFdSEaFQ==} + engines: {node: '>= 10'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@napi-rs/lzma-linux-x64-gnu@1.4.1': + resolution: {integrity: sha512-Sxu7aJxU1sDbUTqjqLVDV3DCOAlbsFKvmuCN/S5uXBJd1IF2wJ9jK3NbFzfqTAo5Hudx8Y7kOb6+3K+fYPI1KQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/lzma-linux-x64-musl@1.4.1': + resolution: {integrity: sha512-4I3BeKBQJSE5gF2/VTEv7wCLLjhapeutbCGpZPmDiLHZ74rm9edmNXAlKpdjADQ4YDLJ2GIBzttvwLXkJ9U+cw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/lzma-wasm32-wasi@1.4.1': + resolution: {integrity: sha512-s32HdKqQWbohf6DGWpG9YMODaBdbKJ++JpNr6Ii7821sKf4h/o+p8IRFTOaWdmdJdllEWlRirnd5crA29VivJQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@napi-rs/lzma-win32-arm64-msvc@1.4.1': + resolution: {integrity: sha512-ISz+v7ML5mKnjEZ7Kk4Z1BIn411r/fz3tDy9j5yDnwQI0MgTsUQFrIQElGUpULWYs2aYc6EZ9PhECbLBfSjh7A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/lzma-win32-ia32-msvc@1.4.1': + resolution: {integrity: sha512-3WKuCpZBrd7Jrw+h1jSu5XAsRWepMJu0sYuRoA4Y4Cwfu9gI7p5Z5Bc510nfjg7M7xvdpkI4UoW2WY7kBFRYrQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/lzma-win32-x64-msvc@1.4.1': + resolution: {integrity: sha512-0ixRo5z1zFXdh62hlrTV+QCTKHK0te5NHKaExOluhtcc6AdpMmpslvM9JhUxNHI+zM46w/DmmcvcOtqsaTmHgg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/lzma@1.4.1': + resolution: {integrity: sha512-5f8K9NHjwHjZKGm3SS+7CFxXQhz8rbg2umBm/9g0xQRXBdYEI31N5z1ACuk9bmBQOusXAq9CArGfs/ZQso2rUA==} + engines: {node: '>= 10'} + + '@napi-rs/tar-android-arm-eabi@0.1.4': + resolution: {integrity: sha512-LMSysWp5AmZj1NOCB2jshc9KCvp4gm7vm0Cra5U2crMvlj/fwGrvv6+EzSw49y8wCkNEcQ8QaGq5NBQKiLogSg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@napi-rs/tar-android-arm64@0.1.4': + resolution: {integrity: sha512-A/2rl8xr7F5yOtHVARROoSwjRRDNL1RlXCsg/K+RE5/V9iPBojsJsLpFPilp7InF6bi+z7aYn+yWCD6wSwfF4A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/tar-darwin-arm64@0.1.4': + resolution: {integrity: sha512-mnGBswBRtxDqUwXUZx+f9Uuy2uPssxuvcWFTYgUSZqlS2pg/XIWZdHZhbqWqKGpjpZrYcr+42roytbWlZ+epMA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/tar-darwin-x64@0.1.4': + resolution: {integrity: sha512-I81Fvl/cfnFVBET49xywNd57dXLJPO7jqrjD9Z2bKeXA0v0Zt1cwV1IOOTihFZXJv7kgu6EfNB7oumoLOTqq5A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/tar-freebsd-x64@0.1.4': + resolution: {integrity: sha512-p5OFr3MqidZHkt9bHV2FgeS6k06g+s0GR2kvj/wm2mIxr7u3/sj3+RTr7GHz5DQq08T7uH85HhsrGYWN3vxmSg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/tar-linux-arm-gnueabihf@0.1.4': + resolution: {integrity: sha512-POp2zb/Yuw6taEmrTlEoQI5S+2HmgfV1VtqGQZgRmCa85NlWLDsR6vbW9euu/6NbLj3ld15cCeJC2oJkv9k/xg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/tar-linux-arm64-gnu@0.1.4': + resolution: {integrity: sha512-TV+2AppSgJx5U9nk1C9bh1afWcBVnnANJ4SmtqUF6ediHcDS2rLebeI8BGljfnX9F149qbT9gOGN+R8tofpCsg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/tar-linux-arm64-musl@0.1.4': + resolution: {integrity: sha512-UFBvKpYmuQRbgmXuSSPb8mRjq4JRZLYJhqwrWWnlfQP13xK2WB7mL2GhewBgynSH4YKDm6biKhK6U5RrSWEDgw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/tar-linux-ppc64-gnu@0.1.4': + resolution: {integrity: sha512-HT+h6Wv51SKXqks8UBF+KVuNJ09fM1GyO+SvCnDB5MF66tGiI2C6/MSX69zf1ZeqjACds1K/UwKiZCmE76/j9A==} + engines: {node: '>= 10'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@napi-rs/tar-linux-s390x-gnu@0.1.4': + resolution: {integrity: sha512-SJ+HSr281Y6cgJrQ4nkYbXaTHAmTLv/FZm5k9ZRA6Khml//ZoWi7CiT8dnPeD4QxYwCzAFA4aYMUOQJM/mk2/w==} + engines: {node: '>= 10'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@napi-rs/tar-linux-x64-gnu@0.1.4': + resolution: {integrity: sha512-LrF0lRFiFOkO40jfgTdF8dRTvYOLV52fdZ/YnJuBodNcxqEl9rChO3v5Uag//sy0me85FjqtobQNRQP8Nd80dA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/tar-linux-x64-musl@0.1.4': + resolution: {integrity: sha512-etGUWbs+Tk1PtzgyWrVzXa2fQrHNKSc/whHm+4x1Num8Oz+wGdjCDTUktYxAVy33PKZhdblVxxE83QXxkgjneQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/tar-wasm32-wasi@0.1.4': + resolution: {integrity: sha512-mANkm93AKy+OspkOBAC5WI64SopXT0VawdTjpeGW1OgyUSJWdUB5rhs3I7B/HW1bi5tsUoZOZQe3rVgYdfzA6g==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@napi-rs/tar-win32-arm64-msvc@0.1.4': + resolution: {integrity: sha512-/5/gp6WR9b36CysJDe8AdyjNeje+NqCniYJz/AZc+UvpKwG8MG9nS6TMpJ9IgrQacJXvc4lWXxYyn6uuPQVvaQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/tar-win32-ia32-msvc@0.1.4': + resolution: {integrity: sha512-HnQi0op9BqJqPekKXhEAI1TWkLtavxKDBDGXNhUSm2//jriMeRykahUcKoUUxr1UGrmtxpc5dx0cThBt13paEw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/tar-win32-x64-msvc@0.1.4': + resolution: {integrity: sha512-vy2ebEXerblni6XOgi3a27+CnI6PdQ9Phy/ru5HM4bVd/oulAJohmynAmBAB1AmXg1NLbAEWu43nATEDp5O2hA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/tar@0.1.4': + resolution: {integrity: sha512-hDsvmMZY8tl2CcLfjnTeE1o5W1eGTSL+ZIX8YEybtcJwA+Cc8SNHb7l6JqMnGcjOrWBZbHt8tzTN+W7qHS5Wmg==} + engines: {node: '>= 10'} + + '@napi-rs/wasm-runtime@0.2.4': + resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} + + '@napi-rs/wasm-tools-android-arm-eabi@0.0.2': + resolution: {integrity: sha512-/b+UU3suXjW4P0DzHRNdrnebQtFKcQf/YMeZJH+xUlKgvwli5kbmWjx8Wqqz0VETVkUTuPqJMBDIVLyc+14FGw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@napi-rs/wasm-tools-android-arm64@0.0.2': + resolution: {integrity: sha512-j57GbDflwJdZtT8pZj5fOV4JAP+LdKN+wzsUYs+QRUoBqpWbbUANudolqfw63bkS9sD4z7fbCuz8iwtJqzxTVA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/wasm-tools-darwin-arm64@0.0.2': + resolution: {integrity: sha512-P2ChgWgVuv9GwmbxN89R84KzIImoTqXINteEixUvmkdnhyFiR+I8deNs89Yed+5w8QLC6MEfrtRqLP9YI+NoQA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/wasm-tools-darwin-x64@0.0.2': + resolution: {integrity: sha512-T/kQQ0gt8+wau1Z821PKVAD76QhmwVoLs2CT7Z9tTBs2pJvwSCP0C/kQiQAHcJIMi7A2E9Ab/Mez0BERy50EFA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/wasm-tools-freebsd-x64@0.0.2': + resolution: {integrity: sha512-GnnHu+r5sfzuxC/1J5UMF/h3BOZnHb3NQZ5hmbCfZYCKzpzRxrAJhzRunlbRN+v0x8M/49dztVTYR3s7K4ooAw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/wasm-tools-linux-arm64-gnu@0.0.2': + resolution: {integrity: sha512-KnZdLT0OnKb1CG2kdt3/WvM43vr9i+FEwXCvSOVC/6Tsifz7ynhMg7LAVESILd03HubzQJfg9nbRsk0bQ+IOwg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/wasm-tools-linux-arm64-musl@0.0.2': + resolution: {integrity: sha512-HkpZOID2U8P6pWqK3mqZ8bxU5xcuT3iA2fO+jrxn78h006iYgfNmdc5JaVhHnHazMmk32xKhSV4iV0VUh8UWDg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/wasm-tools-linux-x64-gnu@0.0.2': + resolution: {integrity: sha512-YksJWBCyOalB9ogtP9+/dZKP+vR6+h7BmzMXaXMT71WW/GvIsifMVgv+DY/FRSNJQupp5Y+ugjqVAOUOc/G65g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/wasm-tools-linux-x64-musl@0.0.2': + resolution: {integrity: sha512-sPtRxPMdw05KdTcxgSPMmSXG2+PiK3vJ/l2+g9hvjnnKtvslJN2Hr7j8zgzuoKRAUFPaJVe6+D2xVh5cpdqhww==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/wasm-tools-wasm32-wasi@0.0.2': + resolution: {integrity: sha512-muRvZK7AIuo88G2AxYx3gA59rHMQgoN004saQkBvXnz3K/DVHKfTZ6TtUebss8zI3dURU6xExL8drxFWYxjEbQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@napi-rs/wasm-tools-win32-arm64-msvc@0.0.2': + resolution: {integrity: sha512-Cn13WQ+tpFqdVwx0DIWKbsI9auFyzVZV4F5UNOUeDt6GgOL+NndgJul0Pc9bSU6fi03AylMPfF/nTCaDWO2Wgw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/wasm-tools-win32-ia32-msvc@0.0.2': + resolution: {integrity: sha512-xsg5DkIQi82a8rcx6246Y3XC8TIqHamY+/C6sIlPLaZEuHctDkMECAw0AANwRf5vN//D2oo2oljOuoYtB1GOKw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/wasm-tools-win32-x64-msvc@0.0.2': + resolution: {integrity: sha512-yHigltEt33eq8bappvKsIliz4MxfMPn1M+NWbIFRWN+IS1Z57mhmc1osuk+IRXrSlq0Tom0R6MYN1jpkZKz81Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/wasm-tools@0.0.2': + resolution: {integrity: sha512-kBvDQCP5BLw2TxTENXLp3Of7vVEx0uyIye824JHE4dduzzOHVgSoOFVhVqAT3Fx/hLV445RVWfEqQbXMg4w/Mw==} + engines: {node: '>= 10'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@octokit/auth-token@5.1.1': + resolution: {integrity: sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==} + engines: {node: '>= 18'} + + '@octokit/core@6.1.2': + resolution: {integrity: sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==} + engines: {node: '>= 18'} + + '@octokit/endpoint@10.1.1': + resolution: {integrity: sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==} + engines: {node: '>= 18'} + + '@octokit/graphql@8.1.1': + resolution: {integrity: sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==} + engines: {node: '>= 18'} + + '@octokit/openapi-types@22.2.0': + resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==} + + '@octokit/plugin-paginate-rest@11.3.3': + resolution: {integrity: sha512-o4WRoOJZlKqEEgj+i9CpcmnByvtzoUYC6I8PD2SA95M+BJ2x8h7oLcVOg9qcowWXBOdcTRsMZiwvM3EyLm9AfA==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-request-log@5.3.1': + resolution: {integrity: sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@13.2.4': + resolution: {integrity: sha512-gusyAVgTrPiuXOdfqOySMDztQHv6928PQ3E4dqVGEtOvRXAKRbJR4b1zQyniIT9waqaWk/UDaoJ2dyPr7Bk7Iw==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/request-error@6.1.5': + resolution: {integrity: sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==} + engines: {node: '>= 18'} + + '@octokit/request@9.1.3': + resolution: {integrity: sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==} + engines: {node: '>= 18'} + + '@octokit/rest@21.0.2': + resolution: {integrity: sha512-+CiLisCoyWmYicH25y1cDfCrv41kRSvTq6pPWtRroRJzhsCZWZyCqGyI8foJT5LmScADSwRAnr/xo+eewL04wQ==} + engines: {node: '>= 18'} + + '@octokit/types@13.5.1': + resolution: {integrity: sha512-F41lGiWBKPIWPBgjSvaDXTTQptBujnozENAK3S//nj7xsFdYdirImKlBB/hTjr+Vii68SM+8jG3UJWRa6DMuDA==} + + '@oxc-resolver/binding-darwin-arm64@1.12.0': + resolution: {integrity: sha512-wYe+dlF8npM7cwopOOxbdNjtmJp17e/xF5c0K2WooQXy5VOh74icydM33+Uh/SZDgwyum09/U1FVCX5GdeQk+A==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@1.12.0': + resolution: {integrity: sha512-FZxxp99om+SlvBr1cjzF8A3TjYcS0BInCqjUlM+2f9m9bPTR2Bng9Zq5Q09ZQyrKJjfGKqlOEHs3akuVOnrx3Q==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@1.12.0': + resolution: {integrity: sha512-BZi0iU6IEOnXGSkqt1OjTTkN9wfyaK6kTpQwL/axl8eCcNDc7wbv1vloHgILf7ozAY1TP75nsLYlASYI4B5kGA==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@1.12.0': + resolution: {integrity: sha512-L2qnMEnZAqxbG9b1J3di/w/THIm+1fMVfbbTMWIQNMMXdMeqqDN6ojnOLDtuP564rAh4TBFPdLyEfGhMz6ipNA==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@1.12.0': + resolution: {integrity: sha512-otVbS4zeo3n71zgGLBYRTriDzc0zpruC0WI3ICwjpIk454cLwGV0yzh4jlGYWQJYJk0BRAmXFd3ooKIF+bKBHw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-arm64-musl@1.12.0': + resolution: {integrity: sha512-IStQDjIT7Lzmqg1i9wXvPL/NsYsxF24WqaQFS8b8rxra+z0VG7saBOsEnOaa4jcEY8MVpLYabFhTV+fSsA2vnA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-x64-gnu@1.12.0': + resolution: {integrity: sha512-SipT7EVORz8pOQSFwemOm91TpSiBAGmOjG830/o+aLEsvQ4pEy223+SAnCfITh7+AahldYsJnVoIs519jmIlKQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-musl@1.12.0': + resolution: {integrity: sha512-mGh0XfUzKdn+WFaqPacziNraCWL5znkHRfQVxG9avGS9zb2KC/N1EBbPzFqutDwixGDP54r2gx4q54YCJEZ4iQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-wasm32-wasi@1.12.0': + resolution: {integrity: sha512-SZN6v7apKmQf/Vwiqb6e/s3Y2Oacw8uW8V2i1AlxtyaEFvnFE0UBn89zq6swEwE3OCajNWs0yPvgAXUMddYc7Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@1.12.0': + resolution: {integrity: sha512-GRe4bqCfFsyghruEn5bv47s9w3EWBdO2q72xCz5kpQ0LWbw+enPHtTjw3qX5PUcFYpKykM55FaO0hFDs1yzatw==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@1.12.0': + resolution: {integrity: sha512-Z3llHH0jfJP4mlWq3DT7bK6qV+/vYe0+xzCgfc67+Tc/U3eYndujl880bexeGdGNPh87JeYznpZAOJ44N7QVVQ==} + cpu: [x64] + os: [win32] + + '@oxlint/darwin-arm64@0.9.9': + resolution: {integrity: sha512-My2KfUByjRl49p0rr/Glx9Y/hjney1uFk0JXNjwHqYToHqO9fY/IZ6XT1fdw9sX+1hdpq9bmj88rnkpvu0/cRw==} + cpu: [arm64] + os: [darwin] + + '@oxlint/darwin-x64@0.9.9': + resolution: {integrity: sha512-k0r0t+MAzrk8yWs0nxyD9Skfb+Ozmu6HRMTJIsUTLV4AKMt9CZBlLVS0OXzXHi72AOHz3UUve5rXxevVVY9lHQ==} + cpu: [x64] + os: [darwin] + + '@oxlint/linux-arm64-gnu@0.9.9': + resolution: {integrity: sha512-atiotr1pN3rr0i7Ww3SpOEOvkFex97S8GwYmiTlSng0kp+FSIZD6Kjlr9k3oayf9RZUDRuAE4WptwV1KYLvIDw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxlint/linux-arm64-musl@0.9.9': + resolution: {integrity: sha512-vmd6Eog6WiudMTT0Fa8u5N+47caaWF2KVUHOSEV/d/WauVx+ZOR9z1LEM+54AZwn3Z2TbtFOJDiz7iDukZPbfw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxlint/linux-x64-gnu@0.9.9': + resolution: {integrity: sha512-AUmSRerK4VXIMcTYYk25KGoOU2/z+NGItUhI6nJgMFktrbF8MUD6hlf3vaQZNle454z7FDJNSjARM0bB+xpBiQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxlint/linux-x64-musl@0.9.9': + resolution: {integrity: sha512-jQYIx5KUYbpXbXBFPgIwRPLSm3AO5wa+32BQYASIOCPcsPywV+HJKbQpzmWqXDYel6hrifmIxspgxHhGMlHJ1Q==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/win32-arm64@0.9.9': + resolution: {integrity: sha512-HSOztIKmiivfUAoxGx4qyerYV+aAXGvKbwWf8j4RorAEg2WWBdhVe9XHoSdqgYsOBi1515+YXxXiSRX3F/0xAg==} + cpu: [arm64] + os: [win32] + + '@oxlint/win32-x64@0.9.9': + resolution: {integrity: sha512-T1/tNxqoYd/MMqi1dhSVzAVL0ZINvXDBEQWm6OCSrrjRM6c9UQydTzsgLWfvm9uHWngcMuRGXhN3F+D6KEYs3w==} + cpu: [x64] + os: [win32] + + '@rollup/plugin-alias@5.1.1': + resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-commonjs@25.0.8': + resolution: {integrity: sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@15.3.0': + resolution: {integrity: sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-replace@5.0.7': + resolution: {integrity: sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@4.2.1': + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + + '@rollup/pluginutils@5.1.2': + resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.22.4': + resolution: {integrity: sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.22.4': + resolution: {integrity: sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.22.4': + resolution: {integrity: sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.22.4': + resolution: {integrity: sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.22.4': + resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.22.4': + resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.22.4': + resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.22.4': + resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': + resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.22.4': + resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-s390x-gnu@4.22.4': + resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.22.4': + resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.22.4': + resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-win32-arm64-msvc@4.22.4': + resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.22.4': + resolution: {integrity: sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.22.4': + resolution: {integrity: sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==} + cpu: [x64] + os: [win32] + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@swc-node/core@1.13.3': + resolution: {integrity: sha512-OGsvXIid2Go21kiNqeTIn79jcaX4l0G93X2rAnas4LFoDyA9wAwVK7xZdm+QsKoMn5Mus2yFLCc4OtX2dD/PWA==} + engines: {node: '>= 10'} + peerDependencies: + '@swc/core': '>= 1.4.13' + '@swc/types': '>= 0.1' + + '@swc-node/register@1.10.9': + resolution: {integrity: sha512-iXy2sjP0phPEpK2yivjRC3PAgoLaT4sjSk0LDWCTdcTBJmR4waEog0E6eJbvoOkLkOtWw37SB8vCkl/bbh4+8A==} + peerDependencies: + '@swc/core': '>= 1.4.13' + typescript: '>= 4.3' + + '@swc-node/sourcemap-support@0.5.1': + resolution: {integrity: sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg==} + + '@swc/core-darwin-arm64@1.7.28': + resolution: {integrity: sha512-BNkj6enHo2pdzOpCtQGKZbXT2A/qWIr0CVtbTM4WkJ3MCK/glbFsyO6X59p1r8+gfaZG4bWYnTTu+RuUAcsL5g==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.7.28': + resolution: {integrity: sha512-96zQ+X5Fd6P/RNPkOyikTJgEc2M4TzznfYvjRd2hye5h22jhxCLL/csoauDgN7lYfd7mwsZ/sVXwJTMKl+vZSA==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.7.28': + resolution: {integrity: sha512-l2100Wx6LdXMOmOW3+KoHhBhyZrGdz8ylkygcVOC0QHp6YIATfuG+rRHksfyEWCSOdL3anM9MJZJX26KT/s+XQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.7.28': + resolution: {integrity: sha512-03m6iQ5Bv9u2VPnNRyaBmE8eHi056eE39L0gXcqGoo46GAGuoqYHt9pDz8wS6EgoN4t85iBMUZrkCNqFKkN6ZQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-arm64-musl@1.7.28': + resolution: {integrity: sha512-vqVOpG/jc8mvTKQjaPBLhr7tnWyzuztOHsPnJqMWmg7zGcMeQC/2c5pU4uzRAfXMTp25iId6s4Y4wWfPS1EeDw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@swc/core-linux-x64-gnu@1.7.28': + resolution: {integrity: sha512-HGwpWuB83Kr+V0E+zT5UwIIY9OxiS8aLd0UVMRVWuO8SrQyKm9HKJ46+zoAb8tfJrpZftfxvbn2ayZWR7gqosA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-x64-musl@1.7.28': + resolution: {integrity: sha512-q2Y2T8y8EgFtIiRyInnAXNe94aaHX74F0ha1Bl9VdRxE0u1/So+3VLbPvtp4V3Z6pj5pOePfCQJKifnllgAQ9A==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@swc/core-win32-arm64-msvc@1.7.28': + resolution: {integrity: sha512-bCqh4uBT/59h3dWK1v91In6qzz8rKoWoFRxCtNQLIK4jP55K0U231ZK9oN7neZD6bzcOUeFvOGgcyMAgDfFWfA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.7.28': + resolution: {integrity: sha512-XTHbHrksnrqK3JSJ2sbuMWvdJ6/G0roRpgyVTmNDfhTYPOwcVaL/mSrPGLwbksYUbq7ckwoKzrobhdxvQzPsDA==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.7.28': + resolution: {integrity: sha512-jyXeoq6nX8abiCy2EpporsC5ywNENs4ocYuvxo1LSxDktWN1E2MTXq3cdJcEWB2Vydxq0rDcsGyzkRPMzFhkZw==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.7.28': + resolution: {integrity: sha512-XapcMgsOS0cKh01AFEj+qXOk6KM4NZhp7a5vPicdhkRR8RzvjrCa7DTtijMxfotU8bqaEHguxmiIag2HUlT8QQ==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.12': + resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==} + + '@taplo/cli@0.7.0': + resolution: {integrity: sha512-Ck3zFhQhIhi02Hl6T4ZmJsXdnJE+wXcJz5f8klxd4keRYgenMnip3JDPMGDRLbnC/2iGd8P0sBIQqI3KxfVjBg==} + hasBin: true + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/mute-stream@0.0.4': + resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + + '@types/node@22.5.2': + resolution: {integrity: sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==} + + '@types/node@22.7.3': + resolution: {integrity: sha512-qXKfhXXqGTyBskvWEzJZPUxSslAiLaB6JGP1ic/XTH9ctGgzdgYguuLP1C601aRTSDNlLb0jbKqXjZ48GNraSA==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/wrap-ansi@3.0.0': + resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} + + '@vercel/nft@0.26.5': + resolution: {integrity: sha512-NHxohEqad6Ra/r4lGknO52uc/GrWILXAMs1BB4401GTqww0fw1bAqzpG1XHuDO+dprg4GvsD9ZLLSsdo78p9hQ==} + engines: {node: '>=16'} + hasBin: true + + '@vitest/expect@2.1.1': + resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} + + '@vitest/mocker@2.1.1': + resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==} + peerDependencies: + '@vitest/spy': 2.1.1 + msw: ^2.3.5 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.1': + resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} + + '@vitest/runner@2.1.1': + resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} + + '@vitest/snapshot@2.1.1': + resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} + + '@vitest/spy@2.1.1': + resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} + + '@vitest/utils@2.1.1': + resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-find-index@1.0.2: + resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} + engines: {node: '>=0.10.0'} + + arrgv@1.0.2: + resolution: {integrity: sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==} + engines: {node: '>=8.0.0'} + + arrify@3.0.0: + resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==} + engines: {node: '>=12'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-sema@3.1.1: + resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + ava@6.1.3: + resolution: {integrity: sha512-tkKbpF1pIiC+q09wNU9OfyTDYZa8yuWvU2up3+lFJ3lr1RmnYh2GBpPwzYUEB0wvTPIUysGjcZLNZr7STDviRA==} + engines: {node: ^18.18 || ^20.8 || ^21 || ^22} + hasBin: true + peerDependencies: + '@ava/typescript': '*' + peerDependenciesMeta: + '@ava/typescript': + optional: true + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + before-after-hook@3.0.2: + resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} + + benchmark@2.1.4: + resolution: {integrity: sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==} + + benny@3.7.1: + resolution: {integrity: sha512-USzYxODdVfOS7JuQq/L0naxB788dWCiUgUTxvN+WLPt/JfcDURNNj8kN/N+uK6PDvuR67/9/55cVKGPleFQINA==} + engines: {node: '>=12'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + blueimp-md5@2.19.0: + resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.0: + resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + callsites@4.2.0: + resolution: {integrity: sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==} + engines: {node: '>=12.20'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001664: + resolution: {integrity: sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==} + + cbor@9.0.2: + resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==} + engines: {node: '>=16'} + + chai@5.1.1: + resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} + engines: {node: '>=12'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chunkd@2.0.1: + resolution: {integrity: sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==} + + ci-info@4.0.0: + resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} + engines: {node: '>=8'} + + ci-parallel-vars@1.0.1: + resolution: {integrity: sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + clipanion@3.2.1: + resolution: {integrity: sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==} + peerDependencies: + typanion: '*' + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + code-excerpt@4.0.0: + resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + common-path-prefix@3.0.0: + resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concordance@5.0.4: + resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==} + engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'} + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + convert-to-spaces@2.0.1: + resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + css-declaration-sorter@7.2.0: + resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@7.0.6: + resolution: {integrity: sha512-ZzrgYupYxEvdGGuqL+JKOY70s7+saoNlHSCK/OGn1vB2pQK8KSET8jvenzItcY+kA7NoWvfbb/YhlzuzNKjOhQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano-utils@5.0.0: + resolution: {integrity: sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano@7.0.6: + resolution: {integrity: sha512-54woqx8SCbp8HwvNZYn68ZFAepuouZW4lTwiMVnBErM3VkO7/Sd4oTOt3Zz3bPx3kxQ36aISppyXj2Md4lg8bw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + currently-unhandled@0.4.1: + resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==} + engines: {node: '>=0.10.0'} + + date-time@3.1.0: + resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==} + engines: {node: '>=6'} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.29: + resolution: {integrity: sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw==} + + emittery@1.0.3: + resolution: {integrity: sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA==} + engines: {node: '>=14.16'} + + emnapi@1.2.0: + resolution: {integrity: sha512-rcq+dJCcfr08I0XM/57LPRqaUokGfni/+SvhO7NaJthTTjRdohhIy0FQwyC5rR1enAsjkldQg83th8J5hURv0Q==} + peerDependencies: + node-addon-api: '>= 6.1.0' + peerDependenciesMeta: + node-addon-api: + optional: true + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up-simple@1.0.0: + resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} + engines: {node: '>=18'} + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@11.0.0: + resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} + engines: {node: 20 || >=22} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + globby@14.0.2: + resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} + engines: {node: '>=18'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.6: + resolution: {integrity: sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ignore-by-default@2.1.0: + resolution: {integrity: sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==} + engines: {node: '>=10 <11 || >=12 <13 || >=14'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inquirer@10.2.2: + resolution: {integrity: sha512-tyao/4Vo36XnUItZ7DnUXX4f1jVao2mSrleV/5IPtW/XAEA26hRVsbc68nuTEKWcr5vMP/1mVoT2O7u8H4v1Vg==} + engines: {node: '>=18'} + + irregular-plurals@3.5.0: + resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==} + engines: {node: '>=8'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@4.0.2: + resolution: {integrity: sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==} + engines: {node: 20 || >=22} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + jiti@2.0.0: + resolution: {integrity: sha512-CJ7e7Abb779OTRv3lomfp7Mns/Sy1+U4pcAx5VbjxCZD5ZM/VJaXPpPjNKjtSvWQy/H86E49REXR34dl1JEz9w==} + hasBin: true + + js-string-escape@1.0.1: + resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} + engines: {node: '>= 0.8'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + json2csv@5.0.7: + resolution: {integrity: sha512-YRZbUnyaJZLZUJSRi2G/MqahCyRv9n/ds+4oIetjDF3jWQA7AG7iSeKTiZiCNqtMZM7HDyt0e/W6lEnoGEmMGA==} + engines: {node: '>= 10', npm: '>= 6.13.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lint-staged@15.2.10: + resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.2.4: + resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==} + engines: {node: '>=18.0.0'} + + load-json-file@7.0.1: + resolution: {integrity: sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-update@4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loupe@3.1.1: + resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} + + lru-cache@11.0.1: + resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + matcher@5.0.0: + resolution: {integrity: sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + md5-hex@3.0.1: + resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} + engines: {node: '>=8'} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + memoize@10.0.0: + resolution: {integrity: sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==} + engines: {node: '>=18'} + + memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mkdist@1.5.9: + resolution: {integrity: sha512-PdJimzhcgDxaHpk1SUabw56gT3BU15vBHUTHkeeus8Kl7jUkpgG7+z0PiS/y23XXgO8TiU/dKP3L1oG55qrP1g==} + hasBin: true + peerDependencies: + sass: ^1.78.0 + typescript: '>=5.5.4' + vue-tsc: ^1.8.27 || ^2.0.21 + peerDependenciesMeta: + sass: + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build@4.8.2: + resolution: {integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==} + hasBin: true + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + nofilter@3.1.0: + resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} + engines: {node: '>=12.19'} + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-run-all2@6.2.3: + resolution: {integrity: sha512-5RsxC7jEc/RjxOYBVdEfrJf5FsJ0pHA7jr2/OxrThXknajETCTYjigOCG3iaGjdYIKEQlDuCG0ir0T1HTva8pg==} + engines: {node: ^14.18.0 || ^16.13.0 || >=18.0.0, npm: '>= 8'} + hasBin: true + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + oxc-resolver@1.12.0: + resolution: {integrity: sha512-YlaCIArvWNKCWZFRrMjhh2l5jK80eXnpYP+bhRc1J/7cW3TiyEY0ngJo73o/5n8hA3+4yLdTmXLNTQ3Ncz50LQ==} + + oxlint@0.9.9: + resolution: {integrity: sha512-EqUmSKgnU7KNCi29uFe5MQnUZ0GH569Inkry/e84k4lwVRv1QuDr4DHLpu0cXBN2atwhKf1Szk/3uxmB2qgPbw==} + engines: {node: '>=14.*'} + hasBin: true + + p-map@7.0.2: + resolution: {integrity: sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q==} + engines: {node: '>=18'} + + package-config@5.0.0: + resolution: {integrity: sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==} + engines: {node: '>=18'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + path-type@5.0.0: + resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} + engines: {node: '>=12'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@3.0.1: + resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==} + engines: {node: '>=10'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-types@1.2.0: + resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} + + platform@1.3.6: + resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} + + plur@5.1.0: + resolution: {integrity: sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + postcss-calc@10.0.2: + resolution: {integrity: sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==} + engines: {node: ^18.12 || ^20.9 || >=22.0} + peerDependencies: + postcss: ^8.4.38 + + postcss-colormin@7.0.2: + resolution: {integrity: sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-convert-values@7.0.4: + resolution: {integrity: sha512-e2LSXPqEHVW6aoGbjV9RsSSNDO3A0rZLCBxN24zvxF25WknMPpX8Dm9UxxThyEbaytzggRuZxaGXqaOhxQ514Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-comments@7.0.3: + resolution: {integrity: sha512-q6fjd4WU4afNhWOA2WltHgCbkRhZPgQe7cXF74fuVB/ge4QbM9HEaOIzGSiMvM+g/cOsNAUGdf2JDzqA2F8iLA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-duplicates@7.0.1: + resolution: {integrity: sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-empty@7.0.0: + resolution: {integrity: sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-overridden@7.0.0: + resolution: {integrity: sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-merge-longhand@7.0.4: + resolution: {integrity: sha512-zer1KoZA54Q8RVHKOY5vMke0cCdNxMP3KBfDerjH/BYHh4nCIh+1Yy0t1pAEQF18ac/4z3OFclO+ZVH8azjR4A==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-merge-rules@7.0.4: + resolution: {integrity: sha512-ZsaamiMVu7uBYsIdGtKJ64PkcQt6Pcpep/uO90EpLS3dxJi6OXamIobTYcImyXGoW0Wpugh7DSD3XzxZS9JCPg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-font-values@7.0.0: + resolution: {integrity: sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-gradients@7.0.0: + resolution: {integrity: sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-params@7.0.2: + resolution: {integrity: sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-selectors@7.0.4: + resolution: {integrity: sha512-JG55VADcNb4xFCf75hXkzc1rNeURhlo7ugf6JjiiKRfMsKlDzN9CXHZDyiG6x/zGchpjQS+UAgb1d4nqXqOpmA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-normalize-charset@7.0.0: + resolution: {integrity: sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-display-values@7.0.0: + resolution: {integrity: sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-positions@7.0.0: + resolution: {integrity: sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-repeat-style@7.0.0: + resolution: {integrity: sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-string@7.0.0: + resolution: {integrity: sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-timing-functions@7.0.0: + resolution: {integrity: sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-unicode@7.0.2: + resolution: {integrity: sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-url@7.0.0: + resolution: {integrity: sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-whitespace@7.0.0: + resolution: {integrity: sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-ordered-values@7.0.1: + resolution: {integrity: sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-initial@7.0.2: + resolution: {integrity: sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-transforms@7.0.0: + resolution: {integrity: sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-svgo@7.0.1: + resolution: {integrity: sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==} + engines: {node: ^18.12.0 || ^20.9.0 || >= 18} + peerDependencies: + postcss: ^8.4.31 + + postcss-unique-selectors@7.0.3: + resolution: {integrity: sha512-J+58u5Ic5T1QjP/LDV9g3Cx4CNOgB5vz+kM6+OxHHhFACdcDeKhBXjQmB7fnIZM12YSTvsL0Opwco83DmacW2g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + + pretty-ms@9.1.0: + resolution: {integrity: sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==} + engines: {node: '>=18'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup-plugin-dts@6.1.1: + resolution: {integrity: sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA==} + engines: {node: '>=16'} + peerDependencies: + rollup: ^3.29.4 || ^4 + typescript: ^4.5 || ^5.0 + + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + rollup@4.22.4: + resolution: {integrity: sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + serialize-error@7.0.1: + resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} + engines: {node: '>=10'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + stylehacks@7.0.4: + resolution: {integrity: sha512-i4zfNrGMt9SB4xRK9L83rlsFCgdGANfeDAYacO1pkqcE7cRHPdWHwnKZVz7WY17Veq/FvyYsRAU++Ga+qDFIww==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + supertap@3.0.1: + resolution: {integrity: sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + temp-dir@3.0.0: + resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} + engines: {node: '>=14.16'} + + time-zone@1.0.0: + resolution: {integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==} + engines: {node: '>=4'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + + tinypool@1.0.1: + resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + typanion@3.14.0: + resolution: {integrity: sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==} + + type-fest@0.13.1: + resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + + unbuild@2.0.0: + resolution: {integrity: sha512-JWCUYx3Oxdzvw2J9kTAp+DKE8df/BnH/JTSj6JyA4SH40ECdFu7FoJJcrm8G92B7TjofQ6GZGjJs50TRxoH6Wg==} + hasBin: true + peerDependencies: + typescript: ^5.1.6 + peerDependenciesMeta: + typescript: + optional: true + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + universal-user-agent@7.0.2: + resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + untyped@1.5.0: + resolution: {integrity: sha512-o2Vjmn2dal08BzCcINxSmWuAteReUUiXseii5VRhmxyLF0b21K0iKZQ9fMYK7RWspVkY+0saqaVQNq4roe3Efg==} + hasBin: true + + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-node@2.1.1: + resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.4.8: + resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@2.1.1: + resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.1 + '@vitest/ui': 2.1.1 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + wasm-sjlj@1.0.5: + resolution: {integrity: sha512-Z/MHJeOkAvJJVWnGX3/YZGYldGaawZbYHX4ldYG9kLhcdB8H31F5x66M7Zc4BP/7pg0aLsusQj1629m2B3Rilg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + well-known-symbols@2.0.0: + resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==} + engines: {node: '>=6'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@arrows/array@1.4.1': + dependencies: + '@arrows/composition': 1.2.2 + + '@arrows/composition@1.2.2': {} + + '@arrows/dispatch@1.0.3': + dependencies: + '@arrows/composition': 1.2.2 + + '@arrows/error@1.0.2': {} + + '@arrows/multimethod@1.4.1': + dependencies: + '@arrows/array': 1.4.1 + '@arrows/composition': 1.2.2 + '@arrows/error': 1.0.2 + fast-deep-equal: 3.1.3 + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.1.0 + + '@babel/compat-data@7.25.4': {} + + '@babel/core@7.25.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.6 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helpers': 7.25.6 + '@babel/parser': 7.25.6 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.25.6': + dependencies: + '@babel/types': 7.25.6 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-compilation-targets@7.25.2': + dependencies: + '@babel/compat-data': 7.25.4 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.24.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.24.8': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-option@7.24.8': {} + + '@babel/helpers@7.25.6': + dependencies: + '@babel/template': 7.25.0 + '@babel/types': 7.25.6 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.1.0 + + '@babel/parser@7.25.6': + dependencies: + '@babel/types': 7.25.6 + + '@babel/standalone@7.25.6': {} + + '@babel/template@7.25.0': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.25.6 + '@babel/types': 7.25.6 + + '@babel/traverse@7.25.6': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.6 + '@babel/parser': 7.25.6 + '@babel/template': 7.25.0 + '@babel/types': 7.25.6 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.25.6': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@emnapi/core@1.2.0': + dependencies: + '@emnapi/wasi-threads': 1.0.1 + tslib: 2.7.0 + + '@emnapi/runtime@1.2.0': + dependencies: + tslib: 2.7.0 + + '@emnapi/wasi-threads@1.0.1': + dependencies: + tslib: 2.7.0 + + '@esbuild/aix-ppc64@0.19.12': + optional: true + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.19.12': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.19.12': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.19.12': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.19.12': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.19.12': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.19.12': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.19.12': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.19.12': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.19.12': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.19.12': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.19.12': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.19.12': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.19.12': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.19.12': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.19.12': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.19.12': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.19.12': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.19.12': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.19.12': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.19.12': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.19.12': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.19.12': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@inquirer/checkbox@2.5.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/figures': 1.0.6 + '@inquirer/type': 1.5.5 + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + + '@inquirer/confirm@3.2.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + + '@inquirer/core@9.2.1': + dependencies: + '@inquirer/figures': 1.0.6 + '@inquirer/type': 2.0.0 + '@types/mute-stream': 0.0.4 + '@types/node': 22.7.3 + '@types/wrap-ansi': 3.0.0 + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 1.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + + '@inquirer/editor@2.2.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + external-editor: 3.1.0 + + '@inquirer/expand@2.3.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + yoctocolors-cjs: 2.1.2 + + '@inquirer/figures@1.0.6': {} + + '@inquirer/input@2.3.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + + '@inquirer/number@1.1.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + + '@inquirer/password@2.2.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + ansi-escapes: 4.3.2 + + '@inquirer/prompts@5.5.0': + dependencies: + '@inquirer/checkbox': 2.5.0 + '@inquirer/confirm': 3.2.0 + '@inquirer/editor': 2.2.0 + '@inquirer/expand': 2.3.0 + '@inquirer/input': 2.3.0 + '@inquirer/number': 1.1.0 + '@inquirer/password': 2.2.0 + '@inquirer/rawlist': 2.3.0 + '@inquirer/search': 1.1.0 + '@inquirer/select': 2.5.0 + + '@inquirer/rawlist@2.3.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + yoctocolors-cjs: 2.1.2 + + '@inquirer/search@1.1.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/figures': 1.0.6 + '@inquirer/type': 1.5.5 + yoctocolors-cjs: 2.1.2 + + '@inquirer/select@2.5.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/figures': 1.0.6 + '@inquirer/type': 1.5.5 + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + + '@inquirer/type@1.5.5': + dependencies: + mute-stream: 1.0.0 + + '@inquirer/type@2.0.0': + dependencies: + mute-stream: 1.0.0 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.3 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@napi-rs/cli@3.0.0-alpha.62(@emnapi/runtime@1.2.0)(emnapi@1.2.0)': + dependencies: + '@napi-rs/cross-toolchain': 0.0.16 + '@napi-rs/wasm-tools': 0.0.2 + '@octokit/rest': 21.0.2 + clipanion: 3.2.1(typanion@3.14.0) + colorette: 2.0.20 + debug: 4.3.7 + inquirer: 10.2.2 + js-yaml: 4.1.0 + lodash-es: 4.17.21 + semver: 7.6.3 + toml: 3.0.0 + typanion: 3.14.0 + wasm-sjlj: 1.0.5 + optionalDependencies: + '@emnapi/runtime': 1.2.0 + emnapi: 1.2.0 + transitivePeerDependencies: + - '@napi-rs/cross-toolchain-arm64-target-aarch64' + - '@napi-rs/cross-toolchain-arm64-target-armv7' + - '@napi-rs/cross-toolchain-arm64-target-x86_64' + - '@napi-rs/cross-toolchain-x64-target-aarch64' + - '@napi-rs/cross-toolchain-x64-target-armv7' + - '@napi-rs/cross-toolchain-x64-target-x86_64' + - supports-color + + '@napi-rs/cross-toolchain@0.0.16': + dependencies: + '@napi-rs/lzma': 1.4.1 + '@napi-rs/tar': 0.1.4 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + '@napi-rs/lzma-android-arm-eabi@1.4.1': + optional: true + + '@napi-rs/lzma-android-arm64@1.4.1': + optional: true + + '@napi-rs/lzma-darwin-arm64@1.4.1': + optional: true + + '@napi-rs/lzma-darwin-x64@1.4.1': + optional: true + + '@napi-rs/lzma-freebsd-x64@1.4.1': + optional: true + + '@napi-rs/lzma-linux-arm-gnueabihf@1.4.1': + optional: true + + '@napi-rs/lzma-linux-arm64-gnu@1.4.1': + optional: true + + '@napi-rs/lzma-linux-arm64-musl@1.4.1': + optional: true + + '@napi-rs/lzma-linux-ppc64-gnu@1.4.1': + optional: true + + '@napi-rs/lzma-linux-riscv64-gnu@1.4.1': + optional: true + + '@napi-rs/lzma-linux-s390x-gnu@1.4.1': + optional: true + + '@napi-rs/lzma-linux-x64-gnu@1.4.1': + optional: true + + '@napi-rs/lzma-linux-x64-musl@1.4.1': + optional: true + + '@napi-rs/lzma-wasm32-wasi@1.4.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.4 + optional: true + + '@napi-rs/lzma-win32-arm64-msvc@1.4.1': + optional: true + + '@napi-rs/lzma-win32-ia32-msvc@1.4.1': + optional: true + + '@napi-rs/lzma-win32-x64-msvc@1.4.1': + optional: true + + '@napi-rs/lzma@1.4.1': + optionalDependencies: + '@napi-rs/lzma-android-arm-eabi': 1.4.1 + '@napi-rs/lzma-android-arm64': 1.4.1 + '@napi-rs/lzma-darwin-arm64': 1.4.1 + '@napi-rs/lzma-darwin-x64': 1.4.1 + '@napi-rs/lzma-freebsd-x64': 1.4.1 + '@napi-rs/lzma-linux-arm-gnueabihf': 1.4.1 + '@napi-rs/lzma-linux-arm64-gnu': 1.4.1 + '@napi-rs/lzma-linux-arm64-musl': 1.4.1 + '@napi-rs/lzma-linux-ppc64-gnu': 1.4.1 + '@napi-rs/lzma-linux-riscv64-gnu': 1.4.1 + '@napi-rs/lzma-linux-s390x-gnu': 1.4.1 + '@napi-rs/lzma-linux-x64-gnu': 1.4.1 + '@napi-rs/lzma-linux-x64-musl': 1.4.1 + '@napi-rs/lzma-wasm32-wasi': 1.4.1 + '@napi-rs/lzma-win32-arm64-msvc': 1.4.1 + '@napi-rs/lzma-win32-ia32-msvc': 1.4.1 + '@napi-rs/lzma-win32-x64-msvc': 1.4.1 + + '@napi-rs/tar-android-arm-eabi@0.1.4': + optional: true + + '@napi-rs/tar-android-arm64@0.1.4': + optional: true + + '@napi-rs/tar-darwin-arm64@0.1.4': + optional: true + + '@napi-rs/tar-darwin-x64@0.1.4': + optional: true + + '@napi-rs/tar-freebsd-x64@0.1.4': + optional: true + + '@napi-rs/tar-linux-arm-gnueabihf@0.1.4': + optional: true + + '@napi-rs/tar-linux-arm64-gnu@0.1.4': + optional: true + + '@napi-rs/tar-linux-arm64-musl@0.1.4': + optional: true + + '@napi-rs/tar-linux-ppc64-gnu@0.1.4': + optional: true + + '@napi-rs/tar-linux-s390x-gnu@0.1.4': + optional: true + + '@napi-rs/tar-linux-x64-gnu@0.1.4': + optional: true + + '@napi-rs/tar-linux-x64-musl@0.1.4': + optional: true + + '@napi-rs/tar-wasm32-wasi@0.1.4': + dependencies: + '@napi-rs/wasm-runtime': 0.2.4 + optional: true + + '@napi-rs/tar-win32-arm64-msvc@0.1.4': + optional: true + + '@napi-rs/tar-win32-ia32-msvc@0.1.4': + optional: true + + '@napi-rs/tar-win32-x64-msvc@0.1.4': + optional: true + + '@napi-rs/tar@0.1.4': + optionalDependencies: + '@napi-rs/tar-android-arm-eabi': 0.1.4 + '@napi-rs/tar-android-arm64': 0.1.4 + '@napi-rs/tar-darwin-arm64': 0.1.4 + '@napi-rs/tar-darwin-x64': 0.1.4 + '@napi-rs/tar-freebsd-x64': 0.1.4 + '@napi-rs/tar-linux-arm-gnueabihf': 0.1.4 + '@napi-rs/tar-linux-arm64-gnu': 0.1.4 + '@napi-rs/tar-linux-arm64-musl': 0.1.4 + '@napi-rs/tar-linux-ppc64-gnu': 0.1.4 + '@napi-rs/tar-linux-s390x-gnu': 0.1.4 + '@napi-rs/tar-linux-x64-gnu': 0.1.4 + '@napi-rs/tar-linux-x64-musl': 0.1.4 + '@napi-rs/tar-wasm32-wasi': 0.1.4 + '@napi-rs/tar-win32-arm64-msvc': 0.1.4 + '@napi-rs/tar-win32-ia32-msvc': 0.1.4 + '@napi-rs/tar-win32-x64-msvc': 0.1.4 + + '@napi-rs/wasm-runtime@0.2.4': + dependencies: + '@emnapi/core': 1.2.0 + '@emnapi/runtime': 1.2.0 + '@tybys/wasm-util': 0.9.0 + + '@napi-rs/wasm-tools-android-arm-eabi@0.0.2': + optional: true + + '@napi-rs/wasm-tools-android-arm64@0.0.2': + optional: true + + '@napi-rs/wasm-tools-darwin-arm64@0.0.2': + optional: true + + '@napi-rs/wasm-tools-darwin-x64@0.0.2': + optional: true + + '@napi-rs/wasm-tools-freebsd-x64@0.0.2': + optional: true + + '@napi-rs/wasm-tools-linux-arm64-gnu@0.0.2': + optional: true + + '@napi-rs/wasm-tools-linux-arm64-musl@0.0.2': + optional: true + + '@napi-rs/wasm-tools-linux-x64-gnu@0.0.2': + optional: true + + '@napi-rs/wasm-tools-linux-x64-musl@0.0.2': + optional: true + + '@napi-rs/wasm-tools-wasm32-wasi@0.0.2': + dependencies: + '@napi-rs/wasm-runtime': 0.2.4 + optional: true + + '@napi-rs/wasm-tools-win32-arm64-msvc@0.0.2': + optional: true + + '@napi-rs/wasm-tools-win32-ia32-msvc@0.0.2': + optional: true + + '@napi-rs/wasm-tools-win32-x64-msvc@0.0.2': + optional: true + + '@napi-rs/wasm-tools@0.0.2': + optionalDependencies: + '@napi-rs/wasm-tools-android-arm-eabi': 0.0.2 + '@napi-rs/wasm-tools-android-arm64': 0.0.2 + '@napi-rs/wasm-tools-darwin-arm64': 0.0.2 + '@napi-rs/wasm-tools-darwin-x64': 0.0.2 + '@napi-rs/wasm-tools-freebsd-x64': 0.0.2 + '@napi-rs/wasm-tools-linux-arm64-gnu': 0.0.2 + '@napi-rs/wasm-tools-linux-arm64-musl': 0.0.2 + '@napi-rs/wasm-tools-linux-x64-gnu': 0.0.2 + '@napi-rs/wasm-tools-linux-x64-musl': 0.0.2 + '@napi-rs/wasm-tools-wasm32-wasi': 0.0.2 + '@napi-rs/wasm-tools-win32-arm64-msvc': 0.0.2 + '@napi-rs/wasm-tools-win32-ia32-msvc': 0.0.2 + '@napi-rs/wasm-tools-win32-x64-msvc': 0.0.2 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@octokit/auth-token@5.1.1': {} + + '@octokit/core@6.1.2': + dependencies: + '@octokit/auth-token': 5.1.1 + '@octokit/graphql': 8.1.1 + '@octokit/request': 9.1.3 + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.5.1 + before-after-hook: 3.0.2 + universal-user-agent: 7.0.2 + + '@octokit/endpoint@10.1.1': + dependencies: + '@octokit/types': 13.5.1 + universal-user-agent: 7.0.2 + + '@octokit/graphql@8.1.1': + dependencies: + '@octokit/request': 9.1.3 + '@octokit/types': 13.5.1 + universal-user-agent: 7.0.2 + + '@octokit/openapi-types@22.2.0': {} + + '@octokit/plugin-paginate-rest@11.3.3(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/types': 13.5.1 + + '@octokit/plugin-request-log@5.3.1(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + + '@octokit/plugin-rest-endpoint-methods@13.2.4(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/types': 13.5.1 + + '@octokit/request-error@6.1.5': + dependencies: + '@octokit/types': 13.5.1 + + '@octokit/request@9.1.3': + dependencies: + '@octokit/endpoint': 10.1.1 + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.5.1 + universal-user-agent: 7.0.2 + + '@octokit/rest@21.0.2': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/plugin-paginate-rest': 11.3.3(@octokit/core@6.1.2) + '@octokit/plugin-request-log': 5.3.1(@octokit/core@6.1.2) + '@octokit/plugin-rest-endpoint-methods': 13.2.4(@octokit/core@6.1.2) + + '@octokit/types@13.5.1': + dependencies: + '@octokit/openapi-types': 22.2.0 + + '@oxc-resolver/binding-darwin-arm64@1.12.0': + optional: true + + '@oxc-resolver/binding-darwin-x64@1.12.0': + optional: true + + '@oxc-resolver/binding-freebsd-x64@1.12.0': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@1.12.0': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@1.12.0': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@1.12.0': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@1.12.0': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@1.12.0': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@1.12.0': + dependencies: + '@napi-rs/wasm-runtime': 0.2.4 + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@1.12.0': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@1.12.0': + optional: true + + '@oxlint/darwin-arm64@0.9.9': + optional: true + + '@oxlint/darwin-x64@0.9.9': + optional: true + + '@oxlint/linux-arm64-gnu@0.9.9': + optional: true + + '@oxlint/linux-arm64-musl@0.9.9': + optional: true + + '@oxlint/linux-x64-gnu@0.9.9': + optional: true + + '@oxlint/linux-x64-musl@0.9.9': + optional: true + + '@oxlint/win32-arm64@0.9.9': + optional: true + + '@oxlint/win32-x64@0.9.9': + optional: true + + '@rollup/plugin-alias@5.1.1(rollup@3.29.5)': + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-commonjs@25.0.8(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@3.29.5) + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 8.1.0 + is-reference: 1.2.1 + magic-string: 0.30.11 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-json@6.1.0(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@3.29.5) + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-node-resolve@15.3.0(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@3.29.5) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.8 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-replace@5.0.7(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@3.29.5) + magic-string: 0.30.11 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/pluginutils@4.2.1': + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + + '@rollup/pluginutils@5.1.2(rollup@3.29.5)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/rollup-android-arm-eabi@4.22.4': + optional: true + + '@rollup/rollup-android-arm64@4.22.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.22.4': + optional: true + + '@rollup/rollup-darwin-x64@4.22.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.22.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.22.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.22.4': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.22.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.22.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.22.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.22.4': + optional: true + + '@sindresorhus/merge-streams@2.3.0': {} + + '@swc-node/core@1.13.3(@swc/core@1.7.28)(@swc/types@0.1.12)': + dependencies: + '@swc/core': 1.7.28 + '@swc/types': 0.1.12 + + '@swc-node/register@1.10.9(@swc/core@1.7.28)(@swc/types@0.1.12)(typescript@5.6.2)': + dependencies: + '@swc-node/core': 1.13.3(@swc/core@1.7.28)(@swc/types@0.1.12) + '@swc-node/sourcemap-support': 0.5.1 + '@swc/core': 1.7.28 + colorette: 2.0.20 + debug: 4.3.7 + oxc-resolver: 1.12.0 + pirates: 4.0.6 + tslib: 2.7.0 + typescript: 5.6.2 + transitivePeerDependencies: + - '@swc/types' + - supports-color + + '@swc-node/sourcemap-support@0.5.1': + dependencies: + source-map-support: 0.5.21 + tslib: 2.7.0 + + '@swc/core-darwin-arm64@1.7.28': + optional: true + + '@swc/core-darwin-x64@1.7.28': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.7.28': + optional: true + + '@swc/core-linux-arm64-gnu@1.7.28': + optional: true + + '@swc/core-linux-arm64-musl@1.7.28': + optional: true + + '@swc/core-linux-x64-gnu@1.7.28': + optional: true + + '@swc/core-linux-x64-musl@1.7.28': + optional: true + + '@swc/core-win32-arm64-msvc@1.7.28': + optional: true + + '@swc/core-win32-ia32-msvc@1.7.28': + optional: true + + '@swc/core-win32-x64-msvc@1.7.28': + optional: true + + '@swc/core@1.7.28': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.12 + optionalDependencies: + '@swc/core-darwin-arm64': 1.7.28 + '@swc/core-darwin-x64': 1.7.28 + '@swc/core-linux-arm-gnueabihf': 1.7.28 + '@swc/core-linux-arm64-gnu': 1.7.28 + '@swc/core-linux-arm64-musl': 1.7.28 + '@swc/core-linux-x64-gnu': 1.7.28 + '@swc/core-linux-x64-musl': 1.7.28 + '@swc/core-win32-arm64-msvc': 1.7.28 + '@swc/core-win32-ia32-msvc': 1.7.28 + '@swc/core-win32-x64-msvc': 1.7.28 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.12': + dependencies: + '@swc/counter': 0.1.3 + + '@taplo/cli@0.7.0': {} + + '@trysound/sax@0.2.0': {} + + '@tybys/wasm-util@0.9.0': + dependencies: + tslib: 2.7.0 + + '@types/estree@1.0.5': {} + + '@types/estree@1.0.6': {} + + '@types/mute-stream@0.0.4': + dependencies: + '@types/node': 22.5.2 + + '@types/node@22.5.2': + dependencies: + undici-types: 6.19.8 + + '@types/node@22.7.3': + dependencies: + undici-types: 6.19.8 + + '@types/resolve@1.20.2': {} + + '@types/wrap-ansi@3.0.0': {} + + '@vercel/nft@0.26.5': + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + '@rollup/pluginutils': 4.2.1 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + async-sema: 3.1.1 + bindings: 1.5.0 + estree-walker: 2.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + node-gyp-build: 4.8.2 + resolve-from: 5.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + '@vitest/expect@2.1.1': + dependencies: + '@vitest/spy': 2.1.1 + '@vitest/utils': 2.1.1 + chai: 5.1.1 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.4.8(@types/node@22.7.3))': + dependencies: + '@vitest/spy': 2.1.1 + estree-walker: 3.0.3 + magic-string: 0.30.11 + optionalDependencies: + vite: 5.4.8(@types/node@22.7.3) + + '@vitest/pretty-format@2.1.1': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.1': + dependencies: + '@vitest/utils': 2.1.1 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.1': + dependencies: + '@vitest/pretty-format': 2.1.1 + magic-string: 0.30.11 + pathe: 1.1.2 + + '@vitest/spy@2.1.1': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.1': + dependencies: + '@vitest/pretty-format': 2.1.1 + loupe: 3.1.1 + tinyrainbow: 1.2.0 + + abbrev@1.1.1: {} + + acorn-import-attributes@1.9.5(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + agent-base@6.0.2: + dependencies: + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + aproba@2.0.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-find-index@1.0.2: {} + + arrgv@1.0.2: {} + + arrify@3.0.0: {} + + assertion-error@2.0.1: {} + + astral-regex@2.0.0: {} + + async-sema@3.1.1: {} + + autoprefixer@10.4.20(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + caniuse-lite: 1.0.30001664 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + ava@6.1.3: + dependencies: + '@vercel/nft': 0.26.5 + acorn: 8.12.1 + acorn-walk: 8.3.4 + ansi-styles: 6.2.1 + arrgv: 1.0.2 + arrify: 3.0.0 + callsites: 4.2.0 + cbor: 9.0.2 + chalk: 5.3.0 + chunkd: 2.0.1 + ci-info: 4.0.0 + ci-parallel-vars: 1.0.1 + cli-truncate: 4.0.0 + code-excerpt: 4.0.0 + common-path-prefix: 3.0.0 + concordance: 5.0.4 + currently-unhandled: 0.4.1 + debug: 4.3.7 + emittery: 1.0.3 + figures: 6.1.0 + globby: 14.0.2 + ignore-by-default: 2.1.0 + indent-string: 5.0.0 + is-plain-object: 5.0.0 + is-promise: 4.0.0 + matcher: 5.0.0 + memoize: 10.0.0 + ms: 2.1.3 + p-map: 7.0.2 + package-config: 5.0.0 + picomatch: 3.0.1 + plur: 5.1.0 + pretty-ms: 9.1.0 + resolve-cwd: 3.0.0 + stack-utils: 2.0.6 + strip-ansi: 7.1.0 + supertap: 3.0.1 + temp-dir: 3.0.0 + write-file-atomic: 5.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + - supports-color + + balanced-match@1.0.2: {} + + before-after-hook@3.0.2: {} + + benchmark@2.1.4: + dependencies: + lodash: 4.17.21 + platform: 1.3.6 + + benny@3.7.1: + dependencies: + '@arrows/composition': 1.2.2 + '@arrows/dispatch': 1.0.3 + '@arrows/multimethod': 1.4.1 + benchmark: 2.1.4 + common-tags: 1.8.2 + fs-extra: 10.1.0 + json2csv: 5.0.7 + kleur: 4.1.5 + log-update: 4.0.0 + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + blueimp-md5@2.19.0: {} + + boolbase@1.0.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.0: + dependencies: + caniuse-lite: 1.0.30001664 + electron-to-chromium: 1.5.29 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.0) + + buffer-from@1.1.2: {} + + cac@6.7.14: {} + + callsites@4.2.0: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.24.0 + caniuse-lite: 1.0.30001664 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001664: {} + + cbor@9.0.2: + dependencies: + nofilter: 3.1.0 + + chai@5.1.1: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.1 + pathval: 2.0.0 + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@5.3.0: {} + + chardet@0.7.0: {} + + check-error@2.1.1: {} + + chownr@2.0.0: {} + + chunkd@2.0.1: {} + + ci-info@4.0.0: {} + + ci-parallel-vars@1.0.1: {} + + citty@0.1.6: + dependencies: + consola: 3.2.3 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + cli-width@4.1.0: {} + + clipanion@3.2.1(typanion@3.14.0): + dependencies: + typanion: 3.14.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + code-excerpt@4.0.0: + dependencies: + convert-to-spaces: 2.0.1 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-support@1.1.3: {} + + colord@2.9.3: {} + + colorette@2.0.20: {} + + commander@12.1.0: {} + + commander@6.2.1: {} + + commander@7.2.0: {} + + common-path-prefix@3.0.0: {} + + common-tags@1.8.2: {} + + commondir@1.0.1: {} + + concat-map@0.0.1: {} + + concordance@5.0.4: + dependencies: + date-time: 3.1.0 + esutils: 2.0.3 + fast-diff: 1.3.0 + js-string-escape: 1.0.1 + lodash: 4.17.21 + md5-hex: 3.0.1 + semver: 7.6.3 + well-known-symbols: 2.0.0 + + confbox@0.1.7: {} + + consola@3.2.3: {} + + console-control-strings@1.1.0: {} + + convert-source-map@2.0.0: {} + + convert-to-spaces@2.0.1: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-declaration-sorter@7.2.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-what@6.1.0: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@7.0.6(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + css-declaration-sorter: 7.2.0(postcss@8.4.47) + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-calc: 10.0.2(postcss@8.4.47) + postcss-colormin: 7.0.2(postcss@8.4.47) + postcss-convert-values: 7.0.4(postcss@8.4.47) + postcss-discard-comments: 7.0.3(postcss@8.4.47) + postcss-discard-duplicates: 7.0.1(postcss@8.4.47) + postcss-discard-empty: 7.0.0(postcss@8.4.47) + postcss-discard-overridden: 7.0.0(postcss@8.4.47) + postcss-merge-longhand: 7.0.4(postcss@8.4.47) + postcss-merge-rules: 7.0.4(postcss@8.4.47) + postcss-minify-font-values: 7.0.0(postcss@8.4.47) + postcss-minify-gradients: 7.0.0(postcss@8.4.47) + postcss-minify-params: 7.0.2(postcss@8.4.47) + postcss-minify-selectors: 7.0.4(postcss@8.4.47) + postcss-normalize-charset: 7.0.0(postcss@8.4.47) + postcss-normalize-display-values: 7.0.0(postcss@8.4.47) + postcss-normalize-positions: 7.0.0(postcss@8.4.47) + postcss-normalize-repeat-style: 7.0.0(postcss@8.4.47) + postcss-normalize-string: 7.0.0(postcss@8.4.47) + postcss-normalize-timing-functions: 7.0.0(postcss@8.4.47) + postcss-normalize-unicode: 7.0.2(postcss@8.4.47) + postcss-normalize-url: 7.0.0(postcss@8.4.47) + postcss-normalize-whitespace: 7.0.0(postcss@8.4.47) + postcss-ordered-values: 7.0.1(postcss@8.4.47) + postcss-reduce-initial: 7.0.2(postcss@8.4.47) + postcss-reduce-transforms: 7.0.0(postcss@8.4.47) + postcss-svgo: 7.0.1(postcss@8.4.47) + postcss-unique-selectors: 7.0.3(postcss@8.4.47) + + cssnano-utils@5.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + cssnano@7.0.6(postcss@8.4.47): + dependencies: + cssnano-preset-default: 7.0.6(postcss@8.4.47) + lilconfig: 3.1.2 + postcss: 8.4.47 + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + currently-unhandled@0.4.1: + dependencies: + array-find-index: 1.0.2 + + date-time@3.1.0: + dependencies: + time-zone: 1.0.0 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + deepmerge@4.3.1: {} + + defu@6.1.4: {} + + delegates@1.0.0: {} + + detect-libc@2.0.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.5.29: {} + + emittery@1.0.3: {} + + emnapi@1.2.0: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + entities@4.5.0: {} + + environment@1.1.0: {} + + esbuild@0.19.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@5.0.0: {} + + esprima@4.0.1: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + + esutils@2.0.3: {} + + eventemitter3@5.0.1: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-uri-to-path@1.0.0: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up-simple@1.0.0: {} + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + fraction.js@4.3.7: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.2.0: {} + + get-func-name@2.0.2: {} + + get-stream@8.0.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob@11.0.0: + dependencies: + foreground-child: 3.3.0 + jackspeak: 4.0.2 + minimatch: 10.0.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + globals@11.12.0: {} + + globby@13.2.2: + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 4.0.0 + + globby@14.0.2: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.2 + ignore: 5.3.2 + path-type: 5.0.0 + slash: 5.1.0 + unicorn-magic: 0.1.0 + + graceful-fs@4.2.11: {} + + has-flag@3.0.0: {} + + has-unicode@2.0.1: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hookable@5.5.3: {} + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + human-signals@5.0.0: {} + + husky@9.1.6: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + ignore-by-default@2.1.0: {} + + ignore@5.3.2: {} + + imurmurhash@0.1.4: {} + + indent-string@5.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + inquirer@10.2.2: + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/prompts': 5.5.0 + '@inquirer/type': 1.5.5 + '@types/mute-stream': 0.0.4 + ansi-escapes: 4.3.2 + mute-stream: 1.0.0 + run-async: 3.0.0 + rxjs: 7.8.1 + + irregular-plurals@3.5.0: {} + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.2.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-module@1.0.0: {} + + is-number@7.0.0: {} + + is-plain-object@5.0.0: {} + + is-promise@4.0.0: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.6 + + is-stream@3.0.0: {} + + is-unicode-supported@2.1.0: {} + + isexe@2.0.0: {} + + jackspeak@4.0.2: + dependencies: + '@isaacs/cliui': 8.0.2 + + jiti@1.21.6: {} + + jiti@2.0.0: {} + + js-string-escape@1.0.1: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@2.5.2: {} + + json-parse-even-better-errors@3.0.2: {} + + json2csv@5.0.7: + dependencies: + commander: 6.2.1 + jsonparse: 1.3.1 + lodash.get: 4.4.2 + + json5@2.2.3: {} + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + kleur@4.1.5: {} + + lilconfig@3.1.2: {} + + lint-staged@15.2.10: + dependencies: + chalk: 5.3.0 + commander: 12.1.0 + debug: 4.3.7 + execa: 8.0.1 + lilconfig: 3.1.2 + listr2: 8.2.4 + micromatch: 4.0.8 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.5.1 + transitivePeerDependencies: + - supports-color + + listr2@8.2.4: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + load-json-file@7.0.1: {} + + lodash-es@4.17.21: {} + + lodash.get@4.4.2: {} + + lodash.memoize@4.1.2: {} + + lodash.uniq@4.5.0: {} + + lodash@4.17.21: {} + + log-update@4.0.0: + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + loupe@3.1.1: + dependencies: + get-func-name: 2.0.2 + + lru-cache@11.0.1: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + matcher@5.0.0: + dependencies: + escape-string-regexp: 5.0.0 + + md5-hex@3.0.1: + dependencies: + blueimp-md5: 2.19.0 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + memoize@10.0.0: + dependencies: + mimic-function: 5.0.1 + + memorystream@0.3.1: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + minimatch@10.0.1: + dependencies: + brace-expansion: 2.0.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@1.0.4: {} + + mkdist@1.5.9(typescript@5.6.2): + dependencies: + autoprefixer: 10.4.20(postcss@8.4.47) + citty: 0.1.6 + cssnano: 7.0.6(postcss@8.4.47) + defu: 6.1.4 + esbuild: 0.23.1 + fast-glob: 3.3.2 + jiti: 1.21.6 + mlly: 1.7.1 + pathe: 1.1.2 + pkg-types: 1.2.0 + postcss: 8.4.47 + postcss-nested: 6.2.0(postcss@8.4.47) + semver: 7.6.3 + optionalDependencies: + typescript: 5.6.2 + + mlly@1.7.1: + dependencies: + acorn: 8.12.1 + pathe: 1.1.2 + pkg-types: 1.2.0 + ufo: 1.5.4 + + mri@1.2.0: {} + + ms@2.1.3: {} + + mute-stream@1.0.0: {} + + nanoid@3.3.7: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build@4.8.2: {} + + node-releases@2.0.18: {} + + nofilter@3.1.0: {} + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + + normalize-range@0.1.2: {} + + npm-normalize-package-bin@3.0.1: {} + + npm-run-all2@6.2.3: + dependencies: + ansi-styles: 6.2.1 + cross-spawn: 7.0.3 + memorystream: 0.3.1 + minimatch: 9.0.5 + pidtree: 0.6.0 + read-package-json-fast: 3.0.2 + shell-quote: 1.8.1 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + os-tmpdir@1.0.2: {} + + oxc-resolver@1.12.0: + optionalDependencies: + '@oxc-resolver/binding-darwin-arm64': 1.12.0 + '@oxc-resolver/binding-darwin-x64': 1.12.0 + '@oxc-resolver/binding-freebsd-x64': 1.12.0 + '@oxc-resolver/binding-linux-arm-gnueabihf': 1.12.0 + '@oxc-resolver/binding-linux-arm64-gnu': 1.12.0 + '@oxc-resolver/binding-linux-arm64-musl': 1.12.0 + '@oxc-resolver/binding-linux-x64-gnu': 1.12.0 + '@oxc-resolver/binding-linux-x64-musl': 1.12.0 + '@oxc-resolver/binding-wasm32-wasi': 1.12.0 + '@oxc-resolver/binding-win32-arm64-msvc': 1.12.0 + '@oxc-resolver/binding-win32-x64-msvc': 1.12.0 + + oxlint@0.9.9: + optionalDependencies: + '@oxlint/darwin-arm64': 0.9.9 + '@oxlint/darwin-x64': 0.9.9 + '@oxlint/linux-arm64-gnu': 0.9.9 + '@oxlint/linux-arm64-musl': 0.9.9 + '@oxlint/linux-x64-gnu': 0.9.9 + '@oxlint/linux-x64-musl': 0.9.9 + '@oxlint/win32-arm64': 0.9.9 + '@oxlint/win32-x64': 0.9.9 + + p-map@7.0.2: {} + + package-config@5.0.0: + dependencies: + find-up-simple: 1.0.0 + load-json-file: 7.0.1 + + package-json-from-dist@1.0.1: {} + + parse-ms@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@2.0.0: + dependencies: + lru-cache: 11.0.1 + minipass: 7.1.2 + + path-type@4.0.0: {} + + path-type@5.0.0: {} + + pathe@1.1.2: {} + + pathval@2.0.0: {} + + picocolors@1.1.0: {} + + picomatch@2.3.1: {} + + picomatch@3.0.1: {} + + pidtree@0.6.0: {} + + pirates@4.0.6: {} + + pkg-types@1.2.0: + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + + platform@1.3.6: {} + + plur@5.1.0: + dependencies: + irregular-plurals: 3.5.0 + + postcss-calc@10.0.2(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-colormin@7.0.2(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-convert-values@7.0.4(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-discard-comments@7.0.3(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + postcss-discard-duplicates@7.0.1(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + postcss-discard-empty@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + postcss-discard-overridden@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + postcss-merge-longhand@7.0.4(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + stylehacks: 7.0.4(postcss@8.4.47) + + postcss-merge-rules@7.0.4(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + caniuse-api: 3.0.0 + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + postcss-minify-font-values@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@7.0.0(postcss@8.4.47): + dependencies: + colord: 2.9.3 + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-minify-params@7.0.2(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@7.0.4(postcss@8.4.47): + dependencies: + cssesc: 3.0.0 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + postcss-nested@6.2.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + postcss-normalize-charset@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + postcss-normalize-display-values@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@7.0.2(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-ordered-values@7.0.1(postcss@8.4.47): + dependencies: + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-reduce-initial@7.0.2(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + caniuse-api: 3.0.0 + postcss: 8.4.47 + + postcss-reduce-transforms@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-svgo@7.0.1(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + svgo: 3.3.2 + + postcss-unique-selectors@7.0.3(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.47: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.0 + source-map-js: 1.2.1 + + prettier@3.3.3: {} + + pretty-bytes@6.1.1: {} + + pretty-ms@9.1.0: + dependencies: + parse-ms: 4.0.0 + + queue-microtask@1.2.3: {} + + read-package-json-fast@3.0.2: + dependencies: + json-parse-even-better-errors: 3.0.2 + npm-normalize-package-bin: 3.0.1 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + require-directory@2.1.1: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@5.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.0.4: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup-plugin-dts@6.1.1(rollup@3.29.5)(typescript@5.6.2): + dependencies: + magic-string: 0.30.11 + rollup: 3.29.5 + typescript: 5.6.2 + optionalDependencies: + '@babel/code-frame': 7.24.7 + + rollup@3.29.5: + optionalDependencies: + fsevents: 2.3.3 + + rollup@4.22.4: + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.22.4 + '@rollup/rollup-android-arm64': 4.22.4 + '@rollup/rollup-darwin-arm64': 4.22.4 + '@rollup/rollup-darwin-x64': 4.22.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.22.4 + '@rollup/rollup-linux-arm-musleabihf': 4.22.4 + '@rollup/rollup-linux-arm64-gnu': 4.22.4 + '@rollup/rollup-linux-arm64-musl': 4.22.4 + '@rollup/rollup-linux-powerpc64le-gnu': 4.22.4 + '@rollup/rollup-linux-riscv64-gnu': 4.22.4 + '@rollup/rollup-linux-s390x-gnu': 4.22.4 + '@rollup/rollup-linux-x64-gnu': 4.22.4 + '@rollup/rollup-linux-x64-musl': 4.22.4 + '@rollup/rollup-win32-arm64-msvc': 4.22.4 + '@rollup/rollup-win32-ia32-msvc': 4.22.4 + '@rollup/rollup-win32-x64-msvc': 4.22.4 + fsevents: 2.3.3 + + run-async@3.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.1: + dependencies: + tslib: 2.7.0 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + scule@1.3.0: {} + + semver@6.3.1: {} + + semver@7.6.3: {} + + serialize-error@7.0.1: + dependencies: + type-fest: 0.13.1 + + set-blocking@2.0.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.1: {} + + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + slash@4.0.0: {} + + slash@5.1.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + sprintf-js@1.0.3: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackback@0.0.2: {} + + std-env@3.7.0: {} + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.2.0 + strip-ansi: 7.1.0 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-final-newline@3.0.0: {} + + stylehacks@7.0.4(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + supertap@3.0.1: + dependencies: + indent-string: 5.0.0 + js-yaml: 3.14.1 + serialize-error: 7.0.1 + strip-ansi: 7.1.0 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + css-what: 6.1.0 + csso: 5.0.5 + picocolors: 1.1.0 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + temp-dir@3.0.0: {} + + time-zone@1.0.0: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.0: {} + + tinypool@1.0.1: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toml@3.0.0: {} + + tr46@0.0.3: {} + + tslib@2.7.0: {} + + typanion@3.14.0: {} + + type-fest@0.13.1: {} + + type-fest@0.21.3: {} + + typescript@5.6.2: {} + + ufo@1.5.4: {} + + unbuild@2.0.0(typescript@5.6.2): + dependencies: + '@rollup/plugin-alias': 5.1.1(rollup@3.29.5) + '@rollup/plugin-commonjs': 25.0.8(rollup@3.29.5) + '@rollup/plugin-json': 6.1.0(rollup@3.29.5) + '@rollup/plugin-node-resolve': 15.3.0(rollup@3.29.5) + '@rollup/plugin-replace': 5.0.7(rollup@3.29.5) + '@rollup/pluginutils': 5.1.2(rollup@3.29.5) + chalk: 5.3.0 + citty: 0.1.6 + consola: 3.2.3 + defu: 6.1.4 + esbuild: 0.19.12 + globby: 13.2.2 + hookable: 5.5.3 + jiti: 1.21.6 + magic-string: 0.30.11 + mkdist: 1.5.9(typescript@5.6.2) + mlly: 1.7.1 + pathe: 1.1.2 + pkg-types: 1.2.0 + pretty-bytes: 6.1.1 + rollup: 3.29.5 + rollup-plugin-dts: 6.1.1(rollup@3.29.5)(typescript@5.6.2) + scule: 1.3.0 + untyped: 1.5.0 + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - sass + - supports-color + - vue-tsc + + undici-types@6.19.8: {} + + unicorn-magic@0.1.0: {} + + universal-user-agent@7.0.2: {} + + universalify@2.0.1: {} + + untyped@1.5.0: + dependencies: + '@babel/core': 7.25.2 + '@babel/standalone': 7.25.6 + '@babel/types': 7.25.6 + defu: 6.1.4 + jiti: 2.0.0 + mri: 1.2.0 + scule: 1.3.0 + transitivePeerDependencies: + - supports-color + + update-browserslist-db@1.1.1(browserslist@4.24.0): + dependencies: + browserslist: 4.24.0 + escalade: 3.2.0 + picocolors: 1.1.0 + + util-deprecate@1.0.2: {} + + vite-node@2.1.1(@types/node@22.7.3): + dependencies: + cac: 6.7.14 + debug: 4.3.7 + pathe: 1.1.2 + vite: 5.4.8(@types/node@22.7.3) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.8(@types/node@22.7.3): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.22.4 + optionalDependencies: + '@types/node': 22.7.3 + fsevents: 2.3.3 + + vitest@2.1.1(@types/node@22.7.3): + dependencies: + '@vitest/expect': 2.1.1 + '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.8(@types/node@22.7.3)) + '@vitest/pretty-format': 2.1.1 + '@vitest/runner': 2.1.1 + '@vitest/snapshot': 2.1.1 + '@vitest/spy': 2.1.1 + '@vitest/utils': 2.1.1 + chai: 5.1.1 + debug: 4.3.7 + magic-string: 0.30.11 + pathe: 1.1.2 + std-env: 3.7.0 + tinybench: 2.9.0 + tinyexec: 0.3.0 + tinypool: 1.0.1 + tinyrainbow: 1.2.0 + vite: 5.4.8(@types/node@22.7.3) + vite-node: 2.1.1(@types/node@22.7.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.7.3 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + wasm-sjlj@1.0.5: {} + + webidl-conversions@3.0.1: {} + + well-known-symbols@2.0.0: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yaml@2.5.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yoctocolors-cjs@2.1.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..924b55f4 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - packages/* diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..bbf217f2 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.81.0" +profile = "default" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000..21b0a5a0 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,7 @@ +tab_spaces = 4 + +# This is also the setting used by [rustc](https://github.com/rust-lang/rust/blob/master/rustfmt.toml) +use_small_heuristics = "Max" + +# Use field initialize shorthand if possible +use_field_init_shorthand = true diff --git a/src/bumps.rs b/src/bumps.rs deleted file mode 100644 index 03c53d6f..00000000 --- a/src/bumps.rs +++ /dev/null @@ -1,974 +0,0 @@ -#![warn(dead_code)] -#![allow(unused_imports)] -#![allow(clippy::all)] - -//! # Bumps -//! -//! This module is responsible for managing the bumps in the monorepo. -use semver::{BuildMetadata, Prerelease, Version as SemVersion}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -use std::collections::HashMap; -use std::fs::OpenOptions; -use std::io::{BufWriter, Write}; -use std::path::PathBuf; - -use crate::conventional::ConventionalPackage; - -use super::changes::{get_package_change, init_changes, Change}; -use super::conventional::{get_conventional_for_package, ConventionalPackageOptions}; -use super::git::{ - git_add_all, git_all_files_changed_since_sha, git_commit, git_config, git_current_branch, - git_current_sha, git_fetch_all, git_push, git_tag, -}; -use super::packages::PackageInfo; -use super::packages::{get_package_info, get_packages}; -use super::paths::get_project_root_path; - -#[cfg(feature = "napi")] -#[napi(string_enum)] -#[derive(Debug, Deserialize, Serialize, PartialEq)] -pub enum Bump { - Major, - Minor, - Patch, - Snapshot, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, Copy, PartialEq)] -/// Enum representing the type of bump to be performed. -pub enum Bump { - Major, - Minor, - Patch, - Snapshot, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct BumpOptions { - pub changes: Vec, - pub since: Option, - pub release_as: Option, - pub fetch_all: Option, - pub fetch_tags: Option, - pub sync_deps: Option, - pub push: Option, - pub cwd: Option, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -/// Struct representing the options for the bump operation. -pub struct BumpOptions { - pub changes: Vec, - pub since: Option, - pub release_as: Option, - pub fetch_all: Option, - pub fetch_tags: Option, - pub sync_deps: Option, - pub push: Option, - pub cwd: Option, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize)] -/// Struct representing the bump package. -pub struct BumpPackage { - pub from: String, - pub to: String, - pub package_info: PackageInfo, - pub conventional_commits: Value, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct BumpPackage { - pub from: String, - pub to: String, - pub package_info: PackageInfo, - pub conventional_commits: Value, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize)] -/// Struct representing the bump package. -pub struct RecommendBumpPackage { - pub from: String, - pub to: String, - pub package_info: PackageInfo, - pub conventional: ConventionalPackage, - pub changed_files: Vec, - pub deploy_to: Vec, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize)] -/// Struct representing the bump package. -pub struct RecommendBumpPackage { - pub from: String, - pub to: String, - pub package_info: PackageInfo, - pub conventional: ConventionalPackage, - pub changed_files: Vec, - pub deploy_to: Vec, -} - -impl Bump { - /// Bumps the version of the package to major. - fn bump_major(version: String) -> SemVersion { - let mut sem_version = SemVersion::parse(&version).unwrap(); - sem_version.major += 1; - sem_version.minor = 0; - sem_version.patch = 0; - sem_version.pre = Prerelease::EMPTY; - sem_version.build = BuildMetadata::EMPTY; - sem_version - } - - /// Bumps the version of the package to minor. - fn bump_minor(version: String) -> SemVersion { - let mut sem_version = SemVersion::parse(&version).unwrap(); - sem_version.minor += 1; - sem_version.patch = 0; - sem_version.pre = Prerelease::EMPTY; - sem_version.build = BuildMetadata::EMPTY; - sem_version - } - - /// Bumps the version of the package to patch. - fn bump_patch(version: String) -> SemVersion { - let mut sem_version = SemVersion::parse(&version).unwrap(); - sem_version.patch += 1; - sem_version.pre = Prerelease::EMPTY; - sem_version.build = BuildMetadata::EMPTY; - sem_version - } - - /// Bumps the version of the package to snapshot appending the sha to the version. - fn bump_snapshot(version: String) -> SemVersion { - let sha = git_current_sha(None); - let alpha = format!("alpha.{}.{}", 0, sha); - - let mut sem_version = SemVersion::parse(&version).unwrap(); - sem_version.pre = Prerelease::new(alpha.as_str()).unwrap_or(Prerelease::EMPTY); - sem_version.build = BuildMetadata::EMPTY; - sem_version - } -} - -pub fn get_package_recommend_bump( - package_info: &PackageInfo, - root: &String, - options: Option, -) -> RecommendBumpPackage { - let ref current_branch = - git_current_branch(Some(root.to_string())).unwrap_or(String::from("origin/main")); - - let package_version = &package_info.version.to_string(); - let package_name = &package_info.name.to_string(); - let package_change = get_package_change( - package_name.to_string(), - current_branch.to_string(), - Some(root.to_string()), - ); - - let settings = options.unwrap_or_else(|| BumpOptions { - changes: vec![], - since: None, - release_as: None, - fetch_all: None, - fetch_tags: None, - sync_deps: None, - push: None, - cwd: None, - }); - - let ref since = settings.since.unwrap_or(String::from("origin/main")); - - let release_as = settings - .release_as - .unwrap_or_else(|| match package_change.to_owned() { - Some(change) => change.release_as, - None => Bump::Patch, - }); - - let deploy_to = match package_change.to_owned() { - Some(change) => change.deploy, - None => vec![String::from("production")], - }; - - let fetch_all = settings.fetch_all.unwrap_or(false); - - let semversion = match release_as { - Bump::Major => Bump::bump_major(package_version.to_string()), - Bump::Minor => Bump::bump_minor(package_version.to_string()), - Bump::Patch => Bump::bump_patch(package_version.to_string()), - Bump::Snapshot => Bump::bump_snapshot(package_version.to_string()), - }; - - let changed_files = git_all_files_changed_since_sha(since.to_string(), Some(root.to_string())); - let ref version = semversion.to_string(); - - let conventional = get_conventional_for_package( - &package_info, - Some(fetch_all), - Some(root.to_string()), - &Some(ConventionalPackageOptions { - version: Some(version.to_string()), - title: Some("# What changed?".to_string()), - }), - ); - - RecommendBumpPackage { - from: package_version.to_string(), - to: version.to_string(), - package_info: package_info.to_owned(), - conventional: conventional.to_owned(), - changed_files: changed_files.to_owned(), - deploy_to: deploy_to.to_owned(), - } -} - -/// Get bumps version of the package. If sync_deps is true, it will also sync the dependencies and dev-dependencies. -pub fn get_bumps(options: &BumpOptions) -> Vec { - let ref root = match options.cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - if options.fetch_tags.is_some() { - git_fetch_all(Some(root.to_string()), options.fetch_tags) - .expect("No possible to fetch tags"); - } - - let since = match options.since { - Some(ref since) => since.to_string(), - None => String::from("origin/main"), - }; - - let current_branch = git_current_branch(Some(root.to_string())).unwrap_or(String::from("main")); - - let ref packages = get_packages(Some(root.to_string())); - let changed_packages = packages - .iter() - .filter(|package| { - options - .changes - .iter() - .any(|change| change.package == package.name) - }) - .map(|package| package.to_owned()) - .collect::>(); - - if changed_packages.len() == 0 { - return vec![]; - } - - let mut bump_changes = HashMap::new(); - let mut bump_dependencies = HashMap::new(); - - for changed_package in changed_packages.iter() { - let change = options - .changes - .iter() - .find(|change| change.package == changed_package.name); - - if change.is_some() { - let release_as = match Some(current_branch.contains("main")) { - Some(true) => change.unwrap().release_as, - Some(false) | None => Bump::Snapshot, - }; - - let change = Change { - release_as, - ..change.unwrap().to_owned() - }; - - bump_changes.insert(changed_package.name.to_string(), change.to_owned()); - } - - if options.sync_deps.unwrap_or(false) { - packages.iter().for_each(|package| { - package.dependencies.iter().for_each(|dependency| { - let release_as = match Some(current_branch.contains("main")) { - Some(true) => Bump::Patch, - Some(false) | None => Bump::Snapshot, - }; - - if dependency.name == changed_package.name { - if change.is_some() && !bump_changes.contains_key(&package.name) { - bump_changes.insert( - package.name.to_string(), - Change { - package: package.name.to_string(), - release_as, - deploy: change.unwrap().deploy.to_owned(), - }, - ); - } - } - }); - }); - } - } - - let mut bumps = bump_changes - .iter() - .map(|(package_name, change)| { - let package = get_package_info(package_name.to_string(), Some(root.to_string())); - - let release_as = match Some(current_branch.contains("main")) { - Some(true) => change.release_as.to_owned(), - Some(false) | None => Bump::Snapshot, - }; - - let recommended_bump = get_package_recommend_bump( - &package.unwrap(), - root, - Some(BumpOptions { - changes: vec![change.to_owned()], - since: Some(since.to_string()), - release_as: Some(release_as.to_owned()), - fetch_all: options.fetch_all.to_owned(), - fetch_tags: options.fetch_tags.to_owned(), - sync_deps: options.sync_deps.to_owned(), - push: options.push.to_owned(), - cwd: Some(root.to_string()), - }), - ); - - let bump = BumpPackage { - from: recommended_bump.from.to_string(), - to: recommended_bump.to.to_string(), - conventional_commits: recommended_bump - .conventional - .conventional_commits - .to_owned(), - package_info: recommended_bump.package_info.to_owned(), - }; - - if bump.package_info.dependencies.len() > 0 { - bump_dependencies.insert( - package_name.to_string(), - bump.package_info.dependencies.to_owned(), - ); - } - - return bump; - }) - .collect::>(); - - bumps.iter_mut().for_each(|bump| { - let version = bump.to.to_string(); - bump.package_info.update_version(version.to_string()); - bump.package_info - .extend_changed_files(vec![String::from("package.json")]); - bump.package_info.write_package_json(); - }); - - if options.sync_deps.unwrap_or(false) { - bump_dependencies.iter().for_each(|(package_name, deps)| { - let temp_bumps = bumps.clone(); - let bump = bumps - .iter_mut() - .find(|b| b.package_info.name == package_name.to_string()) - .unwrap(); - - for dep in deps { - let bump_dep = temp_bumps.iter().find(|b| b.package_info.name == dep.name); - - if bump_dep.is_some() { - bump.package_info.update_dependency_version( - dep.name.to_string(), - bump_dep.unwrap().to.to_string(), - ); - bump.package_info.update_dev_dependency_version( - dep.name.to_string(), - bump_dep.unwrap().to.to_string(), - ); - bump.package_info.write_package_json(); - } - } - }); - } - - bumps -} - -/// Apply version bumps, commit and push changes. Returns a list of packages that have been updated. -/// Also generate changelog file and update dependencies and devDependencies in package.json. -pub fn apply_bumps(options: &BumpOptions) -> Vec { - let ref root = match options.cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let ref changes_data = init_changes(Some(root.to_string()), &None); - let git_user_name = changes_data.git_user_name.to_owned(); - let git_user_email = changes_data.git_user_email.to_owned(); - - git_config( - &git_user_name.unwrap_or(String::from("")), - &git_user_email.unwrap_or(String::from("")), - &root.to_string(), - ) - .expect("Failed to set git user name and email"); - - let bumps = get_bumps(options); - - if bumps.len() != 0 { - for bump in &bumps { - let git_message = changes_data.message.to_owned(); - - let ref bump_pkg_json_file_path = - PathBuf::from(bump.package_info.package_json_path.to_string()); - let ref bump_changelog_file_path = - PathBuf::from(bump.package_info.package_path.to_string()) - .join(String::from("CHANGELOG.md")); - - // Write bump_pkg_json_file_path - let bump_pkg_json_file = OpenOptions::new() - .write(true) - .append(false) - .open(bump_pkg_json_file_path) - .unwrap(); - let pkg_json_writer = BufWriter::new(bump_pkg_json_file); - serde_json::to_writer_pretty(pkg_json_writer, &bump.package_info.pkg_json).unwrap(); - - let conventional = get_conventional_for_package( - &bump.package_info, - options.fetch_all.to_owned(), - Some(root.to_string()), - &Some(ConventionalPackageOptions { - version: Some(bump.to.to_string()), - title: Some("# What changed?".to_string()), - }), - ); - - // Write bump_changelog_file_path - let mut bump_changelog_file = OpenOptions::new() - .write(true) - .create(true) - .append(false) - .open(bump_changelog_file_path) - .unwrap(); - - bump_changelog_file - .write_all(conventional.changelog_output.as_bytes()) - .unwrap(); - - let ref package_tag = format!("{}@{}", bump.package_info.name, bump.to); - - git_add_all(&root.to_string()).expect("Failed to add all files to git"); - git_commit( - git_message.unwrap_or(String::from("chore: release version")), - None, - None, - Some(root.to_string()), - ) - .unwrap(); - git_tag( - package_tag.to_string(), - Some(format!( - "chore: release {} to version {}", - bump.package_info.name, bump.to - )), - Some(root.to_string()), - ) - .unwrap(); - - if options.push.unwrap_or(false) { - git_push(Some(root.to_string()), Some(true)).unwrap(); - } - } - } - - bumps -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::changes::{add_change, get_change, init_changes}; - use crate::manager::PackageManager; - use crate::packages::get_changed_packages; - use crate::paths::get_project_root_path; - use crate::utils::create_test_monorepo; - use std::fs::remove_dir_all; - use std::fs::File; - use std::io::Write; - use std::process::Command; - use std::process::Stdio; - - fn create_single_changes(root: &String) -> Result<(), Box> { - let change_package_a = Change { - package: String::from("@scope/package-a"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - add_change(&change_package_a, Some(root.to_string())); - - Ok(()) - } - - fn create_single_package(monorepo_dir: &PathBuf) -> Result<(), Box> { - let js_path = monorepo_dir.join("packages/package-a/index.js"); - - let branch = Command::new("git") - .current_dir(&monorepo_dir) - .arg("checkout") - .arg("-b") - .arg("feat/message") - .stdout(Stdio::piped()) - .spawn() - .expect("Git branch problem"); - - branch.wait_with_output()?; - - let mut js_file = File::create(&js_path)?; - js_file - .write_all(r#"export const message = "hello package-a";"#.as_bytes()) - .unwrap(); - - let add = Command::new("git") - .current_dir(&monorepo_dir) - .arg("add") - .arg(".") - .stdout(Stdio::piped()) - .spawn() - .expect("Git add problem"); - - add.wait_with_output()?; - - let commit = Command::new("git") - .current_dir(&monorepo_dir) - .arg("commit") - .arg("-m") - .arg("feat: message to the world") - .stdout(Stdio::piped()) - .spawn() - .expect("Git commit problem"); - - commit.wait_with_output()?; - - Ok(()) - } - - fn create_multiple_changes(root: &String) -> Result<(), Box> { - let change_package_a = Change { - package: String::from("@scope/package-a"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - let change_package_c = Change { - package: String::from("@scope/package-c"), - release_as: Bump::Minor, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - add_change(&change_package_a, Some(root.to_string())); - add_change(&change_package_c, Some(root.to_string())); - - Ok(()) - } - - fn create_multiple_packages(monorepo_dir: &PathBuf) -> Result<(), Box> { - let js_path_package_a = monorepo_dir.join("packages/package-a/index.js"); - let js_path_package_c = monorepo_dir.join("packages/package-c/index.js"); - - let branch = Command::new("git") - .current_dir(&monorepo_dir) - .arg("checkout") - .arg("-b") - .arg("feat/message") - .stdout(Stdio::piped()) - .spawn() - .expect("Git branch problem"); - - branch.wait_with_output()?; - - let mut js_file_package_a = File::create(&js_path_package_a)?; - js_file_package_a - .write_all(r#"export const message = "hello package-a";"#.as_bytes()) - .unwrap(); - - let mut js_file_package_c = File::create(&js_path_package_c)?; - js_file_package_c - .write_all(r#"export const message = "hello package-c";"#.as_bytes()) - .unwrap(); - - let add = Command::new("git") - .current_dir(&monorepo_dir) - .arg("add") - .arg(".") - .stdout(Stdio::piped()) - .spawn() - .expect("Git add problem"); - - add.wait_with_output()?; - - let commit = Command::new("git") - .current_dir(&monorepo_dir) - .arg("commit") - .arg("-m") - .arg("feat: message to the world") - .stdout(Stdio::piped()) - .spawn() - .expect("Git commit problem"); - - commit.wait_with_output()?; - - Ok(()) - } - - fn create_single_dependency_changes(root: &String) -> Result<(), Box> { - let change_package_a = Change { - package: String::from("@scope/package-b"), - release_as: Bump::Snapshot, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - add_change(&change_package_a, Some(root.to_string())); - - Ok(()) - } - - fn create_single_dependency_package( - monorepo_dir: &PathBuf, - ) -> Result<(), Box> { - let js_path = monorepo_dir.join("packages/package-b/index.js"); - - let branch = Command::new("git") - .current_dir(&monorepo_dir) - .arg("checkout") - .arg("-b") - .arg("feat/message") - .stdout(Stdio::piped()) - .spawn() - .expect("Git branch problem"); - - branch.wait_with_output()?; - - let mut js_file = File::create(&js_path)?; - js_file - .write_all(r#"export const message = "hello package-b";"#.as_bytes()) - .unwrap(); - - let add = Command::new("git") - .current_dir(&monorepo_dir) - .arg("add") - .arg(".") - .stdout(Stdio::piped()) - .spawn() - .expect("Git add problem"); - - add.wait_with_output()?; - - let commit = Command::new("git") - .current_dir(&monorepo_dir) - .arg("commit") - .arg("-m") - .arg("feat: message to the world") - .stdout(Stdio::piped()) - .spawn() - .expect("Git commit problem"); - - commit.wait_with_output()?; - - Ok(()) - } - - fn create_multiple_dependency_changes(root: &String) -> Result<(), Box> { - let change_package_a = Change { - package: String::from("@scope/package-a"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - let change_package_b = Change { - package: String::from("@scope/package-b"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - add_change(&change_package_a, Some(root.to_string())); - add_change(&change_package_b, Some(root.to_string())); - - Ok(()) - } - - fn create_multiple_dependency_packages( - monorepo_dir: &PathBuf, - ) -> Result<(), Box> { - let js_path = monorepo_dir.join("packages/package-b/index.js"); - let js_path_no_depend = monorepo_dir.join("packages/package-a/index.js"); - - let branch = Command::new("git") - .current_dir(&monorepo_dir) - .arg("checkout") - .arg("-b") - .arg("feat/message") - .stdout(Stdio::piped()) - .spawn() - .expect("Git branch problem"); - - branch.wait_with_output()?; - - let mut js_file_no_depend = File::create(&js_path_no_depend)?; - js_file_no_depend - .write_all(r#"export const message = "hello package-a";"#.as_bytes()) - .unwrap(); - - let mut js_file = File::create(&js_path)?; - js_file - .write_all(r#"export const message = "hello package-b";"#.as_bytes()) - .unwrap(); - - let add = Command::new("git") - .current_dir(&monorepo_dir) - .arg("add") - .arg(".") - .stdout(Stdio::piped()) - .spawn() - .expect("Git add problem"); - - add.wait_with_output()?; - - let commit = Command::new("git") - .current_dir(&monorepo_dir) - .arg("commit") - .arg("-m") - .arg("feat: message to the world") - .stdout(Stdio::piped()) - .spawn() - .expect("Git commit problem"); - - commit.wait_with_output()?; - - Ok(()) - } - - #[test] - fn test_single_get_bumps() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm).unwrap(); - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())).unwrap(); - - let ref root = project_root.to_string(); - - create_single_package(monorepo_dir)?; - create_single_changes(&root)?; - - let changes = get_change(String::from("feat/message"), Some(root.to_string())); - - let bumps = get_bumps(&BumpOptions { - changes, - since: Some(String::from("main")), - release_as: Some(Bump::Major), - fetch_all: None, - fetch_tags: None, - sync_deps: Some(false), - push: Some(false), - cwd: Some(root.to_string()), - }); - - dbg!(&bumps); - - assert_eq!(bumps.len(), 1); - - let first_bump = bumps.get(0); - - assert_eq!(first_bump.is_some(), true); - - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_multiple_get_bumps() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm).unwrap(); - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())).unwrap(); - - let ref root = project_root.to_string(); - - create_multiple_packages(monorepo_dir)?; - create_multiple_changes(&root)?; - - let changes = get_change(String::from("feat/message"), Some(root.to_string())); - - let bumps = get_bumps(&BumpOptions { - changes, - since: Some(String::from("main")), - release_as: None, - fetch_all: None, - fetch_tags: None, - sync_deps: Some(false), - push: Some(false), - cwd: Some(root.to_string()), - }); - - assert_eq!(bumps.len(), 2); - - let first_bump = bumps.get(0); - let second_bump = bumps.get(1); - - assert_eq!(first_bump.is_some(), true); - assert_eq!(second_bump.is_some(), true); - - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_single_dependency_get_bumps() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm).unwrap(); - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())).unwrap(); - - let ref root = project_root.to_string(); - - create_single_dependency_package(monorepo_dir)?; - create_single_dependency_changes(&root)?; - - let changes = get_change(String::from("feat/message"), Some(root.to_string())); - - let bumps = get_bumps(&BumpOptions { - changes, - since: Some(String::from("main")), - release_as: None, - fetch_all: None, - fetch_tags: None, - sync_deps: Some(true), - push: Some(false), - cwd: Some(root.to_string()), - }); - - assert_eq!(bumps.len(), 2); - - let first_bump = bumps.get(0); - let second_bump = bumps.get(1); - - assert_eq!(first_bump.is_some(), true); - assert_eq!(second_bump.is_some(), true); - - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_multiple_dependency_get_bumps() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm).unwrap(); - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())).unwrap(); - - let ref root = project_root.to_string(); - - create_multiple_dependency_packages(monorepo_dir)?; - create_multiple_dependency_changes(&root)?; - - let changes = get_change(String::from("feat/message"), Some(root.to_string())); - - let bumps = get_bumps(&BumpOptions { - changes, - since: Some(String::from("main")), - release_as: None, - fetch_all: None, - fetch_tags: None, - sync_deps: Some(true), - push: Some(false), - cwd: Some(root.to_string()), - }); - - assert_eq!(bumps.len(), 3); - - let first_bump = bumps.get(0); - let second_bump = bumps.get(1); - let third_bump = bumps.get(2); - - assert_eq!(first_bump.is_some(), true); - assert_eq!(second_bump.is_some(), true); - assert_eq!(third_bump.is_some(), true); - - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_apply_bumps() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - create_multiple_dependency_packages(monorepo_dir)?; - - let ref root = project_root.unwrap().to_string(); - - let packages = get_changed_packages(Some(String::from("main")), Some(root.to_string())) - .iter() - .map(|package| package.name.to_string()) - .collect::>(); - - init_changes(Some(root.to_string()), &None); - - for package in packages { - let change_package = Change { - package: package.to_string(), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - add_change(&change_package, Some(root.to_string())); - } - - let changes = get_change(String::from("feat/message"), Some(root.to_string())); - - let main_branch = Command::new("git") - .current_dir(&monorepo_dir) - .arg("checkout") - .arg("main") - .stdout(Stdio::piped()) - .spawn() - .expect("Git checkout main problem"); - - main_branch.wait_with_output()?; - - let merge_branch = Command::new("git") - .current_dir(&monorepo_dir) - .arg("merge") - .arg("feat/message") - .stdout(Stdio::piped()) - .spawn() - .expect("Git merge problem"); - - merge_branch.wait_with_output()?; - - let bump_options = BumpOptions { - changes, - since: Some(String::from("main")), - release_as: Some(Bump::Minor), - fetch_all: None, - fetch_tags: None, - sync_deps: Some(true), - push: Some(false), - cwd: Some(root.to_string()), - }; - - let bumps = apply_bumps(&bump_options); - - assert_eq!(bumps.len(), 3); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } -} diff --git a/src/changes.rs b/src/changes.rs deleted file mode 100644 index 48ed2dfb..00000000 --- a/src/changes.rs +++ /dev/null @@ -1,704 +0,0 @@ -#![allow(clippy::all)] - -//! # Changes -//! -//! This module is responsible for managing the changes in the monorepo. -//! The changes are stored in a `.changes.json` file in the root of the project. -//! -//! # Example -//! ```json -//! { -//! "message": "chore(release): release new version", -//! "gitUserName": "Git Bot", -//! "gitUserEmail": "git.bot@domain.com", -//! "changes": { -//! "BRANCH-NAME": [{ -//! "package": "xxx", -//! "releaseAs": "patch", -//! "deploy": ["int"] -//! }], -//! } -//!} -//!``` -use serde::{Deserialize, Serialize}; -use std::io::BufWriter; -use std::{ - collections::BTreeMap, - fs::File, - io::BufReader, - path::{Path, PathBuf}, -}; - -use crate::bumps::Bump; - -use super::git::git_current_branch; -use super::paths::get_project_root_path; - -/// Dynamic data structure to store changes -type ChangesData = BTreeMap>; - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -/// Options to initialize the changes file -pub struct ChangesOptions { - pub message: Option, - pub git_user_name: Option, - pub git_user_email: Option, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct ChangesOptions { - pub message: Option, - pub git_user_name: Option, - pub git_user_email: Option, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -/// Data structure to store changes file -pub struct ChangesFileData { - pub message: Option, - pub git_user_name: Option, - pub git_user_email: Option, - pub changes: ChangesData, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct ChangesFileData { - pub message: Option, - pub git_user_name: Option, - pub git_user_email: Option, - pub changes: ChangesData, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -/// Data structure to store changes -pub struct Changes { - pub changes: ChangesData, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct Changes { - pub changes: ChangesData, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -/// Data structure to store a change -pub struct Change { - pub package: String, - pub release_as: Bump, - pub deploy: Vec, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct Change { - pub package: String, - pub release_as: Bump, - pub deploy: Vec, -} - -/// Initialize the changes file. If the file does not exist, it will create it with the default message. -/// If the file exists, it will return the content of the file. -pub fn init_changes( - cwd: Option, - change_options: &Option, -) -> ChangesFileData { - let ref root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let root_path = Path::new(root); - let ref changes_path = root_path.join(String::from(".changes.json")); - - if changes_path.exists() { - let changes_file = File::open(changes_path).unwrap(); - let changes_reader = BufReader::new(changes_file); - - let changes: ChangesFileData = serde_json::from_reader(changes_reader).unwrap(); - return changes; - } else { - let message = match &change_options { - Some(options) => match &options.message { - Some(msg) => msg.to_string(), - None => String::from("chore(release): release new version"), - }, - None => String::from("chore(release): release new version"), - }; - - let username = match &change_options { - Some(options) => match &options.git_user_name { - Some(name) => name.to_string(), - None => String::from("Git Bot"), - }, - None => String::from("Git Bot"), - }; - - let email = match &change_options { - Some(options) => match &options.git_user_email { - Some(email) => email.to_string(), - None => String::from("git.bot@domain.com"), - }, - None => String::from("git.bot@domain.com"), - }; - - let changes = ChangesFileData { - message: Some(message), - git_user_name: Some(username), - git_user_email: Some(email), - changes: ChangesData::new(), - }; - - let changes_file = File::create(changes_path).unwrap(); - let changes_writer = BufWriter::new(changes_file); - - serde_json::to_writer_pretty(changes_writer, &changes).unwrap(); - - return changes; - } -} - -/// Add a change to the changes file in the root of the project. -pub fn add_change(change: &Change, cwd: Option) -> bool { - let ref root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let root_path = Path::new(root); - let ref changes_path = root_path.join(String::from(".changes.json")); - - if changes_path.exists() { - let changes_file = File::open(changes_path).unwrap(); - let changes_reader = BufReader::new(changes_file); - - let mut changes: ChangesFileData = serde_json::from_reader(changes_reader).unwrap(); - - let current_branch = git_current_branch(Some(root.to_string())); - - let branch = match current_branch { - Some(branch) => branch, - None => String::from("main"), - }; - - if changes.changes.contains_key(&branch) { - let branch_changes = changes.changes.get_mut(&branch).unwrap(); - - let pkg_already_added = branch_changes - .iter() - .any(|branch_change| branch_change.package.as_str() == change.package.as_str()); - - if !pkg_already_added { - branch_changes.push(Change { - package: change.package.to_string(), - release_as: change.release_as, - deploy: change.deploy.to_vec(), - }); - } - } else { - changes.changes.insert( - branch, - vec![Change { - package: change.package.to_string(), - release_as: change.release_as, - deploy: change.deploy.to_vec(), - }], - ); - } - - let changes_file = File::create(changes_path).unwrap(); - let changes_writer = BufWriter::new(changes_file); - - serde_json::to_writer_pretty(changes_writer, &changes).unwrap(); - - return true; - } - - false -} - -/// Remove a change from the changes file in the root of the project. -pub fn remove_change(branch_name: String, cwd: Option) -> bool { - let ref root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let root_path = Path::new(root); - let ref changes_path = root_path.join(String::from(".changes.json")); - - if changes_path.exists() { - let changes_file = File::open(changes_path).unwrap(); - let changes_reader = BufReader::new(changes_file); - - let mut changes: ChangesFileData = serde_json::from_reader(changes_reader).unwrap(); - - if changes.changes.contains_key(&branch_name) { - changes.changes.remove(&branch_name); - - let changes_file = File::create(changes_path).unwrap(); - let changes_writer = BufWriter::new(changes_file); - - serde_json::to_writer_pretty(changes_writer, &changes).unwrap(); - - return true; - } - } - - false -} - -/// Get all changes from the changes file in the root of the project. -pub fn get_changes(cwd: Option) -> Changes { - let ref root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let root_path = Path::new(root); - let ref changes_path = root_path.join(String::from(".changes.json")); - - if changes_path.exists() { - let changes_file = File::open(changes_path).unwrap(); - let changes_reader = BufReader::new(changes_file); - - let changes: ChangesFileData = serde_json::from_reader(changes_reader).unwrap(); - - return Changes { - changes: changes.changes, - }; - } - - Changes { - changes: ChangesData::new(), - } -} - -/// Get all changes for a specific branch from the changes file in the root of the project. -pub fn get_change(branch: String, cwd: Option) -> Vec { - let ref root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let root_path = Path::new(root); - let ref changes_path = root_path.join(String::from(".changes.json")); - - if changes_path.exists() { - let changes_file = File::open(changes_path).unwrap(); - let changes_reader = BufReader::new(changes_file); - - let changes: ChangesFileData = serde_json::from_reader(changes_reader).unwrap(); - - if changes.changes.contains_key(&branch) { - return changes.changes.get(&branch).unwrap().to_vec(); - } else { - return vec![]; - } - } - - vec![] -} - -/// Get a change for a specific package from the changes file in the root of the project. -pub fn get_package_change( - package_name: String, - branch: String, - cwd: Option, -) -> Option { - let ref root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let root_path = Path::new(root); - let ref changes_path = root_path.join(String::from(".changes.json")); - - if changes_path.exists() { - let changes_file = File::open(changes_path).unwrap(); - let changes_reader = BufReader::new(changes_file); - - let changes: ChangesFileData = serde_json::from_reader(changes_reader).unwrap(); - - if changes.changes.contains_key(&branch) { - let branch_changes = changes.changes.get(&branch).unwrap(); - - let package_change = branch_changes - .iter() - .find(|change| change.package == package_name); - - if let Some(change) = package_change { - return Some(change.clone()); - } - - return None; - } - - return None; - } - - None -} - -/// Check if a change exists in the changes file in the root of the project. -pub fn change_exist(branch: String, packages_name: Vec, cwd: Option) -> bool { - let ref root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let root_path = Path::new(root); - let ref changes_path = root_path.join(String::from(".changes.json")); - - if changes_path.exists() { - let changes_file = File::open(changes_path).unwrap(); - let changes_reader = BufReader::new(changes_file); - - let changes: ChangesFileData = serde_json::from_reader(changes_reader).unwrap(); - - if changes.changes.contains_key(&branch) { - let branch_changes = changes.changes.get(&branch).unwrap(); - - let existing_packages_changes = branch_changes - .iter() - .map(|change| change.package.to_string()) - .collect::>(); - - let package_names_diff = packages_name - .iter() - .filter_map(|p| { - if existing_packages_changes.contains(&p) { - None - } else { - Some(p.to_string()) - } - }) - .collect::>(); - - match package_names_diff.len() { - 0 => return true, - _ => return false, - }; - } - } - - false -} - -/// Check if a changes file exists in the root of the project. -pub fn changes_file_exist(cwd: Option) -> bool { - let ref root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let root_path = Path::new(root); - let ref changes_path = root_path.join(String::from(".changes.json")); - - changes_path.exists() -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::manager::PackageManager; - use crate::paths::get_project_root_path; - use crate::utils::create_test_monorepo; - use std::fs::remove_dir_all; - - #[test] - fn test_init_changes() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let changes_data_file = init_changes(Some(root.to_string()), &None); - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - - assert_eq!(changes_data_file.message.is_some(), true); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_add_change() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let change = Change { - package: String::from("test-package"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - let result = add_change(&change, Some(root.to_string())); - - assert_eq!(result, true); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_duplicate_add_change() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let change = Change { - package: String::from("test-package"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - add_change(&change, Some(root.to_string())); - add_change(&change, Some(root.to_string())); - - let changes = get_changes(Some(root.to_string())); - let length = changes.changes["main"].len(); - - assert_eq!(length, 1); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_remove_change() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let change = Change { - package: String::from("test-package"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - add_change(&change, Some(root.to_string())); - - let result = remove_change(String::from("main"), Some(root.to_string())); - - assert_eq!(result, true); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_get_changes() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let change = Change { - package: String::from("test-package"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - add_change(&change, Some(root.to_string())); - - let changes = get_changes(Some(root.to_string())); - - assert_eq!(changes.changes.contains_key(&String::from("main")), true); - assert_eq!(changes.changes.get(&String::from("main")).unwrap().len(), 1); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_get_change() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let change = Change { - package: String::from("test-package"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - add_change(&change, Some(root.to_string())); - - let changes = get_change(String::from("main"), Some(root.to_string())); - - assert_eq!(changes.len(), 1); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_change_exist() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let change = Change { - package: String::from("test-package"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - add_change(&change, Some(root.to_string())); - - let result = change_exist( - String::from("main"), - vec!["test-package".to_string()], - Some(root.to_string()), - ); - - assert_eq!(result, true); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_multiple_change_exist() { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm).unwrap(); - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())).unwrap(); - - let ref root = project_root.to_string(); - - let change_package_a = Change { - package: String::from("@scope/package-a"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - let change_package_b = Change { - package: String::from("@scope/package-b"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - add_change(&change_package_a, Some(root.to_string())); - add_change(&change_package_b, Some(root.to_string())); - - let result = change_exist( - String::from("main"), - vec![ - "@scope/package-a".to_string(), - "@scope/package-b".to_string(), - ], - Some(root.to_string()), - ); - - assert_eq!(result, true); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir).unwrap(); - } - - #[test] - fn test_change_exist_with_new_package() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let change = Change { - package: String::from("test-package"), - release_as: Bump::Major, - deploy: vec![String::from("production")], - }; - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - add_change(&change, Some(root.to_string())); - - let result = change_exist( - String::from("main"), - vec!["test-package".to_string(), "@scope/package-a".to_string()], - Some(root.to_string()), - ); - - assert_eq!(result, false); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_change_exist_with_empty_packages() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - init_changes(Some(root.to_string()), &None); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - - let result = change_exist( - String::from("main"), - vec!["test-package".to_string(), "@scope/package-a".to_string()], - Some(root.to_string()), - ); - - assert_eq!(result, false); - assert_eq!(changes_path.is_file(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_changes_file_exist() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let ref changes_path = monorepo_dir.join(String::from(".changes.json")); - let result = changes_file_exist(Some(root.to_string())); - - assert_eq!(result, false); - assert_eq!(changes_path.is_file(), false); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } -} diff --git a/src/conventional.rs b/src/conventional.rs deleted file mode 100644 index e0e8cf7a..00000000 --- a/src/conventional.rs +++ /dev/null @@ -1,548 +0,0 @@ -//! # Conventional -//! -//! This module is responsible for generating changelog output for a package based on conventional commits. -#![allow(clippy::all)] -use git_cliff_core::{ - changelog::Changelog, - commit::{Commit as GitCommit, Signature}, - config::{ - Bump, ChangelogConfig, CommitParser, Config, GitConfig, Remote, RemoteConfig, TextProcessor, - }, - release::Release, -}; -use regex::Regex; -use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; -use std::fs::read_to_string; -use std::path::PathBuf; - -use super::git::{ - get_commits_since, get_last_known_publish_tag_info_for_package, git_fetch_all, Commit, -}; -use super::packages::PackageInfo; -use super::packages::PackageRepositoryInfo; -use super::paths::get_project_root_path; - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct ConventionalPackage { - pub package_info: PackageInfo, - pub conventional_config: Value, - pub conventional_commits: Value, - pub changelog_output: String, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize)] -/// A struct that represents a conventional package -pub struct ConventionalPackage { - pub package_info: PackageInfo, - pub conventional_config: Value, - pub conventional_commits: Value, - pub changelog_output: String, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone)] -pub struct ConventionalPackageOptions { - pub version: Option, - pub title: Option, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone)] -/// A struct that represents options for a conventional package -pub struct ConventionalPackageOptions { - pub version: Option, - pub title: Option, -} - -/// Process commits for groupint type, extracting data -fn process_commits<'a>(commits: &Vec, config: &GitConfig) -> Vec> { - commits - .iter() - .filter(|commit| { - let timestamp = chrono::DateTime::parse_from_rfc2822(&commit.author_date).unwrap(); - - let git_commit = GitCommit { - id: commit.hash.to_string(), - message: commit.message.to_string(), - author: Signature { - name: Some(commit.author_name.to_string()), - email: Some(commit.author_email.to_string()), - timestamp: timestamp.timestamp(), - }, - ..GitCommit::default() - }; - - git_commit.into_conventional().is_ok() - }) - .map(|commit| { - let timestamp = chrono::DateTime::parse_from_rfc2822(&commit.author_date).unwrap(); - - let git_commit = GitCommit { - id: commit.hash.to_string(), - message: commit.message.to_string(), - author: Signature { - name: Some(commit.author_name.to_string()), - email: Some(commit.author_email.to_string()), - timestamp: timestamp.timestamp(), - }, - ..GitCommit::default() - }; - - git_commit.process(config).unwrap() - }) - .collect::>() -} - -/// Defines the config for conventional, template usage for changelog -fn define_config( - owner: String, - repo: String, - domain: String, - title: Option, - options: &Option, -) -> Config { - let github_url = format!("{}/{}/{}", domain, owner, repo); - - let cliff_config = match options { - Some(config) => config.to_owned(), - None => { - let config = Config { - bump: Bump::default(), - remote: RemoteConfig { - github: Remote { - owner: String::from(owner), - repo: String::from(repo), - token: None, - is_custom: false, - }, - ..RemoteConfig::default() - }, - changelog: ChangelogConfig { - header: title, - body: Some(String::from( - r#" - {%- macro remote_url() -%} - - {%- endmacro -%} - - {% macro print_commit(commit) -%} - - {% if commit.scope %}*({{ commit.scope }})* {% endif %}{% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }} - ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }})) - {% endmacro -%} - - {% if version %} - {% if previous.version %} - ## [{{ version | trim_start_matches(pat="v") }}] - ({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ now() | date(format="%Y-%m-%d") }} - {% else %} - ## [{{ version | trim_start_matches(pat="v") }}] - {{ now() | date(format="%Y-%m-%d") }} - {% endif %} - {% else %} - ## [unreleased] - {% endif %} - - {% for group, commits in commits | group_by(attribute="group") %} - ### {{ group | striptags | trim | upper_first }} - {% for commit in commits - | filter(attribute="scope") - | sort(attribute="scope") %} - {{ self::print_commit(commit=commit) }} - {%- endfor -%} - {% raw %} - {% endraw %} - {%- for commit in commits %} - {%- if not commit.scope -%} - {{ self::print_commit(commit=commit) }} - {% endif -%} - {% endfor -%} - {% endfor %}"#, - )), - footer: Some(String::from( - r#"-- Total Releases: {{ releases | length }} --"#, - )), - trim: Some(true), - postprocessors: Some(vec![TextProcessor { - pattern: Regex::new("").expect("failed to compile regex"), - replace: Some(String::from(github_url)), - replace_command: None, - }]), - render_always: Some(false), - ..ChangelogConfig::default() - }, - git: GitConfig { - commit_parsers: Some(vec![ - CommitParser { - message: Regex::new("^feat").ok(), - group: Some(String::from("โ›ฐ๏ธ Features")), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^fix").ok(), - group: Some(String::from("๐Ÿ› Bug Fixes")), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^doc").ok(), - group: Some(String::from("๐Ÿ“š Documentation")), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^perf").ok(), - group: Some(String::from("โšก Performance")), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^refactor\\(clippy\\)").ok(), - skip: Some(true), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^refactor").ok(), - group: Some(String::from("๐Ÿšœ Refactor")), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^style").ok(), - group: Some(String::from("๐ŸŽจ Styling")), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^test").ok(), - group: Some(String::from("๐Ÿงช Testing")), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^chore|^ci").ok(), - group: Some(String::from("โš™๏ธ Miscellaneous Tasks")), - ..CommitParser::default() - }, - CommitParser { - body: Regex::new(".*security").ok(), - group: Some(String::from("๐Ÿ›ก๏ธ Security")), - ..CommitParser::default() - }, - CommitParser { - message: Regex::new("^revert").ok(), - group: Some(String::from("โ—€๏ธ Revert")), - ..CommitParser::default() - }, - ]), - protect_breaking_commits: Some(false), - filter_commits: Some(false), - filter_unconventional: Some(true), - conventional_commits: Some(true), - tag_pattern: Regex::new("^((?:@[^/@]+/)?[^/@]+)(?:@([^/]+))?$").ok(), - skip_tags: Regex::new("beta|alpha|snapshot").ok(), - ignore_tags: Regex::new("rc|beta|alpha|snapshot").ok(), - topo_order: Some(false), - sort_commits: Some(String::from("newest")), - ..GitConfig::default() - }, - }; - - config - } - }; - - cliff_config -} - -/// Generate changelog output -fn generate_changelog( - commits: &Vec, - config: &Config, - version: Option, -) -> String { - let releases = Release { - version, - commits: commits.to_vec().to_owned(), - ..Release::default() - }; - - let changelog = Changelog::new(vec![releases], config); - let mut changelog_output = Vec::new(); - - changelog.unwrap().generate(&mut changelog_output).unwrap(); - - String::from_utf8(changelog_output).unwrap_or_default() -} - -/// Prepend changelog output -fn prepend_generate_changelog( - commits: &Vec, - config: &Config, - changelog_content: &String, - version: Option, -) -> String { - let releases = Release { - version, - commits: commits.to_vec().to_owned(), - ..Release::default() - }; - - let changelog = Changelog::new(vec![releases], config); - let mut changelog_output = Vec::new(); - - changelog - .unwrap() - .prepend(changelog_content.to_string(), &mut changelog_output) - .unwrap(); - - String::from_utf8(changelog_output).unwrap_or_default() -} - -/// Give info about commits in a package, generate changelog output -pub fn get_conventional_for_package( - package_info: &PackageInfo, - no_fetch_all: Option, - cwd: Option, - conventional_options: &Option, -) -> ConventionalPackage { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let changelog_dir = - PathBuf::from(package_info.package_path.to_string()).join(String::from("CHANGELOG.md")); - - if no_fetch_all.is_some() { - git_fetch_all(Some(current_working_dir.to_string()), no_fetch_all).expect("Fetch all"); - } - - let tag_info = get_last_known_publish_tag_info_for_package( - package_info, - Some(current_working_dir.to_string()), - ); - - let hash = match tag_info { - Some(tag) => Some(tag.hash), - None => None, - }; - - let conventional_default_options = match conventional_options { - Some(options) => { - let opt_version = options.version.as_ref(); - let default_version = &String::from("0.0.0"); - let version = opt_version.unwrap_or(default_version); - - let opt_title = options.title.as_ref(); - let default_title = &String::from(""); - let title = opt_title.unwrap_or(default_title); - - ConventionalPackageOptions { - version: Some(version.to_string()), - title: Some(title.to_string()), - } - } - None => ConventionalPackageOptions { - version: Some(String::from("0.0.0")), - title: None, - }, - }; - - let repo_info = &package_info.repository_info; - let repository_info = match repo_info { - Some(info) => info.to_owned(), - None => PackageRepositoryInfo { - orga: String::from("my-orga"), - project: String::from("my-repo"), - domain: String::from("https://github.com"), - }, - }; - - let package_relative_path = &package_info.package_relative_path; - let commits_since = get_commits_since( - Some(current_working_dir.to_string()), - hash, - Some(package_relative_path.to_string()), - ); - - let pkg_info = package_info; - let mut conventional_package = ConventionalPackage { - package_info: pkg_info.to_owned(), - conventional_config: json!({}), - conventional_commits: json!([]), - changelog_output: String::new(), - }; - - let orga = &repository_info.orga; - let project = &repository_info.project; - let domain = &repository_info.domain; - - let conventional_config = define_config( - orga.to_string(), - project.to_string(), - domain.to_string(), - conventional_default_options.title, - &None, - ); - - let conventional_commits = process_commits(&commits_since, &conventional_config.git); - - let changelog = match changelog_dir.exists() { - true => { - let changelog_content = read_to_string(&changelog_dir).unwrap(); - prepend_generate_changelog( - &conventional_commits, - &conventional_config, - &changelog_content, - conventional_default_options.version, - ) - } - false => generate_changelog( - &conventional_commits, - &conventional_config, - conventional_default_options.version, - ), - }; - - let changelog_output = &changelog.to_string(); - conventional_package.changelog_output = changelog_output.to_string(); - conventional_package.conventional_commits = - serde_json::to_value(&conventional_commits).unwrap(); - conventional_package.conventional_config = - serde_json::to_value(&conventional_config.git).unwrap(); - - conventional_package -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::manager::PackageManager; - use crate::packages::get_packages; - use crate::paths::get_project_root_path; - use crate::utils::create_test_monorepo; - use std::fs::remove_dir_all; - use std::fs::File; - use std::io::Write; - use std::process::Command; - use std::process::Stdio; - - fn create_package_change(monorepo_dir: &PathBuf) -> Result<(), Box> { - let js_path = monorepo_dir.join("packages/package-b/index.js"); - - let branch = Command::new("git") - .current_dir(&monorepo_dir) - .arg("checkout") - .arg("-b") - .arg("feat/message") - .stdout(Stdio::piped()) - .spawn() - .expect("Git branch problem"); - - branch.wait_with_output()?; - - let mut js_file = File::create(&js_path)?; - js_file - .write_all(r#"export const message = "hello";"#.as_bytes()) - .unwrap(); - - let add = Command::new("git") - .current_dir(&monorepo_dir) - .arg("add") - .arg(".") - .stdout(Stdio::piped()) - .spawn() - .expect("Git add problem"); - - add.wait_with_output()?; - - let commit = Command::new("git") - .current_dir(&monorepo_dir) - .arg("commit") - .arg("-m") - .arg("feat: message to the world") - .stdout(Stdio::piped()) - .spawn() - .expect("Git commit problem"); - - commit.wait_with_output()?; - - let main = Command::new("git") - .current_dir(&monorepo_dir) - .arg("checkout") - .arg("main") - .stdout(Stdio::piped()) - .spawn() - .expect("Git checkout problem"); - - main.wait_with_output()?; - - let merge = Command::new("git") - .current_dir(&monorepo_dir) - .arg("merge") - .arg("feat/message") - .stdout(Stdio::piped()) - .spawn() - .expect("Git merge problem"); - - merge.wait_with_output()?; - - let tag_b = Command::new("git") - .current_dir(&monorepo_dir) - .arg("tag") - .arg("-a") - .arg("@scope/package-b@1.1.0") - .arg("-m") - .arg("chore: release package-b@1.1.0") - .stdout(Stdio::piped()) - .spawn() - .expect("Git tag problem"); - - tag_b.wait_with_output()?; - - Ok(()) - } - - #[test] - fn test_get_conventional_for_package() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let ref root = project_root.unwrap().to_string(); - - let packages = get_packages(Some(root.to_string())); - let package = packages.first(); - - let conventional = - get_conventional_for_package(package.unwrap(), None, Some(root.to_string()), &None); - - assert_eq!(conventional.package_info, package.unwrap().to_owned()); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_get_conventional_for_package_with_changes() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - create_package_change(monorepo_dir)?; - - let ref root = project_root.unwrap().to_string(); - - let packages = get_packages(Some(root.to_string())); - let package = packages - .iter() - .find(|pkg| pkg.name.contains("@scope/package-b")); - - let conventional = - get_conventional_for_package(package.unwrap(), None, Some(root.to_string()), &None); - - assert_eq!( - conventional - .changelog_output - .contains("Message to the world"), - true - ); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } -} diff --git a/src/dependency.rs b/src/dependency.rs deleted file mode 100644 index c671a55d..00000000 --- a/src/dependency.rs +++ /dev/null @@ -1,364 +0,0 @@ -use petgraph::{stable_graph::StableDiGraph, Direction}; - -/// Must be implemented by the type you wish -/// to build a dependency graph for. See the README.md for an example -pub trait Node { - /// Encodes a dependency relationship. In a Package Manager dependency graph for intance, this might be a (package name, version) tuple. - /// It might also just be the exact same type as the one that implements the Node trait, in which case `Node::matches` can be implemented through simple equality. - type DependencyType; - - /// Returns a slice of dependencies for this Node - fn dependencies(&self) -> &[Self::DependencyType]; - - /// Returns true if the `dependency` can be met by us. - fn matches(&self, dependency: &Self::DependencyType) -> bool; -} - -/// Wrapper around dependency graph nodes. -/// Since a graph might have dependencies that cannot be resolved internally, -/// this wrapper is necessary to differentiate between internally resolved and -/// externally (unresolved) dependencies. -/// An Unresolved dependency does not necessarily mean that it *cannot* be resolved, -/// only that no Node within the graph fulfills it. -pub enum Step<'a, N: Node> { - Resolved(&'a N), - Unresolved(&'a N::DependencyType), -} - -impl<'a, N: Node> Step<'a, N> { - pub fn is_resolved(&self) -> bool { - match self { - Step::Resolved(_) => true, - Step::Unresolved(_) => false, - } - } - - pub fn as_resolved(&self) -> Option<&N> { - match self { - Step::Resolved(node) => Some(node), - Step::Unresolved(_) => None, - } - } - - pub fn as_unresolved(&self) -> Option<&N::DependencyType> { - match self { - Step::Resolved(_) => None, - Step::Unresolved(dependency) => Some(dependency), - } - } -} - -/// The [`DependencyGraph`] structure builds an internal [Directed Graph](`petgraph::stable_graph::StableDiGraph`), which can then be traversed -/// in an order which ensures that dependent Nodes are visited before their parents. -pub struct DependencyGraph<'a, N: Node> { - graph: StableDiGraph, &'a N::DependencyType>, -} - -/// The only way to build a [`DependencyGraph`] is from a slice of objects implementing [`Node`]. -/// The graph references the original items, meaning the objects cannot be modified while -/// the [`DependencyGraph`] holds a reference to them. -impl<'a, N> From<&'a [N]> for DependencyGraph<'a, N> -where - N: Node, -{ - fn from(nodes: &'a [N]) -> Self { - let mut graph = StableDiGraph::, &'a N::DependencyType>::new(); - - // Insert the input nodes into the graph, and record their positions. - // We'll be adding the edges next, and filling in any unresolved - // steps we find along the way. - let nodes: Vec<(_, _)> = nodes - .iter() - .map(|node| (node, graph.add_node(Step::Resolved(node)))) - .collect(); - - for (node, index) in nodes.iter() { - for dependency in node.dependencies() { - // Check to see if we can resolve this dependency internally. - if let Some((_, dependent)) = nodes.iter().find(|(dep, _)| dep.matches(dependency)) - { - // If we can, just add an edge between the two nodes. - graph.add_edge(*index, *dependent, dependency); - } else { - // If not, create a new "Unresolved" node, and create an edge to that. - let unresolved = graph.add_node(Step::Unresolved(dependency)); - graph.add_edge(*index, unresolved, dependency); - } - } - } - - Self { graph } - } -} - -impl<'a, N> DependencyGraph<'a, N> -where - N: Node, -{ - /// True if all graph [`Node`]s have only references to other internal [`Node`]s. - /// That is, there are no unresolved dependencies between nodes. - pub fn is_internally_resolvable(&self) -> bool { - self.graph.node_weights().all(Step::is_resolved) - } - - /// Get an iterator over unresolved dependencies, without traversing the whole graph. - /// Useful for doing pre-validation or pre-fetching of external dependencies before - /// starting to resolve internal dependencies. - pub fn unresolved_dependencies(&self) -> impl Iterator { - self.graph.node_weights().filter_map(Step::as_unresolved) - } -} - -/// Iterate over the DependencyGraph in an order which ensures dependencies are resolved before each Node is visited. -/// Note: If a `Step::Unresolved` node is returned, it is the caller's responsibility to ensure the dependency is resolved -/// before continuing. -impl<'a, N> Iterator for DependencyGraph<'a, N> -where - N: Node, -{ - type Item = Step<'a, N>; - - fn next(&mut self) -> Option { - // Returns the first node, which does not have any Outgoing - // edges, which means it is terminal. - for index in self.graph.node_indices().rev() { - if self - .graph - .neighbors_directed(index, Direction::Outgoing) - .count() - == 0 - { - return self.graph.remove_node(index); - } - } - - None - } -} - -#[cfg(test)] -mod tests { - - use super::{DependencyGraph, Node, Step}; - use semver::{BuildMetadata, Prerelease, Version, VersionReq}; - - #[derive(Debug)] - struct Package { - name: &'static str, - version: Version, - dependencies: Vec, - } - - #[derive(Debug)] - struct Dependency { - name: &'static str, - version: VersionReq, - } - - impl Node for Package { - type DependencyType = Dependency; - - fn dependencies(&self) -> &[Self::DependencyType] { - &self.dependencies[..] - } - - fn matches(&self, dependency: &Self::DependencyType) -> bool { - self.name == dependency.name && dependency.version.matches(&self.version) - } - } - - #[test] - fn test_dependencies_synchronous() { - let build = build_test_graph(); - let graph = DependencyGraph::from(&build[..]); - - assert!(!graph.is_internally_resolvable()); - - for node in graph { - match node { - Step::Resolved(build) => println!("build: {:?}", build.name), - Step::Unresolved(lookup) => println!("lookup: {:?}", lookup.name), - } - } - } - - #[test] - fn test_unresolved_dependencies() { - let build = build_test_graph(); - let graph = DependencyGraph::from(&build[..]); - - assert!(!graph.is_internally_resolvable()); - - let unresolved_dependencies: Vec<_> = graph - .unresolved_dependencies() - .map(|dep| dep.name) - .collect(); - - assert_eq!( - unresolved_dependencies, - vec!["@scope/unknown", "@scope/remote"] - ); - } - - #[test] - fn test_generate_dependency_graph() { - let _ = DependencyGraph::from(&build_test_graph()[..]); - } - - fn build_test_graph() -> Vec { - vec![ - Package { - name: "@scope/package-a", - version: semver::Version { - major: 1, - minor: 2, - patch: 3, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![], - }, - Package { - name: "@scope/package-b", - version: semver::Version { - major: 1, - minor: 2, - patch: 3, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![Dependency { - name: "@scope/package-a", - version: ">=1.0.0".parse().unwrap(), - }], - }, - Package { - name: "@scope/package-c", - version: semver::Version { - major: 1, - minor: 2, - patch: 3, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![Dependency { - name: "@scope/package-b", - version: ">=1.0.0".parse().unwrap(), - }], - }, - Package { - name: "@scope/package-d", - version: semver::Version { - major: 1, - minor: 2, - patch: 3, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![ - Dependency { - name: "@scope/package-a", - version: ">=1.0.0".parse().unwrap(), - }, - Dependency { - name: "@scope/package-b", - version: ">=1.0.0".parse().unwrap(), - }, - ], - }, - Package { - name: "@scope/package-e", - version: semver::Version { - major: 1, - minor: 2, - patch: 3, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![], - }, - Package { - name: "@scope/package-f", - version: semver::Version { - major: 1, - minor: 2, - patch: 3, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![ - Dependency { - name: "@scope/unknown", - version: ">=1.0.0".parse().unwrap(), - }, - Dependency { - name: "@scope/remote", - version: "=3.0.0".parse().unwrap(), - }, - ], - }, - ] - } - - #[test] - fn test_internally_resolved() { - let packages = vec![ - Package { - name: "@scope/package-a", - version: semver::Version { - major: 1, - minor: 2, - patch: 3, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![], - }, - Package { - name: "@scope/package-b", - version: semver::Version { - major: 3, - minor: 2, - patch: 0, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![Dependency { - name: "@scope/package-a", - version: "=1.2.3".parse().unwrap(), - }], - }, - Package { - name: "@scope/package-c", - version: semver::Version { - major: 11, - minor: 2, - patch: 4, - pre: Prerelease::new("").unwrap(), - build: BuildMetadata::EMPTY, - }, - dependencies: vec![Dependency { - name: "@scope/package-b", - version: ">=3.0.0".parse().unwrap(), - }], - }, - ]; - - let graph = DependencyGraph::from(&packages[..]); - - for package in graph { - match package { - // Print out the package name so we can verify the order ourselves - Step::Resolved(package) => println!("Building {}!", package.name), - - // Since we know that all our Packages only have internal references to each other, - // we can safely ignore any Unresolved steps in the graph. - // - // If for example `second_order` required some unknown package `external_package`, - // iterating over our graph would yield that as a Step::Unresolved *before* - // the `second_order` package. - Step::Unresolved(_) => unreachable!(), - } - } - } -} diff --git a/src/git.rs b/src/git.rs deleted file mode 100644 index 6dece4f5..00000000 --- a/src/git.rs +++ /dev/null @@ -1,938 +0,0 @@ -//! # Git -//! -//! This module provides a set of functions to interact with git. -#![allow(clippy::all)] -use execute::Execute; -use icu::collator::{Collator, CollatorOptions, Numeric, Strength}; -use regex::Regex; -use serde::{Deserialize, Serialize}; -use std::io::Write; -use std::path::PathBuf; -use std::{ - env::temp_dir, - fs::{remove_file, File}, - path::Path, - process::{Command, Stdio}, -}; -use version_compare::{Cmp, Version}; - -use super::packages::PackageInfo; -use super::paths::get_project_root_path; -use super::utils::{package_scope_name_version, strip_trailing_newline}; - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Commit { - pub hash: String, - pub author_name: String, - pub author_email: String, - pub author_date: String, - pub message: String, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize)] -/// A struct that represents a commit information -pub struct Commit { - pub hash: String, - pub author_name: String, - pub author_email: String, - pub author_date: String, - pub message: String, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct RemoteTags { - pub hash: String, - pub tag: String, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize)] -/// A struct that represents a remote tag information -pub struct RemoteTags { - pub hash: String, - pub tag: String, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct PublishTagInfo { - pub hash: String, - pub tag: String, - pub package: String, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize)] -/// A struct that represents a publish tag information -pub struct PublishTagInfo { - pub hash: String, - pub tag: String, - pub package: String, -} - -/// Stage all uncommitted changes -pub fn git_add_all(cwd: &String) -> Result { - let mut git_add = Command::new("git"); - - git_add.current_dir(cwd.to_string()).arg("add").arg("."); - - git_add.stdout(Stdio::piped()); - git_add.stderr(Stdio::piped()); - - let output = git_add.execute_output().unwrap(); - - if output.status.success() { - Ok(true) - } else { - Ok(false) - } -} - -/// Add a file to the git stage -pub fn git_add(cwd: &String, file: &String) -> Result { - let mut git_add = Command::new("git"); - - git_add.current_dir(cwd.to_string()).arg("add").arg(file); - - git_add.stdout(Stdio::piped()); - git_add.stderr(Stdio::piped()); - - let output = git_add.execute_output().unwrap(); - - if output.status.success() { - Ok(true) - } else { - Ok(false) - } -} - -/// Configure git user name and email -pub fn git_config(username: &String, email: &String, cwd: &String) -> Result { - let mut git_config_user = Command::new("git"); - - git_config_user - .current_dir(cwd.to_string()) - .arg("config") - .arg("user.name") - .arg(username); - - git_config_user.stdout(Stdio::piped()); - git_config_user.stderr(Stdio::piped()); - - let output_user = git_config_user.execute_output().unwrap(); - - let mut git_config_email = Command::new("git"); - git_config_email - .current_dir(cwd.to_string()) - .arg("config") - .arg("user.email") - .arg(email); - - git_config_email.stdout(Stdio::piped()); - git_config_email.stderr(Stdio::piped()); - - let output_email = git_config_email.execute_output().unwrap(); - let status = output_user.status.success() == output_email.status.success(); - - if status { - Ok(true) - } else { - Ok(false) - } -} - -/// Fetch everything from origin including tags -pub fn git_fetch_all( - cwd: Option, - fetch_tags: Option, -) -> Result { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - command.arg("fetch").arg("origin"); - - if fetch_tags.unwrap_or(false) { - command.arg("--tags").arg("--force"); - } - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - if output.status.success() { - Ok(true) - } else { - Ok(false) - } -} - -/// Get the diverged commit from a particular git SHA or tag. -pub fn get_diverged_commit(refer: String, cwd: Option) -> Option { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - command.arg("merge-base").arg(&refer).arg("HEAD"); - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - if !output.status.success() { - return None; - } - - let output = String::from_utf8(output.stdout).unwrap(); - - Some(strip_trailing_newline(&output)) -} - -/// Get the current commit id -pub fn git_current_sha(cwd: Option) -> String { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - command.arg("rev-parse").arg("--short").arg("HEAD"); - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - let hash = String::from_utf8(output.stdout).unwrap(); - strip_trailing_newline(&hash) -} - -/// Get the previous commit id -pub fn git_previous_sha(cwd: Option) -> String { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - command.arg("rev-parse").arg("--short").arg("HEAD~1"); - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - let hash = String::from_utf8(output.stdout).unwrap(); - - strip_trailing_newline(&hash) -} - -/// Get the first commit in a branch -pub fn git_first_sha(cwd: Option, branch: Option) -> String { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let branch = match branch { - Some(branch) => branch, - None => String::from("main"), - }; - - let mut command = Command::new("git"); - command - .arg("log") - .arg(format!("{}..HEAD", branch)) - .arg("--online") - .arg("--pretty=format:%h") - .arg("|") - .arg("tail") - .arg("-1"); - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - let hash = String::from_utf8(output.stdout).unwrap(); - - strip_trailing_newline(&hash) -} - -/// Verify if as uncommited changes in the current working directory -pub fn git_workdir_unclean(cwd: Option) -> bool { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - command.arg("status").arg("--porcelain"); - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - let output = String::from_utf8(output.stdout).unwrap(); - let result = strip_trailing_newline(&output); - - if result.is_empty() { - return false; - } - - true -} - -/// Get the current branch name -pub fn git_current_branch(cwd: Option) -> Option { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - command.arg("rev-parse").arg("--abbrev-ref").arg("HEAD"); - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - let output = String::from_utf8(output.stdout).unwrap(); - let result = strip_trailing_newline(&output); - - if result.is_empty() { - return None; - } - - Some(result) -} - -/// Get the branch (last) name for a commit -pub fn git_branch_from_commit(commit: String, cwd: Option) -> Option { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - // git --no-pager branch --no-color --no-column --format "%(refname:lstrip=2)" --contains - let mut command = Command::new("git"); - command - .arg("--no-pager") - .arg("branch") - .arg("--no-color") - .arg("--no-column") - .arg("--format") - .arg(r#""%(refname:lstrip=2)""#) - .arg("--contains") - .arg(&commit); - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - let output = String::from_utf8(output.stdout).unwrap(); - let result = strip_trailing_newline(&output); - - if result.is_empty() { - return None; - } - - Some(result) -} - -/// Tags the current commit with a message -pub fn git_tag( - tag: String, - message: Option, - cwd: Option, -) -> Result { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let default_message = &tag; - let msg = message.or(Some(default_message.to_string())).unwrap(); - - let mut command = Command::new("git"); - command.arg("tag").arg("-a").arg(&tag).arg("-m").arg(&msg); - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - if output.status.success() { - Ok(true) - } else { - Ok(false) - } -} - -/// Pushes all changes in the monorepo without verification and follow tags -pub fn git_push(cwd: Option, follow_tags: Option) -> Result { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - command.arg("push"); - - if follow_tags.unwrap_or(false) { - command.arg("--follow-tags"); - } - - command.arg("--no-verify"); - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - if output.status.success() { - Ok(true) - } else { - Ok(false) - } -} - -// Commit all changes in the monorepo -pub fn git_commit( - mut message: String, - body: Option, - footer: Option, - cwd: Option, -) -> Result { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - if body.is_some() { - message.push_str("\n\n"); - message.push_str(body.unwrap().as_str()); - } - - if footer.is_some() { - message.push_str("\n\n"); - message.push_str(footer.unwrap().as_str()); - } - - let temp_dir = temp_dir(); - let temp_file_path = temp_dir.join("commit_message.txt"); - - let mut file = File::create(&temp_file_path).unwrap(); - file.write_all(message.as_bytes()).unwrap(); - - let file_path = temp_file_path.as_path(); - - let mut command = Command::new("git"); - command - .arg("commit") - .arg("-F") - .arg(&file_path.to_str().unwrap()) - .arg("--no-verify"); - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - remove_file(file_path).expect("Commit file not deleted"); - - if output.status.success() { - Ok(true) - } else { - Ok(false) - } -} - -/// Given a specific git sha, finds all files that have been modified -/// since the sha and returns the absolute filepaths. -pub fn git_all_files_changed_since_sha(sha: String, cwd: Option) -> Vec { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - command - .arg("--no-pager") - .arg("diff") - .arg("--name-only") - .arg(format!("{}", sha)); - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - if !output.status.success() { - return vec![]; - } - - let output = String::from_utf8(output.stdout).unwrap(); - let root = Path::new(¤t_working_dir); - - output - .split("\n") - .filter(|item| !item.trim().is_empty()) - .map(|item| root.join(item)) - .filter(|item| item.exists()) - .map(|item| item.to_str().unwrap().to_string()) - .collect::>() -} - -/// Returns commits since a particular git SHA or tag. -/// If the "since" parameter isn't provided, all commits -/// from the dawn of man are returned -pub fn get_commits_since( - cwd: Option, - since: Option, - relative: Option, -) -> Vec { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - const DELIMITER: &str = r#"#=#"#; - const BREAK_LINE: &str = r#"#+#"#; - - let mut command = Command::new("git"); - command - .arg("--no-pager") - .arg("log") - .arg(format!( - "--format={}%H{}%an{}%ae{}%ad{}%B{}", - DELIMITER, DELIMITER, DELIMITER, DELIMITER, DELIMITER, BREAK_LINE - )) - .arg("--date=rfc2822"); - - if let Some(since) = since { - command.arg(format!("{}..", since)); - } - - if let Some(relative) = relative { - command.arg("--"); - command.arg(&relative); - } - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - if !output.status.success() { - return vec![]; - } - - let output = String::from_utf8(output.stdout).unwrap(); - - output - .split(BREAK_LINE) - .filter(|item| !item.trim().is_empty()) - .map(|item| { - let item_trimmed = item.trim(); - let items = item_trimmed.split(DELIMITER).collect::>(); - - Commit { - hash: items.get(1).unwrap().to_string(), - author_name: items.get(2).unwrap().to_string(), - author_email: items.get(3).unwrap().to_string(), - author_date: items.get(4).unwrap().to_string(), - message: items.get(5).unwrap().to_string(), - } - }) - .collect::>() -} - -/// Grabs the full list of all tags available on upstream or local -pub fn get_remote_or_local_tags(cwd: Option, local: Option) -> Vec { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut command = Command::new("git"); - - match local { - Some(true) => command.arg("show-ref").arg("--tags"), - Some(false) => command.arg("ls-remote").arg("--tags").arg("origin"), - None => command.arg("ls-remote").arg("--tags").arg("origin"), - }; - - command.current_dir(¤t_working_dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - if !output.status.success() { - return vec![]; - } - - let output = String::from_utf8(output.stdout).unwrap(); - - #[cfg(windows)] - const LINE_ENDING: &'static str = "\r\n"; - #[cfg(not(windows))] - const LINE_ENDING: &'static str = "\n"; - - output - .trim() - .split(LINE_ENDING) - .filter(|tags| !tags.trim().is_empty()) - .map(|tags| { - let hash_tags = Regex::new(r"\s+") - .unwrap() - .split(tags) - .collect::>(); - - RemoteTags { - hash: hash_tags.get(0).unwrap().to_string(), - tag: hash_tags.get(1).unwrap().to_string(), - } - }) - .collect::>() -} - -/// Given an input of the "main" branch name, -/// returns all the files that have changed since the current branch was created -pub fn get_all_files_changed_since_branch( - package_info: &Vec, - branch: &String, - cwd: Option, -) -> Vec { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut all_files = vec![]; - - package_info.iter().for_each(|item| { - let files = git_all_files_changed_since_sha( - branch.to_string(), - Some(current_working_dir.to_string()), - ); - - let pkg_files = files - .iter() - .filter(|file| file.starts_with(item.package_path.as_str())) - .collect::>(); - - all_files.append( - &mut pkg_files - .iter() - .map(|file| file.to_string()) - .collect::>(), - ); - }); - - all_files -} - -/// Grabs the last known publish tag info for a package -pub fn get_last_known_publish_tag_info_for_package( - package_info: &PackageInfo, - cwd: Option, -) -> Option { - let current_working_dir = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let mut remote_tags = - get_remote_or_local_tags(Some(current_working_dir.to_string()), Some(false)); - let mut local_tags = - get_remote_or_local_tags(Some(current_working_dir.to_string()), Some(true)); - - /*let mut remote_tags = vec![ - RemoteTags { - hash: String::from("ddd1fa69be3e6c6a8b2f18af8f8f5607106188db"), - tag: String::from("refs/tags/@b2x/workspace-node@1.0.4") - }, - RemoteTags { - hash: String::from("c5353e1f3c9385c35f64e838a0a09dc4bb8f7b07"), - tag: String::from("refs/tags/@b2x/workspace-node@1.0.2") - } - ]; - - let mut local_tags = vec![ - RemoteTags { - hash: String::from("4a16b15bb5cfeca493c79231452e94e56487d6b4"), - tag: String::from("refs/tags/@b2x/workspace-node@0.9.9") - }, - RemoteTags { - hash: String::from("ee5f8209e6d3b06fbf5712e424652e909a4cb5c2"), - tag: String::from("refs/tags/@b2x/workspace-node@1.0.5") - } - ];*/ - - remote_tags.append(&mut local_tags); - - let mut options = CollatorOptions::new(); - options.strength = Some(Strength::Secondary); - options.numeric = Some(Numeric::On); - - let collator = Collator::try_new(&Default::default(), options).unwrap(); - - remote_tags.sort_by(|a, b| { - let tag_a = a.tag.replace("refs/tags/", ""); - let tag_b = b.tag.replace("refs/tags/", ""); - - collator.compare(&tag_b, &tag_a) - }); - - let package_tag = format!("{}@{}", package_info.name, package_info.version); - - let mut match_tag = remote_tags.iter().find(|item| { - let tag = item.tag.replace("refs/tags/", ""); - let matches: Vec<&str> = tag.matches(&package_tag).collect(); - - if matches.len() > 0 { - return true; - } else { - return false; - } - }); - - if match_tag.is_none() { - let mut highest_tag = None; - - remote_tags.iter().for_each(|item| { - let tag = &item.tag.replace("refs/tags/", ""); - - if tag.contains(&package_info.name) { - if highest_tag.is_none() { - highest_tag = Some(String::from(tag)); - } - - let high_tag = highest_tag.as_ref().unwrap(); - let current_tag_meta = package_scope_name_version(tag).unwrap(); - let highest_tag_meta = package_scope_name_version(high_tag).unwrap(); - - let current_version = Version::from(¤t_tag_meta.version).unwrap(); - let highest_version = Version::from(&highest_tag_meta.version).unwrap(); - - if current_version.compare_to(&highest_version, Cmp::Gt) { - highest_tag = Some(String::from(tag)); - } - } - }); - - if highest_tag.is_some() { - let highest_tag = highest_tag.unwrap(); - let highest_tag_meta = package_scope_name_version(&highest_tag).unwrap(); - - match_tag = remote_tags.iter().find(|item| { - let tag = item.tag.replace("refs/tags/", ""); - let matches: Vec<&str> = tag.matches(&highest_tag_meta.full).collect(); - - if matches.len() > 0 { - return true; - } else { - return false; - } - }); - } - } - - if match_tag.is_some() { - let hash = &match_tag.unwrap().hash; - let tag = &match_tag.unwrap().tag; - let package = &package_info.name; - - return Some(PublishTagInfo { - hash: hash.to_string(), - tag: tag.to_string(), - package: package.to_string(), - }); - } - - None -} - -/// Grabs the last known publish tag info for all packages in the monorepo -pub fn get_last_known_publish_tag_info_for_all_packages( - package_info: &Vec, - cwd: Option, -) -> Vec> { - let root = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - git_fetch_all(Some(root.to_string()), Some(true)).expect("Fetch all tags"); - - package_info - .iter() - .map(|item| get_last_known_publish_tag_info_for_package(&item, Some(root.to_string()))) - .filter(|item| item.is_some()) - .collect::>>() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - manager::PackageManager, paths::get_project_root_path, utils::create_test_monorepo, - }; - use std::fs::{remove_dir_all, File}; - - #[test] - fn test_git_fetch_all() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let result = git_fetch_all(project_root, None)?; - assert_eq!(result, false); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_get_diverged_commit() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let result = get_diverged_commit(String::from("@scope/package-a@1.0.0"), project_root); - - assert!(result.is_some()); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_git_current_sha() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let result = git_current_sha(project_root); - assert_eq!(result.is_empty(), false); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_git_previous_sha() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let result = git_previous_sha(project_root); - assert_eq!(result.is_empty(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_git_workdir_unclean() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - let js_path = monorepo_dir.join("packages/package-a/index.js"); - - let mut js_file = File::create(&js_path)?; - js_file.write_all(r#"export const message = "hello";"#.as_bytes())?; - - let result = git_workdir_unclean(project_root); - assert_eq!(result, true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_git_branch_from_commit() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let commit = git_current_sha(Some(project_root.as_ref().unwrap().to_string())); - let result = git_branch_from_commit(commit, project_root); - assert_eq!(result.is_some(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_get_commits_since() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let result = get_commits_since( - project_root, - Some(String::from("main")), - Some(String::from("packages/package-a")), - ); - let count = result.len(); - - assert_eq!(count, 0); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_get_local_tags() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let result = get_remote_or_local_tags(project_root, Some(true)); - let count = result.len(); - - assert_eq!(count, 3); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn test_git_all_files_changed_since_sha() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let result = git_all_files_changed_since_sha(String::from("main"), project_root); - let count = result.len(); - - assert_eq!(count, 0); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 14739750..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/2073802?s=200&v=4")] -//! # Workspace node tools -//! -//! This crate provides a set of tools to work with node workspaces. -//! It allows you to retrieve information about the workspace, and to interact with the workspace. -#[allow(unused_imports)] -#[macro_use] -#[cfg(feature = "napi-derive")] -extern crate napi_derive; - -mod utils; - -pub mod manager; - -pub mod paths; - -pub mod git; - -pub mod packages; - -pub mod conventional; - -pub mod bumps; - -pub mod changes; - -pub mod dependency; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a96..00000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/src/manager.rs b/src/manager.rs deleted file mode 100644 index 3ede1012..00000000 --- a/src/manager.rs +++ /dev/null @@ -1,141 +0,0 @@ -//! # Package Manager -//! -//! This package ables to detect which package manager is being used in the monorepo. -#![allow(clippy::all)] -use std::{ - collections::HashMap, fmt::Display, fmt::Formatter, fmt::Result as FmtResult, path::Path, -}; - -#[cfg(feature = "napi")] -#[napi(string_enum)] -#[derive(Debug, PartialEq)] -pub enum PackageManager { - Npm, - Yarn, - Pnpm, - Bun, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Copy, PartialEq)] -/// Package manager used in the monorepo. -pub enum PackageManager { - Npm, - Yarn, - Pnpm, - Bun, -} - -impl Display for PackageManager { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - let package_manager = match self { - PackageManager::Npm => "npm".to_string(), - PackageManager::Yarn => "yarn".to_string(), - PackageManager::Pnpm => "pnpm".to_string(), - PackageManager::Bun => "bun".to_string(), - }; - - write!(f, "{}", package_manager) - } -} - -/// Detects which package manager is available in the workspace. -pub fn detect_package_manager(path: &Path) -> Option { - let package_manager_files = HashMap::from([ - ("package-lock.json", PackageManager::Npm), - ("npm-shrinkwrap.json", PackageManager::Npm), - ("yarn.lock", PackageManager::Yarn), - ("pnpm-lock.yaml", PackageManager::Pnpm), - ("bun.lockb", PackageManager::Bun), - ]); - - for (file, package_manager) in package_manager_files.iter() { - let lock_file = path.join(file); - - if lock_file.exists() { - return Some(*package_manager); - } - } - - if let Some(parent) = path.parent() { - return detect_package_manager(&parent); - } - - None -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{paths::get_project_root_path, utils::create_test_monorepo}; - use std::{fs::remove_dir_all, path::PathBuf}; - - #[test] - fn package_manager_for_npm_lock() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let package_manager = - detect_package_manager(&PathBuf::from(project_root.unwrap()).as_path()); - - assert_eq!(package_manager, Some(PackageManager::Npm)); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn package_manager_for_yarn_lock() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Yarn)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let package_manager = - detect_package_manager(&PathBuf::from(project_root.unwrap()).as_path()); - - assert_eq!(package_manager, Some(PackageManager::Yarn)); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn package_manager_for_pnpm_lock() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Pnpm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let package_manager = - detect_package_manager(&PathBuf::from(project_root.unwrap()).as_path()); - - assert_eq!(package_manager, Some(PackageManager::Pnpm)); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn package_manager_for_bun_lock() -> Result<(), std::io::Error> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Bun)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let package_manager = - detect_package_manager(&PathBuf::from(project_root.unwrap()).as_path()); - - assert_eq!(package_manager, Some(PackageManager::Bun)); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn package_manager_not_present() { - let path = std::env::current_dir().expect("Current user home directory"); - let package_manager = detect_package_manager(&path); - - assert_eq!(package_manager, None); - } - - #[test] - #[should_panic] - fn package_manager_empty_display_should_panic() { - let path = std::env::current_dir().expect("Current user home directory"); - let package_manager = detect_package_manager(&path); - - assert_eq!(package_manager.unwrap().to_string(), String::from("")); - } -} diff --git a/src/packages.rs b/src/packages.rs deleted file mode 100644 index 94fb2bbf..00000000 --- a/src/packages.rs +++ /dev/null @@ -1,671 +0,0 @@ -#![allow(clippy::all)] - -//! #Packages module -//! -//! The `packages` module is used to get the list of packages available in the monorepo. -use execute::Execute; -use regex::Regex; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -use std::path::Path; -use std::path::PathBuf; -use std::process::{Command, Stdio}; -use wax::{CandidatePath, Glob, Pattern}; - -use super::dependency::Node; -use super::git::get_all_files_changed_since_branch; -use super::manager::{detect_package_manager, PackageManager}; -use super::paths::get_project_root_path; - -#[derive(Debug, Deserialize, Serialize)] -/// A struct that represents a pnpm workspace. -struct PnpmInfo { - pub name: String, - pub path: String, - pub private: bool, -} - -#[derive(Debug, Deserialize, Serialize)] -/// A struct that represents a yarn workspace. -struct PkgJson { - pub workspaces: Vec, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] -pub struct PackageInfo { - pub name: String, - pub private: bool, - pub package_json_path: String, - pub package_path: String, - pub package_relative_path: String, - pub pkg_json: Value, - pub root: bool, - pub version: String, - pub url: String, - pub repository_info: Option, - pub changed_files: Vec, - pub dependencies: Vec, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] -/// A struct that represents a package in the monorepo. -pub struct PackageInfo { - pub name: String, - pub private: bool, - pub package_json_path: String, - pub package_path: String, - pub package_relative_path: String, - pub pkg_json: Value, - pub root: bool, - pub version: String, - pub url: String, - pub repository_info: Option, - pub changed_files: Vec, - pub dependencies: Vec, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] -pub struct PackageRepositoryInfo { - pub domain: String, - pub orga: String, - pub project: String, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] -/// A struct that represents the repository information of a package. -pub struct PackageRepositoryInfo { - pub domain: String, - pub orga: String, - pub project: String, -} - -#[cfg(feature = "napi")] -#[napi(object)] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] -pub struct DependencyInfo { - pub name: String, - pub version: String, -} - -#[cfg(not(feature = "napi"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] -pub struct DependencyInfo { - pub name: String, - pub version: String, -} - -impl Node for PackageInfo { - type DependencyType = DependencyInfo; - - fn dependencies(&self) -> &[Self::DependencyType] { - &self.dependencies[..] - } - - fn matches(&self, dependency: &Self::DependencyType) -> bool { - let dependency_version = semver::VersionReq::parse(&dependency.version).unwrap(); - let self_version = semver::Version::parse(&self.version).unwrap(); - - // Check that name is an exact match, and that the dependency - // requirements are fulfilled by our own version - self.name == dependency.name && dependency_version.matches(&self_version) - } -} - -impl PackageInfo { - /// Pushes a changed file to the list of changed files. - pub fn push_changed_file(&mut self, file: String) { - self.changed_files.push(file); - } - - /// Returns the list of changed files. - pub fn get_changed_files(&self) -> Vec { - self.changed_files.to_vec() - } - - /// Extends the list of changed files with the provided list. - pub fn extend_changed_files(&mut self, files: Vec) { - let founded_files = files - .iter() - .filter(|file| file.starts_with(&self.package_path)) - .map(|file| file.to_string()) - .collect::>(); - - self.changed_files.extend(founded_files); - } - - pub fn push_dependency(&mut self, dependency: DependencyInfo) { - self.dependencies.push(dependency); - } - - /// Updates the version of the package. - pub fn update_version(&mut self, version: String) { - self.version = version.to_string(); - self.pkg_json["version"] = Value::String(version.to_string()); - } - - /// Updates a dependency version in the package.json file. - pub fn update_dependency_version(&mut self, dependency: String, version: String) { - let package_json = self.pkg_json.as_object().unwrap(); - - if package_json.contains_key("dependencies") { - let dependencies = self.pkg_json["dependencies"].as_object_mut().unwrap(); - let has_dependency = dependencies.contains_key(&dependency); - - if has_dependency { - dependencies.insert(dependency, Value::String(version)); - } - } - } - - /// Updates a dev dependency version in the package.json file. - pub fn update_dev_dependency_version(&mut self, dependency: String, version: String) { - let package_json = self.pkg_json.as_object().unwrap(); - - if package_json.contains_key("devDependencies") { - let dev_dependencies = self.pkg_json["devDependencies"].as_object_mut().unwrap(); - let has_dependency = dev_dependencies.contains_key(&dependency); - - if has_dependency { - dev_dependencies.insert(dependency, Value::String(version)); - } - } - } - - /// Write package.json file with the updated version. - pub fn write_package_json(&self) { - let package_json_file = std::fs::File::create(&self.package_json_path).unwrap(); - let package_json_writer = std::io::BufWriter::new(package_json_file); - - serde_json::to_writer_pretty(package_json_writer, &self.pkg_json).unwrap(); - } -} - -/// Returns package info domain, scope and repository name. -fn get_package_repository_info(url: &String) -> PackageRepositoryInfo { - let regex = Regex::new( - r"(?m)((?[a-z]+)://)((?[^/]*)/)(?([^/]*)/)(?(.*))(\.git)?", - ) - .unwrap(); - - let captures = regex.captures(url).unwrap(); - let domain = captures.name("domain").unwrap().as_str(); - let orga = captures.name("org").unwrap().as_str(); - let project = captures.name("project").unwrap().as_str(); - - PackageRepositoryInfo { - domain: domain.to_string().replace("/", ""), - orga: orga.to_string().replace("/", ""), - project: project.to_string().replace("/", "").replace(".git", ""), - } -} - -/// Returns the package info of the package with the provided name. -pub fn get_package_info(package_name: String, cwd: Option) -> Option { - let project_root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let packages = get_packages(Some(project_root)); - - packages - .into_iter() - .find(|package| package.name == package_name) -} - -/// Get defined package manager in the monorepo -pub fn get_monorepo_package_manager(cwd: Option) -> Option { - let project_root = match cwd { - Some(dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let path = Path::new(&project_root); - - detect_package_manager(&path) -} - -/// Get a list of packages available in the monorepo -pub fn get_packages(cwd: Option) -> Vec { - let project_root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - let package_manager = get_monorepo_package_manager(Some(project_root.to_string())); - - let mut packages = match package_manager { - Some(PackageManager::Pnpm) => { - let path = Path::new(&project_root); - let pnpm_workspace = path.join("pnpm-workspace.yaml"); - - if !pnpm_workspace.as_path().exists() { - panic!("pnpm-workspace.yaml file not found"); - } - - let mut command = Command::new("pnpm"); - command - .current_dir(&project_root) - .arg("list") - .arg("-r") - .arg("--depth") - .arg("-1") - .arg("--json"); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - let pnpm_info = - serde_json::from_slice::>(&output.stdout.as_slice()).unwrap(); - - pnpm_info - .iter() - .map(|info| { - let ref package_json_path = format!("{}/package.json", info.path); - - let package_json_file = - std::fs::File::open(package_json_path.to_string()).unwrap(); - let package_json_reader = std::io::BufReader::new(package_json_file); - let pkg_json: serde_json::Value = - serde_json::from_reader(package_json_reader).unwrap(); - - let ref version = match pkg_json.get("version") { - Some(version) => { - if version.is_string() { - version.as_str().unwrap().to_string() - } else { - String::from("0.0.0") - } - } - None => String::from("0.0.0"), - }; - - let ref repo_url = match pkg_json.get("repository") { - Some(repository) => { - if repository.is_object() { - let repo = repository.as_object().unwrap(); - - match repo.get("url") { - Some(url) => url.as_str().unwrap().to_string(), - None => String::from("https://github.com/my-orga/my-repo"), - } - } else if repository.is_string() { - repository.as_str().unwrap().to_string() - } else { - String::from("https://github.com/my-orga/my-repo") - } - } - None => String::from("https://github.com/my-orga/my-repo"), - }; - - let is_root = info.path == project_root; - - let relative_path = match is_root { - true => String::from("."), - false => { - let mut rel = - info.path.strip_prefix(&project_root).unwrap().to_string(); - rel.remove(0); - rel - } - }; - - let repository_info = get_package_repository_info(repo_url); - let name = &info.name.to_string(); - let package_path = &info.path.to_string(); - - PackageInfo { - name: name.to_string(), - private: info.private, - package_json_path: package_json_path.to_string(), - package_path: package_path.to_string(), - package_relative_path: relative_path, - pkg_json, - root: is_root, - version: version.to_string(), - url: String::from(repo_url), - repository_info: Some(repository_info), - changed_files: vec![], - dependencies: vec![], - } - }) - .filter(|pkg| !pkg.root) - .collect::>() - } - Some(PackageManager::Yarn) | Some(PackageManager::Npm) => { - let path = Path::new(&project_root); - let package_json = path.join("package.json"); - let mut packages = vec![]; - - let package_json = std::fs::read_to_string(&package_json).unwrap(); - - let PkgJson { mut workspaces, .. } = - serde_json::from_str::(&package_json).unwrap(); - - let globs = workspaces - .iter_mut() - .map(|workspace| { - return match workspace.ends_with("/*") { - true => { - workspace.push_str("*/package.json"); - Glob::new(workspace).unwrap() - } - false => { - workspace.push_str("/package.json"); - Glob::new(workspace).unwrap() - } - }; - }) - .collect::>(); - - let patterns = wax::any(globs).unwrap(); - - let glob = Glob::new("**/package.json").unwrap(); - - for entry in glob - .walk(path) - .not([ - "**/node_modules/**", - "**/src/**", - "**/dist/**", - "**/tests/**", - ]) - .unwrap() - { - let entry = entry.unwrap(); - let rel_path = entry - .path() - .strip_prefix(&path) - .unwrap() - .display() - .to_string(); - //rel_path.remove(0); - - if patterns.is_match(CandidatePath::from( - entry.path().strip_prefix(&path).unwrap(), - )) { - let package_json_file = std::fs::File::open(&entry.path()).unwrap(); - let package_json_reader = std::io::BufReader::new(package_json_file); - let pkg_json: serde_json::Value = - serde_json::from_reader(package_json_reader).unwrap(); - - let private = match pkg_json.get("private") { - Some(private) => { - if private.is_boolean() { - private.as_bool().unwrap() - } else { - false - } - } - None => false, - }; - - let ref version = match pkg_json.get("version") { - Some(version) => { - if version.is_string() { - version.as_str().unwrap().to_string() - } else { - String::from("0.0.0") - } - } - None => String::from("0.0.0"), - }; - - let ref repo_url = match pkg_json.get("repository") { - Some(repository) => { - if repository.is_object() { - let repo = repository.as_object().unwrap(); - - match repo.get("url") { - Some(url) => url.as_str().unwrap().to_string(), - None => String::from("https://github.com/my-orga/my-repo"), - } - } else if repository.is_string() { - repository.as_str().unwrap().to_string() - } else { - String::from("https://github.com/my-orga/my-repo") - } - } - None => String::from("https://github.com/my-orga/my-repo"), - }; - - let name = match pkg_json.get("name") { - Some(name) => { - if name.is_string() { - name.as_str().unwrap().to_string() - } else { - String::from("unknown") - } - } - None => String::from("unknown"), - }; - - let repository_info = get_package_repository_info(repo_url); - - let pkg_info = PackageInfo { - name: name.to_string(), - private, - package_json_path: entry.path().to_str().unwrap().to_string(), - package_path: entry.path().parent().unwrap().to_str().unwrap().to_string(), - package_relative_path: rel_path - .strip_suffix("/package.json") - .unwrap() - .to_string(), - pkg_json, - root: false, - version: version.to_string(), - url: repo_url.to_string(), - repository_info: Some(repository_info), - changed_files: vec![], - dependencies: vec![], - }; - - packages.push(pkg_info); - } - } - - packages - } - Some(PackageManager::Bun) => vec![], - None => vec![], - }; - - for pkg in packages.iter_mut() { - let pkg_json: serde_json::Value = serde_json::from_value(pkg.pkg_json.clone()).unwrap(); - let package_json = pkg_json.as_object().unwrap(); - - if package_json.contains_key("dependencies") { - let deps = package_json.get("dependencies").unwrap(); - - if deps.is_object() { - let deps = deps.as_object().unwrap(); - - for (name, version) in deps { - pkg.push_dependency(DependencyInfo { - name: name.to_string(), - version: version.as_str().unwrap().to_string(), - }); - } - } - } - - if package_json.contains_key("devDependencies") { - let deps = package_json.get("devDependencies").unwrap(); - - if deps.is_object() { - let deps = deps.as_object().unwrap(); - - for (name, version) in deps { - pkg.push_dependency(DependencyInfo { - name: name.to_string(), - version: version.as_str().unwrap().to_string(), - }); - } - } - } - } - - packages -} - -/// Get a list of packages that have changed since a given sha -pub fn get_changed_packages(sha: Option, cwd: Option) -> Vec { - let root = match cwd { - Some(ref dir) => get_project_root_path(Some(PathBuf::from(dir))).unwrap(), - None => get_project_root_path(None).unwrap(), - }; - - let packages = get_packages(Some(root.to_string())); - let since = sha.unwrap_or(String::from("main")); - - let changed_files = - get_all_files_changed_since_branch(&packages, &since, Some(root.to_string())); - - packages - .iter() - .flat_map(|pkg| { - let mut pkgs = changed_files - .iter() - .filter(|file| file.starts_with(&pkg.package_path)) - .map(|file| { - let mut pkg_info: PackageInfo = pkg.to_owned(); - pkg_info.push_changed_file(file.to_string()); - - pkg_info - }) - .collect::>(); - - pkgs.dedup_by(|a, b| a.name == b.name); - - pkgs - }) - .collect::>() -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::manager::PackageManager; - use crate::utils::create_test_monorepo; - use std::fs::{remove_dir_all, File}; - use std::io::Write; - use std::path::PathBuf; - use std::process::Command; - - fn create_package_change(monorepo_dir: &PathBuf) -> Result<(), Box> { - let js_path = monorepo_dir.join("packages/package-a/index.js"); - - let branch = Command::new("git") - .current_dir(&monorepo_dir) - .arg("checkout") - .arg("-b") - .arg("feat/message") - .stdout(Stdio::piped()) - .spawn() - .expect("Git branch problem"); - - branch.wait_with_output()?; - - let mut js_file = File::create(&js_path)?; - js_file - .write_all(r#"export const message = "hello";"#.as_bytes()) - .unwrap(); - - let add = Command::new("git") - .current_dir(&monorepo_dir) - .arg("add") - .arg(".") - .stdout(Stdio::piped()) - .spawn() - .expect("Git add problem"); - - add.wait_with_output()?; - - let commit = Command::new("git") - .current_dir(&monorepo_dir) - .arg("commit") - .arg("-m") - .arg("feat: message to the world") - .stdout(Stdio::piped()) - .spawn() - .expect("Git commit problem"); - - commit.wait_with_output()?; - - Ok(()) - } - - #[test] - fn monorepo_package_manager() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Pnpm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let package_manager = get_monorepo_package_manager(project_root); - - assert_eq!(package_manager, Some(PackageManager::Pnpm)); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn npm_get_packages() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let packages = get_packages(project_root); - - assert_eq!(packages.len(), 4); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn yarn_get_packages() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Yarn)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let packages = get_packages(project_root); - - assert_eq!(packages.len(), 4); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn pnpm_get_packages() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Pnpm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - let packages = get_packages(project_root); - - assert_eq!(packages.len(), 4); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn monorepo_get_changed_packages() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - create_package_change(monorepo_dir)?; - - let packages = get_changed_packages(Some("main".to_string()), project_root); - let package = packages.first(); - - let changed_files = package.unwrap().get_changed_files(); - - assert_eq!(packages.len(), 1); - assert_eq!(changed_files.len(), 1); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } -} diff --git a/src/paths.rs b/src/paths.rs deleted file mode 100644 index ee69d5d5..00000000 --- a/src/paths.rs +++ /dev/null @@ -1,187 +0,0 @@ -#![allow(clippy::all)] - -//! #Paths module -//! -//! The `paths` module is used to get the project root path. -use super::utils::strip_trailing_newline; -use execute::Execute; -use std::{ - env, - path::{Path, PathBuf}, - process::{Command, Stdio}, -}; - -/// Get the project root path. -pub fn get_project_root_path(root: Option) -> Option { - let env_dir = match root { - Some(dir) => Ok(dir), - None => env::current_dir(), - }; - - let current_dir = match env_dir { - Ok(dir) => dir, - _ => PathBuf::from("./"), - }; - let current_path = current_dir.as_path(); - - let git_root_dir = walk_reverse_dir(¤t_path); - - let project_root = match git_root_dir { - Some(current) => current, - None => { - let search_root = get_git_root_dir(¤t_path); - search_root.unwrap_or(current_path.to_str().unwrap().to_string()) - } - }; - - let canonic_path = &std::fs::canonicalize(Path::new(&project_root)).unwrap(); - let root = canonic_path.as_path().display().to_string(); - - Some(root) -} - -/// Get the git root directory. -fn get_git_root_dir(dir: &Path) -> Option { - let mut command = Command::new("git"); - command.arg("rev-parse").arg("--show-toplevel"); - - command.current_dir(dir); - - command.stdout(Stdio::piped()); - command.stderr(Stdio::piped()); - - let output = command.execute_output().unwrap(); - - if output.status.success() { - let output = String::from_utf8(output.stdout).unwrap(); - return Some(strip_trailing_newline(&output)); - } - - None -} - -/// Walk reverse directory to find the root project. -fn walk_reverse_dir(path: &Path) -> Option { - let current_path = path.to_path_buf(); - let map_files = vec![ - ("package-lock.json", "npm"), - ("npm-shrinkwrap.json", "npm"), - ("yarn.lock", "yarn"), - ("pnpm-lock.yaml", "pnpm"), - ("bun.lockb", "bun"), - ]; - - for (file, _) in map_files.iter() { - let lock_file = current_path.join(file); - - if lock_file.exists() { - return Some(current_path.to_str().unwrap().to_string()); - } - } - - if let Some(parent) = path.parent() { - return walk_reverse_dir(parent); - } - - None -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::manager::PackageManager; - use crate::utils::create_test_monorepo; - use std::fs::{remove_dir_all, rename}; - use std::path::Path; - - fn git_dir_rename(from: &Path, to: &Path) { - rename(from, to).expect("Rename dir"); - } - - #[test] - fn npm_root_project() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let git_home = monorepo_dir.join(".git"); - let no_git = monorepo_dir.join(".no_git"); - - git_dir_rename(&git_home, &no_git); - - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - assert_eq!( - project_root, - Some(monorepo_dir.to_str().unwrap().to_string()) - ); - - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn yarn_root_project() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Yarn)?; - let git_home = monorepo_dir.join(".git"); - let no_git = monorepo_dir.join(".no_git"); - - git_dir_rename(&git_home, &no_git); - - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - assert_eq!( - project_root, - Some(monorepo_dir.to_str().unwrap().to_string()) - ); - - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn pnpm_root_project() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Pnpm)?; - let git_home = monorepo_dir.join(".git"); - let no_git = monorepo_dir.join(".no_git"); - - git_dir_rename(&git_home, &no_git); - - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - assert_eq!( - project_root, - Some(monorepo_dir.to_str().unwrap().to_string()) - ); - - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn bun_root_project() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Bun)?; - let git_home = monorepo_dir.join(".git"); - let no_git = monorepo_dir.join(".no_git"); - - git_dir_rename(&git_home, &no_git); - - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - assert_eq!( - project_root, - Some(monorepo_dir.to_str().unwrap().to_string()) - ); - - remove_dir_all(&monorepo_dir)?; - Ok(()) - } - - #[test] - fn git_root_project() -> Result<(), Box> { - let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?; - let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf())); - - assert_eq!(project_root.is_some(), true); - remove_dir_all(&monorepo_dir)?; - Ok(()) - } -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 8304e51d..00000000 --- a/src/utils.rs +++ /dev/null @@ -1,494 +0,0 @@ -#![allow(clippy::all)] -#![allow(dead_code)] - -use regex::Regex; -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -use std::path::{Path, PathBuf}; - -#[cfg(test)] -use std::fs::{create_dir, File, OpenOptions}; -#[cfg(test)] -use std::io::{BufWriter, Write}; - -#[cfg(test)] -use rand::distributions::Alphanumeric; -#[cfg(test)] -use rand::{thread_rng, Rng}; - -#[cfg(test)] -#[cfg(not(windows))] -use std::os::unix::fs::PermissionsExt; - -#[cfg(test)] -use super::manager::PackageManager; -#[cfg(test)] -use std::process::{Command, Stdio}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -/// Package scope metadata extracted from a package name. -pub struct PackageScopeMetadata { - pub full: String, - pub name: String, - pub version: String, - pub path: Option, -} - -/// Extracts the package scope name and version from a package name. -pub(crate) fn package_scope_name_version(pkg_name: &str) -> Option { - let regex = Regex::new("^((?:@[^/@]+/)?[^/@]+)(?:@([^/]+))?(/.*)?$").unwrap(); - - let matches = regex.captures(pkg_name).unwrap(); - - if matches.len() > 0 { - return Some(PackageScopeMetadata { - full: matches.get(0).map_or("", |m| m.as_str()).to_string(), - name: matches.get(1).map_or("", |m| m.as_str()).to_string(), - version: matches.get(2).map_or("", |m| m.as_str()).to_string(), - path: matches - .get(3) - .map_or(None, |m| Some(m.as_str().to_string())), - }); - } - - None -} - -/// Strips the trailing newline from a string. -pub(crate) fn strip_trailing_newline(input: &String) -> String { - input - .strip_suffix("\r\n") - .or(input.strip_suffix("\n")) - .unwrap_or(input) - .trim() - .to_string() -} - -#[cfg(test)] -pub(crate) fn create_test_monorepo( - package_manager: &PackageManager, -) -> Result { - let rand_string: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(30) - .map(char::from) - .collect(); - - let temp_dir = std::env::temp_dir(); - let monorepo_temp_dir = temp_dir.join(format!("monorepo-{}", rand_string)); - let monorepo_package_json = monorepo_temp_dir.join("package.json"); - - let monorepo_packages_dir = monorepo_temp_dir.join("packages"); - let monorepo_package_a_dir = monorepo_packages_dir.join("package-a"); - let monorepo_package_b_dir = monorepo_packages_dir.join("package-b"); - let monorepo_package_c_dir = monorepo_packages_dir.join("package-c"); - let monorepo_package_d_dir = monorepo_packages_dir.join("package-d"); - - create_dir(&monorepo_temp_dir)?; - create_dir(&monorepo_packages_dir)?; - create_dir(&monorepo_package_a_dir)?; - create_dir(&monorepo_package_b_dir)?; - create_dir(&monorepo_package_c_dir)?; - create_dir(&monorepo_package_d_dir)?; - - #[cfg(not(windows))] - std::fs::set_permissions(&monorepo_temp_dir, std::fs::Permissions::from_mode(0o777))?; - - let monorepo_root_json = r#" - { - "name": "@scope/root", - "version": "0.0.0", - "workspaces": [ - "packages/package-a", - "packages/package-b", - "packages/package-c", - "packages/package-d" - ] - }"#; - let package_root_json = serde_json::from_str::(monorepo_root_json).unwrap(); - let monorepo_package_root_json_file = OpenOptions::new() - .write(true) - .append(false) - .create(true) - .open(&monorepo_package_json.as_path()) - .unwrap(); - let monorepo_root_json_writer = BufWriter::new(monorepo_package_root_json_file); - serde_json::to_writer_pretty(monorepo_root_json_writer, &package_root_json).unwrap(); - - let package_a_json = r#" - { - "name": "@scope/package-a", - "version": "1.0.0", - "description": "My new package A", - "main": "index.mjs", - "module": "./dist/index.mjs", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.mjs" - } - }, - "typesVersions": { - "*": { - "index.d.ts": [ - "./dist/index.d.ts" - ] - } - }, - "repository": { - "url": "git+ssh://git@github.com/websublime/workspace-node-binding-tools.git", - "type": "git" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev": "node index.mjs" - }, - "dependencies": { - "@scope/package-b": "1.0.0" - }, - "keywords": [], - "author": "Author", - "license": "ISC" - }"#; - let package_a_json = serde_json::from_str::(package_a_json).unwrap(); - let monorepo_package_a_json_file = OpenOptions::new() - .write(true) - .append(false) - .create(true) - .open(&monorepo_package_a_dir.join("package.json").as_path()) - .unwrap(); - let monorepo_package_a_json_writer = BufWriter::new(monorepo_package_a_json_file); - serde_json::to_writer_pretty(monorepo_package_a_json_writer, &package_a_json).unwrap(); - - let package_b_json = r#" - { - "name": "@scope/package-b", - "version": "1.0.0", - "description": "My new package B", - "main": "index.mjs", - "module": "./dist/index.mjs", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.mjs" - } - }, - "typesVersions": { - "*": { - "index.d.ts": [ - "./dist/index.d.ts" - ] - } - }, - "repository": { - "url": "git+ssh://git@github.com/websublime/workspace-node-binding-tools.git", - "type": "git" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev": "node index.mjs" - }, - "keywords": [], - "author": "Author", - "license": "ISC" - }"#; - let package_b_json = serde_json::from_str::(package_b_json).unwrap(); - let monorepo_package_b_json_file = OpenOptions::new() - .write(true) - .append(false) - .create(true) - .open(&monorepo_package_b_dir.join("package.json").as_path()) - .unwrap(); - let monorepo_package_b_json_writer = BufWriter::new(monorepo_package_b_json_file); - serde_json::to_writer_pretty(monorepo_package_b_json_writer, &package_b_json).unwrap(); - - let package_c_json = r#" - { - "name": "@scope/package-c", - "version": "1.0.0", - "description": "My new package C", - "main": "index.mjs", - "module": "./dist/index.mjs", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.mjs" - } - }, - "typesVersions": { - "*": { - "index.d.ts": [ - "./dist/index.d.ts" - ] - } - }, - "repository": { - "url": "git+ssh://git@github.com/websublime/workspace-node-binding-tools.git", - "type": "git" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev": "node index.mjs" - }, - "keywords": [], - "author": "Author", - "license": "ISC" - }"#; - let package_c_json = serde_json::from_str::(package_c_json).unwrap(); - let monorepo_package_c_json_file = OpenOptions::new() - .write(true) - .append(false) - .create(true) - .open(&monorepo_package_c_dir.join("package.json").as_path()) - .unwrap(); - let monorepo_package_c_json_writer = BufWriter::new(monorepo_package_c_json_file); - serde_json::to_writer_pretty(monorepo_package_c_json_writer, &package_c_json).unwrap(); - - let package_d_json = r#" - { - "name": "@scope/package-d", - "version": "1.0.0", - "description": "My new package D", - "main": "index.mjs", - "module": "./dist/index.mjs", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.mjs" - } - }, - "typesVersions": { - "*": { - "index.d.ts": [ - "./dist/index.d.ts" - ] - } - }, - "repository": { - "url": "git+ssh://git@github.com/websublime/workspace-node-binding-tools.git", - "type": "git" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev": "node index.mjs" - }, - "dependencies": { - "@scope/package-a": "1.0.0" - }, - "keywords": [], - "author": "Author", - "license": "ISC" - }"#; - let package_d_json = serde_json::from_str::(package_d_json).unwrap(); - let monorepo_package_d_json_file = OpenOptions::new() - .write(true) - .append(false) - .create(true) - .open(&monorepo_package_d_dir.join("package.json").as_path()) - .unwrap(); - let monorepo_package_d_json_writer = BufWriter::new(monorepo_package_d_json_file); - serde_json::to_writer_pretty(monorepo_package_d_json_writer, &package_d_json).unwrap(); - - match package_manager { - PackageManager::Yarn => { - let yarn_lock = monorepo_temp_dir.join("yarn.lock"); - File::create(&yarn_lock)?; - } - PackageManager::Pnpm => { - let pnpm_lock = monorepo_temp_dir.join("pnpm-lock.yaml"); - let pnpm_workspace = monorepo_temp_dir.join("pnpm-workspace.yaml"); - - let mut lock_file = File::create(&pnpm_lock)?; - lock_file.write_all(r#"lockfileVersion: '9.0'"#.as_bytes())?; - - let mut workspace_file = File::create(&pnpm_workspace)?; - workspace_file.write_all( - r#" - packages: - - "packages/*" - "# - .as_bytes(), - )?; - } - PackageManager::Bun => { - let bun_lock = monorepo_temp_dir.join("bun.lockb"); - File::create(&bun_lock)?; - } - PackageManager::Npm => { - let npm_lock = monorepo_temp_dir.join("package-lock.json"); - File::create(&npm_lock)?; - } - } - - let init = Command::new("git") - .current_dir(&monorepo_temp_dir) - .arg("init") - .arg("--initial-branch") - .arg("main") - .stdout(Stdio::piped()) - .spawn() - .expect("Git init problem"); - - init.wait_with_output()?; - - let config_email = Command::new("git") - .current_dir(&monorepo_temp_dir) - .arg("config") - .arg("user.email") - .arg("machine@websublime.dev") - .stdout(Stdio::piped()) - .spawn() - .expect("Git config user email problem"); - - config_email.wait_with_output()?; - - let config_name = Command::new("git") - .current_dir(&monorepo_temp_dir) - .arg("config") - .arg("user.name") - .arg("Sublime Machine") - .stdout(Stdio::piped()) - .spawn() - .expect("Git config user name problem"); - - config_name.wait_with_output()?; - - let add = Command::new("git") - .current_dir(&monorepo_temp_dir) - .arg("add") - .arg(".") - .stdout(Stdio::piped()) - .spawn() - .expect("Git add problem"); - - add.wait_with_output()?; - - let commit = Command::new("git") - .current_dir(&monorepo_temp_dir) - .arg("commit") - .arg("-m") - .arg("feat: project creation") - .stdout(Stdio::piped()) - .spawn() - .expect("Git commit problem"); - - commit.wait_with_output()?; - - let tag_a = Command::new("git") - .current_dir(&monorepo_temp_dir) - .arg("tag") - .arg("-a") - .arg("@scope/package-a@1.0.0") - .arg("-m") - .arg("chore: release package-a@1.0.0") - .stdout(Stdio::piped()) - .spawn() - .expect("Git tag problem"); - - tag_a.wait_with_output()?; - - let tag_b = Command::new("git") - .current_dir(&monorepo_temp_dir) - .arg("tag") - .arg("-a") - .arg("@scope/package-b@1.0.0") - .arg("-m") - .arg("chore: release package-b@1.0.0") - .stdout(Stdio::piped()) - .spawn() - .expect("Git tag problem"); - - tag_b.wait_with_output()?; - - let tag_c = Command::new("git") - .current_dir(&monorepo_temp_dir) - .arg("tag") - .arg("-a") - .arg("@scope/package-c@1.0.0") - .arg("-m") - .arg("chore: release package-c@1.0.0") - .stdout(Stdio::piped()) - .spawn() - .expect("Git tag problem"); - - tag_c.wait_with_output()?; - - let canonic_path = &std::fs::canonicalize(Path::new(&monorepo_temp_dir)).unwrap(); - let root = canonic_path.as_path().display().to_string(); - - Ok(PathBuf::from(root)) -} - -#[cfg(test)] -mod tests { - //use super::*; - - #[test] - fn test_parse_package_json() { - let package_json = r#" - { - "name": "@websublime/workspace-tools", - "version": "1.0.0", - "description": "My package json", - "main": "./dist/index.js", - "module": "./dist/index.js", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "typesVersions": { - "*": { - "index.d.ts": [ - "./dist/index.d.ts" - ] - } - }, - "scripts": { - "type-check": "vue-tsc --noEmit", - "test": "vitest run --coverage", - "build": "vite build --config vite.config.mts --mode=production --emptyOutDir=true", - "dev": "vite --config vite.config.mts --debug --force" - }, - "keywords": [], - "author": "", - "license": "ISL", - "publishConfig": { - "scope": "@websublime" - }, - "repository": { - "url": "git+ssh://git@github.com/websublime/workspace-tools.git", - "type": "git" - }, - "dependencies": { - "@websublime/workspace-tools": "^0.30.0" - }, - "devDependencies": { - "@types/jsdom": "^21.1.6", - "@types/node": "^20.11.6", - "@workbench/core": "^7.21.0", - "@vitest/coverage-v8": "^2.0.4", - "@vue/test-utils": "2.3.2", - "happy-dom": "^14.12.3", - "typescript": "^5.3.3", - "@vitejs/plugin-vue": "^5.0.3", - "vite": "^5.3.4", - "vitest": "^2.0.4", - "vue": "3.3.4", - "vue-tsc": "^1.8.27" - }, - "files": [ - "dist", - "manifest.json", - "./LICENSE.md", - "./README.md", - "./CHANGELOG.md" - ] - }"#; - let package_json_parsed = serde_json::from_str::(package_json).unwrap(); - - assert_eq!(package_json_parsed.is_object(), true); - } -}