diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..ab7412c --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,9 @@ +# Changesets + +This directory contains changesets β€” short Markdown files that describe changes to packages in this repo. + +When you run `pnpm changeset`, a new file is created here. Commit it with your PR. + +When a PR is merged to `main`, the `release.yml` workflow consumes these files, bumps the relevant `package.json` versions, and opens a **Version Packages** PR. After that PR is merged, the workflow creates git tags and publishes the affected codemods. + +See [Changesets docs](https://github.com/changesets/changesets) for more detail. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..9c40dde --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [], + "privatePackages": { + "version": true, + "tag": true + } +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..e420b2d --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,55 @@ +name: CI + +on: + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + ci: + name: CI + runs-on: ubuntu-slim + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: pnpm/action-setup@v4 + + - run: pnpm install --frozen-lockfile + + - run: pnpm run format:check + + - run: pnpm run lint + + - run: pnpm run check-types + + - name: Run tests for changed codemods + shell: bash + run: | + changed_paths=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) + + if echo "$changed_paths" | grep -qE '^(package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|\.github/workflows/ci\.yml)$'; then + pnpm test + exit 0 + fi + + filters=$( + echo "$changed_paths" \ + | grep '^codemods/' \ + | cut -d/ -f1-3 \ + | sort -u \ + | sed 's|^|--filter ./|' + ) + + if [ -z "$filters" ]; then + echo "No codemod packages affected; skipping tests." + exit 0 + fi + + # shellcheck disable=SC2086 + pnpm $filters test diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml deleted file mode 100644 index 5b689e1..0000000 --- a/.github/workflows/code-quality.yml +++ /dev/null @@ -1,134 +0,0 @@ -name: Code Quality - -on: - push: - paths: - - "codemods/**/*.js" - - "codemods/**/*.ts" - - "codemods/**/*.tsx" - - "codemods/**/*.jsx" - - "codemods/**/codemod.yaml" - - "codemods/**/workflow.yaml" - - "codemods/**/package.json" - - "codemods/**/package-lock.json" - - "codemods/**/tests/**" - - "package.json" - - "package-lock.json" - - "tsconfig.json" - - "biome.jsonc" - - ".github/workflows/code-quality.yml" - pull_request: - paths: - - "codemods/**/*.js" - - "codemods/**/*.ts" - - "codemods/**/*.tsx" - - "codemods/**/*.jsx" - - "codemods/**/codemod.yaml" - - "codemods/**/workflow.yaml" - - "codemods/**/package.json" - - "codemods/**/package-lock.json" - - "codemods/**/tests/**" - - "package.json" - - "package-lock.json" - - "tsconfig.json" - - "biome.jsonc" - - ".github/workflows/code-quality.yml" - types: - - opened - - ready_for_review - - reopened - - synchronize - -permissions: - contents: read - -jobs: - lint-and-types: - name: Lint & types - runs-on: ubuntu-slim - - if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }} - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - show-progress: false - - - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - cache: "npm" - node-version: "lts/*" - - - run: npm ci - - run: node --run check - - run: node --run typecheck - - test: - name: Before/After tests - runs-on: ${{ matrix.os }} - - if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }} - - strategy: - fail-fast: false - matrix: - os: - - macos-latest - - ubuntu-latest - - windows-latest - - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 - with: - egress-policy: audit - - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - persist-credentials: false - show-progress: false - - - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - cache: "npm" - node-version: "lts/*" - - - run: npm ci - - - name: Run tests related to changes - shell: bash - run: | - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - base_sha="${{ github.event.pull_request.base.sha }}" - elif [[ "${{ github.event.before }}" =~ ^0+$ ]]; then - base_sha="$(git merge-base origin/${{ github.event.repository.default_branch }} HEAD)" - else - base_sha="${{ github.event.before }}" - fi - - changed_paths="$(git diff --name-only "$base_sha" "${{ github.sha }}")" - - # Run everything when shared configuration or workflow behavior changes. - if echo "$changed_paths" | grep -qE '^(package(-lock)?\.json|tsconfig\.json|biome\.jsonc|\.github/workflows/code-quality\.yml)$'; then - npm run test --workspaces --if-present - exit 0 - fi - - codemod_paths="$(echo "$changed_paths" | grep '^codemods/' || true)" - workspaces="$( - echo "$codemod_paths" \ - | cut -d/ -f1,2 \ - | sort -u \ - | sed 's/^/--workspace=/' - )" - - if [[ -z "$workspaces" ]]; then - echo "No codemod workspaces changed; skipping before/after tests." - exit 0 - fi - - npm run test --if-present $workspaces diff --git a/.github/workflows/codemod-publish.yaml b/.github/workflows/codemod-publish.yaml deleted file mode 100644 index b1546a5..0000000 --- a/.github/workflows/codemod-publish.yaml +++ /dev/null @@ -1,224 +0,0 @@ -name: Publish Codemod - -# Required for OIDC trusted publisher (no API key needed) -permissions: - id-token: write - contents: read - -on: - push: - tags: - - "*@v*" # eg: codemod-name@v1.0.0 or @scopename/codemodname@v1.0.0 - workflow_dispatch: - inputs: - tag: - description: "Tag to publish (format: codemod-name@v1.0.0 or @scopename/codemodname@v1.0.0)" - required: true - type: string - -jobs: - validate-and-publish: - name: Validate and Publish Codemod - runs-on: ubuntu-latest - - outputs: - version: ${{ steps.parse-tag.outputs.version }} - codemod-name: ${{ steps.parse-tag.outputs.codemod-name }} - codemod-path: ${{ steps.find-codemod.outputs.codemod-path }} - - steps: - - name: Harden Runner - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 - with: - egress-policy: audit - - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - - name: Parse tag and extract metadata - id: parse-tag - env: - EVENT_NAME: ${{ github.event_name }} - INPUT_TAG: ${{ github.event.inputs.tag }} - GITHUB_REF: ${{ github.ref }} - run: | - # Determine the tag based on trigger type - if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then - TAG="$INPUT_TAG" - echo "Using manually provided tag: $TAG" - else - TAG="${GITHUB_REF#refs/tags/}" - echo "Using pushed tag: $TAG" - fi - - # Validate tag format - # Supports: @scopename/codemodname@v1.0.0 or codemodname@v1.0.0 - # Does NOT support: codemodname/subname@v1.0.0 (non-scoped packages can't have slashes) - if [[ ! "$TAG" =~ ^(@[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+|[a-zA-Z0-9_-]+)@v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "❌ Invalid tag format: $TAG" - echo "Expected formats:" - echo " - @scopename/codemodname@v1.0.0 (scoped packages)" - echo " - codemodname@v1.0.0 (non-scoped packages)" - echo "Note: Non-scoped packages cannot contain slashes" - exit 1 - fi - - # Extract components - CODEMOD_NAME="${TAG%@v*}" # Everything before @v (handles both scoped and non-scoped) - VERSION="v${TAG#*@v}" # Everything after @v, with v prefix - - # Set outputs - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "codemod-name=$CODEMOD_NAME" >> $GITHUB_OUTPUT - - echo "βœ“ Parsed tag - Version: $VERSION, Codemod: $CODEMOD_NAME" - - - name: Find codemod directory by searching codemod.yaml files - id: find-codemod - env: - CODEMOD_NAME: ${{ steps.parse-tag.outputs.codemod-name }} - VERSION: ${{ steps.parse-tag.outputs.version }} - run: | - echo "πŸ” Searching for codemod '$CODEMOD_NAME' in all codemod.yaml files..." - - # Find all codemod.yaml files in the codemods directory - CODEMOD_FILES=$(find codemods -name "codemod.yaml" -type f 2>/dev/null || true) - - if [[ -z "$CODEMOD_FILES" ]]; then - echo "❌ No codemod.yaml files found in codemods directory" - echo "Available files:" - find codemods -type f -name "*.yaml" -o -name "*.yml" 2>/dev/null || echo "No YAML files found" - exit 1 - fi - - echo "Found codemod.yaml files:" - echo "$CODEMOD_FILES" - echo "" - - FOUND_PATH="" - - # Search through each codemod.yaml file - while IFS= read -r yaml_file; do - if [[ ! -f "$yaml_file" ]]; then - continue - fi - - echo "Checking: $yaml_file" - - # Extract name from yaml file using yq or basic grep/sed - # First try with yq if available, otherwise use grep/sed - if command -v yq >/dev/null 2>&1; then - YAML_NAME=$(yq eval '.name' "$yaml_file" 2>/dev/null || echo "") - YAML_VERSION=$(yq eval '.version' "$yaml_file" 2>/dev/null || echo "") - else - # Fallback to grep/sed for basic YAML parsing - YAML_NAME=$(grep -E '^[[:space:]]*name[[:space:]]*:' "$yaml_file" | head -1 | sed 's/^[[:space:]]*name[[:space:]]*:[[:space:]]*//' | sed 's/[[:space:]]*$//' | sed 's/^["\x27]//' | sed 's/["\x27]$//' || echo "") - YAML_VERSION=$(grep -E '^[[:space:]]*version[[:space:]]*:' "$yaml_file" | head -1 | sed 's/^[[:space:]]*version[[:space:]]*:[[:space:]]*//' | sed 's/[[:space:]]*$//' | sed 's/^["\x27]//' | sed 's/["\x27]$//' || echo "") - fi - - echo " - Name in file: '$YAML_NAME'" - echo " - Version in file: '$YAML_VERSION'" - - if [[ "$YAML_NAME" == "$CODEMOD_NAME" && "v$YAML_VERSION" == "$VERSION" ]]; then - FOUND_PATH=$(dirname "$yaml_file") - echo " βœ… Match found!" - break - fi - done <<< "$CODEMOD_FILES" - - if [[ -z "$FOUND_PATH" ]]; then - echo "❌ Codemod '$CODEMOD_NAME' not found in any codemod.yaml files" - echo "" - echo "Available codemods:" - while IFS= read -r yaml_file; do - if [[ -f "$yaml_file" ]]; then - if command -v yq >/dev/null 2>&1; then - NAME=$(yq eval '.name' "$yaml_file" 2>/dev/null || echo "unknown") - else - NAME=$(grep -E '^[[:space:]]*name[[:space:]]*:' "$yaml_file" | head -1 | sed 's/^[[:space:]]*name[[:space:]]*:[[:space:]]*//' | sed 's/[[:space:]]*$//' | sed 's/^["\x27]//' | sed 's/["\x27]$//' || echo "unknown") - fi - echo " - $NAME (in $yaml_file)" - fi - done <<< "$CODEMOD_FILES" - exit 1 - fi - - echo "codemod-path=$FOUND_PATH" >> $GITHUB_OUTPUT - echo "βœ… Found codemod at: $FOUND_PATH" - - - name: Verify codemod directory - env: - CODEMOD_PATH: ${{ steps.find-codemod.outputs.codemod-path }} - run: | - echo "βœ“ Using codemod directory: $CODEMOD_PATH" - echo "Directory contents:" - ls -lah "$CODEMOD_PATH" - - # Verify required files exist - if [[ ! -f "$CODEMOD_PATH/codemod.yaml" ]]; then - echo "❌ codemod.yaml not found in $CODEMOD_PATH" - exit 1 - fi - - echo "βœ… codemod.yaml found" - - - uses: actions/setup-node@v4 - with: - node-version: 'lts/*' - - - name: Install project dependencies - run: | - npm ci - - # Run test before login to not waste time if it fails - - name: Validate workflow - working-directory: ${{ steps.find-codemod.outputs.codemod-path }} - run: npx codemod@latest workflow validate --workflow workflow.yaml - - - name: Check if package.json exists - id: check-package-json - working-directory: ${{ steps.find-codemod.outputs.codemod-path }} - run: | - if [[ -f "package.json" ]]; then - echo "has-package-json=true" >> $GITHUB_OUTPUT - else - echo "has-package-json=false" >> $GITHUB_OUTPUT - fi - - - name: Install codemod dependencies - if: steps.check-package-json.outputs.has-package-json == 'true' - working-directory: ${{ steps.find-codemod.outputs.codemod-path }} - run: npm ci - - - name: Run javascript ast-grep codemod Tests - if: steps.check-package-json.outputs.has-package-json == 'true' - working-directory: ${{ steps.find-codemod.outputs.codemod-path }} - run: node --run test - - - name: Publish codemod - uses: codemod/publish-action@v1 - with: - path: ${{ steps.find-codemod.outputs.codemod-path }} - - - name: Create release summary - env: - CODEMOD_NAME: ${{ steps.parse-tag.outputs.codemod-name }} - VERSION: ${{ steps.parse-tag.outputs.version }} - CODEMOD_PATH: ${{ steps.find-codemod.outputs.codemod-path }} - TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }} - TRIGGER: ${{ github.event_name == 'workflow_dispatch' && 'Manual' || 'Tag Push' }} - ACTOR: ${{ github.triggering_actor }} - run: | - cat >> $GITHUB_STEP_SUMMARY << EOF - # πŸš€ Codemod Publication Summary - - **Codemod:** \`$CODEMOD_NAME\` - **Version:** \`$VERSION\` - **Path:** \`$CODEMOD_PATH\` - **Tag:** \`$TAG\` - **Trigger:** $TRIGGER by $ACTOR - - βœ… Codemod has been successfully published to the registry! - EOF diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..a44da10 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,90 @@ +name: Publish + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + codemod_name: + description: 'Codemod name (directory name under codemods/)' + required: true + type: string + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +permissions: + id-token: write + contents: write + +jobs: + tag: + name: Tag released versions + if: github.event_name == 'push' + runs-on: ubuntu-latest + outputs: + changed_dirs: ${{ steps.tag.outputs.changed_dirs }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Detect pending changesets + id: pending + run: | + count="$(find .changeset -maxdepth 1 -type f -name '*.md' ! -name 'README.md' | wc -l | tr -d ' ')" + echo "count=$count" >> "$GITHUB_OUTPUT" + + - name: Tag released versions + if: steps.pending.outputs.count == '0' + id: tag + run: bash scripts/tag-and-publish.sh + + publish: + name: Publish ${{ matrix.dir }} + needs: tag + if: >- + github.event_name == 'push' && + needs.tag.outputs.changed_dirs != '' && + needs.tag.outputs.changed_dirs != '[]' + runs-on: ubuntu-latest + strategy: + matrix: + dir: ${{ fromJson(needs.tag.outputs.changed_dirs) }} + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Publish codemod + uses: codemod/publish-action@v1 + with: + path: ${{ matrix.dir }} + + publish-manual: + name: Publish codemod (manual) + if: github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Publish codemod + uses: codemod/publish-action@v1 + with: + path: codemods/${{ inputs.codemod_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..07df073 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,37 @@ +name: Release + +on: + push: + branches: + - main + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +permissions: + contents: write + pull-requests: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Create Release Pull Request + uses: changesets/action@v1 + with: + version: pnpm version-packages + title: 'chore: version packages' + commit: 'chore: version packages' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index e14259d..6615489 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ node_modules/ # Test files that get transformed (generated outputs under tests/input/) **/tests/input/* +# Build artifacts +dist/ +tmp/ + # System files .DS_Store -**/.DS_Store \ No newline at end of file +**/.DS_Store diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..cb2c84d --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm lint-staged diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs new file mode 100644 index 0000000..f9747e9 --- /dev/null +++ b/.lintstagedrc.mjs @@ -0,0 +1,18 @@ +const LOCKFILE_SUFFIXES = ['pnpm-lock.yaml', 'package-lock.json', 'npm-shrinkwrap.json'] + +/** @param {string[]} files */ +function excludeLockfiles(files) { + return files.filter((f) => !LOCKFILE_SUFFIXES.some((suffix) => f.endsWith(suffix))) +} + +export default { + '*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,md}': 'oxfmt --write --no-error-on-unmatched-pattern', + '*.{js,mjs,cjs,jsx}': 'oxlint --fix --no-error-on-unmatched-pattern', + '*.{ts,mts,cts,tsx}': 'oxlint --type-aware --type-check --fix --no-error-on-unmatched-pattern', + /** @param {string[]} files */ + '*.{json,yaml,yml}': (files) => { + const filtered = excludeLockfiles(files) + return filtered.length ? [`oxfmt --write --no-error-on-unmatched-pattern ${filtered.join(' ')}`] : [] + }, + 'codemods/**/scripts/**/*.ts': ["bash -c 'pnpm run test'"], +} diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..4749392 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,15 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 120, + "singleQuote": true, + "jsxSingleQuote": true, + "semi": false, + "trailingComma": "all", + "endOfLine": "lf", + "ignorePatterns": ["dist/", "node_modules/", "**/tests/", "**/tests-*/"], + "sortImports": { + "groups": ["builtin", "external", "internal", ["parent", "sibling", "index"], "style", "unknown"], + "newlinesBetween": true + }, + "sortPackageJson": true +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..856bb9b --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,126 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "node", "promise"], + "rules": { + "eslint/eqeqeq": "warn", + "eslint/no-self-compare": "warn", + "eslint/no-template-curly-in-string": "warn", + "eslint/prefer-const": "warn", + "eslint/no-var": "warn", + "eslint/prefer-template": "warn", + "eslint/object-shorthand": "warn", + "eslint/prefer-rest-params": "warn", + "eslint/prefer-spread": "warn", + "eslint/prefer-destructuring": "off", + "eslint/prefer-exponentiation-operator": "warn", + "eslint/prefer-object-spread": "warn", + "eslint/prefer-object-has-own": "warn", + "eslint/prefer-numeric-literals": "warn", + "eslint/prefer-promise-reject-errors": "warn", + "eslint/arrow-body-style": "warn", + "eslint/no-useless-constructor": "warn", + "eslint/no-useless-computed-key": "warn", + "eslint/array-callback-return": "warn", + "eslint/no-fallthrough": "warn", + "eslint/no-promise-executor-return": "warn", + "eslint/no-else-return": "warn", + "eslint/curly": "warn", + "eslint/default-case-last": "warn", + "eslint/default-param-last": "warn", + "eslint/no-lonely-if": "warn", + "eslint/symbol-description": "warn", + "eslint/sort-imports": "off", + "eslint/no-eval": "warn", + "eslint/no-new-func": "warn", + "eslint/radix": "warn", + "typescript/no-explicit-any": "warn", + "typescript/consistent-type-imports": "warn", + "typescript/no-non-null-assertion": "warn", + "typescript/prefer-optional-chain": "warn", + "typescript/no-unnecessary-condition": "off", + "typescript/no-deprecated": "warn", + "typescript/return-await": "warn", + "typescript/array-type": "warn", + "typescript/consistent-type-definitions": "warn", + "typescript/consistent-generic-constructors": "warn", + "typescript/prefer-for-of": "warn", + "typescript/prefer-find": "warn", + "typescript/prefer-string-starts-ends-with": "warn", + "typescript/prefer-includes": "warn", + "typescript/prefer-nullish-coalescing": "warn", + "typescript/no-var-requires": "warn", + "typescript/await-thenable": "error", + "typescript/no-floating-promises": "error", + "typescript/no-for-in-array": "error", + "typescript/no-misused-promises": "error", + "typescript/no-unnecessary-type-assertion": "error", + "typescript/no-unsafe-argument": "error", + "typescript/no-unsafe-assignment": "error", + "typescript/no-unsafe-call": "error", + "typescript/no-unsafe-member-access": "error", + "typescript/no-unsafe-return": "error", + "typescript/require-await": "off", + "typescript/restrict-plus-operands": "error", + "typescript/switch-exhaustiveness-check": "warn", + "import/no-cycle": "warn", + "import/no-duplicates": "warn", + "promise/no-return-in-finally": "warn", + "promise/catch-or-return": "warn", + "unicorn/prefer-includes": "warn", + "unicorn/prefer-array-flat-map": "warn", + "unicorn/prefer-array-flat": "warn", + "unicorn/prefer-array-some": "warn", + "unicorn/prefer-array-find": "warn", + "unicorn/prefer-at": "warn", + "unicorn/prefer-string-slice": "warn", + "unicorn/prefer-string-replace-all": "warn", + "unicorn/prefer-string-trim-start-end": "warn", + "unicorn/prefer-number-properties": "warn", + "unicorn/prefer-modern-math-apis": "warn", + "unicorn/prefer-node-protocol": "warn", + "unicorn/prefer-structured-clone": "warn", + "unicorn/prefer-set-has": "warn", + "unicorn/prefer-spread": "warn", + "unicorn/prefer-code-point": "warn", + "unicorn/prefer-date-now": "warn", + "unicorn/prefer-event-target": "warn", + "unicorn/prefer-global-this": "warn", + "unicorn/no-instanceof-array": "warn", + "unicorn/throw-new-error": "warn", + "unicorn/error-message": "warn", + "unicorn/no-negation-in-equality-check": "warn", + "unicorn/switch-case-braces": "warn", + "unicorn/catch-error-name": "warn" + }, + "overrides": [ + { + "files": ["codemods/**/scripts/**/*.ts", "codemods/**/utils/**/*.ts"], + "rules": { + "typescript/no-deprecated": "off", + "typescript/require-await": "off", + "typescript/restrict-template-expressions": "off", + "typescript/no-base-to-string": "off", + "typescript/array-type": "off", + "typescript/consistent-type-definitions": "off", + "eslint/curly": "off", + "eslint/radix": "off", + "import/no-duplicates": "off", + "unicorn/prefer-array-find": "off", + "unicorn/prefer-array-some": "off", + "unicorn/prefer-number-properties": "off", + "unicorn/prefer-string-slice": "off", + "unicorn/prefer-spread": "off" + } + }, + { + "files": ["**/*.mjs"], + "rules": { + "typescript/no-unsafe-argument": "off", + "typescript/no-unsafe-call": "off", + "typescript/no-unsafe-assignment": "off", + "typescript/no-unsafe-member-access": "off" + } + } + ], + "ignorePatterns": ["**/node_modules", "**/dist", "**/tests", "**/tests-*"] +} diff --git a/AGENTS.md b/AGENTS.md index f2f952d..96ba612 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,12 +14,19 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for PR conventions, commit message format ## Build and Test -- **Lint:** `npm run lint` -- **Format:** `npm run format` -- **Types:** `npm run typecheck` -- **Per-codemod tests:** `cd codemods/ && npm test` (runs jssg test) +- **Format:** `pnpm run format` / `pnpm run format:check` +- **Lint:** `pnpm run lint` / `pnpm run lint:fix` +- **Types:** `pnpm run check-types` +- **Tests:** `pnpm run test` (all packages) or `pnpm --filter test` +- **Full CI:** `pnpm run ci` - **Workflow validation:** `npx codemod workflow validate codemods//workflow.yaml` +## Failure modes that most often trip agents + +- **Forgetting `pnpm changeset`.** Pull requests that touch `codemods/` should include a changeset covering each changed package (or the `skip-changeset` label). CI warns but does not fail when changesets are missing. See _Adding a changeset_ in `CONTRIBUTING.md`. +- **Editing `version` in `package.json` by hand.** Version bumps come from changesets when `pnpm run version-packages` runs. See _Release workflow_ in `CONTRIBUTING.md`. +- **Skipping local checks.** Run `pnpm run format`, `pnpm run lint`, and `pnpm run ci` before you call the task done. See _Development setup_ and _CI_ in `CONTRIBUTING.md`. + ## Creating New Codemods 1. **Plan first.** Create a plan and give the user a chance to tweak before implementation. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f91277d..72ce346 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,50 +1,194 @@ # Contributing -Thanks for helping users adopt the latest features with your codemods! +Thanks for helping users adopt the latest features with your codemods! -### Before You Open a PR +Using an AI coding agent (Codex, Cursor, Claude Code, Aider, etc.)? See [`AGENTS.md`](./AGENTS.md) for codemod authoring guidance and common mistakes to avoid. -- **Issue**: Check for an existing issue, or open one first. +## Development setup + +This repository uses **pnpm** (see `packageManager` in the root `package.json`), **Changesets** for releases, and **oxfmt + oxlint** (not Prettier/ESLint) for formatting and linting. + +```bash +# Install dependencies (also wires the Husky pre-commit hook) +pnpm install + +# Format all files +pnpm run format + +# Check formatting without writing +pnpm run format:check + +# Lint all files +pnpm run lint + +# Lint and auto-fix +pnpm run lint:fix + +# Run all codemod package tests +pnpm run test + +# Typecheck all codemod scripts (root tsconfig) +pnpm run check-types + +# Same checks as CI (tests + typecheck) +pnpm run ci + +# Verify URLs in tracked Markdown +pnpm run docs:links +``` + +Run one workspace package (the `pnpm --filter` value is the `name` field in that package's `package.json`): + +```bash +pnpm --filter test +``` + +For example: + +```bash +pnpm --filter sample-codemod test +``` + +Typechecking runs once from the repository root via `pnpm run check-types` (see root `tsconfig.json`). + +Use Node **22** locally (see [`.nvmrc`](./.nvmrc)) to match CI. + +## Pre-commit hook + +After `pnpm install`, Husky runs **lint-staged** before each commit: oxfmt and oxlint on staged files, plus targeted `pnpm test` when you touch `codemods/**/scripts/**/*.ts`. If something fails, fix or stage the updates and try again. + +The hook only inspects **staged** files. CI runs full-repo format, lint, and typecheck on every pull request, so unchanged files can still fail there even if the hook passed. + +## CI + +Three workflows support quality and releases: + +- **`ci.yaml` β€” Pull request checks:** runs on Ubuntu for PRs to `main`. Format, lint, and typecheck run across the full repo. Tests run only for codemod packages changed in the PR, or the full test suite when root tooling files change (for example `package.json`, `pnpm-lock.yaml`, or the workflow itself). +- **`release.yml` + `publish.yml` β€” Release automation:** see _Release workflow_ below. + +Match the local checks (`pnpm run format`, `pnpm run lint`, `pnpm run ci`) before you push. + +## Before you open a PR + +- **Issue**: Check for an existing issue, or open one first. - **Safety**: Codemods must be safe, predictable, and idempotent (running twice should not change code again). Avoid mixing patterns with different safety levels. - **Naming**: In `codemod.yaml`, the codemod name must start with `@`, where `` is this repo's GitHub org. -- **Tests**: Add multiple fixtures (positive and negative). -- **Docs**: Update the README for your codemod. +- **Tests**: Add multiple fixtures (positive and negative). +- **Docs**: Update the README for your codemod. -### Development +## Making changes -- Scaffold a new codemod: - ```bash - npx codemod@latest init - ``` -- Test your codemod locally: - ```bash - cd /path/to/sample/project - npx codemod workflow run -w /path/to/my-codemod/workflow.yaml - ``` +1. Create a branch from `main`. +2. Make your changes and add or update fixtures under `tests//`. +3. Run `pnpm run format`, `pnpm run lint`, and `pnpm run ci` to verify everything passes. +4. Add a changeset for every codemod package you touched (see below). +5. Open a pull request. -### Project Layout +## Adding a changeset -- Place all codemods in the `codemods/` directory. +This repo uses [Changesets](https://github.com/changesets/changesets) for versioning and releases. **Every PR that changes a codemod package under `codemods/` should include a changeset**, unless you use the `skip-changeset` label (see CI). Details live in [`.changeset/README.md`](./.changeset/README.md). -### Checks +```bash +pnpm changeset +``` -- Lint/format: npm run check (Biome) -- Types: npm run typecheck +Follow the prompts: -### Pull Requests +1. Select the affected codemod(s). +2. Choose the semver bump β€” **patch** for fixes, **minor** for new features, **major** for breaking changes. +3. Write a short summary. + +Commit the new Markdown file under `.changeset/` with your PR. + +`pnpm run version-packages` (run by automation on `main`, not usually by hand) runs `changeset version`, which bumps `version` in each affected `package.json`. Do not edit `version` in `package.json` by hand β€” automation owns that field. + +## Release workflow + +Releases are fully automated via `.github/workflows/release.yml` and `.github/workflows/publish.yml` on every push to `main`: + +1. Merge a PR that includes one or more changesets into `main`. +2. The `release` job detects the pending changesets and runs `pnpm run version-packages` (`changeset version`), which bumps `version` in each affected `package.json`. +3. The bot opens (or updates) a **Version Packages** pull request on branch `changeset-release/version-packages` β€” it does not push directly to `main`. +4. Merge that PR (required checks apply like any other PR). +5. On the next push to `main`, `scripts/tag-and-publish.sh` creates a `@v` git tag for every bumped package and pushes the tags. +6. The `publish` job fans out a parallel matrix over the changed directories and publishes each codemod via [`codemod/publish-action`](https://github.com/codemod/publish-action). + +For emergencies (re-publish a specific codemod without a full release cycle), use the **Publish Codemod (Manual)** workflow (`.github/workflows/publish.yml`) and supply the codemod slug. + +Do not hand-edit `version` in `package.json` to simulate a release β€” automation owns bumps. + +## Adding a new codemod + +Scaffold a new codemod with the CLI: + +```bash +npx codemod init +``` + +New packages live under `codemods/`, for example: + +``` +codemods// + scripts/codemod.ts # JSSG transform + tests/ # input / expected fixtures + codemod.yaml # manifest + workflow.yaml + package.json + README.md + SKILL.md +``` + +Conventions: + +- The codemod name in `codemod.yaml` **and** `package.json` must start with `@` (e.g. `@myorg/my-codemod`). +- Keep rewrites conservative. If a step requires a human decision, prefer a detector or recipe parameter over an unsafe transform. +- Use an existing sibling codemod as a template. See [docs/CODEMOD-TEMPLATE.md](./docs/CODEMOD-TEMPLATE.md) for the full package spec. + +Test your codemod locally against a sample project: + +```bash +cd /path/to/sample/project +npx codemod workflow run -w /path/to/my-codemod/workflow.yaml +``` + +## Package shape + +Each codemod package should include: + +- `package.json` with at least a `test` script. +- `codemod.yaml`, `workflow.yaml`, `README.md`, `SKILL.md` +- `scripts/codemod.ts` +- `tests//input.*` and `tests//expected.*` + +Keep transformations atomic and verifiable with fixtures. + +## Checks + +| Command | What it does | +| ----------------------- | ---------------------------------------------------- | +| `pnpm run format` | Auto-format with oxfmt | +| `pnpm run format:check` | Check formatting (no writes) | +| `pnpm run lint` | Lint with oxlint (type-aware) | +| `pnpm run lint:fix` | Lint and auto-fix with oxlint | +| `pnpm run test` | Run all codemod tests | +| `pnpm run check-types` | Typecheck all codemod scripts (root `tsconfig.json`) | +| `pnpm run ci` | Full check (test + typecheck) | +| `pnpm run docs:links` | Verify Markdown links | + +## Pull requests - Describe the codemod and its migration use case. -- Follow Conventional Commits: - -| Type | Usage | -|----------|--------------------------------------| -| feat | New codemod or capability | -| fix | Bugfix in a transform or test | -| docs | Documentation-only changes | -| refactor | Non-feature, non-bugfix code changes | -| test | Add or update fixtures/tests | -| chore | Tooling, CI, formatting, repo hygiene| - -### License +- Follow [Conventional Commits](https://www.conventionalcommits.org/): + +| Type | Usage | +| ---------- | ------------------------------------- | +| `feat` | New codemod or capability | +| `fix` | Bugfix in a transform or test | +| `docs` | Documentation-only changes | +| `refactor` | Non-feature, non-bugfix code changes | +| `test` | Add or update fixtures/tests | +| `chore` | Tooling, CI, formatting, repo hygiene | + +## License By contributing, you agree that your work will be licensed under the MIT License. diff --git a/README.md b/README.md index 3d0df42..bd305be 100644 --- a/README.md +++ b/README.md @@ -3,30 +3,35 @@ > Framework/SDK maintainers: This template includes setup guides, utilities, and a GitHub Action to help you and your community build and publish codemods with ease. > > ## One-time setup +> > NOTE: You need repo creation privileges in your org and permission to install GitHub apps on repos. If you don’t, you can request access from your org admin during setup. +> > 1. Use this template and create a codemods repo in your org. -> 2. Sign up at [Codemod](https://app.codemod.com) with your GitHub account. +> 2. Sign up at [Codemod](https://app.codemod.com) with your GitHub account. > 3. Install the Codemod GitHub app: > 1. Click your profile photo (top left) and select "Add organization" -> 2. Pick GitHub, choose your org and the new codemods repo. This installs the Codemod GitHub App and reserves a **scope** with your org name. -> - Benefit: Only members of your org can publish codemods with a name that starts with your scope. -> - **Important**: In `codemod.yaml`, the name must start with your scope, otherwise it won’t appear when users filter for your scope in the registry. +> 2. Pick GitHub, choose your org and the new codemods repo. This installs the Codemod GitHub App and reserves a **scope** with your org name. +> - Benefit: Only members of your org can publish codemods with a name that starts with your scope. +> - **Important**: In `codemod.yaml`, the name must start with your scope, otherwise it won’t appear when users filter for your scope in the registry. > 4. Configure [trusted publisher](https://docs.codemod.com) in Codemod (no API key needed). The publish workflow uses OIDC to authenticate. > -> βœ… Done! After a codemod PR is merged, you can trigger the GitHub Action to auto-publish it to the [Codemod Registry](https://app.codemod.com/registry) under your org scope. See [Node.js codemods](https://codemod.link/nodejs-official) for an example. -> -> To build a codemod, clone your new repo locally and use [Codemod MCP](https://docs.codemod.com/more-resources/codemod-mcp) in your IDE to replace the boilerplate with a codemod generated using AI. You can also run `npx codemod@latest init` for the initial scaffolding, or use [Codemod Studio](https://codemod.studio) for its live codemod runner and AST viewer. +> βœ… Done! After a codemod PR is merged, Changesets automation opens a **Version Packages** PR. Once merged, codemods are tagged and published to the [Codemod Registry](https://app.codemod.com/registry) under your org scope. See [Node.js codemods](https://codemod.link/nodejs-official) for an example. +> +> To build a codemod, clone your new repo locally and use [Codemod MCP](https://docs.codemod.com/model-context-protocol) in your IDE to replace the boilerplate with a codemod generated using AI. You can also run `npx codemod@latest init` for the initial scaffolding, or use [Codemod Studio](https://codemod.studio) for its live codemod runner and AST viewer. --- + Official codemods to help users adopt new features and handle breaking changes with ease. Community contributions are welcome and appreciated! Check open issues for codemods to build, or open a new one if something’s missing. See the [contribution guide](./CONTRIBUTING.md) for details. ## Running codemods + > [!CAUTION] > Codemods modify code! Run them only on Git-tracked files, and commit or stash changes first. ### From the registry + Recommended for the best UX. This downloads the package from the [Registry](https://app.codemod.com/registry). ```bash diff --git a/biome.jsonc b/biome.jsonc deleted file mode 100644 index e73efab..0000000 --- a/biome.jsonc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "files": { "includes": ["**", "!**/package-lock.json", "!codemods/**/tests"] }, - "formatter": { "enabled": true, "lineWidth": 100 }, - "linter": { "enabled": true }, - "javascript": { "formatter": { "quoteStyle": "double" } } -} diff --git a/codemods/sample-codemod/README.md b/codemods/sample-codemod/README.md index 62fd622..b401b89 100644 --- a/codemods/sample-codemod/README.md +++ b/codemods/sample-codemod/README.md @@ -28,15 +28,15 @@ npx codemod jssg run -l tsx ./scripts/codemod.ts /path/to/project ## Params -| Param | Type | Default | Description | -|------------------|---------|---------|--------------------------------------------------| -| commit_per_step | boolean | false | Commit after each change-producing step | -| run_ai_step | boolean | false | Run AI step for closure-dependent tricky cases | -| publish_pr | boolean | false | Push branch and create PR (off by default) | -| main_branch | string | main | Target branch for PR | -| api_token | string | β€” | GitHub API token when gh CLI unavailable | -| pr_title | string | β€” | Optional PR title | -| pr_body | string | β€” | Optional PR body | +| Param | Type | Default | Description | +| --------------- | ------- | ------- | ---------------------------------------------- | +| commit_per_step | boolean | false | Commit after each change-producing step | +| run_ai_step | boolean | false | Run AI step for closure-dependent tricky cases | +| publish_pr | boolean | false | Push branch and create PR (off by default) | +| main_branch | string | main | Target branch for PR | +| api_token | string | β€” | GitHub API token when gh CLI unavailable | +| pr_title | string | β€” | Optional PR title | +| pr_body | string | β€” | Optional PR body | ## Metrics diff --git a/codemods/sample-codemod/SKILL.md b/codemods/sample-codemod/SKILL.md index 4d931bb..915ae1e 100644 --- a/codemods/sample-codemod/SKILL.md +++ b/codemods/sample-codemod/SKILL.md @@ -1,19 +1,19 @@ --- -schema_version: "1.0" -name: "sample-codemod" -version: "0.1.0" -description: "Move React components defined inside other components to module scope to prevent state resets and re-render bugs" -author: "Codemod Authors" -license: "MIT" -workflow: "workflow.yaml" -category: "migration" -repository: "https://github.com/codemod/migrations-template/tree/main/codemods/sample-codemod" +schema_version: '1.0' +name: 'sample-codemod' +version: '0.1.0' +description: 'Move React components defined inside other components to module scope to prevent state resets and re-render bugs' +author: 'Codemod Authors' +license: 'MIT' +workflow: 'workflow.yaml' +category: 'migration' +repository: 'https://github.com/codemod/migrations-template/tree/main/codemods/sample-codemod' targets: - languages: ["typescript"] -keywords: ["react", "upgrade", "standardization", "performance"] + languages: ['typescript'] +keywords: ['react', 'upgrade', 'standardization', 'performance'] registry: - access: "public" - visibility: "public" + access: 'public' + visibility: 'public' --- # Operational Instructions for sample-codemod @@ -34,8 +34,8 @@ Run nodes in this topological order. Steps inside each node run sequentially. 1. Execute each node in topological order. 2. **After each change-producing step:** Run validation before proceeding. - - `npm test` β€” jssg tests - - `npm run check-types` β€” typecheck (or equivalent) + - `pnpm test` β€” jssg tests + - `pnpm run check-types` β€” typecheck - `npx codemod workflow validate workflow.yaml` β€” workflow schema 3. Review diffs. Proceed only when validation passes and changes look correct. 4. If validation fails or diffs are wrong, fix and retry before continuing. diff --git a/codemods/sample-codemod/codemod.yaml b/codemods/sample-codemod/codemod.yaml index cdb01e9..42d59ee 100644 --- a/codemods/sample-codemod/codemod.yaml +++ b/codemods/sample-codemod/codemod.yaml @@ -1,18 +1,18 @@ -schema_version: "1.0" +schema_version: '1.0' -name: "sample-codemod" -version: "0.1.0" -description: "Move React components defined inside other components to module scope to prevent state resets and re-render bugs" -author: "Codemod Authors" -license: "MIT" -workflow: "workflow.yaml" -category: "migration" -repository: "https://github.com/codemod/migrations-template/tree/main/codemods/sample-codemod" +name: 'sample-codemod' +version: '0.1.0' +description: 'Move React components defined inside other components to module scope to prevent state resets and re-render bugs' +author: 'Codemod Authors' +license: 'MIT' +workflow: 'workflow.yaml' +category: 'migration' +repository: 'https://github.com/codemod/migrations-template/tree/main/codemods/sample-codemod' targets: - languages: ["typescript"] + languages: ['typescript'] -keywords: ["react", "upgrade", "standardization", "performance"] +keywords: ['react', 'upgrade', 'standardization', 'performance'] registry: - access: "public" - visibility: "public" + access: 'public' + visibility: 'public' diff --git a/codemods/sample-codemod/package.json b/codemods/sample-codemod/package.json index f808e3a..a9abdda 100644 --- a/codemods/sample-codemod/package.json +++ b/codemods/sample-codemod/package.json @@ -1,12 +1,12 @@ { - "name": "sample-codemod", - "version": "0.1.0", - "description": "Move React components defined inside other components to module scope", - "type": "module", - "scripts": { - "test": "npx codemod@latest jssg test -l tsx ./scripts/codemod.ts" - }, - "devDependencies": { - "@jssg/utils": "latest" - } + "name": "sample-codemod", + "version": "0.1.0", + "description": "Move React components defined inside other components to module scope", + "type": "module", + "scripts": { + "test": "codemod jssg test -l tsx ./scripts/codemod.ts" + }, + "devDependencies": { + "@codemod.com/jssg-types": "catalog:" + } } diff --git a/codemods/sample-codemod/scripts/codemod.ts b/codemods/sample-codemod/scripts/codemod.ts index 06de57b..9e2c6f0 100644 --- a/codemods/sample-codemod/scripts/codemod.ts +++ b/codemods/sample-codemod/scripts/codemod.ts @@ -1,442 +1,438 @@ -import type { Edit, SgNode, SgRoot } from "codemod:ast-grep"; -import type TSX from "codemod:ast-grep/langs/tsx"; -import { useMetricAtom } from "codemod:metrics"; -import path from "node:path"; +import path from 'node:path' + +import type { Edit, SgNode, SgRoot } from 'codemod:ast-grep' +import type TSX from 'codemod:ast-grep/langs/tsx' +import { useMetricAtom } from 'codemod:metrics' // Cardinalities: // change-type: "hoisted" | "skipped-closure" // component-form: "arrow" | "function-decl" -const hoistedMetric = useMetricAtom("react-hoist-nested-components"); +const hoistedMetric = useMetricAtom('react-hoist-nested-components') function getMetricFilePath(filename: string): string { - const relativePath = path.isAbsolute(filename) - ? path.relative(process.cwd(), filename) - : filename; - return relativePath.split(path.sep).join("/"); + const posix = filename.replaceAll('\\', '/') + const stripped = posix.replace(/^\/\/\?\//, '').replace(/^\/\/\?\\/, '') + + if (stripped.startsWith('tests/')) { + return stripped + } + + const testsIndex = stripped.lastIndexOf('/tests/') + if (testsIndex !== -1) { + return stripped.slice(testsIndex + 1) + } + + const normalized = path.normalize(stripped) + const relativePath = path.isAbsolute(normalized) ? path.relative(process.cwd(), normalized) : normalized + return relativePath.split(path.sep).join('/') } /** Intrinsic HTML/SVG elements - not variable references */ const INTRINSIC_ELEMENTS = new Set([ - "a", - "abbr", - "address", - "area", - "article", - "aside", - "audio", - "b", - "base", - "bdi", - "bdo", - "blockquote", - "body", - "br", - "button", - "canvas", - "caption", - "cite", - "code", - "col", - "colgroup", - "data", - "datalist", - "dd", - "del", - "details", - "dfn", - "dialog", - "div", - "dl", - "dt", - "em", - "embed", - "fieldset", - "figcaption", - "figure", - "footer", - "form", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "head", - "header", - "hgroup", - "hr", - "html", - "i", - "iframe", - "img", - "input", - "ins", - "kbd", - "label", - "legend", - "li", - "link", - "main", - "map", - "mark", - "menu", - "meta", - "meter", - "nav", - "noscript", - "object", - "ol", - "optgroup", - "option", - "output", - "p", - "param", - "picture", - "pre", - "progress", - "q", - "rp", - "rt", - "ruby", - "s", - "samp", - "section", - "select", - "slot", - "small", - "source", - "span", - "strong", - "style", - "sub", - "summary", - "sup", - "table", - "tbody", - "td", - "template", - "textarea", - "tfoot", - "th", - "thead", - "time", - "title", - "tr", - "track", - "u", - "ul", - "var", - "video", - "wbr", - "svg", - "path", - "circle", - "rect", - "line", - "ellipse", - "polyline", - "polygon", - "g", - "defs", - "use", - "stop", - "linearGradient", - "radialGradient", -]); + 'a', + 'abbr', + 'address', + 'area', + 'article', + 'aside', + 'audio', + 'b', + 'base', + 'bdi', + 'bdo', + 'blockquote', + 'body', + 'br', + 'button', + 'canvas', + 'caption', + 'cite', + 'code', + 'col', + 'colgroup', + 'data', + 'datalist', + 'dd', + 'del', + 'details', + 'dfn', + 'dialog', + 'div', + 'dl', + 'dt', + 'em', + 'embed', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'header', + 'hgroup', + 'hr', + 'html', + 'i', + 'iframe', + 'img', + 'input', + 'ins', + 'kbd', + 'label', + 'legend', + 'li', + 'link', + 'main', + 'map', + 'mark', + 'menu', + 'meta', + 'meter', + 'nav', + 'noscript', + 'object', + 'ol', + 'optgroup', + 'option', + 'output', + 'p', + 'param', + 'picture', + 'pre', + 'progress', + 'q', + 'rp', + 'rt', + 'ruby', + 's', + 'samp', + 'section', + 'select', + 'slot', + 'small', + 'source', + 'span', + 'strong', + 'style', + 'sub', + 'summary', + 'sup', + 'table', + 'tbody', + 'td', + 'template', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'time', + 'title', + 'tr', + 'track', + 'u', + 'ul', + 'var', + 'video', + 'wbr', + 'svg', + 'path', + 'circle', + 'rect', + 'line', + 'ellipse', + 'polyline', + 'polygon', + 'g', + 'defs', + 'use', + 'stop', + 'linearGradient', + 'radialGradient', +]) function isReactComponent(node: SgNode): boolean { - if (node.is("function_declaration")) { - return ( - node.has({ rule: { kind: "jsx_element" } }) || - node.has({ rule: { kind: "jsx_self_closing_element" } }) - ); - } - if (node.is("arrow_function") || node.is("function")) { - return ( - node.has({ rule: { kind: "jsx_element" } }) || - node.has({ rule: { kind: "jsx_self_closing_element" } }) - ); - } - return false; + if (node.is('function_declaration')) { + return node.has({ rule: { kind: 'jsx_element' } }) || node.has({ rule: { kind: 'jsx_self_closing_element' } }) + } + if (node.is('arrow_function') || node.is('function')) { + return node.has({ rule: { kind: 'jsx_element' } }) || node.has({ rule: { kind: 'jsx_self_closing_element' } }) + } + return false } function getComponentNode(declOrStmt: SgNode): SgNode | null { - if (declOrStmt.is("function_declaration") && isReactComponent(declOrStmt)) { - return declOrStmt; - } - if (declOrStmt.is("lexical_declaration") || declOrStmt.is("variable_declaration")) { - const declarators = declOrStmt.findAll({ - rule: { kind: "variable_declarator" }, - }); - for (const d of declarators) { - const init = d.field("value"); - if (init && (init.is("arrow_function") || init.is("function")) && isReactComponent(init)) { - return init; - } - } - } - return null; + if (declOrStmt.is('function_declaration') && isReactComponent(declOrStmt)) { + return declOrStmt + } + if (declOrStmt.is('lexical_declaration') || declOrStmt.is('variable_declaration')) { + const declarators = declOrStmt.findAll({ + rule: { kind: 'variable_declarator' }, + }) + for (const d of declarators) { + const init = d.field('value') + if (init && (init.is('arrow_function') || init.is('function')) && isReactComponent(init)) { + return init + } + } + } + return null } function getComponentName(declOrStmt: SgNode): string | null { - if (declOrStmt.is("function_declaration")) { - const name = declOrStmt.field("name"); - return name?.text() ?? null; - } - if (declOrStmt.is("lexical_declaration") || declOrStmt.is("variable_declaration")) { - const declarators = declOrStmt.findAll({ - rule: { - kind: "variable_declarator", - has: { kind: "identifier" }, - }, - }); - for (const d of declarators) { - const init = d.field("value"); - if (init && (init.is("arrow_function") || init.is("function")) && isReactComponent(init)) { - const name = d.field("name"); - if (name?.is("identifier")) return name.text(); - } - } - } - return null; + if (declOrStmt.is('function_declaration')) { + const name = declOrStmt.field('name') + return name?.text() ?? null + } + if (declOrStmt.is('lexical_declaration') || declOrStmt.is('variable_declaration')) { + const declarators = declOrStmt.findAll({ + rule: { + kind: 'variable_declarator', + has: { kind: 'identifier' }, + }, + }) + for (const d of declarators) { + const init = d.field('value') + if (init && (init.is('arrow_function') || init.is('function')) && isReactComponent(init)) { + const name = d.field('name') + if (name?.is('identifier')) return name.text() + } + } + } + return null } /** Returns the statement or declaration node that defines the nested component (for removal) */ function getEnclosingStatement(innerComponent: SgNode): SgNode { - let node: SgNode | null = innerComponent; - while (node) { - if (node.is("statement_block") || node.is("program")) break; - if ( - node.is("function_declaration") || - node.is("lexical_declaration") || - node.is("variable_declaration") - ) { - return node; - } - node = node.parent(); - } - return innerComponent; + let node: SgNode | null = innerComponent + while (node) { + if (node.is('statement_block') || node.is('program')) break + if (node.is('function_declaration') || node.is('lexical_declaration') || node.is('variable_declaration')) { + return node + } + node = node.parent() + } + return innerComponent } /** Collect names declared in a scope (params, variable declarators) */ function getOuterScopeBindings(outerComponent: SgNode): Set { - const bindings = new Set(); - // Params - const params = outerComponent.field("parameters"); - if (params) { - for (const p of params.findAll({ rule: { kind: "identifier" } })) { - bindings.add(p.text()); - } - } - // Local declarations in outer body - const body = outerComponent.field("body"); - if (body) { - for (const decl of body.findAll({ - rule: { - any: [{ kind: "variable_declarator" }, { kind: "function_declaration" }], - }, - })) { - if (decl.is("variable_declarator")) { - const name = decl.field("name"); - if (name?.is("identifier")) bindings.add(name.text()); - // array_pattern [a, b] - if (name?.is("array_pattern")) { - for (const id of name.findAll({ rule: { kind: "identifier" } })) { - bindings.add(id.text()); - } - } - } - if (decl.is("function_declaration")) { - const name = decl.field("name"); - if (name?.is("identifier")) bindings.add(name.text()); - } - } - } - return bindings; + const bindings = new Set() + // Params + const params = outerComponent.field('parameters') + if (params) { + for (const p of params.findAll({ rule: { kind: 'identifier' } })) { + bindings.add(p.text()) + } + } + // Local declarations in outer body + const body = outerComponent.field('body') + if (body) { + for (const decl of body.findAll({ + rule: { + any: [{ kind: 'variable_declarator' }, { kind: 'function_declaration' }], + }, + })) { + if (decl.is('variable_declarator')) { + const name = decl.field('name') + if (name?.is('identifier')) bindings.add(name.text()) + // array_pattern [a, b] + if (name?.is('array_pattern')) { + for (const id of name.findAll({ rule: { kind: 'identifier' } })) { + bindings.add(id.text()) + } + } + } + if (decl.is('function_declaration')) { + const name = decl.field('name') + if (name?.is('identifier')) bindings.add(name.text()) + } + } + } + return bindings } /** Check if node A is a descendant of (or equal to) node B */ function isInside(node: SgNode, ancestor: SgNode): boolean { - let current: SgNode | null = node; - while (current) { - if (current.id() === ancestor.id()) return true; - current = current.parent(); - } - return false; + let current: SgNode | null = node + while (current) { + if (current.id() === ancestor.id()) return true + current = current.parent() + } + return false } /** Collect identifier nodes in inner component that could reference outer scope (excluding params, intrinsics, property names) */ function getInnerReferencedIdentifierNodes(innerComponent: SgNode): SgNode[] { - const innerParams = new Set(); - const params = innerComponent.field("parameters"); - if (params) { - for (const p of params.findAll({ rule: { kind: "identifier" } })) { - innerParams.add(p.text()); - } - } - const nodes: SgNode[] = []; - for (const id of innerComponent.findAll({ rule: { kind: "identifier" } })) { - const name = id.text(); - if (innerParams.has(name)) continue; - if (INTRINSIC_ELEMENTS.has(name)) continue; - const parent = id.parent(); - if (parent?.is("member_expression") && parent.field("property")?.id() === id.id()) continue; - nodes.push(id); - } - return nodes; + const innerParams = new Set() + const params = innerComponent.field('parameters') + if (params) { + for (const p of params.findAll({ rule: { kind: 'identifier' } })) { + innerParams.add(p.text()) + } + } + const nodes: SgNode[] = [] + for (const id of innerComponent.findAll({ rule: { kind: 'identifier' } })) { + const name = id.text() + if (innerParams.has(name)) continue + if (INTRINSIC_ELEMENTS.has(name)) continue + const parent = id.parent() + if (parent?.is('member_expression') && parent.field('property')?.id() === id.id()) continue + nodes.push(id) + } + return nodes } /** Check if inner component references outer scope (closure-dependent) using semantic analysis */ -function hasClosureDependency( - innerComponent: SgNode, - outerComponent: SgNode, - innerName: string, -): boolean { - const outerBindings = getOuterScopeBindings(outerComponent); - outerBindings.delete(innerName); // Self-reference is safe to hoist - const identifierNodes = getInnerReferencedIdentifierNodes(innerComponent); - for (const idNode of identifierNodes) { - const name = idNode.text(); - const def = idNode.definition(); - if (def === null) { - // Semantic analysis couldn't resolve (e.g. globals) β€” conservative fallback to name check - if (outerBindings.has(name)) return true; - continue; - } - if (def.kind === "import" || def.kind === "external") { - // Defined elsewhere β€” not a closure dependency on outer scope - continue; - } - // def.kind === "local" β€” check if definition is in outer scope (inside outer, outside inner) - if (isInside(def.node, outerComponent) && !isInside(def.node, innerComponent)) return true; - } - return false; +function hasClosureDependency(innerComponent: SgNode, outerComponent: SgNode, innerName: string): boolean { + const outerBindings = getOuterScopeBindings(outerComponent) + outerBindings.delete(innerName) // Self-reference is safe to hoist + const identifierNodes = getInnerReferencedIdentifierNodes(innerComponent) + for (const idNode of identifierNodes) { + const name = idNode.text() + const def = idNode.definition() + if (def === null) { + // Semantic analysis couldn't resolve (e.g. globals) β€” conservative fallback to name check + if (outerBindings.has(name)) return true + continue + } + if (def.kind === 'import' || def.kind === 'external') { + // Defined elsewhere β€” not a closure dependency on outer scope + continue + } + // def.kind === "local" β€” check if definition is in outer scope (inside outer, outside inner) + if (isInside(def.node, outerComponent) && !isInside(def.node, innerComponent)) return true + } + return false } function findOuterComponent(innerStatement: SgNode): SgNode | null { - let node: SgNode | null = innerStatement.parent(); - while (node) { - if (node.is("program")) return null; - if (node.is("function_declaration") && isReactComponent(node)) return node; - if (node.is("arrow_function") || node.is("function")) { - if (isReactComponent(node)) return node; - } - node = node.parent(); - } - return null; + let node: SgNode | null = innerStatement.parent() + while (node) { + if (node.is('program')) return null + if (node.is('function_declaration') && isReactComponent(node)) return node + if (node.is('arrow_function') || node.is('function')) { + if (isReactComponent(node)) return node + } + node = node.parent() + } + return null } async function transform(root: SgRoot): Promise { - const rootNode = root.root(); - const program = rootNode; - const edits: Edit[] = []; - const metricFile = getMetricFilePath(root.filename()); - - // Find nested components: function decl or const/let = arrow/function inside another component - const functionDecls = program.findAll({ - rule: { - kind: "function_declaration", - inside: { kind: "statement_block" }, - }, - }); - - const lexicalDecls = program.findAll({ - rule: { - kind: "lexical_declaration", - inside: { kind: "statement_block" }, - }, - }); - - const candidates: Array<{ node: SgNode; outer: SgNode }> = []; - - for (const decl of functionDecls) { - if (!isReactComponent(decl)) continue; - const outer = findOuterComponent(decl); - if (outer) candidates.push({ node: decl, outer }); - } - - for (const decl of lexicalDecls) { - const component = getComponentNode(decl); - if (!component) continue; - const outer = findOuterComponent(decl); - if (outer) candidates.push({ node: decl, outer }); - } - - // Process innermost first (by position end, descending) so edits don't shift - candidates.sort((a, b) => { - const aEnd = a.node.range().end.index; - const bEnd = b.node.range().end.index; - return bEnd - aEnd; - }); - - for (const { node: declOrStmt, outer } of candidates) { - const component = declOrStmt.is("function_declaration") - ? declOrStmt - : getComponentNode(declOrStmt); - if (!component) continue; - - const name = getComponentName(declOrStmt); - if (!name) continue; - - const closureDependent = hasClosureDependency(component, outer, name); - - if (closureDependent) { - hoistedMetric.increment({ "change-type": "skipped-closure", file: metricFile }); - // Add a flag comment above the nested component (preserve indentation) - const stmt = getEnclosingStatement(component); - const start = stmt.range().start.index; - const before = rootNode.text().slice(0, start); - const lineStart = before.lastIndexOf("\n") + 1; - const indent = rootNode.text().slice(lineStart, start).match(/^\s*/)?.[0] ?? ""; - edits.push({ - startPos: lineStart, - endPos: lineStart, - insertedText: `${indent}// codemod: closure-dependent β€” hoist manually and pass outer vars as props\n`, - }); - continue; - } - - // Safe to hoist - insert before the outer component's declaration - const stmt = getEnclosingStatement(component); - let componentText = stmt.text(); - // Strip one level of leading indent (component was nested inside another) - componentText = componentText.replace(/^\s+/, ""); - if (stmt.is("function_declaration")) { - // Reduce body indent by one tab/level for each line - componentText = componentText.replace(/\n(\t)/g, "\n"); - } - const outerStmt = getEnclosingStatement(outer); - const outerStart = outerStmt.range().start.index; - // function_declaration needs no trailing semicolon; const/let already has one - const textToInsert = stmt.is("function_declaration") - ? `${componentText}\n\n` - : componentText.endsWith(";") - ? `${componentText}\n\n` - : `${componentText};\n\n`; - - edits.push({ - startPos: outerStart, - endPos: outerStart, - insertedText: textToInsert, - }); - // Remove statement and the newline after it (up to but not including next statement's indent) - const stmtRange = stmt.range(); - const nextStmt = stmt.next(); - const removalEnd = nextStmt ? nextStmt.range().start.index : stmtRange.end.index; - edits.push({ - startPos: stmtRange.start.index, - endPos: removalEnd, - insertedText: "", - }); - - const form = declOrStmt.is("function_declaration") ? "function-decl" : "arrow"; - hoistedMetric.increment({ "change-type": "hoisted", "component-form": form, file: metricFile }); - } - - if (edits.length === 0) return null; - - return rootNode.commitEdits(edits); + const rootNode = root.root() + const program = rootNode + const edits: Edit[] = [] + const metricFile = getMetricFilePath(root.filename()) + + // Find nested components: function decl or const/let = arrow/function inside another component + const functionDecls = program.findAll({ + rule: { + kind: 'function_declaration', + inside: { kind: 'statement_block' }, + }, + }) + + const lexicalDecls = program.findAll({ + rule: { + kind: 'lexical_declaration', + inside: { kind: 'statement_block' }, + }, + }) + + const candidates: Array<{ node: SgNode; outer: SgNode }> = [] + + for (const decl of functionDecls) { + if (!isReactComponent(decl)) continue + const outer = findOuterComponent(decl) + if (outer) candidates.push({ node: decl, outer }) + } + + for (const decl of lexicalDecls) { + const component = getComponentNode(decl) + if (!component) continue + const outer = findOuterComponent(decl) + if (outer) candidates.push({ node: decl, outer }) + } + + // Process innermost first (by position end, descending) so edits don't shift + candidates.sort((a, b) => { + const aEnd = a.node.range().end.index + const bEnd = b.node.range().end.index + return bEnd - aEnd + }) + + for (const { node: declOrStmt, outer } of candidates) { + const component = declOrStmt.is('function_declaration') ? declOrStmt : getComponentNode(declOrStmt) + if (!component) continue + + const name = getComponentName(declOrStmt) + if (!name) continue + + const closureDependent = hasClosureDependency(component, outer, name) + + if (closureDependent) { + hoistedMetric.increment({ 'change-type': 'skipped-closure', file: metricFile }) + // Add a flag comment above the nested component (preserve indentation) + const stmt = getEnclosingStatement(component) + const start = stmt.range().start.index + const before = rootNode.text().slice(0, start) + const lineStart = before.lastIndexOf('\n') + 1 + const indent = rootNode.text().slice(lineStart, start).match(/^\s*/)?.[0] ?? '' + edits.push({ + startPos: lineStart, + endPos: lineStart, + insertedText: `${indent}// codemod: closure-dependent β€” hoist manually and pass outer vars as props\n`, + }) + continue + } + + // Safe to hoist - insert before the outer component's declaration + const stmt = getEnclosingStatement(component) + let componentText = stmt.text() + // Strip one level of leading indent (component was nested inside another) + componentText = componentText.replace(/^\s+/, '') + if (stmt.is('function_declaration')) { + // Reduce body indent by one tab/level for each line + componentText = componentText.replaceAll(/\n(\t)/g, '\n') + } + const outerStmt = getEnclosingStatement(outer) + const outerStart = outerStmt.range().start.index + // function_declaration needs no trailing semicolon; const/let already has one + const textToInsert = stmt.is('function_declaration') + ? `${componentText}\n\n` + : componentText.endsWith(';') + ? `${componentText}\n\n` + : `${componentText};\n\n` + + edits.push({ + startPos: outerStart, + endPos: outerStart, + insertedText: textToInsert, + }) + // Remove statement and the newline after it (up to but not including next statement's indent) + const stmtRange = stmt.range() + const nextStmt = stmt.next() + const removalEnd = nextStmt ? nextStmt.range().start.index : stmtRange.end.index + edits.push({ + startPos: stmtRange.start.index, + endPos: removalEnd, + insertedText: '', + }) + + const form = declOrStmt.is('function_declaration') ? 'function-decl' : 'arrow' + hoistedMetric.increment({ 'change-type': 'hoisted', 'component-form': form, file: metricFile }) + } + + if (edits.length === 0) return null + + return rootNode.commitEdits(edits) } -export default transform; +export default transform diff --git a/codemods/sample-codemod/workflow.yaml b/codemods/sample-codemod/workflow.yaml index 1daccfb..a2e166a 100644 --- a/codemods/sample-codemod/workflow.yaml +++ b/codemods/sample-codemod/workflow.yaml @@ -1,6 +1,6 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/codemod/codemod/refs/heads/main/schemas/workflow.json -version: "1" +version: '1' params: schema: @@ -18,7 +18,7 @@ params: description: Push branch and create PR (off by default) main_branch: type: string - default: "main" + default: 'main' description: Target branch for PR api_token: type: string @@ -37,21 +37,21 @@ nodes: name: Apply AST Transformations type: automatic steps: - - name: "Hoist nested React components" + - name: 'Hoist nested React components' js-ast-grep: js_file: scripts/codemod.ts - language: "tsx" + language: 'tsx' semantic_analysis: workspace include: - - "**/*.tsx" - - "**/*.jsx" + - '**/*.tsx' + - '**/*.jsx' exclude: - - "**/node_modules/**" - - "**/vendor/**" - - "**/*.test.*" - - "**/*.spec.*" - - "**/__pycache__/**" - - name: "Commit AST transformations" + - '**/node_modules/**' + - '**/vendor/**' + - '**/*.test.*' + - '**/*.spec.*' + - '**/__pycache__/**' + - name: 'Commit AST transformations' run: | git add -A [ -n "$(git status --porcelain)" ] && git commit -m "fix: hoist nested React components to module scope" || true @@ -62,7 +62,7 @@ nodes: type: automatic depends_on: [apply-transforms] steps: - - name: "AI step for closure-dependent cases" + - name: 'AI step for closure-dependent cases' if: params.run_ai_step ai: prompt: | @@ -76,7 +76,7 @@ nodes: 3. Preserve behavior and formatting. Constraints: Add TODO if unsure; keep existing logic intact. - - name: "Commit AI fixes" + - name: 'Commit AI fixes' run: | git add -A [ -n "$(git status --porcelain)" ] && git commit -m "fix: AI-assisted resolution of closure-dependent nested components" || true @@ -87,12 +87,12 @@ nodes: type: automatic depends_on: [ai-tricky-cases] steps: - - name: "Push branch to remote" + - name: 'Push branch to remote' run: | BRANCH=$(git branch --show-current) git push -u origin "$BRANCH" if: params.publish_pr - - name: "Create PR to main branch" + - name: 'Create PR to main branch' run: | BRANCH=$(git branch --show-current) BASE="${PARAM_MAIN_BRANCH:-main}" diff --git a/docs/CODEMOD-TEMPLATE.md b/docs/CODEMOD-TEMPLATE.md index 34e6633..78f19b0 100644 --- a/docs/CODEMOD-TEMPLATE.md +++ b/docs/CODEMOD-TEMPLATE.md @@ -35,19 +35,19 @@ Use `@jssg/utils` for common transformations such as import manipulations. ## 2. codemod.yaml ```yaml -schema_version: "1.0" +schema_version: '1.0' -name: "my-awesome-codemod" -version: "0.1.0" -description: "Transform legacy patterns to modern syntax" -author: "Your Name " -license: "MIT" -workflow: "workflow.yaml" -category: "migration" -repository: "" +name: 'my-awesome-codemod' +version: '0.1.0' +description: 'Transform legacy patterns to modern syntax' +author: 'Your Name ' +license: 'MIT' +workflow: 'workflow.yaml' +category: 'migration' +repository: '' targets: - languages: ["typescript"] + languages: ['typescript'] # Transformation type keywords/tags β€” pick one primary per codemod (avoid mixing breaking-change and feature-adoption): # upgrade - upgrade code to newer versions (encompasses breaking changes and feature adoption) @@ -59,37 +59,38 @@ targets: # a11y - accessibility improvements and compliance # standardization - unify patterns, conventions, or APIs across a codebase # code-mining - detect-only: identify, flag, or extract patterns without transforming -keywords: ["upgrade", "breaking-change", "react", "v17-to-v18"] +keywords: ['upgrade', 'breaking-change', 'react', 'v17-to-v18'] registry: - access: "public" - visibility: "public" + access: 'public' + visibility: 'public' ``` --- ## 3. workflow.yaml -> **IMPORTANT (agents):** New codemods MUST implement the full node structure below: `apply-transforms` (js-ast-grep and optional Commit steps), `ai-tricky-cases` (when the migration has tricky cases), and `publish`. Include the `params` block. Codemods run on the current branch. +> **IMPORTANT (agents):** New codemods MUST implement the full node structure below: `apply-transforms` (js-ast-grep and optional Commit steps), `ai-tricky-cases` (when the migration has tricky cases), and `publish`. Include the `params` block. Codemods run on the current branch. **Schema comment (first line):** + ```yaml # yaml-language-server: $schema=https://raw.githubusercontent.com/codemod/codemod/refs/heads/main/schemas/workflow.json ``` **Params schema** β€” each param must have `name`, `description`, `type`, and `default` where applicable: -| Param | Type | Default | Notes | -|-------|------|---------|-------| -| commit_per_step | boolean | **false** | Commit after each change-producing step with a meaningful message | -| run_ai_step | boolean | false | Run AI step for tricky cases β€” only add if AI step is needed | -| publish_pr | boolean | false | Push branch and create PR (when true; agents must not push by default) | -| main_branch | string | "main" | Target branch for PR | -| api_token | string | β€” | secret: true, for GitHub API when gh CLI unavailable | -| pr_title | string | β€” | Optional | -| pr_body | string | β€” | Optional, multi_line: true | +| Param | Type | Default | Notes | +| --------------- | ------- | --------- | ---------------------------------------------------------------------- | +| commit_per_step | boolean | **false** | Commit after each change-producing step with a meaningful message | +| run_ai_step | boolean | false | Run AI step for tricky cases β€” only add if AI step is needed | +| publish_pr | boolean | false | Push branch and create PR (when true; agents must not push by default) | +| main_branch | string | "main" | Target branch for PR | +| api_token | string | β€” | secret: true, for GitHub API when gh CLI unavailable | +| pr_title | string | β€” | Optional | +| pr_body | string | β€” | Optional, multi_line: true | -**Node structure:** Implement all three nodes below at minimum. Omit only the AI *step* (inside ai-tricky-cases) when all patterns can be handled by the AST codemod; keep the ai-tricky-cases node with `depends_on`. Include all steps (Commit, Push, Create PR) β€” they are gated by params. **Do not push to remote by default** β€” the Push step must use `if: params.publish_pr`. **Commit steps** use `if: params.commit_per_step` and must have task-specific, meaningful messages (e.g. `fix: migrate X to Y`, `fix: AI-assisted resolution of [case]`). If the migration can be divided into multiple shippable PRs, add a dedicated node for each part. +**Node structure:** Implement all three nodes below at minimum. Omit only the AI _step_ (inside ai-tricky-cases) when all patterns can be handled by the AST codemod; keep the ai-tricky-cases node with `depends_on`. Include all steps (Commit, Push, Create PR) β€” they are gated by params. **Do not push to remote by default** β€” the Push step must use `if: params.publish_pr`. **Commit steps** use `if: params.commit_per_step` and must have task-specific, meaningful messages (e.g. `fix: migrate X to Y`, `fix: AI-assisted resolution of [case]`). If the migration can be divided into multiple shippable PRs, add a dedicated node for each part. ### 1. apply-transforms (type: automatic) @@ -165,19 +166,45 @@ registry: "description": "[one-line description]", "type": "module", "scripts": { - "test": "npx codemod@latest jssg test -l ./scripts/codemod.ts", - "check-types": "tsc --noEmit" + "test": "codemod jssg test -l ./scripts/codemod.ts" }, "devDependencies": { - "@codemod.com/jssg-types": "latest", - "@jssg/utils": "latest", - "typescript": "latest" + "@codemod.com/jssg-types": "catalog:" } } ``` Use `-l ` with ast-grep alias: `tsx`, `typescript`, `python`, `go`, `rust`, `java`, etc. +## 5b. Typechecking (root tsconfig.json) + +Do **not** add a `tsconfig.json` per codemod package. The monorepo uses a single root `tsconfig.json` that typechecks all codemod scripts: + +```json +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["ESNext"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "types": ["@codemod.com/jssg-types", "node"], + "allowImportingTsExtensions": true, + "noEmit": true, + "skipLibCheck": true, + "strict": true, + "strictNullChecks": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "esModuleInterop": true + }, + "include": ["codemods/**/scripts/**/*.ts", "codemods/**/utils/**/*.ts"], + "exclude": ["**/node_modules", "**/tests", "**/tests-*"] +} +``` + +Run `pnpm run check-types` from the repository root after adding or changing a codemod script. + --- ## 6. README.md @@ -209,7 +236,7 @@ Create `SKILL.md` at the package root to make the codemod Agent Skill compatible - **Read the workflow graph:** Parse `workflow.yaml` nodes and `depends_on` to understand execution order (e.g. apply-transforms β†’ ai-tricky-cases β†’ publish). - **Minimal adaptation:** How to adjust `include`/`exclude` globs or `scripts/codemod.ts` logic based on user code patterns. -- **Step-by-step execution:** Run steps in topological order (respect `depends_on`). After each step: validate outputs (`npm test`, `npm run check-types`, `npx codemod workflow validate workflow.yaml`), review diffs, then proceed. +- **Step-by-step execution:** Run steps in topological order (respect `depends_on`). After each step: validate outputs (`pnpm test`, `pnpm run check-types`, `npx codemod workflow validate workflow.yaml`), review diffs, then proceed. - **AI steps:** Treat the AI step prompt as context for edge cases that AST transforms cannot handle. Apply AI steps only where indicated by the workflow (`run_ai_step=true`). Keep changes localized and reviewable. - **Explicit exclusions:** Do not include a high-level "description of changes" or migration overview. Focus on: understand, tweak, run, adapt. diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index bbecb30..0000000 --- a/package-lock.json +++ /dev/null @@ -1,239 +0,0 @@ -{ - "name": "-codemods", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "-codemods", - "version": "0.1.0", - "workspaces": [ - "codemods/*" - ], - "devDependencies": { - "@biomejs/biome": "^2.4.15", - "@codemod.com/jssg-types": "^1.6.0", - "@types/node": "^25.8.0", - "typescript": "^6.0.3" - } - }, - "codemods/sample-codemod": { - "version": "0.1.0", - "devDependencies": { - "@jssg/utils": "latest" - } - }, - "node_modules/@biomejs/biome": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.15.tgz", - "integrity": "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.15", - "@biomejs/cli-darwin-x64": "2.4.15", - "@biomejs/cli-linux-arm64": "2.4.15", - "@biomejs/cli-linux-arm64-musl": "2.4.15", - "@biomejs/cli-linux-x64": "2.4.15", - "@biomejs/cli-linux-x64-musl": "2.4.15", - "@biomejs/cli-win32-arm64": "2.4.15", - "@biomejs/cli-win32-x64": "2.4.15" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.15.tgz", - "integrity": "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.15.tgz", - "integrity": "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.15.tgz", - "integrity": "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.15.tgz", - "integrity": "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.15.tgz", - "integrity": "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.15.tgz", - "integrity": "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.15.tgz", - "integrity": "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.15.tgz", - "integrity": "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@codemod.com/jssg-types": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@codemod.com/jssg-types/-/jssg-types-1.6.0.tgz", - "integrity": "sha512-9b6rsL3RZJvOPJ+gUj1W0041mHF6eYwLt+wsa4zqxJWNsxLha0JvFyaQdcw5XfQ5Ldd+H/kV/yZ4dpN+hC9OXw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@jssg/utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@jssg/utils/-/utils-0.0.2.tgz", - "integrity": "sha512-eCQv5Xs9yfI6OKq2PQ8SyKsxPhdiaJY/XAFJmsZiVbsK5DIBBmGBA95CsyD4JFWXDKkNv6Q7ER/Kbp6/uWzB2w==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@types/node": { - "version": "25.8.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz", - "integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "node_modules/sample-codemod": { - "resolved": "codemods/sample-codemod", - "link": true - }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/package.json b/package.json index fdf9409..a312ac7 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,37 @@ { - "name": "-codemods", - "private": true, - "version": "0.1.0", - "description": "Codemods for ", - "type": "module", - "scripts": { - "check": "biome check .", - "lint": "biome lint .", - "format": "biome format --write .", - "typecheck": "tsc --noEmit && echo 'TypeScript type check passed!'", - "test": "npm run test --workspaces --if-present" - }, - "workspaces": [ - "codemods/*" - ], - "devDependencies": { - "@biomejs/biome": "^2.4.15", - "@codemod.com/jssg-types": "^1.6.0", - "@types/node": "^25.8.0", - "typescript": "^6.0.3" - } + "name": "-codemods", + "version": "0.1.0", + "private": true, + "description": "Codemods for ", + "type": "module", + "scripts": { + "format": "oxfmt .", + "format:check": "oxfmt --check .", + "lint": "oxlint --type-aware --type-check --deny-warnings .", + "lint:fix": "oxlint --type-aware --type-check --fix .", + "test": "pnpm --filter \"./codemods/*\" test", + "check-types": "tsc --noEmit", + "ci": "pnpm run test && pnpm run check-types", + "docs:links": "markdown-link-check README.md CONTRIBUTING.md docs/CODEMOD-TEMPLATE.md", + "changeset": "changeset", + "version-packages": "changeset version && node scripts/sync-codemod-yaml-versions.mjs", + "prepare": "husky" + }, + "devDependencies": { + "@changesets/cli": "^2.27.12", + "@codemod.com/jssg-types": "catalog:", + "@types/node": "catalog:", + "codemod": "^1.12.3", + "husky": "^9.1.7", + "lint-staged": "^17.0.4", + "markdown-link-check": "^3.13.6", + "oxfmt": "^0.49.0", + "oxlint": "^1.66.0", + "oxlint-tsgolint": "^0.22.1", + "typescript": "catalog:" + }, + "engines": { + "node": ">=22" + }, + "packageManager": "pnpm@10.19.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..5d5a0cd --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2220 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + default: + '@codemod.com/jssg-types': + specifier: ^1.6.0 + version: 1.6.2 + '@types/node': + specifier: ^22.15.0 + version: 22.19.21 + typescript: + specifier: ^5.8.3 + version: 5.9.3 + +importers: + + .: + devDependencies: + '@changesets/cli': + specifier: ^2.27.12 + version: 2.31.0(@types/node@22.19.21) + '@codemod.com/jssg-types': + specifier: 'catalog:' + version: 1.6.2 + '@types/node': + specifier: 'catalog:' + version: 22.19.21 + codemod: + specifier: ^1.12.3 + version: 1.12.3 + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^17.0.4 + version: 17.0.7 + markdown-link-check: + specifier: ^3.13.6 + version: 3.14.2 + oxfmt: + specifier: ^0.49.0 + version: 0.49.0 + oxlint: + specifier: ^1.66.0 + version: 1.69.0(oxlint-tsgolint@0.22.1) + oxlint-tsgolint: + specifier: ^0.22.1 + version: 0.22.1 + typescript: + specifier: 'catalog:' + version: 5.9.3 + + codemods/sample-codemod: + devDependencies: + '@codemod.com/jssg-types': + specifier: 'catalog:' + version: 1.6.2 + +packages: + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@changesets/apply-release-plan@7.1.1': + resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} + + '@changesets/assemble-release-plan@6.0.10': + resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} + + '@changesets/changelog-git@0.2.1': + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + + '@changesets/cli@2.31.0': + resolution: {integrity: sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg==} + hasBin: true + + '@changesets/config@3.1.4': + resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.4': + resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} + + '@changesets/get-release-plan@4.0.16': + resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.4': + resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} + + '@changesets/pre@2.0.2': + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} + + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} + + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + + '@changesets/write@0.4.0': + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + + '@codemod.com/cli-darwin-arm64@1.12.3': + resolution: {integrity: sha512-AN6NmKl0QHTKDIrINXQr8s9kepjULhUThsZbviQZ3RTLs35OtGeJDh+L/4jkavNrhGIDLyN8KNdhTsGBBVb53A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@codemod.com/cli-darwin-x64@1.12.3': + resolution: {integrity: sha512-Ag5VQZc6vnoPwpQq3JEqaDtjc5vnXM339p5bAiJFF6/gq25W6pl0qsgr5OOL4s8Ct49t1BbQzbMxf1Y2z7LduQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@codemod.com/cli-linux-arm64-gnu@1.12.3': + resolution: {integrity: sha512-/p+oqY9bqBg899G9FZHFlOQ80371lq9QKw2uQWha55Ln+Ozc5Ebz3M4VGn7vO9Y8+7RFOnDMpeJHzatP+l13yg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@codemod.com/cli-linux-x64-gnu@1.12.3': + resolution: {integrity: sha512-9nobWgnAqlFF9gAQLeiNsiGJGiQ+G+ZJ7DlDmplLJTUFdQRElTNZV/lNa1fb9eLgW1q6QCQFErM25EKOurRn3w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@codemod.com/cli-win32-x64-msvc@1.12.3': + resolution: {integrity: sha512-Z8tMg3/SxcKwQSbIc3GxofJt/70ygC31Qxv5i4/+S6Kw8CWRpUGS6LLhkbgjZJfex/bLE5nQD328kv6fEYXXnQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@codemod.com/jssg-types@1.6.2': + resolution: {integrity: sha512-YnKYEwyD+wi0/ahDpsSxxutVBkOON46SrZjj7Z8w2CAAGhuMmighTM6g/MNk1GGjVUj1dULWVof5ArKLSYvnzg==} + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@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'} + + '@oozcitak/dom@2.0.2': + resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} + engines: {node: '>=20.0'} + + '@oozcitak/infra@2.0.2': + resolution: {integrity: sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==} + engines: {node: '>=20.0'} + + '@oozcitak/url@3.0.0': + resolution: {integrity: sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==} + engines: {node: '>=20.0'} + + '@oozcitak/util@10.0.0': + resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==} + engines: {node: '>=20.0'} + + '@oxfmt/binding-android-arm-eabi@0.49.0': + resolution: {integrity: sha512-HbifJ84prIh9+55CTPAU35JdRQrwg47y16cGerCC+iejSKOuHXYo2WDql6l7cQlzrYVtc3f4UWY+dBj2lRmOeA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.49.0': + resolution: {integrity: sha512-Ef7SKJqAaH2d7E6eXZZa2OffIShbhFMxnGK0zd93p4qiyTJr75B0qf7lrPD+qQOwcf04BrjYJ0JUxq8d5+yZwg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.49.0': + resolution: {integrity: sha512-8x5DN9CsFfb432sHa9NyqX5XisGUdA53LPEGSdv/VniS+v4uEOR8Orv7A9QSB98Xxgp0t6r31DzQA/wpIobGqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.49.0': + resolution: {integrity: sha512-e0+DSVzk4ewhMVKNYDaRTmP81jNMBWR1X9al0cVKWS+hDM/dElNqD5zjTOCuLOZc4oOdp2Gx2ldrVL+yYo9TZQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.49.0': + resolution: {integrity: sha512-W+mjtYtrQvFbXT/uNT+221OBhGRZ8UqNsLxjTWsjZ4GsQnRdvRC/N2NCK86BcamWr7lsTxwpwN3PULnr78sgcQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.49.0': + resolution: {integrity: sha512-Rtv6UevV7czDlLqil+NZUe4d8gs8jQo/zScSpumwyf7I+fSdLc+hc8AF3MQC7ymxSMMD9+vfiqQlsIf7wOAzXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.49.0': + resolution: {integrity: sha512-sBi+8C/Q/MdKa5FL8ibAUCdhFBGFH7HFN/Qoyd5xQbZ/0ky3NMPpKfIBpaH0lhK2dXkGLczVQUoZ+xuNSerCdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.49.0': + resolution: {integrity: sha512-JIfWenFhlzx+O8YygyZhoHFzTsdgDhxhbDRnE2iJLnnM5pWKScFvPECO2vOlA7JqJ/9S1g3uzEKuRCkHFwTjvA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-arm64-musl@0.49.0': + resolution: {integrity: sha512-iNzkMPG18jPkwBOZ4/HEjwqfzAjq4RrUQ0CgId/fC1ENvYD5jLVAaU/gWgpiqP1ys07kxSsSggDd1fp3E7mQHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-ppc64-gnu@0.49.0': + resolution: {integrity: sha512-BPHA/NN3LvoIXiid+iz3BHt5V0Rzx0tXAqRUovwE1NsbDaLG9e8mtv7evDGRIkVQacqTDBv0XL25THHsxSJosQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-gnu@0.49.0': + resolution: {integrity: sha512-3Eroshe+s69htC9JIL0+zLGQczLtRKezkMhwqQC21VC5Z/fuLvzLfbAOLgJLUq601H8gDYjy7deYycfOBjCvWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-musl@0.49.0': + resolution: {integrity: sha512-fnaERGgsxGm0lKAmO72EYR4BA3qBnzBTJBTi6EtUMq1D4R7EexRBMU4voXnx4TXla3SEDl9x4uNp/18SbkPjGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-s390x-gnu@0.49.0': + resolution: {integrity: sha512-rBwasMl1Uul1MCCeTGEFKnOTL7VUxHf+634jWStrQAbzpBJgd5Yz5m4F7exVCsoI8PHn57dNjssXagXLCLB5yA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxfmt/binding-linux-x64-gnu@0.49.0': + resolution: {integrity: sha512-BoC/F9xHe2y/deuBGA5Aw7bes07OD2gcL2wlpzTrfImR92vPP7S/k3LBTyspQZCNIVNdagkELcqKELwMLGIfAg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-linux-x64-musl@0.49.0': + resolution: {integrity: sha512-umY6jFADAo/oztFKl8D/S6vSrG6oBpEskcentiRuz42kZVU2kfDXMWCYavxyZR2bwPjqkHpcHZ6EZFiH3Qj9ZA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-openharmony-arm64@0.49.0': + resolution: {integrity: sha512-J85zQMiw2pXiGPK+OusmDvSnJ/dgpgN7VgmB2zOBtgS8F+nsOUfSg9ZEBrwbQscjZ7tkPbm38CG4VF5f53MsiA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.49.0': + resolution: {integrity: sha512-38K67XR++CoFFORDd4sMFwUVAnD6msYBdGTei+qvKGrRPO6S2PbrYPNL/eQQ1RgnnxOegNba0YQwg6uRkNcw6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.49.0': + resolution: {integrity: sha512-rXVe0HICwQF0dBgbQtBCoYf8x/SidPIdhyQl+iPuJlV7suV+qDv7yUEB3wQ4qC3nOeNxz287SwFXKzyr0kWgEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.49.0': + resolution: {integrity: sha512-gwWLwSEmBBfIK/Wh7GGd658161o4RKAvHWRaRQbJm571iQXGKfyr7UKsI1vsWvDlNLc30CxJDc8mMmCvJ/kczQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint-tsgolint/darwin-arm64@0.22.1': + resolution: {integrity: sha512-4150Lpgc1YM09GcjA6GSrra1JoPjC7aOpfywLjWEY4vW0Sd1qKzqHF1WRaiw0/qUZ40OATYdv3aRd7ipPkWQbw==} + cpu: [arm64] + os: [darwin] + + '@oxlint-tsgolint/darwin-x64@0.22.1': + resolution: {integrity: sha512-vFWcPWYOgZs4HWcgS1EjUZg33NLcNfEYU49KGImmCfZWkflENrmBYV4HN/C0YeAPum6ZZ/goPSvQrB/cOD+NfA==} + cpu: [x64] + os: [darwin] + + '@oxlint-tsgolint/linux-arm64@0.22.1': + resolution: {integrity: sha512-6LiUpP0Zir3+29FvBm7Y28q/dBjSHqTZ5MhG1Ckw4fGhI4cAvbcwXaKvbjx1TP7rRmBNOoq/M5xdpHjTb+GAew==} + cpu: [arm64] + os: [linux] + + '@oxlint-tsgolint/linux-x64@0.22.1': + resolution: {integrity: sha512-fuX1hEQfpHauUbXADsfqVhRzrUrGabzGXbj5wsp2vKhV5uk/Rze8Mba9GdjFGECzvXudMGqHqxB4r6jGRdhxVA==} + cpu: [x64] + os: [linux] + + '@oxlint-tsgolint/win32-arm64@0.22.1': + resolution: {integrity: sha512-8SZidAj+jrbZf9ZjBEYW0tiNZ+KasqB2zgW26qdiPpQSF/DzURnPmXz651IeA9YsmbVdHGIooEHUmev6QJdquA==} + cpu: [arm64] + os: [win32] + + '@oxlint-tsgolint/win32-x64@0.22.1': + resolution: {integrity: sha512-QweSk9H5lFh5Y+WUf2Kq/OAN88V6+62ZwGhP38gqdRotI90luXSMkruFTj7Q2rYrzH4ZVNaSqx7NY8JpSfIzqg==} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.69.0': + resolution: {integrity: sha512-DKQQbD5cZ/MYfDgDI7YGyGD9FSxABlsBsYFo5p26lloob543tP9+4N3guwdXIYJN+7HSZxLe8YJuwcOWw5qnHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.69.0': + resolution: {integrity: sha512-lEhb+I5pr4inux+JFwfCa1HRq3Os7NirEFQ0H1I35SVEHPm6byX0Ah47xmRha3qi6LAkxUcxViL8o/9PivjzBg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.69.0': + resolution: {integrity: sha512-GY2YE8lOZW59BW1Ia1y+1gR0XyjrZRvVWHAr8LGeGhYHE0OQJ/7cRKXTkx1P+E9/6awEc3SX8a68SFTjh/E//A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.69.0': + resolution: {integrity: sha512-ax1oZnOjHX3LB7myQyHEaQkDwfLb6str3/nSP6O7EVUviQGNkEGzGV0EqcBJWK+Ufwx0l4xPgyYayurvhAdl2Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.69.0': + resolution: {integrity: sha512-kHWeHv4g2h8NY+mpCxzCtY4uerMJWTN/TSnNj1CPbakFpHEJ6cTya2wWV0pDSYWOJ2+0UiEbhn3AtXxHtsnKjg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.69.0': + resolution: {integrity: sha512-gq84vM1a1oEehXo27YCDzGVcxPsZDI1yswZwz2Da1/cbnWtrL16XZZnz0G/+gIU8edtHpfjxq5c+vWEHqJfWoQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.69.0': + resolution: {integrity: sha512-kIqEa98JQ0VRyrcncxA417m2AzasqTlD+FyVT1AksjvjkqQcvm7pBWYvoW3/mpyOP2XYvi5nSCCTIe6De1yu5g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.69.0': + resolution: {integrity: sha512-j+xYiXozxGWx2cpjCrwwGR4awTxPFsRv3JZrv23RCogEPMc4R7UqjHW47p/RG0aRlbWiROCJ8coUfCwy0dvzHA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-arm64-musl@1.69.0': + resolution: {integrity: sha512-xEPpNppTfN1l/nM7gYSf9iocscu/as+p/7vxkLeLEKnYU+09Dm+5V6IhDYDh+Uz6FajEupWwCLt5SOG0y1PCKg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-ppc64-gnu@1.69.0': + resolution: {integrity: sha512-Ug0+eU7HJBlek+SjklYH62IlOMirEJsdxpihH0kSqX0XdrDD4NdHpQc10fK1JC35yn6KrrcN+uYzlHD38XAf8Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxlint/binding-linux-riscv64-gnu@1.69.0': + resolution: {integrity: sha512-iEyI3GIg0l/s3G4qy2TlaaWKdzj4PJJStwtlocpDTC00PY9hZueotf6OKUj9+yfQh0lrpBW/pLMgTztbAHKJEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-riscv64-musl@1.69.0': + resolution: {integrity: sha512-NjHjpiI4WIKSMwuoJSZi5VToPeoYOS1FR52HLIDG6lidMdqquusgtODb4iLk0+lb1q3Z0nv2/aPRcC/olmpQGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-s390x-gnu@1.69.0': + resolution: {integrity: sha512-Ai/prDewoItkDXbp38gwGZi41DycZbUTZJ3UidwoHgQC0/DaqC2TGdtBTQLJ6hSD+SAxASzh8+/eSBPmxfOacA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxlint/binding-linux-x64-gnu@1.69.0': + resolution: {integrity: sha512-Gt3KHgp46mRKz4sJeaASmKvD8ayXookRw07RMf+NowhEztGGDZ7VrXpoW96XuKJLjFukWizOFVNjmYb/u7caNQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-linux-x64-musl@1.69.0': + resolution: {integrity: sha512-7tQhJ2+p/oHv1zcfnjYI7YVzC/7iBaVOfIvFYtxdJ5F45mWgEdrCyXZXZGfiLey5t/5JhOhsaMnnv1kAzckd7g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-openharmony-arm64@1.69.0': + resolution: {integrity: sha512-vmWz6TKp/3hfA4lksR0zHBv/6xuX1jhym6eqOjdH2DXsDDHZWcp2f0KG0VCAnlVbIrjk29G4wAWMXb/Hn1YobA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.69.0': + resolution: {integrity: sha512-9RExaLgmaw6IoIkU9cTpT71mLfI0xZ86iZH8x518LVsOkjquJMYqb9P7KpC8lgd1t0Dxs41p2pxynq4XR3Ttzw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.69.0': + resolution: {integrity: sha512-1907kRPF8/PrcIw1E7LMs9JbVrpgnt/MvFdss3an8oDkYNAACXzTntV3t3869ZZhMZxb2AzRGbz1pA/jdFatXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.69.0': + resolution: {integrity: sha512-w8SOXv3mT9Fi6jY8OXdXCfnvX/3KNLXGNr4HEz2TA7S4Mv/PYAOmpB8y/ge40mxvBMgGNaSaaDwZpAsQn7HtWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@22.19.21': + resolution: {integrity: sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + basic-ftp@5.3.1: + resolution: {integrity: sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==} + engines: {node: '>=10.0.0'} + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.2.0: + resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} + engines: {node: '>=20.18.1'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + + codemod@1.12.3: + resolution: {integrity: sha512-/LMLkf2UDU3uHjUg+6PgIn0a4+scG2IvOPU/myhzg+tB7zlJouJIf6vj8LRYlsWlbFgC8iuBTT88FR464puE0A==} + engines: {node: '>= 16.0.0'} + hasBin: true + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + 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.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} + engines: {node: '>= 14'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + html-link-extractor@1.0.5: + resolution: {integrity: sha512-ADd49pudM157uWHwHQPUSX4ssMsvR/yHIswOR5CUfBdK9g9ZYGMhVSE6KZVHJ6kCkR0gH4htsfzU6zECDNVwyw==} + + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-id@4.2.0: + resolution: {integrity: sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==} + hasBin: true + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + + is-absolute-url@4.0.1: + resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-relative-url@4.1.0: + resolution: {integrity: sha512-vhIXKasjAuxS7n+sdv7pJQykEAgS+YU8VBQOENXwo/VZpOHDgBBsIbHo7zFKaWBjYWF4qxERdhbPRRtFAeJKfg==} + engines: {node: '>=14.16'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + link-check@5.5.1: + resolution: {integrity: sha512-GrtE4Zp/FBduvElmad375NrPeMYnKwNt9rH/TDG/rbQbHL0QVC4S/cEPVKZ0CkhXlVuiK+/5flGpRxQzoLbjEA==} + + lint-staged@17.0.7: + resolution: {integrity: sha512-JrSobt+tW3rH8IOMi8tDZd3foorM5yPEkLD/V2NxobgHrFfHWGee4MOLVuZeScgxftEwbHrPHIFA/ZL+nUJeuA==} + engines: {node: '>=22.22.1'} + hasBin: true + + listr2@10.2.1: + resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==} + engines: {node: '>=22.13.0'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + markdown-link-check@3.14.2: + resolution: {integrity: sha512-DPJ+itd3D5fcfXD5s1i53lugH0Z/h80kkQxlYCBh8tFwEZGhyVgDcLl0rnKlWssAVDAmSmcbePpHpMEY+JcMMQ==} + hasBin: true + + markdown-link-extractor@4.0.3: + resolution: {integrity: sha512-aEltJiQ4/oC0h6Jbw/uuATGSHZPkcH8DIunNH1A0e+GSFkvZ6BbBkdvBTVfIV8r6HapCU3yTd0eFdi3ZeM1eAQ==} + + marked@17.0.6: + resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==} + engines: {node: '>= 20'} + hasBin: true + + 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-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + 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==} + + needle@3.5.0: + resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==} + engines: {node: '>= 4.4.x'} + hasBin: true + + netmask@2.1.1: + resolution: {integrity: sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==} + engines: {node: '>= 0.4.0'} + + node-email-verifier@3.4.1: + resolution: {integrity: sha512-69JMeWgEUrCji+dOLULirdSoosRxgAq2y+imfmHHBGvgTwyTKqvm65Ls3+W30DCIWMrYj5kKVb/DHTQDK7OVwQ==} + engines: {node: '>=18.0.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + oxfmt@0.49.0: + resolution: {integrity: sha512-IAHFMdlJSWe+oAr65dx22UvjCtV9DBMisAuLnKpDqMQrctzCkGnj3QRwNHm0d+uwSWPalsDF8ZYLz9rh6nH2IQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + oxlint-tsgolint@0.22.1: + resolution: {integrity: sha512-YUSGSLUnoolsu8gxISEDio3q1rtsCozwfOzASUn3DT2mR2EeQ93uEEnen7s+6LpF+lyTQFln1pQfqwBh/fsVEg==} + hasBin: true + + oxlint@1.69.0: + resolution: {integrity: sha512-ypZkK/aDc5NQV8zIR6s2H2Tl3aNW8FmJ1m9+2qsaYuRenl8vgnHNCGwTHviWJdUQzglOlHFchgopdtGhSy17Rw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=0.22.1' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + package-manager-detector@0.2.11: + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + engines: {node: '>=10'} + hasBin: true + + 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'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.9: + resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} + engines: {node: '>=20'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@7.27.2: + resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} + engines: {node: '>=20.18.1'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + validator@13.15.35: + resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} + engines: {node: '>= 0.10'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + xmlbuilder2@4.0.3: + resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} + engines: {node: '>=20.0'} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + +snapshots: + + '@babel/runtime@7.29.7': {} + + '@changesets/apply-release-plan@7.1.1': + dependencies: + '@changesets/config': 3.1.4 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.8.4 + + '@changesets/assemble-release-plan@6.0.10': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.8.4 + + '@changesets/changelog-git@0.2.1': + dependencies: + '@changesets/types': 6.1.0 + + '@changesets/cli@2.31.0(@types/node@22.19.21)': + dependencies: + '@changesets/apply-release-plan': 7.1.1 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.4 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/get-release-plan': 4.0.16 + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@22.19.21) + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + enquirer: 2.4.1 + fs-extra: 7.0.1 + mri: 1.2.0 + package-manager-detector: 0.2.11 + picocolors: 1.1.1 + resolve-from: 5.0.0 + semver: 7.8.4 + spawndamnit: 3.0.1 + term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@changesets/config@3.1.4': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.4': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.8.4 + + '@changesets/get-release-plan@4.0.16': + dependencies: + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/config': 3.1.4 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.4': + dependencies: + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.1 + + '@changesets/parse@0.4.3': + dependencies: + '@changesets/types': 6.1.0 + js-yaml: 4.2.0 + + '@changesets/pre@2.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.7': + dependencies: + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.3 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 + + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.1.0': {} + + '@changesets/write@0.4.0': + dependencies: + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + human-id: 4.2.0 + prettier: 2.8.8 + + '@codemod.com/cli-darwin-arm64@1.12.3': + optional: true + + '@codemod.com/cli-darwin-x64@1.12.3': + optional: true + + '@codemod.com/cli-linux-arm64-gnu@1.12.3': + optional: true + + '@codemod.com/cli-linux-x64-gnu@1.12.3': + optional: true + + '@codemod.com/cli-win32-x64-msvc@1.12.3': + optional: true + + '@codemod.com/jssg-types@1.6.2': {} + + '@inquirer/external-editor@1.0.3(@types/node@22.19.21)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 22.19.21 + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.29.7 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.29.7 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@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.20.1 + + '@oozcitak/dom@2.0.2': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/url': 3.0.0 + '@oozcitak/util': 10.0.0 + + '@oozcitak/infra@2.0.2': + dependencies: + '@oozcitak/util': 10.0.0 + + '@oozcitak/url@3.0.0': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + + '@oozcitak/util@10.0.0': {} + + '@oxfmt/binding-android-arm-eabi@0.49.0': + optional: true + + '@oxfmt/binding-android-arm64@0.49.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.49.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.49.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.49.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.49.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.49.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.49.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.49.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.49.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.49.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.49.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.49.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.49.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.49.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.49.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.49.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.49.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.49.0': + optional: true + + '@oxlint-tsgolint/darwin-arm64@0.22.1': + optional: true + + '@oxlint-tsgolint/darwin-x64@0.22.1': + optional: true + + '@oxlint-tsgolint/linux-arm64@0.22.1': + optional: true + + '@oxlint-tsgolint/linux-x64@0.22.1': + optional: true + + '@oxlint-tsgolint/win32-arm64@0.22.1': + optional: true + + '@oxlint-tsgolint/win32-x64@0.22.1': + optional: true + + '@oxlint/binding-android-arm-eabi@1.69.0': + optional: true + + '@oxlint/binding-android-arm64@1.69.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.69.0': + optional: true + + '@oxlint/binding-darwin-x64@1.69.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.69.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.69.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.69.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.69.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.69.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.69.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.69.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.69.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.69.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.69.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.69.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.69.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.69.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.69.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.69.0': + optional: true + + '@tootallnate/quickjs-emscripten@0.23.0': {} + + '@types/node@12.20.55': {} + + '@types/node@22.19.21': + dependencies: + undici-types: 6.21.0 + + agent-base@7.1.4: {} + + ansi-colors@4.1.3: {} + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@6.2.3: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + + async@3.2.6: {} + + basic-ftp@5.3.1: {} + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + boolbase@1.0.0: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + chalk@5.6.2: {} + + chardet@2.1.1: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.2.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.1.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.27.2 + whatwg-mimetype: 4.0.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.1 + + codemod@1.12.3: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + '@codemod.com/cli-darwin-arm64': 1.12.3 + '@codemod.com/cli-darwin-x64': 1.12.3 + '@codemod.com/cli-linux-arm64-gnu': 1.12.3 + '@codemod.com/cli-linux-x64-gnu': 1.12.3 + '@codemod.com/cli-win32-x64-msvc': 1.12.3 + + commander@14.0.3: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-what@6.2.2: {} + + data-uri-to-buffer@6.0.2: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + + detect-indent@6.1.0: {} + + detect-libc@2.1.2: {} + + 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.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + emoji-regex@10.6.0: {} + + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + entities@4.5.0: {} + + entities@6.0.1: {} + + entities@7.0.1: {} + + environment@1.1.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + esprima@4.0.1: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + eventemitter3@5.0.4: {} + + extendable-error@0.1.7: {} + + fast-glob@3.3.3: + 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.20.1: + dependencies: + reusify: 1.1.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + get-east-asian-width@1.6.0: {} + + get-uri@6.0.5: + dependencies: + basic-ftp: 5.3.1 + data-uri-to-buffer: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + graceful-fs@4.2.11: {} + + html-link-extractor@1.0.5: + dependencies: + cheerio: 1.2.0 + + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-id@4.2.0: {} + + husky@9.1.7: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ip-address@10.2.0: {} + + is-absolute-url@4.0.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.6.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-relative-url@4.1.0: + dependencies: + is-absolute-url: 4.0.1 + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-windows@1.0.2: {} + + isexe@2.0.0: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.2.0: + dependencies: + argparse: 2.0.1 + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + link-check@5.5.1: + dependencies: + is-relative-url: 4.1.0 + ms: 2.1.3 + needle: 3.5.0 + node-email-verifier: 3.4.1 + proxy-agent: 6.5.0 + transitivePeerDependencies: + - supports-color + + lint-staged@17.0.7: + dependencies: + listr2: 10.2.1 + picomatch: 4.0.4 + string-argv: 0.3.2 + tinyexec: 1.2.4 + optionalDependencies: + yaml: 2.9.0 + + listr2@10.2.1: + dependencies: + cli-truncate: 5.2.0 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 10.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lodash.startcase@4.4.0: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + lru-cache@7.18.3: {} + + markdown-link-check@3.14.2: + dependencies: + async: 3.2.6 + chalk: 5.6.2 + commander: 14.0.3 + link-check: 5.5.1 + markdown-link-extractor: 4.0.3 + needle: 3.5.0 + progress: 2.0.3 + proxy-agent: 6.5.0 + xmlbuilder2: 4.0.3 + transitivePeerDependencies: + - supports-color + + markdown-link-extractor@4.0.3: + dependencies: + html-link-extractor: 1.0.5 + marked: 17.0.6 + + marked@17.0.6: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mimic-function@5.0.1: {} + + mri@1.2.0: {} + + ms@2.1.3: {} + + needle@3.5.0: + dependencies: + iconv-lite: 0.6.3 + sax: 1.6.0 + + netmask@2.1.1: {} + + node-email-verifier@3.4.1: + dependencies: + ms: 2.1.3 + validator: 13.15.35 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + outdent@0.5.0: {} + + oxfmt@0.49.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.49.0 + '@oxfmt/binding-android-arm64': 0.49.0 + '@oxfmt/binding-darwin-arm64': 0.49.0 + '@oxfmt/binding-darwin-x64': 0.49.0 + '@oxfmt/binding-freebsd-x64': 0.49.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.49.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.49.0 + '@oxfmt/binding-linux-arm64-gnu': 0.49.0 + '@oxfmt/binding-linux-arm64-musl': 0.49.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.49.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.49.0 + '@oxfmt/binding-linux-riscv64-musl': 0.49.0 + '@oxfmt/binding-linux-s390x-gnu': 0.49.0 + '@oxfmt/binding-linux-x64-gnu': 0.49.0 + '@oxfmt/binding-linux-x64-musl': 0.49.0 + '@oxfmt/binding-openharmony-arm64': 0.49.0 + '@oxfmt/binding-win32-arm64-msvc': 0.49.0 + '@oxfmt/binding-win32-ia32-msvc': 0.49.0 + '@oxfmt/binding-win32-x64-msvc': 0.49.0 + + oxlint-tsgolint@0.22.1: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 0.22.1 + '@oxlint-tsgolint/darwin-x64': 0.22.1 + '@oxlint-tsgolint/linux-arm64': 0.22.1 + '@oxlint-tsgolint/linux-x64': 0.22.1 + '@oxlint-tsgolint/win32-arm64': 0.22.1 + '@oxlint-tsgolint/win32-x64': 0.22.1 + + oxlint@1.69.0(oxlint-tsgolint@0.22.1): + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.69.0 + '@oxlint/binding-android-arm64': 1.69.0 + '@oxlint/binding-darwin-arm64': 1.69.0 + '@oxlint/binding-darwin-x64': 1.69.0 + '@oxlint/binding-freebsd-x64': 1.69.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.69.0 + '@oxlint/binding-linux-arm-musleabihf': 1.69.0 + '@oxlint/binding-linux-arm64-gnu': 1.69.0 + '@oxlint/binding-linux-arm64-musl': 1.69.0 + '@oxlint/binding-linux-ppc64-gnu': 1.69.0 + '@oxlint/binding-linux-riscv64-gnu': 1.69.0 + '@oxlint/binding-linux-riscv64-musl': 1.69.0 + '@oxlint/binding-linux-s390x-gnu': 1.69.0 + '@oxlint/binding-linux-x64-gnu': 1.69.0 + '@oxlint/binding-linux-x64-musl': 1.69.0 + '@oxlint/binding-openharmony-arm64': 1.69.0 + '@oxlint/binding-win32-arm64-msvc': 1.69.0 + '@oxlint/binding-win32-ia32-msvc': 1.69.0 + '@oxlint/binding-win32-x64-msvc': 1.69.0 + oxlint-tsgolint: 0.22.1 + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-map@2.1.0: {} + + p-try@2.2.0: {} + + pac-proxy-agent@7.2.0: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.4 + debug: 4.4.3 + get-uri: 6.0.5 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.1.1 + + package-manager-detector@0.2.11: + dependencies: + quansync: 0.2.11 + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pify@4.0.1: {} + + prettier@2.8.8: {} + + progress@2.0.3: {} + + proxy-agent@6.5.0: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.2.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + proxy-from-env@1.1.0: {} + + quansync@0.2.11: {} + + queue-microtask@1.2.3: {} + + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.2 + pify: 4.0.1 + strip-bom: 3.0.0 + + resolve-from@5.0.0: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safer-buffer@2.1.2: {} + + sax@1.6.0: {} + + semver@7.8.4: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.9 + transitivePeerDependencies: + - supports-color + + socks@2.8.9: + dependencies: + ip-address: 10.2.0 + smart-buffer: 4.2.0 + + source-map@0.6.1: + optional: true + + spawndamnit@3.0.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + sprintf-js@1.0.3: {} + + string-argv@0.3.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string-width@8.2.1: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + term-size@2.2.1: {} + + tinyexec@1.2.4: {} + + tinypool@2.1.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + undici@7.27.2: {} + + universalify@0.1.2: {} + + validator@13.15.35: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.1 + strip-ansi: 7.2.0 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + xmlbuilder2@4.0.3: + dependencies: + '@oozcitak/dom': 2.0.2 + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + js-yaml: 4.2.0 + + yaml@2.9.0: + optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..89bb488 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,8 @@ +packages: + - 'codemods/*' + - '!codemods/*/tests/**' + +catalog: + '@codemod.com/jssg-types': '^1.6.0' + '@types/node': '^22.15.0' + 'typescript': '^5.8.3' diff --git a/scripts/sync-codemod-yaml-versions.mjs b/scripts/sync-codemod-yaml-versions.mjs new file mode 100644 index 0000000..2160d1f --- /dev/null +++ b/scripts/sync-codemod-yaml-versions.mjs @@ -0,0 +1,29 @@ +#!/usr/bin/env node +import { execSync } from 'node:child_process' +import { readFileSync, writeFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const root = join(dirname(fileURLToPath(import.meta.url)), '..') +const versionPattern = /^version:\s*(['"]?)([^'"\n]+)\1\s*$/m + +const yamlPaths = execSync("find codemods -path '*/node_modules/*' -prune -o -name codemod.yaml -print", { + cwd: root, + encoding: 'utf8', +}) + .trim() + .split('\n') + .filter((path) => path !== '') + +for (const relativePath of yamlPaths) { + const dir = join(root, dirname(relativePath)) + const { version } = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) + const yamlPath = join(dir, 'codemod.yaml') + const content = readFileSync(yamlPath, 'utf8') + const updated = content.replace(versionPattern, `version: '${version}'`) + + if (updated !== content) { + writeFileSync(yamlPath, updated) + console.log(`${relativePath} -> ${version}`) + } +} diff --git a/scripts/tag-and-publish.sh b/scripts/tag-and-publish.sh new file mode 100755 index 0000000..f657105 --- /dev/null +++ b/scripts/tag-and-publish.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Creates git tags for each codemod whose version was bumped by changesets. +# Outputs the list of changed codemod directories for the publish job. +# Tags follow the pattern: @v +# +# Idempotent: a tag is considered "already released" if it exists locally +# OR on the remote. The local check alone is unreliable on shallow CI +# clones where tags may not be fetched. + +set -euo pipefail + +# Snapshot remote tags once so we don't run `git ls-remote` per codemod. +remote_tags="$(git ls-remote --tags origin | awk '{print $2}' | sed 's|^refs/tags/||;s|\^{}$||' | sort -u)" + +tag_exists_anywhere() { + local tag="$1" + if git rev-parse "$tag" >/dev/null 2>&1; then + return 0 + fi + if grep -Fxq "$tag" <<<"$remote_tags"; then + return 0 + fi + return 1 +} + +changed_dirs="[]" +new_tags=() + +while IFS= read -r codemod_yaml; do + [ -z "$codemod_yaml" ] && continue + dir="$(dirname "$codemod_yaml")" + pkg_json="$dir/package.json" + if [ ! -f "$pkg_json" ]; then + echo "Skipping $dir: package.json not found" >&2 + continue + fi + name="$(node -p "require('./$pkg_json').name")" + version="$(node -p "require('./$pkg_json').version")" + tag="${name}@v${version}" + + if tag_exists_anywhere "$tag"; then + echo "Tag $tag already exists, skipping" + continue + fi + + echo "Creating tag $tag" + git tag "$tag" + new_tags+=("$tag") + changed_dirs="$(echo "$changed_dirs" | node -p "JSON.stringify([...JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')), \"$dir\"])")" +done < <(find codemods -path '*/node_modules/*' -prune -o -name codemod.yaml -print | sort) + +for tag in "${new_tags[@]}"; do + git push origin "refs/tags/$tag" +done + +echo "changed_dirs=$changed_dirs" >> "$GITHUB_OUTPUT" diff --git a/tsconfig.json b/tsconfig.json index cc7d6ff..3d14d05 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,21 @@ { - "include": ["codemods/**/*.ts", "codemods/**/*.tsx", "codemods/**/*.js", "codemods/**/*.jsx"], - "exclude": ["**/tests/**/*"], - "compilerOptions": { - "target": "ES2020", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "noEmit": true, - "skipLibCheck": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "types": ["@codemod.com/jssg-types", "node"] - } + "compilerOptions": { + "target": "ESNext", + "lib": ["ESNext"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "types": ["@codemod.com/jssg-types", "node"], + "allowImportingTsExtensions": true, + "noEmit": true, + "skipLibCheck": true, + "strict": true, + "strictNullChecks": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "esModuleInterop": true, + "erasableSyntaxOnly": true + }, + "include": ["codemods/**/scripts/**/*.ts", "codemods/**/utils/**/*.ts"], + "exclude": ["**/node_modules", "**/tests", "**/tests-*"] }