From c8cdf8d276ca43c66028898b85618bba00db7bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 18 Jul 2026 21:42:54 +0200 Subject: [PATCH 1/5] Run tests on Windows --- .github/workflows/ci.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e73b0f..5d5c5d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,9 +39,12 @@ jobs: advanced-security: false test: - name: Test - runs-on: ubuntu-latest + name: Test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} timeout-minutes: 10 + strategy: + matrix: + os: [ubuntu-latest, windows-latest] env: # Save dprint plugins cache to node_nodules in CI for simpler caching DPRINT_CACHE_DIR: ${{ github.workspace }}/node_modules/.dprint-cache From 61833dc8c500b27ea53f9ca46f30dc36acae88af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 18 Jul 2026 22:01:47 +0200 Subject: [PATCH 2/5] separate test job --- .github/workflows/ci.yml | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d5c5d2..5da28e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,13 +38,10 @@ jobs: annotations: true advanced-security: false - test: - name: Test (${{ matrix.os }}) - runs-on: ${{ matrix.os }} + checks: + name: Build, lint, and typecheck + runs-on: ubuntu-latest timeout-minutes: 10 - strategy: - matrix: - os: [ubuntu-latest, windows-latest] env: # Save dprint plugins cache to node_nodules in CI for simpler caching DPRINT_CACHE_DIR: ${{ github.workspace }}/node_modules/.dprint-cache @@ -56,12 +53,6 @@ jobs: - uses: ./.github/actions/ci-setup - # Deno is needed for tests only - - name: Set up Deno - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4 - with: - deno-version: 2.x - - name: Build run: pnpm build @@ -71,6 +62,27 @@ jobs: - name: Typecheck run: pnpm typecheck + test: + name: "Test on ${{ matrix.os }}" + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + fail-fast: false + steps: + - name: Check out repo + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + + - uses: ./.github/actions/ci-setup + + - name: Set up Deno + uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4 + with: + deno-version: 2.x + - name: Test run: pnpm test @@ -78,7 +90,7 @@ jobs: name: CI OK runs-on: ubuntu-latest if: always() - needs: [lint-workflows, test] + needs: [lint-workflows, checks, test] steps: - name: Exit with error if some jobs are not successful run: exit 1 From 251b63b2d1bd74509685372a25cd28dffdd19e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 18 Jul 2026 23:28:44 +0200 Subject: [PATCH 3/5] eol=lf --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf From 33847b6bc6b1aac0696c8fb724e2f7407d14e2ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 18 Jul 2026 23:38:33 +0200 Subject: [PATCH 4/5] use tinyexec --- package.json | 3 ++- pnpm-lock.yaml | 13 +++++-------- src/utils.ts | 24 ++++-------------------- tests/format.test.ts | 27 ++++++--------------------- 4 files changed, 17 insertions(+), 50 deletions(-) diff --git a/package.json b/package.json index a4e003b..f21b255 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "release": "changeset publish" }, "dependencies": { - "package-manager-detector": "^1.6.0" + "package-manager-detector": "^1.6.0", + "tinyexec": "^1.2.4" }, "devDependencies": { "@changesets/changelog-github": "^1.0.0-next.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fcbc413..29a4ec3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,9 @@ importers: package-manager-detector: specifier: ^1.6.0 version: 1.6.0 + tinyexec: + specifier: ^1.2.4 + version: 1.2.4 devDependencies: '@changesets/changelog-github': specifier: ^1.0.0-next.3 @@ -933,10 +936,6 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.1.2: - resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} - engines: {node: '>=18'} - tinyexec@1.2.4: resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} @@ -1207,7 +1206,7 @@ snapshots: package-manager-detector: 1.6.0 picocolors: 1.1.1 semver: 7.7.4 - tinyexec: 1.1.2 + tinyexec: 1.2.4 transitivePeerDependencies: - '@types/node' @@ -1250,7 +1249,7 @@ snapshots: '@changesets/types': 7.0.0-next.3 '@manypkg/get-packages': 3.1.0 picomatch: 4.0.4 - tinyexec: 1.1.2 + tinyexec: 1.2.4 '@changesets/logger@1.0.0-next.2': dependencies: @@ -1813,8 +1812,6 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.1.2: {} - tinyexec@1.2.4: {} tinyglobby@0.2.17: diff --git a/src/utils.ts b/src/utils.ts index 4384c1b..87aebb8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,6 @@ -import cp from "node:child_process"; import path from "node:path"; import { resolveCommand, type Agent } from "package-manager-detector"; +import { exec } from "tinyexec"; /** * Traverse upwards from `startDir` to `stopDir` (inclusive), calling `cb` in each directory. If @@ -37,24 +37,8 @@ export async function packageManagerExecute( } export async function spawnProcess(command: string, args: string[], cwd: string): Promise { - return new Promise((resolve, reject) => { - const child = cp.spawn(command, args, { cwd }); - - let stderr = ""; - child.stderr.on("data", (data) => { - stderr += data.toString(); - }); - - child.on("exit", (code) => { - if (code === 0) { - resolve(); - } else { - reject(new Error(`${command} exited with code ${code}. Stderr:\n${stderr}`)); - } - }); - - child.on("error", (err) => { - reject(err); - }); + await exec(command, args, { + nodeOptions: { cwd }, + throwOnError: true, }); } diff --git a/tests/format.test.ts b/tests/format.test.ts index 25c60da..5f20dba 100644 --- a/tests/format.test.ts +++ b/tests/format.test.ts @@ -3,28 +3,10 @@ import { expect, test, vi } from "vitest"; import { format, type FormatterName } from "../src/index.ts"; const mocks = vi.hoisted(() => ({ - spawn: vi.fn().mockReturnValue({ - // mock the bare-minimum based on how `src/utils.ts` uses `spawn` - on: (eventName: string, listener: Function) => { - if (eventName === "exit") { - listener(0); // simulate successful exit - } - }, - stderr: { on: () => {} }, - }), + exec: vi.fn().mockResolvedValue({ exitCode: 0, stderr: "", stdout: "" }), })); -vi.mock(import("node:child_process"), async (importOriginal) => { - const mod = await importOriginal(); - return { - ...mod, - spawn: mocks.spawn, - default: { - ...mod.default, - spawn: mocks.spawn, - }, - }; -}); +vi.mock(import("tinyexec"), () => ({ exec: mocks.exec })); const cases: { formatter: FormatterName; expectedCommand: string[] }[] = [ { formatter: "prettier", expectedCommand: ["npx", "prettier", "--write", "file.ts"] }, @@ -42,5 +24,8 @@ test.for(cases)("executes the correct command for $formatter", async (c) => { const result = await format(["file.ts"], { cwd: fixture.path, formatter: c.formatter }); expect(result).toBe(true); const [command, ...args] = c.expectedCommand; - expect(mocks.spawn).toHaveBeenCalledWith(command, args, { cwd: fixture.path }); + expect(mocks.exec).toHaveBeenCalledWith(command, args, { + nodeOptions: { cwd: fixture.path }, + throwOnError: true, + }); }); From 33c4560b2b24039204a39be722174be90b7028ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 18 Jul 2026 23:39:38 +0200 Subject: [PATCH 5/5] add changeset --- .changeset/friendly-tools-run.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/friendly-tools-run.md diff --git a/.changeset/friendly-tools-run.md b/.changeset/friendly-tools-run.md new file mode 100644 index 0000000..ba36420 --- /dev/null +++ b/.changeset/friendly-tools-run.md @@ -0,0 +1,5 @@ +--- +"@changesets/format": patch +--- + +Fix formatter command execution on Windows.