diff --git a/.github/workflows/ci-node18.yml b/.github/workflows/ci-node18.yml new file mode 100644 index 0000000..fb3f38f --- /dev/null +++ b/.github/workflows/ci-node18.yml @@ -0,0 +1,36 @@ +name: CI - Node 18 + +on: + workflow_call: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - 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 type-check + + - name: Lint + 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..ee1f47c --- /dev/null +++ b/.github/workflows/ci-node20.yml @@ -0,0 +1,36 @@ +name: CI - Node 20 + +on: + workflow_call: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - 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 type-check + + - name: Lint + 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..ef33102 --- /dev/null +++ b/.github/workflows/ci-node22.yml @@ -0,0 +1,44 @@ +name: CI - Node 22 + +on: + workflow_call: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - 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 type-check + + - name: Lint + 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: + 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 index 44ce511..5ad70ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,118 +11,19 @@ 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 - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Format code with Prettier - run: npm run format - - - name: Check for formatting changes - id: verify-changed-files - run: | - if [ -n "$(git status --porcelain)" ]; then - echo "changed=true" >> $GITHUB_OUTPUT - else - echo "changed=false" >> $GITHUB_OUTPUT - fi - - - name: Check if fork PR - id: check-fork - if: steps.verify-changed-files.outputs.changed == 'true' - run: | - if [ "${{ github.event_name }}" == "pull_request" ] && [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then - echo "is_fork=true" >> $GITHUB_OUTPUT - else - echo "is_fork=false" >> $GITHUB_OUTPUT - fi - - - name: Warn about fork PRs - 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." + uses: ./.github/workflows/format.yml + secrets: inherit - - name: Commit formatting changes - if: steps.verify-changed-files.outputs.changed == 'true' && steps.check-fork.outputs.is_fork == 'false' - env: - BRANCH_NAME: ${{ github.head_ref }} - REF_NAME: ${{ github.ref_name }} - run: | - set -e - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add . - git commit -m "Auto-format code with Prettier + test-node18: + needs: format + uses: ./.github/workflows/ci-node18.yml - This commit was automatically generated by GitHub Actions - to ensure consistent code formatting across the project." + test-node20: + needs: format + uses: ./.github/workflows/ci-node20.yml - if [ "${{ github.event_name }}" == "pull_request" ]; then - git push origin HEAD:"$BRANCH_NAME" - else - git push origin HEAD:"$REF_NAME" - fi + test-node22: + needs: format + uses: ./.github/workflows/ci-node22.yml + secrets: inherit diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000..25643cb --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,78 @@ +name: Format + +on: + workflow_call: + +permissions: + contents: write + pull-requests: write + +jobs: + format: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + 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 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Format code with Prettier + run: npm run format + + - name: Check for formatting changes + id: verify-changed-files + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "changed=true" >> $GITHUB_OUTPUT + else + echo "changed=false" >> $GITHUB_OUTPUT + fi + + - name: Check if fork PR + id: check-fork + if: steps.verify-changed-files.outputs.changed == 'true' + run: | + if [ "${{ github.event_name }}" == "pull_request" ] && [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then + echo "is_fork=true" >> $GITHUB_OUTPUT + else + echo "is_fork=false" >> $GITHUB_OUTPUT + fi + + - name: Warn about fork PRs + 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' + env: + BRANCH_NAME: ${{ github.head_ref }} + REF_NAME: ${{ github.ref_name }} + run: | + set -e + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add . + git commit -m "Auto-format code with Prettier + + This commit was automatically generated by GitHub Actions + to ensure consistent code formatting across the project." + + if [ "${{ github.event_name }}" == "pull_request" ]; then + git push origin HEAD:"$BRANCH_NAME" + else + git push origin HEAD:"$REF_NAME" + fi diff --git a/README.md b/README.md index 1ff4f11..700bec2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # @jackmisner/utm-toolkit +[![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. ## Features 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, } }