diff --git a/.changeset/standalone-release-assets.md b/.changeset/standalone-release-assets.md new file mode 100644 index 0000000..2f30667 --- /dev/null +++ b/.changeset/standalone-release-assets.md @@ -0,0 +1,7 @@ +--- +'@pull-up/cli': minor +--- + +Release pull-up 0.1.0 with YAML-only configuration and external custom commands for both npm and standalone executables. Remove the public JavaScript configuration API. JavaScript/TypeScript configs, `.pulluprc` configs, and the `package.json` `pullup` field are ignored. + +Publish Linux and macOS executables for x64 and arm64 through GitHub Releases, and support `pullup --help` and `pullup --version`. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e267e5..8ecbe3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,3 +15,26 @@ jobs: - run: yarn format:check - run: yarn test - run: yarn typecheck + + exe: + name: Test binary (${{ matrix.target }}) + runs-on: ${{ matrix.runner }} + permissions: + contents: read + strategy: + fail-fast: false + matrix: + include: + - target: linux-x64 + runner: ubuntu-24.04 + - target: linux-arm64 + runner: ubuntu-24.04-arm + - target: darwin-x64 + runner: macos-15-intel + - target: darwin-arm64 + runner: macos-15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: jdx/mise-action@v3 + - run: yarn install --immutable + - run: yarn test:exe diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13b63d9..479464f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,9 +5,87 @@ on: branches: [main] workflow_dispatch: +concurrency: + group: release-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: false + jobs: + prepare: + name: Prepare release version + if: github.repository == 'toss/pull-up' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: jdx/mise-action@v3 + - if: github.event_name == 'workflow_dispatch' + run: yarn install --immutable + - name: Create canary version + if: github.event_name == 'workflow_dispatch' + run: | + yarn changeset version --snapshot canary --snapshot-prerelease-template "{tag}.${GITHUB_RUN_ID}.${GITHUB_RUN_ATTEMPT}" + version=$(node -p "require('./package.json').version") + case "$version" in + *"-canary.${GITHUB_RUN_ID}.${GITHUB_RUN_ATTEMPT}") ;; + *) echo "::error::No canary version was calculated. Add a changeset before publishing."; exit 1 ;; + esac + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Read release version + id: version + run: | + version=$(node -p "require('./package.json').version") + printf 'version=%s\n' "$version" >> "$GITHUB_OUTPUT" + + binaries: + name: Build and package binary (${{ matrix.target }}) + needs: prepare + runs-on: ${{ matrix.runner }} + permissions: + contents: read + env: + RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + RELEASE_TARGET: ${{ matrix.target }} + strategy: + fail-fast: false + matrix: + include: + - target: linux-x64 + runner: ubuntu-24.04 + - target: linux-arm64 + runner: ubuntu-24.04-arm + - target: darwin-x64 + runner: macos-15-intel + - target: darwin-arm64 + runner: macos-15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: jdx/mise-action@v3 + - name: Set release version + run: npm pkg set version="$RELEASE_VERSION" + - run: yarn install --immutable + - run: yarn test:exe + - name: Package standalone executable + uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7 + with: + version: v2.18.2 + args: release --snapshot --clean + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + path: build/release/pullup-v${{ needs.prepare.outputs.version }}-${{ matrix.target }}.tar.gz + archive: false + if-no-files-found: error + overwrite: true + build: if: github.repository == 'toss/pull-up' && github.event_name == 'push' + needs: binaries runs-on: ubuntu-latest permissions: id-token: write @@ -28,43 +106,76 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_CONFIG_PROVENANCE: true + - name: Download tested archives + if: steps.changesets.outputs.published == 'true' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + pattern: pullup-v${{ fromJSON(steps.changesets.outputs.publishedPackages)[0].version }}-*.tar.gz + path: release-assets + merge-multiple: true + - name: Upload archives to GitHub Release + if: steps.changesets.outputs.published == 'true' + uses: softprops/action-gh-release@5113cdc90fd4d541c801c55356214017bf5ae34b # v3 + with: + tag_name: v${{ fromJSON(steps.changesets.outputs.publishedPackages)[0].version }} + files: release-assets/pullup-v${{ fromJSON(steps.changesets.outputs.publishedPackages)[0].version }}-*.tar.gz + fail_on_unmatched_files: true canary: name: Publish canary if: github.repository == 'toss/pull-up' && github.event_name == 'workflow_dispatch' + needs: [prepare, binaries] runs-on: ubuntu-latest concurrency: group: publish-canary cancel-in-progress: false permissions: id-token: write - contents: read + contents: write pull-requests: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: jdx/mise-action@v3 + - name: Set canary version + run: npm pkg set version="$RELEASE_VERSION" + env: + RELEASE_VERSION: ${{ needs.prepare.outputs.version }} - run: yarn install --immutable - run: yarn lint - run: yarn format:check - run: yarn test - run: yarn typecheck - - name: Create canary version - run: | - yarn changeset version --snapshot canary --snapshot-prerelease-template "{tag}.${GITHUB_RUN_ID}.${GITHUB_RUN_ATTEMPT}" - version=$(node -p "require('./package.json').version") - case "$version" in - *"-canary.${GITHUB_RUN_ID}.${GITHUB_RUN_ATTEMPT}") ;; - *) echo "::error::No canary version was calculated. Add a changeset before publishing."; exit 1 ;; - esac - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Publish canary to npm - run: npm publish --tag canary --access public + run: yarn changeset publish --tag canary --no-git-tag env: NPM_CONFIG_PROVENANCE: true - - name: Show install command + - name: Download tested archives + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + pattern: pullup-v${{ needs.prepare.outputs.version }}-*.tar.gz + path: release-assets + merge-multiple: true + - name: Upload standalone canary + uses: softprops/action-gh-release@5113cdc90fd4d541c801c55356214017bf5ae34b # v3 + with: + tag_name: v${{ needs.prepare.outputs.version }} + target_commitish: ${{ github.sha }} + draft: true + prerelease: true + make_latest: false + overwrite_files: false + files: release-assets/pullup-v${{ needs.prepare.outputs.version }}-*.tar.gz + fail_on_unmatched_files: true + - name: Publish standalone canary + run: gh release edit "$RELEASE_TAG" --draft=false --prerelease --latest=false + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: v${{ needs.prepare.outputs.version }} + - name: Show canary downloads run: | - version=$(node -p "require('./package.json').version") - printf 'Run this canary:\n\n npx @pull-up/cli@%s --help\n' "$version" >> "$GITHUB_STEP_SUMMARY" + printf 'Run this canary:\n\n npx @pull-up/cli@%s --help\n\nStandalone binaries: %s\n' "$RELEASE_VERSION" "$RELEASE_URL" >> "$GITHUB_STEP_SUMMARY" + env: + RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + RELEASE_URL: ${{ github.server_url }}/${{ github.repository }}/releases/tag/v${{ needs.prepare.outputs.version }} diff --git a/.gitignore b/.gitignore index c2616eb..4ec2ad3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ .pnp.* dist +build +release-assets *.tgz diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..2116154 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,27 @@ +version: 2 +project_name: pullup +dist: build/release + +builds: + - builder: node + skip: true + +archives: + - meta: true + formats: [tar.gz] + name_template: 'pullup-v{{ .Env.RELEASE_VERSION }}-{{ .Env.RELEASE_TARGET }}' + files: + - src: 'build/pullup-{{ .Env.RELEASE_TARGET }}' + dst: pullup + info: + mode: 0755 + - LICENSE + +snapshot: + version_template: '{{ .Env.RELEASE_VERSION }}' + +checksum: + disable: true + +changelog: + disable: true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..160cd06 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Viva Republica, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 5db4e42..26c57ad 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,29 @@ yarn add -D @pull-up/cli pnpm add -D @pull-up/cli ``` +### Standalone executable + +Download an archive from [GitHub Releases](https://github.com/toss/pull-up/releases), replacing `` with the release version: + +| Platform | Archive | +| ------------------- | --------------------------------------- | +| Linux x64 | `pullup-v-linux-x64.tar.gz` | +| Linux arm64 | `pullup-v-linux-arm64.tar.gz` | +| macOS Intel | `pullup-v-darwin-x64.tar.gz` | +| macOS Apple Silicon | `pullup-v-darwin-arm64.tar.gz` | + +Each archive contains the executable `pullup` and `LICENSE`. Extracting the archive preserves the executable's permissions. + +For example, on macOS Apple Silicon, replacing `0.1.0` with the downloaded release version: + +```bash +tar -xzf pullup-v0.1.0-darwin-arm64.tar.gz +./pullup --version +./pullup sync +``` + +Use YAML configuration with the standalone executable. Built-in jobs do not require Node.js or a JavaScript package manager. Custom commands still require any runtimes or tools they invoke. + ## Configuration Create `pullup.yml` or `pullup.yaml` in your project root: @@ -53,7 +76,7 @@ jobs: `jobs` maps job identifiers to their settings. YAML configuration is validated before jobs run; invalid settings cause the command to exit with code 1. -`pullup.yaml` is searched before `pullup.yml`. +Configuration search starts at the working directory, or `--cwd`, and walks up to the nearest YAML configuration. Within each directory, `pullup.yaml` is searched before `pullup.yml`; empty files are skipped. `--root` sets the repository root for job inputs and outputs without changing where configuration search starts. ### Built-in Jobs @@ -132,117 +155,54 @@ The package includes `schema.json` for autocomplete and validation in editors th The path is relative to the YAML file. For Yarn Plug'n'Play or a remote schema, use `https://unpkg.com/@pull-up/cli@/schema.json`, replacing `` with your installed package version. -### JavaScript and TypeScript Configuration - -Starting in 0.0.8, JavaScript and TypeScript configuration files are deprecated. They continue to work, but pull-up prints one warning to stderr per command when it loads a `.js`, `.ts`, `.cjs`, or `.mjs` configuration file, including `.pulluprc` files with these extensions. See [Migration](#migration) to move to YAML. +### Migration -The `package.json` `pullup` field and JSON/YAML `.pulluprc` configurations remain supported without this deprecation warning. +Starting in 0.1.0, npm and standalone installations support only `pullup.yml` or `pullup.yaml`. Old configuration formats are ignored, and the npm package no longer exports JavaScript configuration helpers. -Use `pullup.config.ts`, `pullup.config.js`, `pullup.config.cjs`, or `pullup.config.mjs` for a JavaScript or TypeScript configuration: +Move built-in job settings from `pullup.config.ts`: ```ts -import { defineConfig, codeownersJob } from '@pull-up/cli'; +import { codeownersJob, defineConfig } from '@pull-up/cli'; -export default defineConfig(codeownersJob()); +export default defineConfig(codeownersJob({ input: ['packages/**/CODEOWNERS'] })); ``` -The `package.json` `pullup` field and `.pulluprc` files are also supported. These formats are searched before `pullup.yaml` and `pullup.yml`. - -#### Custom Jobs - -Use `defineJob` to implement a custom transform in a JavaScript or TypeScript config: - -```ts -import { defineConfig, defineJob } from '@pull-up/cli'; - -const myJob = defineJob({ - name: 'my-job', - input: ['packages/*/config.json'], - output: 'merged-config.json', - transform: (sources) => { - return JSON.stringify(sources.map((s) => JSON.parse(s.contents))); - }, -}); - -export default defineConfig([myJob()]); -``` - -Transform functions receive source files as `{ path, contents }` objects and a context containing `rootDir` and `outputPath`. They can return a string or a promise of a string. - -### Migration - -Replace a built-in job in `pullup.config.mjs`: - -```js -import { defineConfig, codeownersJob } from '@pull-up/cli'; - -export default defineConfig( - codeownersJob({ - input: ['**/CODEOWNERS', '!**/fixtures/**'], - output: '.github/CODEOWNERS', - }), -); -``` - -With `pullup.yml`: +Into `pullup.yml`: ```yaml jobs: codeowners: type: codeowners - input: ['**/CODEOWNERS', '!**/fixtures/**'] - output: .github/CODEOWNERS + input: ['packages/**/CODEOWNERS'] ``` -For a custom callback, move the transform into a command. For example, replace this `pullup.config.mjs`: +For a custom callback such as: ```js -import { defineConfig, defineJob } from '@pull-up/cli'; +const transform = (sources) => sources.map(({ contents }) => contents).join('\n'); +``` + +Move its body into `scripts/combine.mjs`, reading sources from stdin and writing the result to stdout: -const mergeConfigs = defineJob({ - name: 'merge-configs', - input: ['packages/*/config.json'], - output: 'merged-config.json', - transform: (sources) => `${JSON.stringify(sources.map((source) => JSON.parse(source.contents)))}\n`, -}); +```js +import { readFileSync } from 'node:fs'; -export default defineConfig([mergeConfigs()]); +const { sources } = JSON.parse(readFileSync(0, 'utf8')); +process.stdout.write(sources.map(({ contents }) => contents).join('\n')); ``` -With `pullup.yml`: +Then configure the command in `pullup.yml`: ```yaml jobs: - merge-configs: + combined: type: custom - command: 'node scripts/merge-configs.mjs' - input: ['packages/*/config.json'] - output: merged-config.json + command: node scripts/combine.mjs + input: ['packages/**/config.txt'] + output: combined.txt ``` -Create `scripts/merge-configs.mjs`: - -```js -process.stdin.setEncoding('utf8'); -let input = ''; -for await (const chunk of process.stdin) { - input += chunk; -} - -const { sources, context } = JSON.parse(input); - -try { - const configs = sources.map((source) => JSON.parse(source.contents)); - process.stdout.write(`${JSON.stringify(configs)}\n`); -} catch (error) { - console.error(`Failed to generate ${context.outputPath}: ${error.message}`); - process.exitCode = 1; -} -``` - -The script reads sources and context from JSON on stdin and writes the generated contents, including the final newline, to stdout. It does not write the output file itself. Input patterns and output paths remain relative to the repository root; the command also runs from that root. Your environment must provide the runtime used by the command, such as Node.js in this example. - -After moving your jobs, delete the old JavaScript or TypeScript configuration file. Legacy configurations take precedence over `pullup.yaml` and `pullup.yml`, so leaving the old file in place prevents pull-up from loading your new YAML configuration. Run `pullup sync --dry-run` to preview the migrated jobs, then use `pullup sync` and `pullup check` as usual. +This example requires Node.js in the user's environment. See [Custom Jobs](#custom-jobs) for the full stdin/stdout contract. Run `pullup sync --dry-run` to preview the migrated jobs, then `pullup sync` and `pullup check`. ## Usage @@ -275,3 +235,18 @@ pullup check | `--root ` | Repository root path | | `--cwd ` | Working directory | | `--dry-run` | Preview without writing (sync only) | +| `--help` | Show command help | +| `--version` | Show the installed version | + +## Development + +```bash +yarn test # Source unit tests and the npm CLI +yarn test:unit # Source tests without building +yarn test:integration # Build and test the npm CLI +yarn test:exe # Build and test the native standalone executable +``` + +The npm and standalone projects run the same tests in `tests/cli`. The standalone test environment provides only `sh` on PATH to verify that built-in jobs do not need an installed Node.js runtime. + +`yarn build` generates the npm CLI, `schema.json`, and the standalone executable for the current machine together. CI builds and tests all four supported platforms on native runners, uses GoReleaser to package each executable and `LICENSE` as a versioned `.tar.gz`, and attaches the archives to the matching Changesets GitHub Release. diff --git a/eslint.config.mjs b/eslint.config.mjs index 141e8b7..6a2174a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,7 +7,7 @@ import tseslint from 'typescript-eslint'; export default defineConfig( { - ignores: ['**/node_modules/**', '**/dist/**', '.yarn/**', '**/.pnp.cjs', '**/.pnp.loader.mjs'], + ignores: ['**/node_modules/**', '**/dist/**', '**/build/**', '.yarn/**', '**/.pnp.cjs', '**/.pnp.loader.mjs'], }, eslint.configs.recommended, tseslint.configs.recommended, diff --git a/fixtures/custom-job/ownership/generate-codeowners.mjs b/fixtures/custom-job/ownership/generate-codeowners.mjs index 05a83e8..737bd07 100644 --- a/fixtures/custom-job/ownership/generate-codeowners.mjs +++ b/fixtures/custom-job/ownership/generate-codeowners.mjs @@ -1,12 +1,12 @@ import { readFileSync } from 'node:fs'; import path from 'node:path'; -import { defaultLoaders } from 'cosmiconfig'; +import { load } from 'js-yaml'; const { sources } = JSON.parse(readFileSync(0, 'utf8')); const configs = sources.map(({ path: sourcePath, contents }) => ({ path: sourcePath, - config: defaultLoaders['.yaml'](sourcePath, contents), + config: load(contents, { filename: sourcePath }), })); const registry = configs.find(({ path: sourcePath }) => sourcePath === 'codeowners.yaml').config; const declarations = configs diff --git a/mise.lock b/mise.lock index c75e036..7eef308 100644 --- a/mise.lock +++ b/mise.lock @@ -50,37 +50,37 @@ url_api = "https://api.github.com/repos/evilmartians/lefthook/releases/assets/60 provenance = "github-attestations" [[tools.node]] -version = "24.12.0" +version = "26.10.0" backend = "core:node" -specifiers = ["24.12.0"] +specifiers = ["26.10.0"] [tools.node."platforms.linux-arm64"] -checksum = "sha256:9b2a2eeb98a8eb37361224e2a1d060300ad2dd143af58dfdb16de785df0f1228" -url = "https://nodejs.org/dist/v24.12.0/node-v24.12.0-linux-arm64.tar.gz" +checksum = "sha256:423a41bff8e2a2fa15e702fefe2919ef95823b2378744daccb8439302534b44f" +url = "https://nodejs.org/dist/v26.10.0/node-v26.10.0-linux-arm64.tar.gz" [tools.node."platforms.linux-arm64-musl"] -checksum = "sha256:ef0dc66e375a19442d54b25c28e2481059319a3ecd7118a530372ed80fbf86ac" -url = "https://unofficial-builds.nodejs.org/download/release/v24.12.0/node-v24.12.0-linux-arm64-musl.tar.gz" +checksum = "sha256:d26027bff3197050d2a2b0bfa151b18e8378ed81affa2bf5093804db158979b6" +url = "https://unofficial-builds.nodejs.org/download/release/v26.10.0/node-v26.10.0-linux-arm64-musl.tar.gz" [tools.node."platforms.linux-x64"] -checksum = "sha256:6159227e0af7d7c3c6bb2fa900452b04a6cb8841a702a79acc613209d70b04d0" -url = "https://nodejs.org/dist/v24.12.0/node-v24.12.0-linux-x64.tar.gz" +checksum = "sha256:cb5c9ce9c80d7b8821e3a258543c71b939138cf17c74d5cc44bbe85d6dbc5ad8" +url = "https://nodejs.org/dist/v26.10.0/node-v26.10.0-linux-x64.tar.gz" [tools.node."platforms.linux-x64-musl"] -checksum = "sha256:7ad71a3170aac1c5448f2ef3dca05557e6e89e1201fa23e8600c73eebd4a352f" -url = "https://unofficial-builds.nodejs.org/download/release/v24.12.0/node-v24.12.0-linux-x64-musl.tar.gz" +checksum = "sha256:e42da94821b9422eb76bf5aaf4de0977e04ba320b8aff6148d895a94b08ee223" +url = "https://unofficial-builds.nodejs.org/download/release/v26.10.0/node-v26.10.0-linux-x64-musl.tar.gz" [tools.node."platforms.macos-arm64"] -checksum = "sha256:319f221adc5e44ff0ed57e8a441b2284f02b8dc6fc87b8eb92a6a93643fd8080" -url = "https://nodejs.org/dist/v24.12.0/node-v24.12.0-darwin-arm64.tar.gz" +checksum = "sha256:751fdf7439f115d87ee2a8f3f18c065b6151852068e3e666ac60ac2996f75ac9" +url = "https://nodejs.org/dist/v26.10.0/node-v26.10.0-darwin-arm64.tar.gz" [tools.node."platforms.macos-x64"] -checksum = "sha256:b82ea4c62fd08e250cab59d625e75d77cc5b0a3d60c6698ebee4545c88a169c5" -url = "https://nodejs.org/dist/v24.12.0/node-v24.12.0-darwin-x64.tar.gz" +checksum = "sha256:ebbe9ab9b58ad6bb54390d6e2c862c1afa7d4475fb7e8ae8146acde211bf70df" +url = "https://nodejs.org/dist/v26.10.0/node-v26.10.0-darwin-x64.tar.gz" [tools.node."platforms.windows-x64"] -checksum = "sha256:9c125f61ae947b52e779095830f9cac267846a043ef7192183c84016aaad2812" -url = "https://nodejs.org/dist/v24.12.0/node-v24.12.0-win-x64.zip" +checksum = "sha256:9fef7eca6743a6b910989cd8e78712376b394fcb9b6e1e9c44a0799a287f90c5" +url = "https://nodejs.org/dist/v26.10.0/node-v26.10.0-win-x64.zip" [[tools.yarn]] version = "4.12.0" diff --git a/mise.toml b/mise.toml index 24b6ce7..28e8c7f 100644 --- a/mise.toml +++ b/mise.toml @@ -1,6 +1,6 @@ [tools] lefthook = "2.1.16" -node = "24.12.0" +node = "26.10.0" yarn = "4.12.0" [hooks.postinstall] diff --git a/package.json b/package.json index 8c00774..05e7ad0 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,8 @@ "bin": { "pullup": "./dist/bin/index.mjs" }, - "main": "./dist/core/index.mjs", - "exports": { - ".": { - "types": "./dist/core/index.d.mts", - "import": "./dist/core/index.mjs" - } - }, "author": "Hoyeon Lee ", + "license": "MIT", "bugs": "https://github.com/toss/pull-up/issues", "keywords": [ "pull-up", @@ -28,15 +22,16 @@ "url": "git+https://github.com/toss/pull-up.git" }, "files": [ - "dist", - "schema.json" + "dist/bin/**", + "./schema.json" ], "scripts": { "build": "tsdown", - "test": "vitest run", + "test": "vitest run --project unit --project integration", "test:unit": "vitest run --project unit", "test:integration": "vitest run --project integration", - "dev": "tsdown --watch", + "test:exe": "vitest run --project exe", + "dev": "tsdown --watch --no-exe", "prepack": "yarn build", "lint": "eslint .", "lint:fix": "eslint --fix .", @@ -49,27 +44,26 @@ "@changesets/cli": "^2.29.8", "@eslint/js": "^9.39.2", "@fixture-kit/core": "^1.0.1", + "@types/js-yaml": "^4.0.9", "@types/node": "^24.10.0", "@valibot/to-json-schema": "^1.8.0", + "clipanion": "^4.0.0-rc.4", "eslint": "^9.7.0", "eslint-plugin-import-x": "^4.0.0", "eslint-plugin-simple-import-sort": "^12.1.1", + "execa": "^10.0.1", + "fast-glob": "^3.3.3", + "find-up": "^8.0.0", "globals": "^17.0.0", + "js-yaml": "^4.1.1", "prettier": "^3.8.0", - "tsdown": "^0.19.0", + "tsdown": "^0.23.0", "typescript": "^5.9.2", "typescript-eslint": "^8.27.0", + "valibot": "^1.5.0", "vite": "^8.3.1", "vitest": "^5.0.2" }, - "dependencies": { - "clipanion": "^4.0.0-rc.4", - "cosmiconfig": "^9.0.0", - "execa": "^10.0.1", - "fast-glob": "^3.3.3", - "find-up": "^8.0.0", - "valibot": "^1.5.0" - }, "engines": { "node": ">=22.8.0" }, diff --git a/src/cli/commands/check.ts b/src/cli/commands/check.ts index a937256..66b42cc 100644 --- a/src/cli/commands/check.ts +++ b/src/cli/commands/check.ts @@ -29,7 +29,7 @@ export class CheckCommand extends Command { if (jobs.length === 0) { console.log(styleText('yellow', '✘ No jobs found to check')); - return; + return 0; } const results = await Promise.all(jobs.map((job) => runJob(job, repoRoot))); @@ -37,10 +37,11 @@ export class CheckCommand extends Command { for (const { isSame, jobInfo } of results) { if (!isSame) { console.error(styleText('red', `✘ ${jobInfo.name} is outdated. Run 'pullup sync' to update.`)); - process.exit(1); + return 1; } } console.log(styleText('green', '✔ All files are up to date')); + return 0; } } diff --git a/src/cli/index.ts b/src/cli/index.ts index 6957c1c..bcafaec 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -1,4 +1,4 @@ -import { Cli } from 'clipanion'; +import { Builtins, Cli } from 'clipanion'; import packageJson from '../../package.json' with { type: 'json' }; import { CheckCommand, SyncCommand } from './commands'; @@ -13,5 +13,7 @@ const cli = new Cli({ cli.register(CheckCommand); cli.register(SyncCommand); +cli.register(Builtins.HelpCommand); +cli.register(Builtins.VersionCommand); cli.runExit(args); diff --git a/src/cli/utils.ts b/src/cli/utils.ts index f20a1ed..dab5012 100644 --- a/src/cli/utils.ts +++ b/src/cli/utils.ts @@ -2,7 +2,7 @@ import { findUp } from 'find-up'; import path from 'path'; export async function resolveRepositoryRoot(cwd: string, root?: string) { - if (root != null) return root; + if (root != null) return path.resolve(root); const dotGit = await findUp('.git', { type: 'both', diff --git a/src/core/config/define-config.ts b/src/core/config/define-config.ts deleted file mode 100644 index c95f7ce..0000000 --- a/src/core/config/define-config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Job } from '../types'; - -export function defineConfig(config: Job | Job[]) { - if (Array.isArray(config)) { - return config; - } - - return [config]; -} diff --git a/src/core/config/index.ts b/src/core/config/index.ts deleted file mode 100644 index 35809ee..0000000 --- a/src/core/config/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './define-config'; diff --git a/src/core/config/resolve-config.spec.ts b/src/core/config/resolve-config.spec.ts index 6dbace2..2d766ca 100644 --- a/src/core/config/resolve-config.spec.ts +++ b/src/core/config/resolve-config.spec.ts @@ -1,33 +1,16 @@ +import { chmod } from 'node:fs/promises'; import path from 'node:path'; import { Fixture } from '@fixture-kit/core'; import { resolveConfig } from './resolve-config'; -const customJobs = [{ name: 'custom-job', input: ['packages/*/config.json'], output: 'merged-config.json' }]; -const jsonConfig = JSON.stringify(customJobs); -const esmConfig = `export default ${jsonConfig};`; -const tsConfig = `export default ${jsonConfig} as const;`; -const cjsConfig = `module.exports = ${jsonConfig};`; -const rcConfig = `- name: custom-job - input: - - 'packages/*/config.json' - output: merged-config.json -`; const defaultYamlConfig = `jobs: owners: type: codeowners `; describe('resolveConfig', () => { - beforeEach(() => { - vi.spyOn(console, 'warn').mockImplementation(() => {}); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - it.each(['pullup.yaml', 'pullup.yml'])('resolves default codeowners options from %s', async (filename) => { await using fixture = await Fixture.create({ [filename]: defaultYamlConfig }); @@ -39,7 +22,6 @@ describe('resolveConfig', () => { transform: expect.any(Function), }, ]); - expect(console.warn).not.toHaveBeenCalled(); }); it('resolves explicit input and output from pullup.yaml', async () => { @@ -75,88 +57,84 @@ describe('resolveConfig', () => { ]); }); - it('returns no jobs for an empty jobs mapping in pullup.yml', async () => { - await using fixture = await Fixture.create({ 'pullup.yml': 'jobs: {}\n' }); - - expect(await resolveConfig(fixture.root)).toEqual([]); - }); - - it.each([ - ['pullup.config.js', esmConfig], - ['pullup.config.ts', tsConfig], - ['pullup.config.cjs', cjsConfig], - ['pullup.config.mjs', esmConfig], - ['.pulluprc.js', esmConfig], - ['.pulluprc.ts', tsConfig], - ['.pulluprc.cjs', cjsConfig], - ['.pulluprc.mjs', esmConfig], - ])('resolves jobs from %s and warns about deprecation once', async (filename, contents) => { + it('resolves an external custom command from YAML', async () => { await using fixture = await Fixture.create({ - 'package.json': JSON.stringify({ type: 'module' }), - [filename]: contents, + 'pullup.yml': `jobs: + custom: + type: custom + input: ['packages/*/config.json'] + output: merged-config.json + command: printf generated +`, }); - expect(await resolveConfig(fixture.root)).toEqual(customJobs); - expect(console.warn).toHaveBeenCalledOnce(); - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining(path.join(fixture.root, filename))); - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('[DEPRECATED]')); - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('pullup.yml or pullup.yaml')); - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('external commands')); - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('https://github.com/toss/pull-up#migration')); + expect(await resolveConfig(fixture.root)).toEqual([ + { + name: 'custom', + input: ['packages/*/config.json'], + output: 'merged-config.json', + transform: expect.any(Function), + }, + ]); }); - it.each([ - ['.pulluprc', rcConfig], - ['.pulluprc.json', jsonConfig], - ['.pulluprc.yaml', rcConfig], - ['.pulluprc.yml', rcConfig], - ])('resolves jobs from %s without a deprecation warning', async (filename, contents) => { - await using fixture = await Fixture.create({ - 'package.json': JSON.stringify({ type: 'module' }), - [filename]: contents, - }); + it('returns no jobs for an empty jobs mapping', async () => { + await using fixture = await Fixture.create({ 'pullup.yml': 'jobs: {}\n' }); - expect(await resolveConfig(fixture.root)).toEqual(customJobs); - expect(console.warn).not.toHaveBeenCalled(); + expect(await resolveConfig(fixture.root)).toEqual([]); }); - it('warns about an empty JavaScript configuration', async () => { - await using fixture = await Fixture.create({ 'pullup.config.cjs': 'module.exports = [];' }); + it('returns no jobs when no config exists', async () => { + await using fixture = await Fixture.create({}); expect(await resolveConfig(fixture.root)).toEqual([]); - expect(console.warn).toHaveBeenCalledOnce(); - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining(path.join(fixture.root, 'pullup.config.cjs'))); }); - it('warns about a script configuration discovered inside .config', async () => { - await using fixture = await Fixture.create({ '.config': { 'pulluprc.cjs': cjsConfig } }); + it('skips a directory named pullup.yaml before trying pullup.yml', async () => { + await using fixture = await Fixture.create({ + 'pullup.yaml': {}, + 'pullup.yml': defaultYamlConfig, + }); + + expect(await resolveConfig(fixture.root)).toMatchObject([{ name: 'codeowners' }]); + }); - expect(await resolveConfig(fixture.root)).toEqual(customJobs); - expect(console.warn).toHaveBeenCalledOnce(); - expect(console.warn).toHaveBeenCalledWith( - expect.stringContaining(path.join(fixture.root, '.config', 'pulluprc.cjs')), - ); + it('skips an unreadable pullup.yaml before trying pullup.yml', async () => { + await using fixture = await Fixture.create({ + 'pullup.yaml': defaultYamlConfig, + 'pullup.yml': `${defaultYamlConfig} output: readable/CODEOWNERS\n`, + }); + const unreadablePath = path.join(fixture.root, 'pullup.yaml'); + await chmod(unreadablePath, 0); + try { + expect(await resolveConfig(fixture.root)).toMatchObject([{ output: 'readable/CODEOWNERS' }]); + } finally { + await chmod(unreadablePath, 0o644); + } }); - it('resolves jobs from package.json#pullup', async () => { - await using fixture = await Fixture.create({ 'package.json': JSON.stringify({ pullup: customJobs }) }); + it('finds an ancestor YAML from a subdirectory without a Git repository', async () => { + await using fixture = await Fixture.create({ 'pullup.yml': defaultYamlConfig, nested: { child: {} } }); - expect(await resolveConfig(fixture.root)).toEqual(customJobs); - expect(console.warn).not.toHaveBeenCalled(); + expect(await resolveConfig(path.join(fixture.root, 'nested/child'))).toMatchObject([{ name: 'codeowners' }]); }); - it('resolves jobs from .config/pulluprc', async () => { - await using fixture = await Fixture.create({ '.config': { pulluprc: rcConfig } }); + it('skips empty YAML in a child before finding an ancestor', async () => { + await using fixture = await Fixture.create({ + 'pullup.yml': defaultYamlConfig, + nested: { 'pullup.yaml': 'null\n', 'pullup.yml': '# empty\n' }, + }); - expect(await resolveConfig(fixture.root)).toEqual(customJobs); - expect(console.warn).not.toHaveBeenCalled(); + expect(await resolveConfig(path.join(fixture.root, 'nested'))).toMatchObject([{ name: 'codeowners' }]); }); - it('returns no jobs when no config exists', async () => { - await using fixture = await Fixture.create({}); + it('does not fall back to an ancestor when the nearest YAML is invalid', async () => { + await using fixture = await Fixture.create({ + 'pullup.yml': defaultYamlConfig, + nested: { 'pullup.yml': 'jobs: []\n' }, + }); - expect(await resolveConfig(fixture.root)).toEqual([]); - expect(console.warn).not.toHaveBeenCalled(); + await expect(resolveConfig(path.join(fixture.root, 'nested'))).rejects.toThrow('jobs must be an object.'); }); it('prefers pullup.yaml over pullup.yml', async () => { @@ -168,34 +146,30 @@ describe('resolveConfig', () => { expect(await resolveConfig(fixture.root)).toMatchObject([{ name: 'codeowners', output: 'yaml/CODEOWNERS' }]); }); - it('prefers pullup.config.js over pullup.yml', async () => { - await using fixture = await Fixture.create({ - 'package.json': JSON.stringify({ type: 'module' }), - 'pullup.config.js': esmConfig, - 'pullup.yml': 'jobs: []\n', - }); - expect(await resolveConfig(fixture.root)).toEqual(customJobs); - expect(console.warn).toHaveBeenCalledOnce(); - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining(path.join(fixture.root, 'pullup.config.js'))); - }); + it.each(['', '# empty configuration\n', 'null\n', '~\n', '---\n'])( + 'skips empty or null YAML before trying pullup.yml (%j)', + async (contents) => { + await using fixture = await Fixture.create({ + 'pullup.yaml': contents, + 'pullup.yml': defaultYamlConfig, + }); - it('prefers package.json over pullup.config.js', async () => { - await using fixture = await Fixture.create({ - 'package.json': JSON.stringify({ type: 'module', pullup: customJobs }), - 'pullup.config.js': 'export default [{ name: "other-job", input: [], output: "other.json" }];', - }); + expect(await resolveConfig(fixture.root)).toMatchObject([{ name: 'codeowners' }]); + }, + ); - expect(await resolveConfig(fixture.root)).toEqual(customJobs); - expect(console.warn).not.toHaveBeenCalled(); - }); + it('reports the source path for malformed YAML', async () => { + await using fixture = await Fixture.create({ 'pullup.yml': 'jobs: [\n' }); - it('uses cosmiconfig.searchPlaces from package.json', async () => { - await using fixture = await Fixture.create({ - 'package.json': JSON.stringify({ cosmiconfig: { searchPlaces: ['pullup.yml'], mergeSearchPlaces: false } }), - 'pullup.yml': `${defaultYamlConfig} output: custom/CODEOWNERS\n`, + await expect(resolveConfig(fixture.root)).rejects.toMatchObject({ + name: 'YAMLException', + message: expect.stringContaining(path.join(fixture.root, 'pullup.yml')), }); - vi.spyOn(process, 'cwd').mockReturnValue(fixture.root); + }); + + it('validates the YAML schema', async () => { + await using fixture = await Fixture.create({ 'pullup.yml': 'jobs: []\n' }); - expect(await resolveConfig(fixture.root)).toMatchObject([{ name: 'codeowners', output: 'custom/CODEOWNERS' }]); + await expect(resolveConfig(fixture.root)).rejects.toThrow('jobs must be an object.'); }); }); diff --git a/src/core/config/resolve-config.ts b/src/core/config/resolve-config.ts index a78f714..232acf4 100644 --- a/src/core/config/resolve-config.ts +++ b/src/core/config/resolve-config.ts @@ -1,43 +1,35 @@ +import { readFile } from 'node:fs/promises'; import path from 'node:path'; -import { styleText } from 'node:util'; -import { cosmiconfig, getDefaultSearchPlaces } from 'cosmiconfig'; +import { findUp } from 'find-up'; +import { load } from 'js-yaml'; import * as v from 'valibot'; -import { codeownersJob } from '../jobs'; +import { codeownersJob } from '../jobs/codeowners/codeowners-job'; import { customJob } from '../jobs/custom-job'; import type { Job } from '../types'; import { type Config, ConfigSchema } from './schema'; export async function resolveConfig(cwd: string): Promise { - const explorer = cosmiconfig('pullup', { - searchPlaces: [...getDefaultSearchPlaces('pullup'), 'pullup.yaml', 'pullup.yml'], - }); - const result = await explorer.search(cwd); - - if (result == null) { - return []; - } - - if (['pullup.yaml', 'pullup.yml'].includes(path.basename(result.filepath))) { - return toJobs(v.parse(ConfigSchema, result.config)); - } - - if (['.js', '.ts', '.cjs', '.mjs'].includes(path.extname(result.filepath))) { - console.warn( - styleText( - 'yellow', - [ - `[DEPRECATED] JavaScript/TypeScript configuration is deprecated: ${result.filepath}`, - 'Use pullup.yml or pullup.yaml for built-in jobs and external commands for custom transforms.', - 'Migration guide: https://github.com/toss/pull-up#migration', - '', - ].join('\n'), - ), - ); - } - - return result.config as Job[]; + let config: unknown; + const filepath = await findUp( + async (directory) => { + for (const filename of ['pullup.yaml', 'pullup.yml']) { + const configPath = path.join(directory, filename); + try { + config = load(await readFile(configPath, 'utf8'), { filename: configPath }); + } catch (error) { + if (['ENOENT', 'EISDIR', 'ENOTDIR', 'EACCES'].includes((error as NodeJS.ErrnoException).code ?? '')) continue; + throw error; + } + if (config !== undefined && config !== null) return filename; + } + return undefined; + }, + { cwd }, + ); + + return filepath === undefined ? [] : toJobs(v.parse(ConfigSchema, config)); } function toJobs(config: Config): Job[] { diff --git a/src/core/define-job.ts b/src/core/define-job.ts deleted file mode 100644 index 38574c1..0000000 --- a/src/core/define-job.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Job } from './types'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function defineJob(jobFactory: Job | ((...args: Args) => Job)) { - if (typeof jobFactory === 'function') { - return jobFactory; - } - - return () => jobFactory; -} diff --git a/src/core/index.ts b/src/core/index.ts deleted file mode 100644 index 5b4d646..0000000 --- a/src/core/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './config'; -export * from './define-job'; -export * from './jobs'; -export type * from './types'; diff --git a/src/core/jobs/codeowners/codeowners-job.ts b/src/core/jobs/codeowners/codeowners-job.ts index 07642be..ad3e517 100644 --- a/src/core/jobs/codeowners/codeowners-job.ts +++ b/src/core/jobs/codeowners/codeowners-job.ts @@ -1,7 +1,6 @@ import path from 'node:path'; -import { defineJob } from '../../define-job'; -import type { Source } from '../../types'; +import type { Job, Source } from '../../types'; import { Codeowners } from './codeowners'; interface CodeownersJobOptions { @@ -12,29 +11,31 @@ interface CodeownersJobOptions { const DEFAULT_FROM_PATTERN = ['**/CODEOWNERS']; const DEFAULT_OUTPUT_PATH = '.github/CODEOWNERS'; -export const codeownersJob = defineJob((options?: CodeownersJobOptions) => ({ - name: 'codeowners', - input: options?.input ?? DEFAULT_FROM_PATTERN, - output: options?.output ?? DEFAULT_OUTPUT_PATH, - transform: (inputFiles, { rootDir }) => { - const sortedInputFiles = sortByDirectory(inputFiles, rootDir); +export function codeownersJob(options?: CodeownersJobOptions): Job { + return { + name: 'codeowners', + input: options?.input ?? DEFAULT_FROM_PATTERN, + output: options?.output ?? DEFAULT_OUTPUT_PATH, + transform: (inputFiles, { rootDir }) => { + const sortedInputFiles = sortByDirectory(inputFiles, rootDir); - const codeowners = Codeowners.merge( - sortedInputFiles.map(({ file, baseDir }) => - Codeowners.from(file.contents).map(({ pattern, owners }) => ({ - pattern: toAbsolutePattern(pattern, baseDir), - owners, - })), - ), - ); + const codeowners = Codeowners.merge( + sortedInputFiles.map(({ file, baseDir }) => + Codeowners.from(file.contents).map(({ pattern, owners }) => ({ + pattern: toAbsolutePattern(pattern, baseDir), + owners, + })), + ), + ); - if (codeowners.isEmpty()) { - return ''; - } + if (codeowners.isEmpty()) { + return ''; + } - return codeowners.stringify(); - }, -})); + return codeowners.stringify(); + }, + }; +} // Compare directory segments to keep parents before their descendants. const sortByDirectory = (inputFiles: Source[], rootDir: string) => diff --git a/src/core/jobs/codeowners/index.ts b/src/core/jobs/codeowners/index.ts deleted file mode 100644 index 588e8de..0000000 --- a/src/core/jobs/codeowners/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './codeowners-job'; diff --git a/src/core/jobs/custom-job.ts b/src/core/jobs/custom-job.ts index 2d1d6cc..59e2de2 100644 --- a/src/core/jobs/custom-job.ts +++ b/src/core/jobs/custom-job.ts @@ -1,6 +1,6 @@ import { execa } from 'execa'; -import { defineJob } from '../define-job'; +import type { Job } from '../types'; interface CustomJobOptions { command: string; @@ -8,19 +8,21 @@ interface CustomJobOptions { output: string; } -export const customJob = defineJob((options: CustomJobOptions) => ({ - name: 'custom', - input: options.input ?? [], - output: options.output, - transform: async (inputFiles, context) => { - const { stdout } = await execa('sh', ['-c', options.command], { - cwd: context.rootDir, - input: JSON.stringify({ sources: inputFiles, context }), - stripFinalNewline: false, - maxBuffer: Infinity, - killDescendants: true, - }); +export function customJob(options: CustomJobOptions): Job { + return { + name: 'custom', + input: options.input ?? [], + output: options.output, + transform: async (inputFiles, context) => { + const { stdout } = await execa('sh', ['-c', options.command], { + cwd: context.rootDir, + input: JSON.stringify({ sources: inputFiles, context }), + stripFinalNewline: false, + maxBuffer: Infinity, + killDescendants: true, + }); - return stdout; - }, -})); + return stdout; + }, + }; +} diff --git a/src/core/jobs/index.ts b/src/core/jobs/index.ts deleted file mode 100644 index 6a8ca6e..0000000 --- a/src/core/jobs/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './codeowners/codeowners-job'; diff --git a/tests/build-cli.ts b/tests/build-cli.ts index 7707b0e..a62b111 100644 --- a/tests/build-cli.ts +++ b/tests/build-cli.ts @@ -1,9 +1,51 @@ -import { execFile } from 'node:child_process'; +import { symlink } from 'node:fs/promises'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { promisify } from 'node:util'; -export default async function buildCli() { - await promisify(execFile)('yarn', ['build'], { - cwd: fileURLToPath(new URL('../', import.meta.url)), +import { Fixture } from '@fixture-kit/core'; +import { execa } from 'execa'; +import type { TestProject } from 'vitest/node'; + +export default async function buildCli(project: TestProject) { + const executable = project.name === 'exe'; + const rootDir = fileURLToPath(new URL('../', import.meta.url)); + if (executable && !['darwin', 'linux'].includes(process.platform)) { + throw new Error('Executable tests require macOS or Linux.'); + } + + await execa('yarn', ['build'], { + cwd: rootDir, }); + + const env: NodeJS.ProcessEnv = { ...process.env, NO_COLOR: '1' }; + delete env.FORCE_COLOR; + + if (!executable) { + project.provide('cli', { + command: process.execPath, + args: [path.join(rootDir, 'dist/bin/index.mjs')], + env, + }); + return; + } + + const fixture = await Fixture.create({}); + try { + await symlink('/bin/sh', path.join(fixture.root, 'sh')); + env.PATH = fixture.root; + for (const key of Object.keys(env)) { + if (['NODE_OPTIONS', 'NODE_PATH', 'PNPAPI'].includes(key) || /^(?:YARN_|PNP_)/.test(key)) { + delete env[key]; + } + } + project.provide('cli', { + command: path.join(rootDir, 'build', `pullup-${process.platform}-${process.arch}`), + args: [], + env, + }); + return () => fixture.cleanup(); + } catch (error) { + await fixture.cleanup(); + throw error; + } } diff --git a/tests/cli/cli-options.spec.ts b/tests/cli/cli-options.spec.ts new file mode 100644 index 0000000..0c33397 --- /dev/null +++ b/tests/cli/cli-options.spec.ts @@ -0,0 +1,28 @@ +import { Fixture } from '@fixture-kit/core'; + +import packageJson from '../../package.json' with { type: 'json' }; +import { runCli } from './run-cli'; + +describe('CLI options', () => { + it('shows help without a repository or config', async () => { + await using fixture = await Fixture.create({}); + + const result = await runCli('--help', fixture.root); + + expect(result.code).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toContain('pullup'); + expect(result.stdout).toContain('pullup sync'); + expect(result.stdout).toContain('pullup check'); + }); + + it('shows the package version without a repository or config', async () => { + await using fixture = await Fixture.create({}); + + const result = await runCli('--version', fixture.root); + + expect(result.code).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toBe(`${packageJson.version}\n`); + }); +}); diff --git a/tests/codeowners-cli.spec.ts b/tests/cli/codeowners-cli.spec.ts similarity index 52% rename from tests/codeowners-cli.spec.ts rename to tests/cli/codeowners-cli.spec.ts index cb1f047..48f90e9 100644 --- a/tests/codeowners-cli.spec.ts +++ b/tests/cli/codeowners-cli.spec.ts @@ -1,45 +1,40 @@ -import { execFile } from 'node:child_process'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { promisify } from 'node:util'; import { Fixture } from '@fixture-kit/core'; -const execFileAsync = promisify(execFile); -const CLI_PATH = fileURLToPath(new URL('../dist/bin/index.mjs', import.meta.url)); -const CORE_URL = new URL('../dist/core/index.mjs', import.meta.url).href; -const config = `import { codeownersJob } from ${JSON.stringify(CORE_URL)}; -export default [codeownersJob()]; -`; +import { runCli } from './run-cli'; -function expectScriptConfigWarning(result: { stdout: string; stderr: string }, configPath: string) { - expect(result.stderr).toBe( - `[DEPRECATED] JavaScript/TypeScript configuration is deprecated: ${configPath}\n` + - 'Use pullup.yml or pullup.yaml for built-in jobs and external commands for custom transforms.\n' + - 'Migration guide: https://github.com/toss/pull-up#migration\n\n', - ); - expect(result.stderr.match(/\[DEPRECATED\]/g)).toHaveLength(1); - expect(result.stdout).not.toContain('[DEPRECATED]'); -} +const config = `jobs: + codeowners: + type: codeowners +`; describe('codeowners-cli', () => { it.each([ - { name: 'the repository root', cwd: 'repo', explicitRoot: false }, - { name: 'a subdirectory', cwd: 'repo/services/auth', explicitRoot: false }, + { name: 'the repository root', cwd: 'repo', output: 'root-CODEOWNERS', explicitRoot: false }, + { + name: 'a subdirectory', + cwd: 'repo/services/auth', + output: 'nested-CODEOWNERS', + explicitRoot: false, + }, { name: 'another repository with --root', cwd: 'other-repo', + output: 'other-CODEOWNERS', explicitRoot: true, }, { name: 'a subdirectory selected with --cwd', cwd: 'other-repo', + output: 'nested-CODEOWNERS', explicitRoot: false, explicitCwd: true, }, ])('sync and check resolve CODEOWNERS paths from $name', async (scenario) => { - await using fixture = await Fixture.fromDirectory(fileURLToPath(new URL('../fixtures/', import.meta.url))); + await using fixture = await Fixture.fromDirectory(fileURLToPath(new URL('../../fixtures/', import.meta.url))); const rootDir = path.join(fixture.root, 'repo'); const subdirectory = path.join(rootDir, 'services/auth'); const otherRoot = path.join(fixture.root, 'other-repo'); @@ -48,24 +43,22 @@ describe('codeowners-cli', () => { mkdir(path.join(rootDir, '.git'), { recursive: true }), mkdir(path.join(otherRoot, '.git'), { recursive: true }), ]); - // Config discovery uses cwd independently of the repository root. - for (const directory of [rootDir, subdirectory, otherRoot]) { - await writeFile(path.join(directory, 'pullup.config.mjs'), config); + for (const { directory, output } of [ + { directory: rootDir, output: 'root-CODEOWNERS' }, + { directory: subdirectory, output: 'nested-CODEOWNERS' }, + { directory: otherRoot, output: 'other-CODEOWNERS' }, + ]) { + await writeFile(path.join(directory, 'pullup.yml'), `${config} output: .github/${output}\n`); } const options: string[] = []; if (scenario.explicitRoot) options.push('--root', rootDir); if (scenario.explicitCwd === true) options.push('--cwd', subdirectory); - const configDirectory = scenario.explicitCwd === true ? subdirectory : path.join(fixture.root, scenario.cwd); - const configPath = path.join(configDirectory, 'pullup.config.mjs'); - - const sync = await execFileAsync(process.execPath, [CLI_PATH, 'sync', ...options], { - cwd: path.join(fixture.root, scenario.cwd), - timeout: 10_000, - }); + const sync = await runCli('sync', path.join(fixture.root, scenario.cwd), options); + expect(sync.code).toBe(0); expect(sync.stdout).toContain('codeowners synced'); - expectScriptConfigWarning(sync, configPath); + expect(sync.stderr).toBe(''); - expect(await readFile(path.join(rootDir, '.github/CODEOWNERS'), 'utf8')).toBe( + expect(await readFile(path.join(rootDir, '.github', scenario.output), 'utf8')).toBe( '* @root-team\n' + '/docs/ @docs-team\n' + '/services/ads/ @ads-team\n' + @@ -80,15 +73,17 @@ describe('codeowners-cli', () => { '/services/cart/ @cart-team\n' + '/tools/catalog-cli/ @tools-team\n', ); - const check = await execFileAsync(process.execPath, [CLI_PATH, 'check', ...options], { - cwd: path.join(fixture.root, scenario.cwd), - timeout: 10_000, - }); + const check = await runCli('check', path.join(fixture.root, scenario.cwd), options); + expect(check.code).toBe(0); expect(check.stdout).toContain('All files are up to date'); - expectScriptConfigWarning(check, configPath); + expect(check.stderr).toBe(''); + for (const output of ['root-CODEOWNERS', 'nested-CODEOWNERS', 'other-CODEOWNERS']) { + if (output === scenario.output) continue; + await expect(readFile(path.join(rootDir, '.github', output), 'utf8')).rejects.toMatchObject({ code: 'ENOENT' }); + } if (scenario.explicitRoot || scenario.explicitCwd === true) { - await expect(readFile(path.join(otherRoot, '.github/CODEOWNERS'), 'utf8')).rejects.toMatchObject({ + await expect(readFile(path.join(otherRoot, '.github', scenario.output), 'utf8')).rejects.toMatchObject({ code: 'ENOENT', }); } @@ -104,20 +99,20 @@ describe('codeowners-cli', () => { input: ['services/auth/CODEOWNERS', 'services/../services/auth/login/CODEOWNERS'], }, ])('sync normalizes $name in CODEOWNERS input paths before sorting', async (scenario) => { - await using fixture = await Fixture.fromDirectory(fileURLToPath(new URL('../fixtures/repo/', import.meta.url))); + await using fixture = await Fixture.fromDirectory(fileURLToPath(new URL('../../fixtures/repo/', import.meta.url))); const rootDir = fixture.root; - const customConfig = `import { codeownersJob } from ${JSON.stringify(CORE_URL)}; -export default [codeownersJob({ input: ${JSON.stringify(scenario.input)} })]; + const customConfig = `jobs: + codeowners: + type: codeowners + input: ${JSON.stringify(scenario.input)} `; await mkdir(path.join(rootDir, '.git')); - await writeFile(path.join(rootDir, 'pullup.config.mjs'), customConfig); - const sync = await execFileAsync(process.execPath, [CLI_PATH, 'sync'], { - cwd: rootDir, - timeout: 10_000, - }); + await writeFile(path.join(rootDir, 'pullup.yml'), customConfig); + const sync = await runCli('sync', rootDir); - expectScriptConfigWarning(sync, path.join(rootDir, 'pullup.config.mjs')); + expect(sync.code).toBe(0); + expect(sync.stderr).toBe(''); expect(await readFile(path.join(rootDir, '.github/CODEOWNERS'), 'utf8')).toBe( '/services/auth/ @auth-team\n' + '/services/auth/login/ @login-team\n', ); @@ -126,7 +121,7 @@ export default [codeownersJob({ input: ${JSON.stringify(scenario.input)} })]; it('sync preserves unanchored patterns from root and nested CODEOWNERS files', async () => { await using fixture = await Fixture.create({ '.git': {}, - 'pullup.config.mjs': config, + 'pullup.yml': config, CODEOWNERS: '*.js @root-team\n', packages: { web: { @@ -135,10 +130,11 @@ export default [codeownersJob({ input: ${JSON.stringify(scenario.input)} })]; }, }); - const sync = await execFileAsync(process.execPath, [CLI_PATH, 'sync'], { cwd: fixture.root, timeout: 10_000 }); + const sync = await runCli('sync', fixture.root); + expect(sync.code).toBe(0); expect(sync.stdout).toContain('codeowners synced'); - expectScriptConfigWarning(sync, path.join(fixture.root, 'pullup.config.mjs')); + expect(sync.stderr).toBe(''); expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe( '*.js @root-team\n' + '/packages/web/**/*.ts @frontend-team\n' + @@ -147,9 +143,10 @@ export default [codeownersJob({ input: ${JSON.stringify(scenario.input)} })]; '/packages/web/src/*.ts @source-team\n', ); - const check = await execFileAsync(process.execPath, [CLI_PATH, 'check'], { cwd: fixture.root, timeout: 10_000 }); + const check = await runCli('check', fixture.root); + expect(check.code).toBe(0); expect(check.stdout).toContain('All files are up to date'); - expectScriptConfigWarning(check, path.join(fixture.root, 'pullup.config.mjs')); + expect(check.stderr).toBe(''); }); }); diff --git a/tests/cli/config-cli.spec.ts b/tests/cli/config-cli.spec.ts new file mode 100644 index 0000000..91fa8c1 --- /dev/null +++ b/tests/cli/config-cli.spec.ts @@ -0,0 +1,256 @@ +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; + +import { Fixture } from '@fixture-kit/core'; + +import { runCli } from './run-cli'; + +const existingContents = '* @existing-team\n'; +const config = `jobs: + codeowners: + type: codeowners + input: + - '**/CODEOWNERS' + - '!**/fixtures/**' + output: .github/CODEOWNERS +`; + +describe('YAML config CLI', () => { + it.each(['pullup.yaml', 'pullup.yml'])('syncs and checks a codeowners job from %s', async (filename) => { + await using fixture = await Fixture.create({ + '.git': {}, + [filename]: config, + '.github': { CODEOWNERS: existingContents }, + CODEOWNERS: '* @root-team\n', + packages: { + web: { CODEOWNERS: '*.ts @frontend-team\n' }, + fixtures: { CODEOWNERS: '* @ignored-team\n' }, + }, + }); + const outputPath = path.join(fixture.root, '.github/CODEOWNERS'); + + const sync = await runCli('sync', fixture.root); + + expect(sync.code).toBe(0); + expect(sync.stderr).toBe(''); + expect(sync.stdout).toBe('✔ codeowners synced\n'); + expect(await readFile(outputPath, 'utf8')).toBe('* @root-team\n/packages/web/**/*.ts @frontend-team\n'); + + const check = await runCli('check', fixture.root); + + expect(check.code).toBe(0); + expect(check.stderr).toBe(''); + expect(check.stdout).toBe('✔ All files are up to date\n'); + }); + + it('previews sync --dry-run without changing the output', async () => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': config, + '.github': { CODEOWNERS: existingContents }, + CODEOWNERS: '* @root-team\n', + packages: { web: { CODEOWNERS: '*.ts @frontend-team\n' } }, + }); + + const result = await runCli('sync', fixture.root, ['--dry-run']); + + expect(result.code).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toBe( + '┌─ [Job] codeowners\n' + + '│ Output: .github/CODEOWNERS\n' + + '│\n' + + '│ * @root-team\n' + + '│ /packages/web/**/*.ts @frontend-team\n' + + '│ \n' + + '└─\n', + ); + expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); + }); + + it('fails an outdated check without changing the output', async () => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': config, + '.github': { CODEOWNERS: existingContents }, + CODEOWNERS: '* @source-team\n', + }); + + const result = await runCli('check', fixture.root); + + expect(result.code).toBe(1); + expect(result.stdout).toBe(''); + expect(result.stderr).toBe("✘ codeowners is outdated. Run 'pullup sync' to update.\n"); + expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); + }); + + it('rejects malformed YAML without changing the output', async () => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': 'jobs: [\n', + '.github': { CODEOWNERS: existingContents }, + CODEOWNERS: '* @source-team\n', + }); + + const result = await runCli('sync', fixture.root); + const output = result.stdout + result.stderr; + + expect(result.code).toBe(1); + expect(output).toContain('YAMLException'); + expect(output).toContain(path.join(fixture.root, 'pullup.yml')); + expect(output).not.toContain('synced'); + expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); + }); + + describe.each(['sync', 'check'])('%s', (command) => { + it.each([ + { name: 'the nearest YAML in a subdirectory', cwd: 'repo/nested', output: 'nested-owners.txt' }, + { name: 'an ancestor YAML when the child has none', cwd: 'repo/nested/child', output: 'nested-owners.txt' }, + { + name: 'the cwd YAML with a different --root', + cwd: 'other-repo', + output: 'other-owners.txt', + explicitRoot: true, + }, + { + name: 'the YAML selected by --cwd', + cwd: 'other-repo', + output: 'nested-owners.txt', + explicitCwd: true, + }, + ])('uses $name while resolving input and output from the repository root', async (scenario) => { + const generatedContents = '* @repo-team\n'; + await using fixture = await Fixture.create({ + repo: { + '.git': {}, + 'pullup.yaml': + 'jobs:\n owners:\n type: codeowners\n input: [CODEOWNERS]\n output: root-owners.txt\n', + CODEOWNERS: generatedContents, + 'root-owners.txt': existingContents, + 'nested-owners.txt': existingContents, + 'other-owners.txt': existingContents, + [scenario.output]: command === 'check' ? generatedContents : existingContents, + nested: { + 'pullup.yml': + 'jobs:\n owners:\n type: codeowners\n input: [CODEOWNERS]\n output: nested-owners.txt\n', + child: {}, + }, + }, + 'other-repo': { + '.git': {}, + 'pullup.yml': + 'jobs:\n owners:\n type: codeowners\n input: [CODEOWNERS]\n output: other-owners.txt\n', + CODEOWNERS: '* @other-team\n', + }, + }); + const rootDir = path.join(fixture.root, 'repo'); + const args: string[] = []; + if (scenario.explicitRoot === true) args.push('--root', rootDir); + if (scenario.explicitCwd === true) args.push('--cwd', path.join(rootDir, 'nested')); + + const result = await runCli(command, path.join(fixture.root, scenario.cwd), args); + + expect(result.code).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toBe(command === 'sync' ? '✔ codeowners synced\n' : '✔ All files are up to date\n'); + for (const output of ['root-owners.txt', 'nested-owners.txt', 'other-owners.txt']) { + expect(await readFile(path.join(rootDir, output), 'utf8')).toBe( + output === scenario.output ? generatedContents : existingContents, + ); + } + await expect(readFile(path.join(fixture.root, 'other-repo', scenario.output), 'utf8')).rejects.toMatchObject({ + code: 'ENOENT', + }); + }); + + it('uses YAML while ignoring script configs and package config overrides', async () => { + const generatedContents = '* @yaml-team\n'; + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': config, + 'pullup.config.ts': 'throw new Error("Legacy config executed");\n', + '.config': { 'config.ts': 'throw new Error("Meta config executed");\n' }, + 'package.json': JSON.stringify({ + pullup: [{ name: 'legacy', input: [], output: 'legacy-output.txt' }], + cosmiconfig: { searchPlaces: ['pullup.config.ts'], mergeSearchPlaces: false }, + }), + CODEOWNERS: generatedContents, + '.github': { CODEOWNERS: command === 'check' ? generatedContents : existingContents }, + }); + + const result = await runCli(command, fixture.root); + + expect(result.code).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toBe(command === 'sync' ? '✔ codeowners synced\n' : '✔ All files are up to date\n'); + expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(generatedContents); + await expect(readFile(path.join(fixture.root, 'legacy-output.txt'), 'utf8')).rejects.toMatchObject({ + code: 'ENOENT', + }); + }); + + it.each([ + { name: 'an empty jobs array', config: 'jobs: []\n' }, + { name: 'a nonempty jobs array', config: 'jobs:\n - type: codeowners\n' }, + ])('rejects $name without changing the output', async (scenario) => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': scenario.config, + '.github': { CODEOWNERS: existingContents }, + CODEOWNERS: '* @source-team\n', + }); + + const result = await runCli(command, fixture.root); + const output = result.stdout + result.stderr; + + expect(result.code).toBe(1); + expect(output).toContain('jobs must be an object.'); + expect(output).not.toContain('synced'); + expect(output).not.toContain('All files are up to date'); + expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); + }); + + it.each( + ['constructor', 'prototype', '__proto__'].flatMap((key) => [ + { + name: `a ${key} job`, + config: `jobs:\n ${key}:\n type: codeowners\n`, + }, + { + name: `a ${key} job alongside an owners job`, + config: `jobs:\n owners:\n type: codeowners\n output: .github/CODEOWNERS\n ${key}:\n type: codeowners\n`, + }, + ]), + )('rejects $name without changing the output', async (scenario) => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': scenario.config, + '.github': { CODEOWNERS: existingContents }, + CODEOWNERS: '* @source-team\n', + }); + + const result = await runCli(command, fixture.root); + const output = result.stdout + result.stderr; + + expect(result.code).toBe(1); + expect(output).toContain('jobs must not use constructor, prototype, or __proto__ as keys.'); + expect(output).not.toContain('synced'); + expect(output).not.toContain('All files are up to date'); + expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); + }); + + it('succeeds without a config and leaves the output unchanged', async () => { + await using fixture = await Fixture.create({ + '.git': {}, + '.github': { CODEOWNERS: existingContents }, + }); + + const result = await runCli(command, fixture.root); + + expect(result.code).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toContain(`No jobs found to ${command}`); + expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); + }); + }); +}); diff --git a/tests/cli/custom-job-cli.spec.ts b/tests/cli/custom-job-cli.spec.ts new file mode 100644 index 0000000..a2feecd --- /dev/null +++ b/tests/cli/custom-job-cli.spec.ts @@ -0,0 +1,140 @@ +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; + +import { Fixture } from '@fixture-kit/core'; + +import { runCli } from './run-cli'; + +const sourceContents = '한글 source\r\n'; +const existingContents = 'existing output\n'; +const config = String.raw`jobs: + custom: + type: custom + input: ['source.txt'] + output: generated.txt + command: >- + IFS= read -r request || :; printf '%s\r\n\n' "$request" +`; +const generatedOutput = 'custom generated output\n'; +const fixedConfig = String.raw`jobs: + custom: + type: custom + output: generated.txt + command: printf '%s\n' 'custom generated output' +`; + +describe('custom command CLI', () => { + it.each([ + { name: 'repository discovery', args: [] }, + { name: 'relative --root with another --cwd', args: ['--root', '.', '--cwd', 'nested'] }, + ])('passes sources and absolute context on stdin and preserves stdout with $name', async ({ args }) => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': args.length === 0 ? config : config.replace('output: generated.txt', 'output: root-generated.txt'), + 'source.txt': sourceContents, + 'generated.txt': existingContents, + nested: { 'pullup.yml': config }, + }); + const outputPath = path.join(fixture.root, 'generated.txt'); + const generatedContents = + JSON.stringify({ + sources: [{ path: 'source.txt', contents: sourceContents }], + context: { rootDir: fixture.root, outputPath }, + }) + '\r\n\n'; + + const sync = await runCli('sync', fixture.root, args); + + expect(sync.code).toBe(0); + expect(sync.stdout).toBe('✔ custom synced\n'); + expect(sync.stderr).toBe(''); + expect(await readFile(outputPath, 'utf8')).toBe(generatedContents); + + const check = await runCli('check', fixture.root, args); + + expect(check.code).toBe(0); + expect(check.stdout).toBe('✔ All files are up to date\n'); + expect(check.stderr).toBe(''); + expect(await readFile(outputPath, 'utf8')).toBe(generatedContents); + await expect(readFile(path.join(fixture.root, 'root-generated.txt'), 'utf8')).rejects.toMatchObject({ + code: 'ENOENT', + }); + }); + + it('previews a custom command without changing the output', async () => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': fixedConfig, + 'generated.txt': existingContents, + }); + + const result = await runCli('sync', fixture.root, ['--dry-run']); + + expect(result.code).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toBe( + '┌─ [Job] custom\n' + + '│ Output: generated.txt\n' + + '│\n' + + `│ ${generatedOutput.trimEnd()}\n` + + '│ \n' + + '└─\n', + ); + expect(await readFile(path.join(fixture.root, 'generated.txt'), 'utf8')).toBe(existingContents); + }); + + it('fails an outdated custom check without changing the output', async () => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': fixedConfig, + 'generated.txt': existingContents, + }); + + const result = await runCli('check', fixture.root); + + expect(result.code).toBe(1); + expect(result.stdout).toBe(''); + expect(result.stderr).toBe("✘ custom is outdated. Run 'pullup sync' to update.\n"); + expect(await readFile(path.join(fixture.root, 'generated.txt'), 'utf8')).toBe(existingContents); + }); + + it('writes empty stdout without adding a newline', async () => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': config.replace(/printf .+/, ':'), + 'source.txt': sourceContents, + 'generated.txt': existingContents, + }); + + const result = await runCli('sync', fixture.root); + + expect(result.code).toBe(0); + expect(result.stdout).toBe('✔ custom synced\n'); + expect(result.stderr).toBe(''); + expect(await readFile(path.join(fixture.root, 'generated.txt'), 'utf8')).toBe(''); + }); + + it('reports a failed command without writing its partial stdout', async () => { + await using fixture = await Fixture.create({ + '.git': {}, + 'pullup.yml': String.raw`jobs: + custom: + type: custom + input: ['source.txt'] + output: generated.txt + command: >- + IFS= read -r request || :; printf '%s' 'partial output'; printf '%s\n' 'custom transform failed' >&2; exit 7 +`, + 'source.txt': sourceContents, + 'generated.txt': existingContents, + }); + + const result = await runCli('sync', fixture.root); + const output = result.stdout + result.stderr; + + expect(result.code).toBe(1); + expect(output).toContain('exit code 7'); + expect(output).toContain('custom transform failed'); + expect(output).not.toContain('synced'); + expect(await readFile(path.join(fixture.root, 'generated.txt'), 'utf8')).toBe(existingContents); + }); +}); diff --git a/tests/output-read-errors.spec.ts b/tests/cli/output-read-errors.spec.ts similarity index 50% rename from tests/output-read-errors.spec.ts rename to tests/cli/output-read-errors.spec.ts index 8899363..363455d 100644 --- a/tests/output-read-errors.spec.ts +++ b/tests/cli/output-read-errors.spec.ts @@ -1,64 +1,43 @@ -import { execFile } from 'node:child_process'; import { chmod, mkdir, readFile, stat, writeFile } from 'node:fs/promises'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { promisify } from 'node:util'; import { Fixture } from '@fixture-kit/core'; -const execFileAsync = promisify(execFile); -const CLI_PATH = fileURLToPath(new URL('../dist/bin/index.mjs', import.meta.url)); -const fixtureDirectory = fileURLToPath(new URL('../fixtures/repo/', import.meta.url)); +import { runCli } from './run-cli'; + const output = '.github/CODEOWNERS'; const existingContents = 'existing output\n'; -const config = `export default [ - { name: 'writable', input: [], output: 'writable.txt', transform: () => 'updated output\\n' }, - { name: 'unreadable', input: [], output: '${output}', transform: () => 'generated output\\n' }, -]; +const config = String.raw`jobs: + writable: + type: custom + output: writable.txt + command: >- + IFS= read -r request || :; printf 'updated output\n' + unreadable: + type: custom + output: .github/CODEOWNERS + command: >- + IFS= read -r request || :; printf 'generated output\n' `; -function scriptConfigWarning(rootDir: string) { - return ( - `[DEPRECATED] JavaScript/TypeScript configuration is deprecated: ${path.join(rootDir, 'pullup.config.mjs')}\n` + - 'Use pullup.yml or pullup.yaml for built-in jobs and external commands for custom transforms.\n' + - 'Migration guide: https://github.com/toss/pull-up#migration\n\n' - ); -} - -function expectScriptConfigWarning(result: { stdout: string; stderr: string }, rootDir: string) { - expect(result.stderr).toContain(scriptConfigWarning(rootDir)); - expect(result.stderr.match(/\[DEPRECATED\]/g)).toHaveLength(1); - expect(result.stdout).not.toContain('[DEPRECATED]'); -} - -async function prepareRepository(rootDir: string) { - await mkdir(path.join(rootDir, '.git')); - await mkdir(path.join(rootDir, '.github'), { recursive: true }); - await writeFile(path.join(rootDir, 'pullup.config.mjs'), config); - await writeFile(path.join(rootDir, 'writable.txt'), existingContents); -} - -function runCli(command: string, rootDir: string) { - return execFileAsync(process.execPath, [CLI_PATH, command], { - cwd: rootDir, - timeout: 10_000, - }).then( - (result) => ({ ...result, code: 0 }), - (error: { code: number; stdout: string; stderr: string }) => error, - ); +function createRepository() { + return Fixture.create({ + '.git': {}, + '.github': {}, + 'pullup.yml': config, + 'writable.txt': existingContents, + }); } describe('output read errors', () => { it.each(['check', 'sync'])('%s reports an output directory without changing other outputs', async (command) => { - await using fixture = await Fixture.fromDirectory(fixtureDirectory); - await prepareRepository(fixture.root); + await using fixture = await createRepository(); const outputPath = path.join(fixture.root, output); await mkdir(outputPath); const result = await runCli(command, fixture.root); expect(result.code).toBe(1); - expectScriptConfigWarning(result, fixture.root); expect(result.stdout + result.stderr).toContain('EISDIR'); expect(result.stdout + result.stderr).toContain(outputPath); expect(result.stdout + result.stderr).not.toContain('outdated'); @@ -71,8 +50,7 @@ describe('output read errors', () => { it.skipIf(process.platform === 'win32' || process.getuid?.() === 0).each(['check', 'sync'])( '%s reports an unreadable but writable output without overwriting it', async (command) => { - await using fixture = await Fixture.fromDirectory(fixtureDirectory); - await prepareRepository(fixture.root); + await using fixture = await createRepository(); const outputPath = path.join(fixture.root, output); await writeFile(outputPath, existingContents); await chmod(outputPath, 0o200); @@ -81,7 +59,6 @@ describe('output read errors', () => { const result = await runCli(command, fixture.root); expect(result.code).toBe(1); - expectScriptConfigWarning(result, fixture.root); expect(result.stdout + result.stderr).toContain('EACCES'); expect(result.stdout + result.stderr).toContain(outputPath); expect(result.stdout + result.stderr).not.toContain('outdated'); @@ -97,22 +74,19 @@ describe('output read errors', () => { ); it('still creates missing outputs and checks their generated contents', async () => { - await using fixture = await Fixture.fromDirectory(fixtureDirectory); - await prepareRepository(fixture.root); + await using fixture = await createRepository(); const sync = await runCli('sync', fixture.root); expect(sync.code).toBe(0); - expect(sync.stderr).toBe(scriptConfigWarning(fixture.root)); - expectScriptConfigWarning(sync, fixture.root); - expect(sync.stdout).toContain('unreadable synced'); + expect(sync.stderr).toBe(''); + expect(sync.stdout).toContain('custom synced'); expect(await readFile(path.join(fixture.root, output), 'utf8')).toBe('generated output\n'); const check = await runCli('check', fixture.root); expect(check.code).toBe(0); - expect(check.stderr).toBe(scriptConfigWarning(fixture.root)); - expectScriptConfigWarning(check, fixture.root); + expect(check.stderr).toBe(''); expect(check.stdout).toContain('All files are up to date'); }); }); diff --git a/tests/cli/run-cli.ts b/tests/cli/run-cli.ts new file mode 100644 index 0000000..191ce77 --- /dev/null +++ b/tests/cli/run-cli.ts @@ -0,0 +1,31 @@ +import { execa } from 'execa'; +import { inject } from 'vitest'; + +export interface CliRunner { + command: string; + args: string[]; + env: NodeJS.ProcessEnv; +} + +declare module 'vitest' { + export interface ProvidedContext { + cli: CliRunner; + } +} + +export async function runCli(command: string, rootDir: string, args: string[] = []) { + const cli = inject('cli'); + const result = await execa(cli.command, [...cli.args, command, ...args], { + cwd: rootDir, + env: cli.env, + extendEnv: false, + reject: false, + stripFinalNewline: false, + timeout: 30_000, + maxBuffer: 4 * 1024 * 1024, + }); + if (result.exitCode === undefined) { + throw result; + } + return { code: result.exitCode, stdout: result.stdout, stderr: result.stderr }; +} diff --git a/tests/config-cli.spec.ts b/tests/config-cli.spec.ts deleted file mode 100644 index 0144574..0000000 --- a/tests/config-cli.spec.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { execFile } from 'node:child_process'; -import { readFile } from 'node:fs/promises'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { promisify } from 'node:util'; - -import { Fixture } from '@fixture-kit/core'; - -const execFileAsync = promisify(execFile); -const CLI_PATH = fileURLToPath(new URL('../dist/bin/index.mjs', import.meta.url)); -const existingContents = '* @existing-team\n'; -const generatedContents = 'custom generated output\n'; -const scriptConfig = `export default [ - { name: 'custom', input: [], output: '.github/CODEOWNERS', transform: () => ${JSON.stringify(generatedContents)} }, -]; -`; -const config = `jobs: - codeowners: - type: codeowners - input: - - '**/CODEOWNERS' - - '!**/fixtures/**' - output: .github/CODEOWNERS -`; - -function scriptConfigWarning(configPath: string) { - return ( - `[DEPRECATED] JavaScript/TypeScript configuration is deprecated: ${configPath}\n` + - 'Use pullup.yml or pullup.yaml for built-in jobs and external commands for custom transforms.\n' + - 'Migration guide: https://github.com/toss/pull-up#migration\n\n' - ); -} - -function runCli(command: string, rootDir: string, args: string[] = []) { - return execFileAsync(process.execPath, [CLI_PATH, command, ...args], { - cwd: rootDir, - timeout: 10_000, - }).then( - (result) => ({ ...result, code: 0 }), - (error: { code: number; stdout: string; stderr: string }) => error, - ); -} - -describe('YAML config CLI', () => { - it.each(['pullup.yaml', 'pullup.yml'])('syncs and checks a codeowners job from %s', async (filename) => { - await using fixture = await Fixture.create({ - '.git': {}, - [filename]: config, - '.github': { CODEOWNERS: existingContents }, - CODEOWNERS: '* @root-team\n', - packages: { - web: { CODEOWNERS: '*.ts @frontend-team\n' }, - fixtures: { CODEOWNERS: '* @ignored-team\n' }, - }, - }); - const outputPath = path.join(fixture.root, '.github/CODEOWNERS'); - - const sync = await runCli('sync', fixture.root); - - expect(sync.code).toBe(0); - expect(sync.stderr).toBe(''); - expect(sync.stdout).toContain('codeowners synced'); - expect(await readFile(outputPath, 'utf8')).toBe('* @root-team\n/packages/web/**/*.ts @frontend-team\n'); - - const check = await runCli('check', fixture.root); - - expect(check.code).toBe(0); - expect(check.stderr).toBe(''); - expect(check.stdout).toContain('All files are up to date'); - }); - - describe.each(['sync', 'check'])('%s', (command) => { - it.each([ - { name: 'an empty jobs array', config: 'jobs: []\n' }, - { name: 'a nonempty jobs array', config: 'jobs:\n - type: codeowners\n' }, - ])('rejects $name without changing the output', async (scenario) => { - await using fixture = await Fixture.create({ - '.git': {}, - 'pullup.yml': scenario.config, - '.github': { CODEOWNERS: existingContents }, - CODEOWNERS: '* @source-team\n', - }); - - const result = await runCli(command, fixture.root); - const output = result.stdout + result.stderr; - - expect(result.code).toBe(1); - expect(output).toContain('jobs must be an object.'); - expect(output).not.toContain('synced'); - expect(output).not.toContain('All files are up to date'); - expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); - }); - - it.each( - ['constructor', 'prototype', '__proto__'].flatMap((key) => [ - { - name: `a ${key} job`, - config: `jobs:\n ${key}:\n type: codeowners\n`, - }, - { - name: `a ${key} job alongside an owners job`, - config: `jobs:\n owners:\n type: codeowners\n output: .github/CODEOWNERS\n ${key}:\n type: codeowners\n`, - }, - ]), - )('rejects $name without changing the output', async (scenario) => { - await using fixture = await Fixture.create({ - '.git': {}, - 'pullup.yml': scenario.config, - '.github': { CODEOWNERS: existingContents }, - CODEOWNERS: '* @source-team\n', - }); - - const result = await runCli(command, fixture.root); - const output = result.stdout + result.stderr; - - expect(result.code).toBe(1); - expect(output).toContain('jobs must not use constructor, prototype, or __proto__ as keys.'); - expect(output).not.toContain('synced'); - expect(output).not.toContain('All files are up to date'); - expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); - }); - - it('succeeds without a config and leaves the output unchanged', async () => { - await using fixture = await Fixture.create({ - '.git': {}, - '.github': { CODEOWNERS: existingContents }, - }); - - const result = await runCli(command, fixture.root); - - expect(result.code).toBe(0); - expect(result.stderr).toBe(''); - expect(result.stdout).toContain(`No jobs found to ${command}`); - expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); - }); - - it('uses pullup.config.mjs before pullup.yml', async () => { - await using fixture = await Fixture.create({ - '.git': {}, - 'pullup.config.mjs': scriptConfig, - 'pullup.yml': 'jobs: []\n', - '.github': { CODEOWNERS: generatedContents }, - }); - - const result = await runCli(command, fixture.root); - - expect(result.code).toBe(0); - expect(result.stderr).toBe(scriptConfigWarning(path.join(fixture.root, 'pullup.config.mjs'))); - expect(result.stderr.match(/\[DEPRECATED\]/g)).toHaveLength(1); - expect(result.stdout).not.toContain('[DEPRECATED]'); - expect(result.stdout).toContain(command === 'sync' ? 'custom synced' : 'All files are up to date'); - expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(generatedContents); - }); - }); -}); - -describe('script config CLI deprecation warning', () => { - it('warns once for sync --dry-run while preserving the preview and existing output', async () => { - await using fixture = await Fixture.create({ - '.git': {}, - 'pullup.config.mjs': scriptConfig, - '.github': { CODEOWNERS: existingContents }, - }); - - const result = await runCli('sync', fixture.root, ['--dry-run']); - - expect(result.code).toBe(0); - expect(result.stderr).toBe(scriptConfigWarning(path.join(fixture.root, 'pullup.config.mjs'))); - expect(result.stderr.match(/\[DEPRECATED\]/g)).toHaveLength(1); - expect(result.stdout).not.toContain('[DEPRECATED]'); - expect(result.stdout).toContain('[Job] custom'); - expect(result.stdout).toContain('Output: .github/CODEOWNERS'); - expect(result.stdout).toContain(generatedContents.trimEnd()); - expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); - }); - - it('warns once without changing the outdated check failure or existing output', async () => { - await using fixture = await Fixture.create({ - '.git': {}, - 'pullup.config.mjs': scriptConfig, - '.github': { CODEOWNERS: existingContents }, - }); - - const result = await runCli('check', fixture.root); - - expect(result.code).toBe(1); - expect(result.stderr).toContain(scriptConfigWarning(path.join(fixture.root, 'pullup.config.mjs'))); - expect(result.stderr.match(/\[DEPRECATED\]/g)).toHaveLength(1); - expect(result.stderr).toContain("custom is outdated. Run 'pullup sync' to update."); - expect(result.stdout).toBe(''); - expect(await readFile(path.join(fixture.root, '.github/CODEOWNERS'), 'utf8')).toBe(existingContents); - }); -}); diff --git a/tsdown.config.ts b/tsdown.config.ts index 8f975c1..ed635d5 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -14,21 +14,18 @@ async function generateSchema() { await writeFile('./schema.json', `${JSON.stringify(schema, null, 2)}\n`); } -export default defineConfig([ - { - entry: 'src/cli/index.ts', - banner: '#!/usr/bin/env node', - dts: false, - minify: true, - noExternal: ['clipanion', 'picocolors'], - outDir: 'dist/bin', +export default defineConfig({ + entry: 'src/cli/index.ts', + banner: '#!/usr/bin/env node', + dts: false, + minify: true, + outputOptions: { keepNames: true }, + outDir: 'dist/bin', + exe: { + fileName: `pullup-${process.platform}-${process.arch}`, + outDir: 'build', }, - { - entry: 'src/core/index.ts', - dts: true, - outDir: 'dist/core', - hooks: { - 'build:done': generateSchema, - }, + hooks: { + 'build:done': generateSchema, }, -]); +}); diff --git a/vitest.config.ts b/vitest.config.ts index a5f24cc..4e8ec3f 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -17,6 +17,15 @@ export default defineConfig({ name: 'integration', include: ['tests/**/*.spec.ts'], globalSetup: './tests/build-cli.ts', + testTimeout: 30_000, + }, + }, + { + test: { + name: 'exe', + include: ['tests/cli/**/*.spec.ts'], + globalSetup: './tests/build-cli.ts', + testTimeout: 30_000, }, }, ], diff --git a/yarn.lock b/yarn.lock index 343354d..7607661 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,55 +5,6 @@ __metadata: version: 8 cacheKey: 10c0 -"@babel/code-frame@npm:^7.0.0": - version: 7.28.6 - resolution: "@babel/code-frame@npm:7.28.6" - dependencies: - "@babel/helper-validator-identifier": "npm:^7.28.5" - js-tokens: "npm:^4.0.0" - picocolors: "npm:^1.1.1" - checksum: 10c0/ed5d57f99455e3b1c23e75ebb8430c6b9800b4ecd0121b4348b97cecb65406a47778d6db61f0d538a4958bb01b4b277e90348a68d39bd3beff1d7c940ed6dd66 - languageName: node - linkType: hard - -"@babel/generator@npm:^7.28.5": - version: 7.28.6 - resolution: "@babel/generator@npm:7.28.6" - dependencies: - "@babel/parser": "npm:^7.28.6" - "@babel/types": "npm:^7.28.6" - "@jridgewell/gen-mapping": "npm:^0.3.12" - "@jridgewell/trace-mapping": "npm:^0.3.28" - jsesc: "npm:^3.0.2" - checksum: 10c0/162fa358484a9a18e8da1235d998f10ea77c63bab408c8d3e327d5833f120631a77ff022c5ed1d838ee00523f8bb75df1f08196d3657d0bca9f2cfeb8503cc12 - languageName: node - linkType: hard - -"@babel/helper-string-parser@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/helper-string-parser@npm:7.27.1" - checksum: 10c0/8bda3448e07b5583727c103560bcf9c4c24b3c1051a4c516d4050ef69df37bb9a4734a585fe12725b8c2763de0a265aa1e909b485a4e3270b7cfd3e4dbe4b602 - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.28.5": - version: 7.28.5 - resolution: "@babel/helper-validator-identifier@npm:7.28.5" - checksum: 10c0/42aaebed91f739a41f3d80b72752d1f95fd7c72394e8e4bd7cdd88817e0774d80a432451bcba17c2c642c257c483bf1d409dd4548883429ea9493a3bc4ab0847 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.28.5, @babel/parser@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/parser@npm:7.28.6" - dependencies: - "@babel/types": "npm:^7.28.6" - bin: - parser: ./bin/babel-parser.js - checksum: 10c0/d6bfe8aa8e067ef58909e9905496157312372ca65d8d2a4f2b40afbea48d59250163755bba8ae626a615da53d192b084bcfc8c9dad8b01e315b96967600de581 - languageName: node - linkType: hard - "@babel/runtime@npm:^7.5.5": version: 7.28.6 resolution: "@babel/runtime@npm:7.28.6" @@ -61,16 +12,6 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.28.5, @babel/types@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/types@npm:7.28.6" - dependencies: - "@babel/helper-string-parser": "npm:^7.27.1" - "@babel/helper-validator-identifier": "npm:^7.28.5" - checksum: 10c0/54a6a9813e48ef6f35aa73c03b3c1572cad7fa32b61b35dd07e4230bc77b559194519c8a4d8106a041a27cc7a94052579e238a30a32d5509aa4da4d6fd83d990 - languageName: node - linkType: hard - "@changesets/apply-release-plan@npm:^7.0.14": version: 7.0.14 resolution: "@changesets/apply-release-plan@npm:7.0.14" @@ -326,7 +267,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/core@npm:^1.4.3, @emnapi/core@npm:^1.7.1": +"@emnapi/core@npm:^1.4.3": version: 1.8.1 resolution: "@emnapi/core@npm:1.8.1" dependencies: @@ -336,7 +277,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/runtime@npm:^1.4.3, @emnapi/runtime@npm:^1.7.1": +"@emnapi/runtime@npm:^1.4.3": version: 1.8.1 resolution: "@emnapi/runtime@npm:1.8.1" dependencies: @@ -532,16 +473,6 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.12": - version: 0.3.13 - resolution: "@jridgewell/gen-mapping@npm:0.3.13" - dependencies: - "@jridgewell/sourcemap-codec": "npm:^1.5.0" - "@jridgewell/trace-mapping": "npm:^0.3.24" - checksum: 10c0/9a7d65fb13bd9aec1fbab74cda08496839b7e2ceb31f5ab922b323e94d7c481ce0fc4fd7e12e2610915ed8af51178bdc61e168e92a8c8b8303b030b03489b13b - languageName: node - linkType: hard - "@jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.2 resolution: "@jridgewell/resolve-uri@npm:3.1.2" @@ -549,7 +480,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": +"@jridgewell/sourcemap-codec@npm:^1.4.14": version: 1.5.5 resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 @@ -563,7 +494,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:0.3.31, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28": +"@jridgewell/trace-mapping@npm:0.3.31": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" dependencies: @@ -610,17 +541,6 @@ __metadata: languageName: node linkType: hard -"@napi-rs/wasm-runtime@npm:^1.1.1": - version: 1.1.1 - resolution: "@napi-rs/wasm-runtime@npm:1.1.1" - dependencies: - "@emnapi/core": "npm:^1.7.1" - "@emnapi/runtime": "npm:^1.7.1" - "@tybys/wasm-util": "npm:^0.10.1" - checksum: 10c0/04d57b67e80736e41fe44674a011878db0a8ad893f4d44abb9d3608debb7c174224cba2796ed5b0c1d367368159f3ca6be45f1c59222f70e32ddc880f803d447 - languageName: node - linkType: hard - "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -648,20 +568,6 @@ __metadata: languageName: node linkType: hard -"@oxc-project/types@npm:=0.107.0": - version: 0.107.0 - resolution: "@oxc-project/types@npm:0.107.0" - checksum: 10c0/ba829953969238396a3d9558cc42597027bcd4189892165484ba9d8c2df96870ed895f84f3dc22d9a7905a89d4727a09cb89d94534add2252fae62cf82d82813 - languageName: node - linkType: hard - -"@oxc-project/types@npm:=0.110.0": - version: 0.110.0 - resolution: "@oxc-project/types@npm:0.110.0" - checksum: 10c0/7d0ecc6d54fd2ed390c4dc7fb15278d7bc7bca50937459077490ed57f9fb8bf6455c7b673055588e320f2e0762551b61643ac2729afaafe396dc9211eb2e3c58 - languageName: node - linkType: hard - "@oxc-project/types@npm:=0.151.0": version: 0.151.0 resolution: "@oxc-project/types@npm:0.151.0" @@ -669,6 +575,13 @@ __metadata: languageName: node linkType: hard +"@oxc-project/types@npm:=0.152.0": + version: 0.152.0 + resolution: "@oxc-project/types@npm:0.152.0" + checksum: 10c0/5958369f7a2d9fb1f8061ab3aa1abf9b3565cc8895e7e8b3800cd08b6d8077d7ddb4e383ff472811a63302954170a2a52c60fcd7cca38e416910c5f39861d0db + languageName: node + linkType: hard + "@pull-up/cli@workspace:.": version: 0.0.0-use.local resolution: "@pull-up/cli@workspace:." @@ -677,10 +590,10 @@ __metadata: "@changesets/cli": "npm:^2.29.8" "@eslint/js": "npm:^9.39.2" "@fixture-kit/core": "npm:^1.0.1" + "@types/js-yaml": "npm:^4.0.9" "@types/node": "npm:^24.10.0" "@valibot/to-json-schema": "npm:^1.8.0" clipanion: "npm:^4.0.0-rc.4" - cosmiconfig: "npm:^9.0.0" eslint: "npm:^9.7.0" eslint-plugin-import-x: "npm:^4.0.0" eslint-plugin-simple-import-sort: "npm:^12.1.1" @@ -688,8 +601,9 @@ __metadata: fast-glob: "npm:^3.3.3" find-up: "npm:^8.0.0" globals: "npm:^17.0.0" + js-yaml: "npm:^4.1.1" prettier: "npm:^3.8.0" - tsdown: "npm:^0.19.0" + tsdown: "npm:^0.23.0" typescript: "npm:^5.9.2" typescript-eslint: "npm:^8.27.0" valibot: "npm:^1.5.0" @@ -716,17 +630,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-android-arm64@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-android-arm64@npm:1.0.0-beta.59" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@rolldown/binding-android-arm64@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-android-arm64@npm:1.0.0-rc.1" - conditions: os=android & cpu=arm64 +"@rolldown/binding-android-arm-eabi@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-android-arm-eabi@npm:1.2.12" + conditions: os=android & cpu=arm languageName: node linkType: hard @@ -737,17 +644,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-darwin-arm64@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-darwin-arm64@npm:1.0.0-beta.59" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@rolldown/binding-darwin-arm64@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-darwin-arm64@npm:1.0.0-rc.1" - conditions: os=darwin & cpu=arm64 +"@rolldown/binding-android-arm64@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-android-arm64@npm:1.2.12" + conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -758,17 +658,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-darwin-x64@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-darwin-x64@npm:1.0.0-beta.59" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@rolldown/binding-darwin-x64@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-darwin-x64@npm:1.0.0-rc.1" - conditions: os=darwin & cpu=x64 +"@rolldown/binding-darwin-arm64@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-darwin-arm64@npm:1.2.12" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -779,17 +672,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-freebsd-x64@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-freebsd-x64@npm:1.0.0-beta.59" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@rolldown/binding-freebsd-x64@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-freebsd-x64@npm:1.0.0-rc.1" - conditions: os=freebsd & cpu=x64 +"@rolldown/binding-darwin-x64@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-darwin-x64@npm:1.2.12" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -800,17 +686,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-beta.59" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.1" - conditions: os=linux & cpu=arm +"@rolldown/binding-freebsd-x64@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-freebsd-x64@npm:1.2.12" + conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -821,17 +700,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-linux-arm64-gnu@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.0.0-beta.59" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.1" - conditions: os=linux & cpu=arm64 & libc=glibc +"@rolldown/binding-linux-arm-gnueabihf@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.2.12" + conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -842,23 +714,23 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-linux-arm64-musl@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-linux-arm64-musl@npm:1.0.0-beta.59" - conditions: os=linux & cpu=arm64 & libc=musl +"@rolldown/binding-linux-arm64-gnu@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.2.12" + conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.1" +"@rolldown/binding-linux-arm64-musl@npm:1.2.11": + version: 1.2.11 + resolution: "@rolldown/binding-linux-arm64-musl@npm:1.2.11" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rolldown/binding-linux-arm64-musl@npm:1.2.11": - version: 1.2.11 - resolution: "@rolldown/binding-linux-arm64-musl@npm:1.2.11" +"@rolldown/binding-linux-arm64-musl@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-linux-arm64-musl@npm:1.2.12" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard @@ -870,6 +742,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-linux-ppc64-gnu@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-linux-ppc64-gnu@npm:1.2.12" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + "@rolldown/binding-linux-s390x-gnu@npm:1.2.11": version: 1.2.11 resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.2.11" @@ -877,17 +756,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-linux-x64-gnu@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-linux-x64-gnu@npm:1.0.0-beta.59" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.1" - conditions: os=linux & cpu=x64 & libc=glibc +"@rolldown/binding-linux-s390x-gnu@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.2.12" + conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard @@ -898,17 +770,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-linux-x64-musl@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-linux-x64-musl@npm:1.0.0-beta.59" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.1" - conditions: os=linux & cpu=x64 & libc=musl +"@rolldown/binding-linux-x64-gnu@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-linux-x64-gnu@npm:1.2.12" + conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard @@ -919,17 +784,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-openharmony-arm64@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-openharmony-arm64@npm:1.0.0-beta.59" - conditions: os=openharmony & cpu=arm64 - languageName: node - linkType: hard - -"@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.1" - conditions: os=openharmony & cpu=arm64 +"@rolldown/binding-linux-x64-musl@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-linux-x64-musl@npm:1.2.12" + conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -940,35 +798,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-wasm32-wasi@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-wasm32-wasi@npm:1.0.0-beta.59" - dependencies: - "@napi-rs/wasm-runtime": "npm:^1.1.1" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.1" - dependencies: - "@napi-rs/wasm-runtime": "npm:^1.1.1" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@rolldown/binding-win32-arm64-msvc@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.0.0-beta.59" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.1" - conditions: os=win32 & cpu=arm64 +"@rolldown/binding-openharmony-arm64@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-openharmony-arm64@npm:1.2.12" + conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard @@ -979,17 +812,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-win32-x64-msvc@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/binding-win32-x64-msvc@npm:1.0.0-beta.59" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.1" - conditions: os=win32 & cpu=x64 +"@rolldown/binding-win32-arm64-msvc@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.2.12" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -1000,17 +826,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/pluginutils@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "@rolldown/pluginutils@npm:1.0.0-beta.59" - checksum: 10c0/1c359ad2b878963b475e3a82b8dafd745e0f1aa48eb7ac6e6a9163e845f26fd6a9ec36acf686a17332834c559f4c40ce5f6385d0028ed5caf9624fc8b04c4790 - languageName: node - linkType: hard - -"@rolldown/pluginutils@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "@rolldown/pluginutils@npm:1.0.0-rc.1" - checksum: 10c0/6d13bd792c81d7ad218e9b8e842113a1cf8a7908072e0fc62215b8debe9526042e6d823f47dbf1afd020f0287122851f6deb8090c6f03c51bd4082ac796bfcee +"@rolldown/binding-win32-x64-msvc@npm:1.2.12": + version: 1.2.12 + resolution: "@rolldown/binding-win32-x64-msvc@npm:1.2.12" + conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1035,7 +854,7 @@ __metadata: languageName: node linkType: hard -"@tybys/wasm-util@npm:^0.10.0, @tybys/wasm-util@npm:^0.10.1": +"@tybys/wasm-util@npm:^0.10.0": version: 0.10.1 resolution: "@tybys/wasm-util@npm:0.10.1" dependencies: @@ -1075,6 +894,13 @@ __metadata: languageName: node linkType: hard +"@types/js-yaml@npm:^4.0.9": + version: 4.0.9 + resolution: "@types/js-yaml@npm:4.0.9" + checksum: 10c0/24de857aa8d61526bbfbbaa383aa538283ad17363fcd5bb5148e2c7f604547db36646440e739d78241ed008702a8920665d1add5618687b6743858fae00da211 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.15": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -1404,6 +1230,181 @@ __metadata: languageName: node linkType: hard +"@yuku-codegen/binding-android-arm64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-android-arm64@npm:0.10.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@yuku-codegen/binding-darwin-arm64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-darwin-arm64@npm:0.10.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@yuku-codegen/binding-darwin-x64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-darwin-x64@npm:0.10.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@yuku-codegen/binding-freebsd-x64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-freebsd-x64@npm:0.10.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@yuku-codegen/binding-linux-arm-gnu@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-linux-arm-gnu@npm:0.10.2" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@yuku-codegen/binding-linux-arm-musl@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-linux-arm-musl@npm:0.10.2" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@yuku-codegen/binding-linux-arm64-gnu@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-linux-arm64-gnu@npm:0.10.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@yuku-codegen/binding-linux-arm64-musl@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-linux-arm64-musl@npm:0.10.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@yuku-codegen/binding-linux-x64-gnu@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-linux-x64-gnu@npm:0.10.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@yuku-codegen/binding-linux-x64-musl@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-linux-x64-musl@npm:0.10.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@yuku-codegen/binding-win32-arm64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-win32-arm64@npm:0.10.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@yuku-codegen/binding-win32-x64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-codegen/binding-win32-x64@npm:0.10.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@yuku-parser/binding-android-arm64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-android-arm64@npm:0.10.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@yuku-parser/binding-darwin-arm64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-darwin-arm64@npm:0.10.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@yuku-parser/binding-darwin-x64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-darwin-x64@npm:0.10.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@yuku-parser/binding-freebsd-x64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-freebsd-x64@npm:0.10.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@yuku-parser/binding-linux-arm-gnu@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-linux-arm-gnu@npm:0.10.2" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@yuku-parser/binding-linux-arm-musl@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-linux-arm-musl@npm:0.10.2" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@yuku-parser/binding-linux-arm64-gnu@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-linux-arm64-gnu@npm:0.10.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@yuku-parser/binding-linux-arm64-musl@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-linux-arm64-musl@npm:0.10.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@yuku-parser/binding-linux-x64-gnu@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-linux-x64-gnu@npm:0.10.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@yuku-parser/binding-linux-x64-musl@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-linux-x64-musl@npm:0.10.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@yuku-parser/binding-win32-arm64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-win32-arm64@npm:0.10.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@yuku-parser/binding-win32-x64@npm:0.10.2": + version: 0.10.2 + resolution: "@yuku-parser/binding-win32-x64@npm:0.10.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@yuku-toolchain/types@npm:^0.10.2": + version: 0.10.2 + resolution: "@yuku-toolchain/types@npm:0.10.2" + checksum: 10c0/a56b8b1b69d84287c0ffc4ba9fb69bd306b419fb835c42057bd95d80b0c68e1a28ed95db0238393251e48ed7e93d479b1f71aa20a248cfb1e80211466f6374c0 + languageName: node + linkType: hard + "abbrev@npm:^5.0.0": version: 5.0.0 resolution: "abbrev@npm:5.0.0" @@ -1464,13 +1465,6 @@ __metadata: languageName: node linkType: hard -"ansis@npm:^4.2.0": - version: 4.2.0 - resolution: "ansis@npm:4.2.0" - checksum: 10c0/cd6a7a681ecd36e72e0d79c1e34f1f3bcb1b15bcbb6f0f8969b4228062d3bfebbef468e09771b00d93b2294370b34f707599d4a113542a876de26823b795b5d2 - languageName: node - linkType: hard - "argparse@npm:^1.0.7": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -1501,16 +1495,6 @@ __metadata: languageName: node linkType: hard -"ast-kit@npm:^2.2.0": - version: 2.2.0 - resolution: "ast-kit@npm:2.2.0" - dependencies: - "@babel/parser": "npm:^7.28.5" - pathe: "npm:^2.0.3" - checksum: 10c0/d885f3a4e9837e730451a667d26936eef34773d6e5ecacd771a3e9d1f82fdc45d38958ab35e18880e0cf667896604a599497c5186f2578cf73c0d802ed7fc697 - languageName: node - linkType: hard - "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -1527,13 +1511,6 @@ __metadata: languageName: node linkType: hard -"birpc@npm:^4.0.0": - version: 4.0.0 - resolution: "birpc@npm:4.0.0" - checksum: 10c0/61f4e893ff4c5948b2c587c971c04883af0d8b2658d4632c8e77073db9f9e8b040402f985d56308021890b2ad32ef8392e36a8335cab1e3771d99e1b025d1af6 - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.12 resolution: "brace-expansion@npm:1.1.12" @@ -1562,10 +1539,10 @@ __metadata: languageName: node linkType: hard -"cac@npm:^6.7.14": - version: 6.7.14 - resolution: "cac@npm:6.7.14" - checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 +"cac@npm:^7.0.0": + version: 7.0.0 + resolution: "cac@npm:7.0.0" + checksum: 10c0/e9da33cb9f0425546ae92a450d479276f9969a050fe64f5d6fedf058bdd87f22a370797fe1c158e07655fa9fc183749df7cb2037431e3772faa7bee9919fb763 languageName: node linkType: hard @@ -1655,23 +1632,6 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^9.0.0": - version: 9.0.0 - resolution: "cosmiconfig@npm:9.0.0" - dependencies: - env-paths: "npm:^2.2.1" - import-fresh: "npm:^3.3.0" - js-yaml: "npm:^4.1.0" - parse-json: "npm:^5.2.0" - peerDependencies: - typescript: ">=4.9.5" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/1c1703be4f02a250b1d6ca3267e408ce16abfe8364193891afc94c2d5c060b69611fdc8d97af74b7e6d5d1aac0ab2fb94d6b079573146bc2d756c2484ce5f0ee - languageName: node - linkType: hard - "cross-spawn@npm:^7.0.5, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" @@ -1709,10 +1669,10 @@ __metadata: languageName: node linkType: hard -"defu@npm:^6.1.4": - version: 6.1.4 - resolution: "defu@npm:6.1.4" - checksum: 10c0/2d6cc366262dc0cb8096e429368e44052fdf43ed48e53ad84cc7c9407f890301aa5fcb80d0995abaaf842b3949f154d060be4160f7a46cb2bc2f7726c81526f5 +"defu@npm:^6.1.7": + version: 6.1.7 + resolution: "defu@npm:6.1.7" + checksum: 10c0/e6635388103c8be3c574ac31302f6930e5e6eeedba32cb1b30cf993c7d9fb571aec2485446dfa23bfa63e55e66156fe109027a9695db82a50f931e91e8d4bedb languageName: node linkType: hard @@ -1746,22 +1706,22 @@ __metadata: languageName: node linkType: hard -"dts-resolver@npm:^2.1.3": - version: 2.1.3 - resolution: "dts-resolver@npm:2.1.3" +"dts-resolver@npm:^3.0.0": + version: 3.0.0 + resolution: "dts-resolver@npm:3.0.0" peerDependencies: oxc-resolver: ">=11.0.0" peerDependenciesMeta: oxc-resolver: optional: true - checksum: 10c0/bf589ba9bfacdb23ff9c075948175f5a21ae0bccb2ca36f8315bff2729358902256ee7aca972f5b259641f08a4b5973034e082a730113d5af76e64062e45fe3a + checksum: 10c0/ae52c4e09b43def702b02d7edbfa04798522907f22879809f8890e5c61fc762403f46a1c415c2d1a6440b01960bbaf98c72def1771b0962d3b15fec40983a4ab languageName: node linkType: hard -"empathic@npm:^2.0.0": - version: 2.0.0 - resolution: "empathic@npm:2.0.0" - checksum: 10c0/7d3b14b04a93b35c47bcc950467ec914fd241cd9acc0269b0ea160f13026ec110f520c90fae64720fde72cc1757b57f3f292fb606617b7fccac1f4d008a76506 +"empathic@npm:^2.0.1": + version: 2.1.0 + resolution: "empathic@npm:2.1.0" + checksum: 10c0/cf13e2919d680a22140d7cd1637a01f6cf9a727f21d3a54b23489ec01c80eab548b9d832d2e167d00e124a54377660c1d6fd2b5947d905366e4d22fde7b9e082 languageName: node linkType: hard @@ -1775,22 +1735,13 @@ __metadata: languageName: node linkType: hard -"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": +"env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 languageName: node linkType: hard -"error-ex@npm:^1.3.1": - version: 1.3.4 - resolution: "error-ex@npm:1.3.4" - dependencies: - is-arrayish: "npm:^0.2.1" - checksum: 10c0/b9e34ff4778b8f3b31a8377e1c654456f4c41aeaa3d10a1138c3b7635d8b7b2e03eb2475d46d8ae055c1f180a1063e100bffabf64ea7e7388b37735df5328664 - languageName: node - linkType: hard - "es-module-lexer@npm:^2.3.2": version: 2.3.2 resolution: "es-module-lexer@npm:2.3.2" @@ -2211,7 +2162,16 @@ __metadata: languageName: node linkType: hard -"get-tsconfig@npm:^4.10.1, get-tsconfig@npm:^4.13.0": +"get-tsconfig@npm:5.0.0-beta.6": + version: 5.0.0-beta.6 + resolution: "get-tsconfig@npm:5.0.0-beta.6" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/661d07b469ca6a76d8075a40aef66b90e22775fe2cc5462b349f5af14def39d8ee8572a63a1cd65acb91fcf7f546969d20b330f8afe480ab5360136aaacc0fc3 + languageName: node + linkType: hard + +"get-tsconfig@npm:^4.10.1": version: 4.13.0 resolution: "get-tsconfig@npm:4.13.0" dependencies: @@ -2280,10 +2240,10 @@ __metadata: languageName: node linkType: hard -"hookable@npm:^6.0.1": - version: 6.0.1 - resolution: "hookable@npm:6.0.1" - checksum: 10c0/a53592937c1aa74a650b3b92a9d8cf8bd58eee48422124a90299344f81e90cd2ee2f3d9f3686c45c0bc87edbc9fede4de709edf6b987dd6837bdcfa502447fa0 +"hookable@npm:^6.1.1": + version: 6.1.2 + resolution: "hookable@npm:6.1.2" + checksum: 10c0/ea5cdb1b1a1919b48cfbbfb55c8009f6843473ff50f67d6e959fd291cb2fd0a4934e85124bfd6531b1d2edf1e0675bc5954f85a12318a59b15c7a3e5667f0d4e languageName: node linkType: hard @@ -2326,7 +2286,7 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": +"import-fresh@npm:^3.2.1": version: 3.3.1 resolution: "import-fresh@npm:3.3.1" dependencies: @@ -2336,10 +2296,10 @@ __metadata: languageName: node linkType: hard -"import-without-cache@npm:^0.2.5": - version: 0.2.5 - resolution: "import-without-cache@npm:0.2.5" - checksum: 10c0/5cf7a00e317a23569f16c87391170270277c073cba498c913bf043af56c56118d023c8d041046535566135c34503c90a9320483448b070a4ab4ae29949547a71 +"import-without-cache@npm:^0.4.0": + version: 0.4.1 + resolution: "import-without-cache@npm:0.4.1" + checksum: 10c0/85c7f1581605813a892ce44d0b93e94c44f60f76fe558865b0a94f7b6ac3c6b6486388ca8d125e7b108ae1dedaebfc1f8adf67f3783ade0406c8c93ae9cfe656 languageName: node linkType: hard @@ -2350,13 +2310,6 @@ __metadata: languageName: node linkType: hard -"is-arrayish@npm:^0.2.1": - version: 0.2.1 - resolution: "is-arrayish@npm:0.2.1" - checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 - languageName: node - linkType: hard - "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -2431,13 +2384,6 @@ __metadata: languageName: node linkType: hard -"js-tokens@npm:^4.0.0": - version: 4.0.0 - resolution: "js-tokens@npm:4.0.0" - checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed - languageName: node - linkType: hard - "js-yaml@npm:^3.6.1": version: 3.14.2 resolution: "js-yaml@npm:3.14.2" @@ -2450,7 +2396,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1": +"js-yaml@npm:^4.1.1": version: 4.1.1 resolution: "js-yaml@npm:4.1.1" dependencies: @@ -2461,15 +2407,6 @@ __metadata: languageName: node linkType: hard -"jsesc@npm:^3.0.2": - version: 3.1.0 - resolution: "jsesc@npm:3.1.0" - bin: - jsesc: bin/jsesc - checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1 - languageName: node - linkType: hard - "json-buffer@npm:3.0.1": version: 3.0.1 resolution: "json-buffer@npm:3.0.1" @@ -2477,13 +2414,6 @@ __metadata: languageName: node linkType: hard -"json-parse-even-better-errors@npm:^2.3.0": - version: 2.3.1 - resolution: "json-parse-even-better-errors@npm:2.3.1" - checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 - languageName: node - linkType: hard - "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -2649,13 +2579,6 @@ __metadata: languageName: node linkType: hard -"lines-and-columns@npm:^1.1.6": - version: 1.2.4 - resolution: "lines-and-columns@npm:1.2.4" - checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d - languageName: node - linkType: hard - "locate-path@npm:^5.0.0": version: 5.0.0 resolution: "locate-path@npm:5.0.0" @@ -2860,13 +2783,6 @@ __metadata: languageName: node linkType: hard -"obug@npm:^2.1.1": - version: 2.1.1 - resolution: "obug@npm:2.1.1" - checksum: 10c0/59dccd7de72a047e08f8649e94c1015ec72f94eefb6ddb57fb4812c4b425a813bc7e7cd30c9aca20db3c59abc3c85cc7a62bb656a968741d770f4e8e02bc2e78 - languageName: node - linkType: hard - "obug@npm:^2.1.4": version: 2.2.1 resolution: "obug@npm:2.2.1" @@ -2874,6 +2790,13 @@ __metadata: languageName: node linkType: hard +"obug@npm:^3.0.0": + version: 3.0.0 + resolution: "obug@npm:3.0.0" + checksum: 10c0/985625b70ea05a39956da94f406040a0c7d27b1699a14af87b1ea75cba915b5957b75c4acdaccaefa2a86eaf40fc968bc00f6c28755a91496fc91a92b1513b38 + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" @@ -2990,18 +2913,6 @@ __metadata: languageName: node linkType: hard -"parse-json@npm:^5.2.0": - version: 5.2.0 - resolution: "parse-json@npm:5.2.0" - dependencies: - "@babel/code-frame": "npm:^7.0.0" - error-ex: "npm:^1.3.1" - json-parse-even-better-errors: "npm:^2.3.0" - lines-and-columns: "npm:^1.1.6" - checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 - languageName: node - linkType: hard - "parse-ms@npm:^4.0.0": version: 4.0.0 resolution: "parse-ms@npm:4.0.0" @@ -3037,13 +2948,6 @@ __metadata: languageName: node linkType: hard -"pathe@npm:^2.0.3": - version: 2.0.3 - resolution: "pathe@npm:2.0.3" - checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1 - languageName: node - linkType: hard - "picocolors@npm:^1.1.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" @@ -3199,57 +3103,62 @@ __metadata: languageName: node linkType: hard -"rolldown-plugin-dts@npm:^0.20.0": - version: 0.20.0 - resolution: "rolldown-plugin-dts@npm:0.20.0" +"rolldown-plugin-dts@npm:^0.28.5": + version: 0.28.6 + resolution: "rolldown-plugin-dts@npm:0.28.6" dependencies: - "@babel/generator": "npm:^7.28.5" - "@babel/parser": "npm:^7.28.5" - "@babel/types": "npm:^7.28.5" - ast-kit: "npm:^2.2.0" - birpc: "npm:^4.0.0" - dts-resolver: "npm:^2.1.3" - get-tsconfig: "npm:^4.13.0" - obug: "npm:^2.1.1" + dts-resolver: "npm:^3.0.0" + get-tsconfig: "npm:5.0.0-beta.6" + obug: "npm:^3.0.0" + yuku-ast: "npm:^0.10.1" + yuku-codegen: "npm:^0.10.1" + yuku-parser: "npm:^0.10.1" peerDependencies: - "@ts-macro/tsc": ^0.3.6 - "@typescript/native-preview": ">=7.0.0-dev.20250601.1" - rolldown: ^1.0.0-beta.57 - typescript: ^5.0.0 - vue-tsc: ~3.2.0 + "@typescript/native-preview": "*" + "@volar/typescript": ~2.4.0 + "@vue/language-core": ~3.2.0 || ~3.3.0 + rolldown: ^1.2.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 peerDependenciesMeta: - "@ts-macro/tsc": - optional: true "@typescript/native-preview": optional: true + "@volar/typescript": + optional: true + "@vue/language-core": + optional: true typescript: optional: true vue-tsc: optional: true - checksum: 10c0/68543dd81afd5d2c29d7862ee75035415d3e383f9bae51c6e9cd1daea3b485481f782d11a086f831c5f0d0ccac6dd572fe73ec7f7e5b17c1b17c08a9b03b4d7c - languageName: node - linkType: hard - -"rolldown@npm:1.0.0-beta.59": - version: 1.0.0-beta.59 - resolution: "rolldown@npm:1.0.0-beta.59" - dependencies: - "@oxc-project/types": "npm:=0.107.0" - "@rolldown/binding-android-arm64": "npm:1.0.0-beta.59" - "@rolldown/binding-darwin-arm64": "npm:1.0.0-beta.59" - "@rolldown/binding-darwin-x64": "npm:1.0.0-beta.59" - "@rolldown/binding-freebsd-x64": "npm:1.0.0-beta.59" - "@rolldown/binding-linux-arm-gnueabihf": "npm:1.0.0-beta.59" - "@rolldown/binding-linux-arm64-gnu": "npm:1.0.0-beta.59" - "@rolldown/binding-linux-arm64-musl": "npm:1.0.0-beta.59" - "@rolldown/binding-linux-x64-gnu": "npm:1.0.0-beta.59" - "@rolldown/binding-linux-x64-musl": "npm:1.0.0-beta.59" - "@rolldown/binding-openharmony-arm64": "npm:1.0.0-beta.59" - "@rolldown/binding-wasm32-wasi": "npm:1.0.0-beta.59" - "@rolldown/binding-win32-arm64-msvc": "npm:1.0.0-beta.59" - "@rolldown/binding-win32-x64-msvc": "npm:1.0.0-beta.59" - "@rolldown/pluginutils": "npm:1.0.0-beta.59" + checksum: 10c0/36b22e9e2c2bf32c8436a576823fe33cae2c13adb1d9def68853e406c34900a76afe4c0c688072295a9c3e0a25928b5ebc43837d41db76a6f1aa3729e483818a + languageName: node + linkType: hard + +"rolldown@npm:~1.2.7": + version: 1.2.12 + resolution: "rolldown@npm:1.2.12" + dependencies: + "@oxc-project/types": "npm:=0.152.0" + "@rolldown/binding-android-arm-eabi": "npm:1.2.12" + "@rolldown/binding-android-arm64": "npm:1.2.12" + "@rolldown/binding-darwin-arm64": "npm:1.2.12" + "@rolldown/binding-darwin-x64": "npm:1.2.12" + "@rolldown/binding-freebsd-x64": "npm:1.2.12" + "@rolldown/binding-linux-arm-gnueabihf": "npm:1.2.12" + "@rolldown/binding-linux-arm64-gnu": "npm:1.2.12" + "@rolldown/binding-linux-arm64-musl": "npm:1.2.12" + "@rolldown/binding-linux-ppc64-gnu": "npm:1.2.12" + "@rolldown/binding-linux-s390x-gnu": "npm:1.2.12" + "@rolldown/binding-linux-x64-gnu": "npm:1.2.12" + "@rolldown/binding-linux-x64-musl": "npm:1.2.12" + "@rolldown/binding-openharmony-arm64": "npm:1.2.12" + "@rolldown/binding-win32-arm64-msvc": "npm:1.2.12" + "@rolldown/binding-win32-x64-msvc": "npm:1.2.12" + "@rolldown/pluginutils": "npm:^1.0.0" dependenciesMeta: + "@rolldown/binding-android-arm-eabi": + optional: true "@rolldown/binding-android-arm64": optional: true "@rolldown/binding-darwin-arm64": @@ -3264,57 +3173,9 @@ __metadata: optional: true "@rolldown/binding-linux-arm64-musl": optional: true - "@rolldown/binding-linux-x64-gnu": - optional: true - "@rolldown/binding-linux-x64-musl": - optional: true - "@rolldown/binding-openharmony-arm64": - optional: true - "@rolldown/binding-wasm32-wasi": - optional: true - "@rolldown/binding-win32-arm64-msvc": - optional: true - "@rolldown/binding-win32-x64-msvc": - optional: true - bin: - rolldown: bin/cli.mjs - checksum: 10c0/babbd597d2a93ba8e345d115913fbb928dba9c4e27eed20d1968e4016178ca68cb4b94ebf04d742521b2a54e407fb425f3ee2f67c9cf6f1d08d8ede5542322c9 - languageName: node - linkType: hard - -"rolldown@npm:1.0.0-rc.1": - version: 1.0.0-rc.1 - resolution: "rolldown@npm:1.0.0-rc.1" - dependencies: - "@oxc-project/types": "npm:=0.110.0" - "@rolldown/binding-android-arm64": "npm:1.0.0-rc.1" - "@rolldown/binding-darwin-arm64": "npm:1.0.0-rc.1" - "@rolldown/binding-darwin-x64": "npm:1.0.0-rc.1" - "@rolldown/binding-freebsd-x64": "npm:1.0.0-rc.1" - "@rolldown/binding-linux-arm-gnueabihf": "npm:1.0.0-rc.1" - "@rolldown/binding-linux-arm64-gnu": "npm:1.0.0-rc.1" - "@rolldown/binding-linux-arm64-musl": "npm:1.0.0-rc.1" - "@rolldown/binding-linux-x64-gnu": "npm:1.0.0-rc.1" - "@rolldown/binding-linux-x64-musl": "npm:1.0.0-rc.1" - "@rolldown/binding-openharmony-arm64": "npm:1.0.0-rc.1" - "@rolldown/binding-wasm32-wasi": "npm:1.0.0-rc.1" - "@rolldown/binding-win32-arm64-msvc": "npm:1.0.0-rc.1" - "@rolldown/binding-win32-x64-msvc": "npm:1.0.0-rc.1" - "@rolldown/pluginutils": "npm:1.0.0-rc.1" - dependenciesMeta: - "@rolldown/binding-android-arm64": - optional: true - "@rolldown/binding-darwin-arm64": - optional: true - "@rolldown/binding-darwin-x64": - optional: true - "@rolldown/binding-freebsd-x64": - optional: true - "@rolldown/binding-linux-arm-gnueabihf": - optional: true - "@rolldown/binding-linux-arm64-gnu": + "@rolldown/binding-linux-ppc64-gnu": optional: true - "@rolldown/binding-linux-arm64-musl": + "@rolldown/binding-linux-s390x-gnu": optional: true "@rolldown/binding-linux-x64-gnu": optional: true @@ -3322,15 +3183,13 @@ __metadata: optional: true "@rolldown/binding-openharmony-arm64": optional: true - "@rolldown/binding-wasm32-wasi": - optional: true "@rolldown/binding-win32-arm64-msvc": optional: true "@rolldown/binding-win32-x64-msvc": optional: true bin: - rolldown: bin/cli.mjs - checksum: 10c0/96380276099e61ab7c53b3a8cb82fa2519a7a5248462820168ed509e2afed68e53272613b9cf8b1ca73b5ad7a93e79152add9c7e9bc10aa052da779dbfe01ce1 + rolldown: ./bin/cli.mjs + checksum: 10c0/5e01b955608d9ee601359caa228155c99b2ac4e9e5ef8cbff1962654ab32e5f406ae6872576aa69104b94705bb3743bdb042038c120d5197c1edb18588272aac languageName: node linkType: hard @@ -3560,14 +3419,7 @@ __metadata: languageName: node linkType: hard -"tinyexec@npm:^1.0.2": - version: 1.0.2 - resolution: "tinyexec@npm:1.0.2" - checksum: 10c0/1261a8e34c9b539a9aae3b7f0bb5372045ff28ee1eba035a2a059e532198fe1a182ec61ac60fa0b4a4129f0c4c4b1d2d57355b5cb9aa2d17ac9454ecace502ee - languageName: node - linkType: hard - -"tinyexec@npm:^1.3.0": +"tinyexec@npm:^1.3.0, tinyexec@npm:^1.3.1": version: 1.3.1 resolution: "tinyexec@npm:1.3.1" checksum: 10c0/c590369cb3fa73cc18a3998caec8a00f9712d91309c881be3ac52b160fb2f80c7b1fbf3591858ab6abab47ad55385b0c86ec19982e32441a01f55298dab9ccd2 @@ -3628,49 +3480,56 @@ __metadata: languageName: node linkType: hard -"tsdown@npm:^0.19.0": - version: 0.19.0 - resolution: "tsdown@npm:0.19.0" +"tsdown@npm:^0.23.0": + version: 0.23.0 + resolution: "tsdown@npm:0.23.0" dependencies: - ansis: "npm:^4.2.0" - cac: "npm:^6.7.14" - defu: "npm:^6.1.4" - empathic: "npm:^2.0.0" - hookable: "npm:^6.0.1" - import-without-cache: "npm:^0.2.5" - obug: "npm:^2.1.1" - picomatch: "npm:^4.0.3" - rolldown: "npm:1.0.0-beta.59" - rolldown-plugin-dts: "npm:^0.20.0" - semver: "npm:^7.7.3" - tinyexec: "npm:^1.0.2" - tinyglobby: "npm:^0.2.15" + cac: "npm:^7.0.0" + defu: "npm:^6.1.7" + empathic: "npm:^2.0.1" + hookable: "npm:^6.1.1" + import-without-cache: "npm:^0.4.0" + obug: "npm:^2.1.4" + picomatch: "npm:^4.0.7" + rolldown: "npm:~1.2.7" + rolldown-plugin-dts: "npm:^0.28.5" + tinyexec: "npm:^1.3.1" + tinyglobby: "npm:^0.2.17" tree-kill: "npm:^1.2.2" - unconfig-core: "npm:^7.4.2" - unrun: "npm:^0.2.24" + unconfig-core: "npm:^7.5.0" + verkit: "npm:^0.4.0" peerDependencies: "@arethetypeswrong/core": ^0.18.1 + "@tsdown/css": 0.23.0 + "@tsdown/exe": 0.23.0 "@vitejs/devtools": "*" - publint: ^0.3.0 - typescript: ^5.0.0 - unplugin-lightningcss: ^0.4.0 - unplugin-unused: ^0.5.0 + publint: ^0.3.8 + tsx: "*" + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + unplugin-unused: ">=0.5.0" + unrun: "*" peerDependenciesMeta: "@arethetypeswrong/core": optional: true + "@tsdown/css": + optional: true + "@tsdown/exe": + optional: true "@vitejs/devtools": optional: true publint: optional: true - typescript: + tsx: optional: true - unplugin-lightningcss: + typescript: optional: true unplugin-unused: optional: true + unrun: + optional: true bin: - tsdown: dist/run.mjs - checksum: 10c0/24eaec94190f2f5c0c69fb4c86e1e75cdc6cad49b8680f814ea9a4b12e12b7137a38fa9ae25fcee35cf5a3d7cca7a80cc237f3f1eba558c483afd2aef7543cce + tsdown: ./dist/run.mjs + checksum: 10c0/0b9f77653ec9955a5b830576d6a4d6ba1feb82d4adb7624a1b529e34fdf772c165cdbf817ef44ad907d507fa1170ad960ba99bd88f448f3d57e0da07cf6c9643 languageName: node linkType: hard @@ -3732,13 +3591,13 @@ __metadata: languageName: node linkType: hard -"unconfig-core@npm:^7.4.2": - version: 7.4.2 - resolution: "unconfig-core@npm:7.4.2" +"unconfig-core@npm:^7.5.0": + version: 7.5.0 + resolution: "unconfig-core@npm:7.5.0" dependencies: "@quansync/fs": "npm:^1.0.0" quansync: "npm:^1.0.0" - checksum: 10c0/12aa85c78114a505fb49adc5fd6c2ce58b1661c517caea7f8943b9f56912634bfd085443a84d7675a0b245598e6220daaff0a69e3dd712819e0d05195fcfb75b + checksum: 10c0/9c9f745254aa8e267140430a432353d17ee87f625b0ea0668e189d88736828d117b66bd2dd41f1d48bd40d44d5b7c7d160e8b7996a60c8f484e2975ef73c7cb7 languageName: node linkType: hard @@ -3837,22 +3696,6 @@ __metadata: languageName: node linkType: hard -"unrun@npm:^0.2.24": - version: 0.2.26 - resolution: "unrun@npm:0.2.26" - dependencies: - rolldown: "npm:1.0.0-rc.1" - peerDependencies: - synckit: ^0.11.11 - peerDependenciesMeta: - synckit: - optional: true - bin: - unrun: dist/cli.mjs - checksum: 10c0/a71b70741e9075589d2516e0efcb0475871b5e7d57dedfe951cd71502b55538e6bf699f656879e8dfc3820508d5e3099ae64a6e13dcd1816d544095bea5fa5cf - languageName: node - linkType: hard - "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -3874,6 +3717,13 @@ __metadata: languageName: node linkType: hard +"verkit@npm:^0.4.0": + version: 0.4.1 + resolution: "verkit@npm:0.4.1" + checksum: 10c0/b184269beec1d8ee90915d504f3d3671663a9c840cae01e664ea2614fcd5ff7e42472ce0b2795b66a967abaef6dfe2ecf89c5d2a6f1826c8cab9e65a41b7fd0f + languageName: node + linkType: hard + "vite@npm:^8.3.1": version: 8.3.1 resolution: "vite@npm:8.3.1" @@ -4083,3 +3933,105 @@ __metadata: checksum: 10c0/ca179aa83401aa0711f268fda38145a44c828e612a5f5265439efb5796cd63c46ca6b786bae3b74c75440a48518c36b5b23cba7fbb76ea8af5a9f85f830a5754 languageName: node linkType: hard + +"yuku-ast@npm:^0.10.1, yuku-ast@npm:^0.10.2": + version: 0.10.2 + resolution: "yuku-ast@npm:0.10.2" + dependencies: + "@yuku-toolchain/types": "npm:^0.10.2" + checksum: 10c0/ecaaf37d5c7436f3eeade5ac68e50aaded476387008e8ef9643da8d3369c0d824de028c44d73b2d8ed3ccf5adafae770bbac5d0dec63c8e7b3957b784b45530d + languageName: node + linkType: hard + +"yuku-codegen@npm:^0.10.1": + version: 0.10.2 + resolution: "yuku-codegen@npm:0.10.2" + dependencies: + "@yuku-codegen/binding-android-arm64": "npm:0.10.2" + "@yuku-codegen/binding-darwin-arm64": "npm:0.10.2" + "@yuku-codegen/binding-darwin-x64": "npm:0.10.2" + "@yuku-codegen/binding-freebsd-x64": "npm:0.10.2" + "@yuku-codegen/binding-linux-arm-gnu": "npm:0.10.2" + "@yuku-codegen/binding-linux-arm-musl": "npm:0.10.2" + "@yuku-codegen/binding-linux-arm64-gnu": "npm:0.10.2" + "@yuku-codegen/binding-linux-arm64-musl": "npm:0.10.2" + "@yuku-codegen/binding-linux-x64-gnu": "npm:0.10.2" + "@yuku-codegen/binding-linux-x64-musl": "npm:0.10.2" + "@yuku-codegen/binding-win32-arm64": "npm:0.10.2" + "@yuku-codegen/binding-win32-x64": "npm:0.10.2" + "@yuku-toolchain/types": "npm:^0.10.2" + dependenciesMeta: + "@yuku-codegen/binding-android-arm64": + optional: true + "@yuku-codegen/binding-darwin-arm64": + optional: true + "@yuku-codegen/binding-darwin-x64": + optional: true + "@yuku-codegen/binding-freebsd-x64": + optional: true + "@yuku-codegen/binding-linux-arm-gnu": + optional: true + "@yuku-codegen/binding-linux-arm-musl": + optional: true + "@yuku-codegen/binding-linux-arm64-gnu": + optional: true + "@yuku-codegen/binding-linux-arm64-musl": + optional: true + "@yuku-codegen/binding-linux-x64-gnu": + optional: true + "@yuku-codegen/binding-linux-x64-musl": + optional: true + "@yuku-codegen/binding-win32-arm64": + optional: true + "@yuku-codegen/binding-win32-x64": + optional: true + checksum: 10c0/df143c7be4a4db7fba1489bd2d741f2b666077837b1cc66423ac399244e1d1c6b1fd4b0ff31224eeced440db5d2180a3c0864d9bf674956bc3425c492bb74bd2 + languageName: node + linkType: hard + +"yuku-parser@npm:^0.10.1": + version: 0.10.2 + resolution: "yuku-parser@npm:0.10.2" + dependencies: + "@yuku-parser/binding-android-arm64": "npm:0.10.2" + "@yuku-parser/binding-darwin-arm64": "npm:0.10.2" + "@yuku-parser/binding-darwin-x64": "npm:0.10.2" + "@yuku-parser/binding-freebsd-x64": "npm:0.10.2" + "@yuku-parser/binding-linux-arm-gnu": "npm:0.10.2" + "@yuku-parser/binding-linux-arm-musl": "npm:0.10.2" + "@yuku-parser/binding-linux-arm64-gnu": "npm:0.10.2" + "@yuku-parser/binding-linux-arm64-musl": "npm:0.10.2" + "@yuku-parser/binding-linux-x64-gnu": "npm:0.10.2" + "@yuku-parser/binding-linux-x64-musl": "npm:0.10.2" + "@yuku-parser/binding-win32-arm64": "npm:0.10.2" + "@yuku-parser/binding-win32-x64": "npm:0.10.2" + "@yuku-toolchain/types": "npm:^0.10.2" + yuku-ast: "npm:^0.10.2" + dependenciesMeta: + "@yuku-parser/binding-android-arm64": + optional: true + "@yuku-parser/binding-darwin-arm64": + optional: true + "@yuku-parser/binding-darwin-x64": + optional: true + "@yuku-parser/binding-freebsd-x64": + optional: true + "@yuku-parser/binding-linux-arm-gnu": + optional: true + "@yuku-parser/binding-linux-arm-musl": + optional: true + "@yuku-parser/binding-linux-arm64-gnu": + optional: true + "@yuku-parser/binding-linux-arm64-musl": + optional: true + "@yuku-parser/binding-linux-x64-gnu": + optional: true + "@yuku-parser/binding-linux-x64-musl": + optional: true + "@yuku-parser/binding-win32-arm64": + optional: true + "@yuku-parser/binding-win32-x64": + optional: true + checksum: 10c0/6e8e06dca54ba1c6b130d3ac3456052912d270076dfba820ded61b0f575d097a233b8d4bbd324d5275c208d08a2d9e9b8544e517b8655813478701aa01151d6d + languageName: node + linkType: hard