From 1fc757b147b6c94fffe7fcf8d25ee73604e011e4 Mon Sep 17 00:00:00 2001 From: Kurt Stohrer Date: Mon, 22 Jun 2026 19:07:55 -0400 Subject: [PATCH 1/2] fix(test): use Node-20-compatible readdir instead of fs.globSync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The transform smoke tests imported fs.globSync, which only exists in Node 22+. CI (and publish.yml) run Node 20, so 'pnpm test' threw 'TypeError: globSync is not a function' — turning main CI red and blocking the npm publish step. Replaced with a recursive readdirSync (Node 18.17+/20), which lists the same playground sources. Test-only; shipped code is unaffected. --- src/plugin/__tests__/transform-react.test.ts | 9 +++++++-- src/plugin/__tests__/transform-svelte.test.ts | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/plugin/__tests__/transform-react.test.ts b/src/plugin/__tests__/transform-react.test.ts index b9b9000..93531a7 100644 --- a/src/plugin/__tests__/transform-react.test.ts +++ b/src/plugin/__tests__/transform-react.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { globSync, readFileSync } from 'node:fs' +import { readdirSync, readFileSync } from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import ts from 'typescript' @@ -354,7 +354,12 @@ describe('transformJSX', () => { path.dirname(fileURLToPath(import.meta.url)), '../../../playgrounds/simple/react-vite' ) - const files = globSync('src/**/*.{tsx,jsx}', { cwd: playgroundRoot }) + // Recursive readdir (Node 18.17+/20) — NOT fs.globSync, which is Node 22+ + // and would throw on CI's Node 20. + const files = readdirSync(path.join(playgroundRoot, 'src'), { recursive: true }) + .map(String) + .filter((f) => f.endsWith('.tsx') || f.endsWith('.jsx')) + .map((f) => path.join('src', f)) expect(files.length).toBeGreaterThan(0) for (const rel of files) { const file = path.join(playgroundRoot, rel) diff --git a/src/plugin/__tests__/transform-svelte.test.ts b/src/plugin/__tests__/transform-svelte.test.ts index 0e12d4c..24a3ee2 100644 --- a/src/plugin/__tests__/transform-svelte.test.ts +++ b/src/plugin/__tests__/transform-svelte.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { globSync, readFileSync } from 'node:fs' +import { readdirSync, readFileSync } from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import { compile } from 'svelte/compiler' @@ -287,7 +287,12 @@ describe('transformSvelte', () => { path.dirname(fileURLToPath(import.meta.url)), '../../../playgrounds/simple/svelte-vite' ) - const files = globSync('src/**/*.svelte', { cwd: playgroundRoot }) + // Recursive readdir (Node 18.17+/20) — NOT fs.globSync, which is Node 22+ + // and would throw on CI's Node 20. + const files = readdirSync(path.join(playgroundRoot, 'src'), { recursive: true }) + .map(String) + .filter((f) => f.endsWith('.svelte')) + .map((f) => path.join('src', f)) expect(files.length).toBeGreaterThan(0) for (const rel of files) { const file = path.join(playgroundRoot, rel) From f5ffd00b7b68601a9e2db5cfbba8f8a84358a915 Mon Sep 17 00:00:00 2001 From: Kurt Stohrer Date: Mon, 22 Jun 2026 19:17:29 -0400 Subject: [PATCH 2/2] ci: auto-publish on release by calling publish.yml as a reusable workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit release.yml created the GitHub Release with GITHUB_TOKEN, whose events don't trigger other workflows — so publish.yml's 'release: published' trigger never fired and npm was never published. Make release.yml call publish.yml directly (workflow_call + secrets: inherit) after cutting the tag/release, gated on a version change. A direct reusable-workflow call isn't subject to the GITHUB_TOKEN recursion guard, so a version bump on main now auto-publishes — no manual dispatch, no PAT secret. publish.yml keeps its release/dispatch triggers for manual runs. --- .github/workflows/publish.yml | 9 +++++++++ .github/workflows/release.yml | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d2e2e49..71d282e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,6 +3,15 @@ name: Publish to npm on: release: types: [published] + # Called directly by release.yml after it cuts the tag/release. A reusable + # workflow_call is a direct invocation, so it runs even though the release + # was created with GITHUB_TOKEN (whose events don't trigger other workflows). + workflow_call: + inputs: + dry-run: + description: 'Dry run (skip actual publish)' + type: boolean + default: false workflow_dispatch: inputs: dry-run: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a66c276..ec94615 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,9 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + outputs: + changed: ${{ steps.version.outputs.changed }} + version: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v4 with: @@ -41,3 +44,13 @@ jobs: gh release create "$TAG" \ --title "$TAG" \ --generate-notes + + # Publish straight after the release is cut. Called as a reusable workflow + # (not via the `release: published` event, which GITHUB_TOKEN-created releases + # never fire) so a version bump on main now auto-publishes to npm with no + # manual dispatch and no PAT. Skipped when the version didn't change. + publish: + needs: release + if: needs.release.outputs.changed == 'true' + uses: ./.github/workflows/publish.yml + secrets: inherit