From 71171dfcc5cdd338af85d1f40acca6298810484c Mon Sep 17 00:00:00 2001 From: Jack Misner Date: Wed, 21 Jan 2026 17:02:10 +0000 Subject: [PATCH 1/2] Split CI into separate workflows per Node version - Rename ci.yml to format.yml (handles auto-formatting only) - Add ci-node18.yml, ci-node20.yml, ci-node22.yml for testing - Test workflows trigger via workflow_run after Format completes - This prevents duplicate CI runs when auto-format commits - Add Format badge and update CI badges in README --- .github/workflows/ci-node18.yml | 39 +++++++++++++++++ .github/workflows/ci-node20.yml | 39 +++++++++++++++++ .github/workflows/ci-node22.yml | 46 ++++++++++++++++++++ .github/workflows/{ci.yml => format.yml} | 55 ++---------------------- README.md | 5 +++ 5 files changed, 133 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/ci-node18.yml create mode 100644 .github/workflows/ci-node20.yml create mode 100644 .github/workflows/ci-node22.yml rename .github/workflows/{ci.yml => format.yml} (66%) diff --git a/.github/workflows/ci-node18.yml b/.github/workflows/ci-node18.yml new file mode 100644 index 0000000..cccb032 --- /dev/null +++ b/.github/workflows/ci-node18.yml @@ -0,0 +1,39 @@ +name: CI - Node 18 + +on: + workflow_run: + workflows: ["Format"] + types: + - completed + +jobs: + test: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_branch }} + + - name: Setup Node.js 18 + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: TypeScript type checking + run: npm run lint + + - name: Run tests + env: + CI: true + NODE_ENV: test + run: npm test + + - name: Build package + run: npm run build diff --git a/.github/workflows/ci-node20.yml b/.github/workflows/ci-node20.yml new file mode 100644 index 0000000..9350271 --- /dev/null +++ b/.github/workflows/ci-node20.yml @@ -0,0 +1,39 @@ +name: CI - Node 20 + +on: + workflow_run: + workflows: ["Format"] + types: + - completed + +jobs: + test: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_branch }} + + - name: Setup Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: TypeScript type checking + run: npm run lint + + - name: Run tests + env: + CI: true + NODE_ENV: test + run: npm test + + - name: Build package + run: npm run build diff --git a/.github/workflows/ci-node22.yml b/.github/workflows/ci-node22.yml new file mode 100644 index 0000000..5dcb51d --- /dev/null +++ b/.github/workflows/ci-node22.yml @@ -0,0 +1,46 @@ +name: CI - Node 22 + +on: + workflow_run: + workflows: ["Format"] + types: + - completed + +jobs: + test: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_branch }} + + - name: Setup Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: TypeScript type checking + run: npm run lint + + - name: Run tests with coverage + env: + CI: true + NODE_ENV: test + run: npm run coverage + + - name: Build package + run: npm run build + + - name: Upload coverage reports + uses: codecov/codecov-action@v4 + with: + directory: ./coverage + name: coverage + fail_ci_if_error: false diff --git a/.github/workflows/ci.yml b/.github/workflows/format.yml similarity index 66% rename from .github/workflows/ci.yml rename to .github/workflows/format.yml index 44ce511..6b6c393 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/format.yml @@ -1,4 +1,4 @@ -name: CI +name: Format on: push: @@ -11,57 +11,8 @@ permissions: pull-requests: write jobs: - test: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18, 20, 22] - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 - - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: TypeScript type checking - run: npm run lint - - - name: Format check - run: npm run format:check - - - name: Run tests with coverage - env: - CI: true - NODE_ENV: test - run: npm run coverage - - - name: Build package - run: npm run build - - - name: Upload coverage reports - if: matrix.node-version == 22 - uses: codecov/codecov-action@v4 - with: - directory: ./coverage - name: coverage - fail_ci_if_error: false - - # Auto-format job (only runs on Node 22, separate from matrix) format: runs-on: ubuntu-latest - needs: test - if: failure() == false steps: - name: Checkout code @@ -69,11 +20,12 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '22' + node-version: 22 cache: 'npm' - name: Install dependencies @@ -105,6 +57,7 @@ jobs: if: steps.verify-changed-files.outputs.changed == 'true' && steps.check-fork.outputs.is_fork == 'true' run: | echo "::warning::Formatting changes detected in fork PR. Please run 'npm run format' locally and push the changes." + exit 1 - name: Commit formatting changes if: steps.verify-changed-files.outputs.changed == 'true' && steps.check-fork.outputs.is_fork == 'false' diff --git a/README.md b/README.md index 1ff4f11..35e5905 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # @jackmisner/utm-toolkit +[![Format](https://github.com/jackmisner/utm-toolkit/actions/workflows/format.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/format.yml) +[![CI - Node 18](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node18.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node18.yml) +[![CI - Node 20](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node20.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node20.yml) +[![CI - Node 22](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node22.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node22.yml) + A comprehensive TypeScript library for capturing, storing, and appending UTM tracking parameters. Framework-agnostic core with optional React integration. ## Features From 327a99e0d5648ad7a150b3a6d19521767f94208e Mon Sep 17 00:00:00 2001 From: Jack Misner Date: Wed, 21 Jan 2026 17:16:22 +0000 Subject: [PATCH 2/2] Add oxlint linting and refactor CI to use workflow_call CI improvements: - Add ci.yml orchestrator that triggers on push/PR - Convert format.yml and ci-node*.yml to reusable workflows (workflow_call) - Format runs first, test workflows depend on it via `needs` - Add Codecov token for coverage uploads - Update README to single CI badge Linting: - Add oxlint for actual linting (separate from type checking) - Rename `lint` script to `type-check` (tsc --noEmit) - Add `lint` and `lint:fix` scripts for oxlint - Fix lint warning: remove unnecessary fallback in spread --- .github/workflows/ci-node18.yml | 11 +-- .github/workflows/ci-node20.yml | 11 +-- .github/workflows/ci-node22.yml | 12 ++- .github/workflows/ci.yml | 29 +++++++ .github/workflows/format.yml | 5 +- README.md | 5 +- package-lock.json | 147 ++++++++++++++++++++++++++++++++ package.json | 5 +- src/config/loader.ts | 2 +- 9 files changed, 196 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci-node18.yml b/.github/workflows/ci-node18.yml index cccb032..fb3f38f 100644 --- a/.github/workflows/ci-node18.yml +++ b/.github/workflows/ci-node18.yml @@ -1,21 +1,15 @@ name: CI - Node 18 on: - workflow_run: - workflows: ["Format"] - types: - - completed + workflow_call: jobs: test: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout code uses: actions/checkout@v4 - with: - ref: ${{ github.event.workflow_run.head_branch }} - name: Setup Node.js 18 uses: actions/setup-node@v4 @@ -27,6 +21,9 @@ jobs: run: npm ci - name: TypeScript type checking + run: npm run type-check + + - name: Lint run: npm run lint - name: Run tests diff --git a/.github/workflows/ci-node20.yml b/.github/workflows/ci-node20.yml index 9350271..ee1f47c 100644 --- a/.github/workflows/ci-node20.yml +++ b/.github/workflows/ci-node20.yml @@ -1,21 +1,15 @@ name: CI - Node 20 on: - workflow_run: - workflows: ["Format"] - types: - - completed + workflow_call: jobs: test: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout code uses: actions/checkout@v4 - with: - ref: ${{ github.event.workflow_run.head_branch }} - name: Setup Node.js 20 uses: actions/setup-node@v4 @@ -27,6 +21,9 @@ jobs: run: npm ci - name: TypeScript type checking + run: npm run type-check + + - name: Lint run: npm run lint - name: Run tests diff --git a/.github/workflows/ci-node22.yml b/.github/workflows/ci-node22.yml index 5dcb51d..ef33102 100644 --- a/.github/workflows/ci-node22.yml +++ b/.github/workflows/ci-node22.yml @@ -1,21 +1,15 @@ name: CI - Node 22 on: - workflow_run: - workflows: ["Format"] - types: - - completed + workflow_call: jobs: test: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout code uses: actions/checkout@v4 - with: - ref: ${{ github.event.workflow_run.head_branch }} - name: Setup Node.js 22 uses: actions/setup-node@v4 @@ -27,6 +21,9 @@ jobs: run: npm ci - name: TypeScript type checking + run: npm run type-check + + - name: Lint run: npm run lint - name: Run tests with coverage @@ -41,6 +38,7 @@ jobs: - name: Upload coverage reports uses: codecov/codecov-action@v4 with: + token: ${{ secrets.CODECOV_TOKEN }} directory: ./coverage name: coverage fail_ci_if_error: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5ad70ed --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + format: + uses: ./.github/workflows/format.yml + secrets: inherit + + test-node18: + needs: format + uses: ./.github/workflows/ci-node18.yml + + test-node20: + needs: format + uses: ./.github/workflows/ci-node20.yml + + test-node22: + needs: format + uses: ./.github/workflows/ci-node22.yml + secrets: inherit diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 6b6c393..25643cb 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -1,10 +1,7 @@ name: Format on: - push: - branches: [main] - pull_request: - branches: [main] + workflow_call: permissions: contents: write diff --git a/README.md b/README.md index 35e5905..700bec2 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ # @jackmisner/utm-toolkit -[![Format](https://github.com/jackmisner/utm-toolkit/actions/workflows/format.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/format.yml) -[![CI - Node 18](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node18.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node18.yml) -[![CI - Node 20](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node20.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node20.yml) -[![CI - Node 22](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node22.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci-node22.yml) +[![CI](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/jackmisner/utm-toolkit/actions/workflows/ci.yml) A comprehensive TypeScript library for capturing, storing, and appending UTM tracking parameters. Framework-agnostic core with optional React integration. diff --git a/package-lock.json b/package-lock.json index a3c467d..8be57d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@types/react": "^18.0.0", "@vitest/coverage-v8": "^2.0.0", "jsdom": "^25.0.0", + "oxlint": "^1.0.0", "prettier": "^3.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", @@ -715,6 +716,118 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@oxlint/darwin-arm64": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.41.0.tgz", + "integrity": "sha512-K0Bs0cNW11oWdSrKmrollKF44HMM2HKr4QidZQHMlhJcSX8pozxv0V5FLdqB4sddzCY0J9Wuuw+oRAfR8sdRwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/darwin-x64": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.41.0.tgz", + "integrity": "sha512-1LCCXCe9nN8LbrJ1QOGari2HqnxrZrveYKysWDIg8gFsQglIg00XF/8lRbA0kWHMdLgt4X0wfNYhhFz+c3XXLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/linux-arm64-gnu": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.41.0.tgz", + "integrity": "sha512-Fow7H84Bs8XxuaK1yfSEWBC8HI7rfEQB9eR2A0J61un1WgCas7jNrt1HbT6+p6KmUH2bhR+r/RDu/6JFAvvj4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-arm64-musl": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.41.0.tgz", + "integrity": "sha512-WoRRDNwgP5W3rjRh42Zdx8ferYnqpKoYCv2QQLenmdrLjRGYwAd52uywfkcS45mKEWHeY1RPwPkYCSROXiGb2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-gnu": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.41.0.tgz", + "integrity": "sha512-75k3CKj3fOc/a/2aSgO81s3HsTZOFROthPJ+UI2Oatic1LhvH6eKjKfx3jDDyVpzeDS2qekPlc/y3N33iZz5Og==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-musl": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.41.0.tgz", + "integrity": "sha512-8r82eBwGPoAPn67ZvdxTlX/Z3gVb+ZtN6nbkyFzwwHWAh8yGutX+VBcVkyrePSl6XgBP4QAaddPnHmkvJjqY0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/win32-arm64": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.41.0.tgz", + "integrity": "sha512-aK+DAcckQsNCOXKruatyYuY/ROjNiRejQB1PeJtkZwM21+8rV9ODYbvKNvt0pW+YCws7svftBSFMCpl3ke2unw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint/win32-x64": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.41.0.tgz", + "integrity": "sha512-dVBXkZ6MGLd3owV7jvuqJsZwiF3qw7kEkDVsYVpS/O96eEvlHcxVbaPjJjrTBgikXqyC22vg3dxBU7MW0utGfw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2300,6 +2413,40 @@ "dev": true, "license": "MIT" }, + "node_modules/oxlint": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.41.0.tgz", + "integrity": "sha512-Dyaoup82uhgAgp5xLNt4dPdvl5eSJTIzqzL7DcKbkooUE4PDViWURIPlSUF8hu5a+sCnNIp/LlQMDsKoyaLTBA==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/darwin-arm64": "1.41.0", + "@oxlint/darwin-x64": "1.41.0", + "@oxlint/linux-arm64-gnu": "1.41.0", + "@oxlint/linux-arm64-musl": "1.41.0", + "@oxlint/linux-x64-gnu": "1.41.0", + "@oxlint/linux-x64-musl": "1.41.0", + "@oxlint/win32-arm64": "1.41.0", + "@oxlint/win32-x64": "1.41.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=0.11.1" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + } + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", diff --git a/package.json b/package.json index 39190c4..cc2af0b 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,9 @@ "test": "vitest run", "test:watch": "vitest", "coverage": "vitest run --coverage", - "lint": "tsc --noEmit", + "type-check": "tsc --noEmit", + "lint": "oxlint", + "lint:fix": "oxlint --fix", "format": "prettier --write \"src/**/*.{ts,tsx}\" \"__tests__/**/*.{ts,tsx}\"", "format:check": "prettier --check \"src/**/*.{ts,tsx}\" \"__tests__/**/*.{ts,tsx}\"", "prepublishOnly": "npm run clean && npm run build && npm run test" @@ -60,6 +62,7 @@ "@types/react": "^18.0.0", "@vitest/coverage-v8": "^2.0.0", "jsdom": "^25.0.0", + "oxlint": "^1.0.0", "prettier": "^3.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", diff --git a/src/config/loader.ts b/src/config/loader.ts index b3442b2..e4b6c3f 100644 --- a/src/config/loader.ts +++ b/src/config/loader.ts @@ -24,7 +24,7 @@ function mergeShareContextParams( if (value !== undefined) { // Merge platform-specific params with base result[key] = { - ...(base[key] || {}), + ...base[key], ...value, } }