From 9a839d312cc3c7f5eef056db433f980d0283eee7 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 14 Sep 2026 11:55:38 -0400 Subject: [PATCH 1/4] chore(repo): report cyclomatic complexity as an ESLint warning --- .changeset/complexity-lint-warning.md | 2 ++ .claude/skills/clerk-monorepo/SKILL.md | 3 +++ AGENTS.md | 1 + eslint.config.mjs | 1 + 4 files changed, 7 insertions(+) create mode 100644 .changeset/complexity-lint-warning.md diff --git a/.changeset/complexity-lint-warning.md b/.changeset/complexity-lint-warning.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/complexity-lint-warning.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index e94e35f3480..5b06e00ed99 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -85,6 +85,9 @@ pnpm --filter @clerk/backend test:node path/to/file.test.ts pnpm lint pnpm format # workspace packages plus root files, docs/, integration/, scripts/ pnpm prettier --write '.claude/**/*.md' # pnpm format does not cover .claude/; format skill files this way +# Cyclomatic complexity (ESLint `complexity` at warn, max 20, modified variant) is a warning, not a gate. +# Check just the files this branch changed before opening a PR: +pnpm eslint $(git diff --name-only origin/main -- '*.ts' '*.tsx') # Changesets — write the file, don't run the CLI (both scripts are interactive prompts) pnpm changeset status --since=origin/main # what CI checks; run this to verify diff --git a/AGENTS.md b/AGENTS.md index 8e14f6f2746..44e12c0df5c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,7 @@ Clerk's JavaScript SDK and library monorepo. - Use `pnpm` only. `npm` and `yarn` are blocked by `preinstall`. Node `>=24.15`, pnpm `>=10.33`. - Every PR needs a changeset. `pnpm changeset` for package changes, `pnpm changeset:empty` for tooling/repo-only. Empty changesets are two `---` delimiters with no body. A changeset is a changelog entry for users upgrading the package, not a summary of the work done in the PR. Describe the user-facing change (what changed for someone consuming the library and how it affects them) rather than the implementation details of the diff. If a change has no user-facing impact, use an empty changeset. - PR descriptions follow `.github/PULL_REQUEST_TEMPLATE.md` and add no sections of their own. Never add a "Testing" (or "Test plan" / "How to test") section summarizing the tests written or the checks run; the Checklist covers that and reviewers read the diff. Describe the change, not the work done on it. +- Keep functions under a cyclomatic complexity of 20 (ESLint `complexity`, modified variant, reported as a warning). A change that pushes a function past that should split it, or the PR should say why it can't. The threshold is a starting point; the aim is to lower it over time as existing warnings are cleared. ## References diff --git a/eslint.config.mjs b/eslint.config.mjs index 325bcc948d7..a68f0ea89e0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -398,6 +398,7 @@ export default tseslint.config([ 'sort-imports': 'off', ...pluginTurbo.configs['flat/recommended'].rules, 'unused-imports/no-unused-imports': 'error', + complexity: ['warn', { max: 20, variant: 'modified' }], // TYPESCRIPT RULE DISABLES '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unsafe-argument': 'off', From b06a0cb05ef3181e250ebf875d859f858e43d826 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 14 Sep 2026 12:24:52 -0400 Subject: [PATCH 2/4] chore(repo): enforce cyclomatic complexity with bulk suppressions --- .claude/skills/clerk-monorepo/SKILL.md | 7 +- .prettierignore | 2 + AGENTS.md | 2 +- eslint.config.mjs | 2 +- packages/backend/eslint-suppressions.json | 22 +++ packages/clerk-js/eslint-suppressions.json | 27 +++ .../eslint-plugin/eslint-suppressions.json | 7 + packages/expo/eslint-suppressions.json | 17 ++ packages/express/eslint-suppressions.json | 7 + packages/headless/eslint-suppressions.json | 7 + packages/hono/eslint-suppressions.json | 7 + packages/nextjs/eslint-suppressions.json | 12 ++ .../react-router/eslint-suppressions.json | 7 + packages/shared/eslint-suppressions.json | 27 +++ .../eslint-suppressions.json | 7 + packages/testing/eslint-suppressions.json | 7 + packages/ui/eslint-suppressions.json | 167 ++++++++++++++++++ packages/upgrade/eslint-suppressions.json | 12 ++ 18 files changed, 341 insertions(+), 5 deletions(-) create mode 100644 packages/backend/eslint-suppressions.json create mode 100644 packages/clerk-js/eslint-suppressions.json create mode 100644 packages/eslint-plugin/eslint-suppressions.json create mode 100644 packages/expo/eslint-suppressions.json create mode 100644 packages/express/eslint-suppressions.json create mode 100644 packages/headless/eslint-suppressions.json create mode 100644 packages/hono/eslint-suppressions.json create mode 100644 packages/nextjs/eslint-suppressions.json create mode 100644 packages/react-router/eslint-suppressions.json create mode 100644 packages/shared/eslint-suppressions.json create mode 100644 packages/tanstack-react-start/eslint-suppressions.json create mode 100644 packages/testing/eslint-suppressions.json create mode 100644 packages/ui/eslint-suppressions.json create mode 100644 packages/upgrade/eslint-suppressions.json diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index 5b06e00ed99..f1001c026e8 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -85,9 +85,10 @@ pnpm --filter @clerk/backend test:node path/to/file.test.ts pnpm lint pnpm format # workspace packages plus root files, docs/, integration/, scripts/ pnpm prettier --write '.claude/**/*.md' # pnpm format does not cover .claude/; format skill files this way -# Cyclomatic complexity (ESLint `complexity` at warn, max 20, modified variant) is a warning, not a gate. -# Check just the files this branch changed before opening a PR: -pnpm eslint $(git diff --name-only origin/main -- '*.ts' '*.tsx') +# Cyclomatic complexity (ESLint `complexity`, max 20, modified variant) is an error. Existing violations are +# grandfathered in each package's eslint-suppressions.json. After simplifying one of those functions, drop its +# stale entry (lint fails until you do) from inside that package: +pnpm eslint src --prune-suppressions # Changesets — write the file, don't run the CLI (both scripts are interactive prompts) pnpm changeset status --since=origin/main # what CI checks; run this to verify diff --git a/.prettierignore b/.prettierignore index 933efe001d3..e1f3a7dfd10 100644 --- a/.prettierignore +++ b/.prettierignore @@ -27,3 +27,5 @@ renovate.json5 # Frozen snapshots of TypeDoc-generated MDX; must match raw `extract-methods.mjs` output. .typedoc/__tests__/__snapshots__/ CLAUDE.md +# Written by `eslint --suppress-rule` / `--prune-suppressions`; ESLint owns the formatting. +/**/eslint-suppressions.json diff --git a/AGENTS.md b/AGENTS.md index 44e12c0df5c..d0483e3685e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,7 @@ Clerk's JavaScript SDK and library monorepo. - Use `pnpm` only. `npm` and `yarn` are blocked by `preinstall`. Node `>=24.15`, pnpm `>=10.33`. - Every PR needs a changeset. `pnpm changeset` for package changes, `pnpm changeset:empty` for tooling/repo-only. Empty changesets are two `---` delimiters with no body. A changeset is a changelog entry for users upgrading the package, not a summary of the work done in the PR. Describe the user-facing change (what changed for someone consuming the library and how it affects them) rather than the implementation details of the diff. If a change has no user-facing impact, use an empty changeset. - PR descriptions follow `.github/PULL_REQUEST_TEMPLATE.md` and add no sections of their own. Never add a "Testing" (or "Test plan" / "How to test") section summarizing the tests written or the checks run; the Checklist covers that and reviewers read the diff. Describe the change, not the work done on it. -- Keep functions under a cyclomatic complexity of 20 (ESLint `complexity`, modified variant, reported as a warning). A change that pushes a function past that should split it, or the PR should say why it can't. The threshold is a starting point; the aim is to lower it over time as existing warnings are cleared. +- Keep functions under a cyclomatic complexity of 20 (ESLint `complexity`, modified variant, reported as an error). Pre-existing violations are grandfathered in each package's `eslint-suppressions.json`. A change that pushes a function past the limit must split it; do not add new suppressions or `eslint-disable` comments for this rule. When a grandfathered function is simplified, run `pnpm eslint src --prune-suppressions` in that package and commit the result. ## References diff --git a/eslint.config.mjs b/eslint.config.mjs index a68f0ea89e0..d695a8d919e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -398,7 +398,7 @@ export default tseslint.config([ 'sort-imports': 'off', ...pluginTurbo.configs['flat/recommended'].rules, 'unused-imports/no-unused-imports': 'error', - complexity: ['warn', { max: 20, variant: 'modified' }], + complexity: ['error', { max: 20, variant: 'modified' }], // TYPESCRIPT RULE DISABLES '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unsafe-argument': 'off', diff --git a/packages/backend/eslint-suppressions.json b/packages/backend/eslint-suppressions.json new file mode 100644 index 00000000000..2a5b86259d8 --- /dev/null +++ b/packages/backend/eslint-suppressions.json @@ -0,0 +1,22 @@ +{ + "src/api/request.ts": { + "complexity": { + "count": 1 + } + }, + "src/proxy.ts": { + "complexity": { + "count": 1 + } + }, + "src/tokens/authenticateContext.ts": { + "complexity": { + "count": 1 + } + }, + "src/tokens/request.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/clerk-js/eslint-suppressions.json b/packages/clerk-js/eslint-suppressions.json new file mode 100644 index 00000000000..648c457c400 --- /dev/null +++ b/packages/clerk-js/eslint-suppressions.json @@ -0,0 +1,27 @@ +{ + "src/core/clerk.ts": { + "complexity": { + "count": 4 + } + }, + "src/core/fapiClient.ts": { + "complexity": { + "count": 1 + } + }, + "src/core/resources/Base.ts": { + "complexity": { + "count": 1 + } + }, + "src/core/resources/User.ts": { + "complexity": { + "count": 1 + } + }, + "src/core/tokenCache.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/eslint-plugin/eslint-suppressions.json b/packages/eslint-plugin/eslint-suppressions.json new file mode 100644 index 00000000000..7fb068dc704 --- /dev/null +++ b/packages/eslint-plugin/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/next/require-auth-protection.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/expo/eslint-suppressions.json b/packages/expo/eslint-suppressions.json new file mode 100644 index 00000000000..b49f8d445be --- /dev/null +++ b/packages/expo/eslint-suppressions.json @@ -0,0 +1,17 @@ +{ + "src/hooks/useSignInWithApple.ios.ts": { + "complexity": { + "count": 1 + } + }, + "src/provider/nativeClientSync.tsx": { + "complexity": { + "count": 1 + } + }, + "src/provider/singleton/createClerkInstance.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/express/eslint-suppressions.json b/packages/express/eslint-suppressions.json new file mode 100644 index 00000000000..758031e47c6 --- /dev/null +++ b/packages/express/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/authenticateRequest.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/headless/eslint-suppressions.json b/packages/headless/eslint-suppressions.json new file mode 100644 index 00000000000..554a63dfe26 --- /dev/null +++ b/packages/headless/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/primitives/drawer/use-drawer-drag.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/hono/eslint-suppressions.json b/packages/hono/eslint-suppressions.json new file mode 100644 index 00000000000..8a6ec90cff2 --- /dev/null +++ b/packages/hono/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/clerkMiddleware.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/nextjs/eslint-suppressions.json b/packages/nextjs/eslint-suppressions.json new file mode 100644 index 00000000000..fc75cd6cd90 --- /dev/null +++ b/packages/nextjs/eslint-suppressions.json @@ -0,0 +1,12 @@ +{ + "src/server/clerkMiddleware.ts": { + "complexity": { + "count": 1 + } + }, + "src/utils/mergeNextClerkPropsWithEnv.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/react-router/eslint-suppressions.json b/packages/react-router/eslint-suppressions.json new file mode 100644 index 00000000000..867c44b80c8 --- /dev/null +++ b/packages/react-router/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/server/loadOptions.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/shared/eslint-suppressions.json b/packages/shared/eslint-suppressions.json new file mode 100644 index 00000000000..9c0dcf9de50 --- /dev/null +++ b/packages/shared/eslint-suppressions.json @@ -0,0 +1,27 @@ +{ + "src/authorization.ts": { + "complexity": { + "count": 1 + } + }, + "src/errors/parseError.ts": { + "complexity": { + "count": 1 + } + }, + "src/internal/clerk-js/web3.ts": { + "complexity": { + "count": 1 + } + }, + "src/react/hooks/useBillingIsEnabled.ts": { + "complexity": { + "count": 1 + } + }, + "src/react/hooks/useOrganizationDirectorySyncUsers.tsx": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/tanstack-react-start/eslint-suppressions.json b/packages/tanstack-react-start/eslint-suppressions.json new file mode 100644 index 00000000000..867c44b80c8 --- /dev/null +++ b/packages/tanstack-react-start/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/server/loadOptions.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/testing/eslint-suppressions.json b/packages/testing/eslint-suppressions.json new file mode 100644 index 00000000000..c35c061c571 --- /dev/null +++ b/packages/testing/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/common/setup.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/ui/eslint-suppressions.json b/packages/ui/eslint-suppressions.json new file mode 100644 index 00000000000..1b6aea25b65 --- /dev/null +++ b/packages/ui/eslint-suppressions.json @@ -0,0 +1,167 @@ +{ + "src/Components.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/Checkout/CheckoutComplete.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/Checkout/CheckoutForm.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/ConfigureDirectorySync/steps/ConfigureStep.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/OAuthConsent/OAuthConsent.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/OAuthDeviceVerification/OAuthDeviceVerification.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/OrganizationList/OrganizationListPage.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/OrganizationProfile/OrganizationMembers.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/PaymentAttempts/PaymentAttemptPage.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/PricingTable/PricingTableDefault.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/SessionTasks/tasks/TaskChooseOrganization/ChooseOrganizationScreen.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/SignIn/SignInStart.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/SignUp/SignUpForm.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/SignUp/SignUpStart.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/UserProfile/ConnectedAccountsSection.tsx": { + "complexity": { + "count": 1 + } + }, + "src/components/UserProfile/ProfileForm.tsx": { + "complexity": { + "count": 1 + } + }, + "src/contexts/components/SignIn.ts": { + "complexity": { + "count": 1 + } + }, + "src/contexts/components/SignUp.ts": { + "complexity": { + "count": 1 + } + }, + "src/elements/FormControl.tsx": { + "complexity": { + "count": 1 + } + }, + "src/elements/SocialButtons.tsx": { + "complexity": { + "count": 1 + } + }, + "src/elements/UserPreview.tsx": { + "complexity": { + "count": 1 + } + }, + "src/hooks/__tests__/useCoreOrganization.test.tsx": { + "complexity": { + "count": 4 + } + }, + "src/mosaic/components/button/button.tsx": { + "complexity": { + "count": 1 + } + }, + "src/mosaic/components/input/input.tsx": { + "complexity": { + "count": 1 + } + }, + "src/mosaic/user-button/user-button.model.tsx": { + "complexity": { + "count": 1 + } + }, + "src/mosaic/user-profile/user-profile-account-section/user-profile-edit-name.view.tsx": { + "complexity": { + "count": 1 + } + }, + "src/router/pathToRegexp.ts": { + "complexity": { + "count": 3 + } + }, + "src/utils/passwordUtils.ts": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/upgrade/eslint-suppressions.json b/packages/upgrade/eslint-suppressions.json new file mode 100644 index 00000000000..09580f5e85e --- /dev/null +++ b/packages/upgrade/eslint-suppressions.json @@ -0,0 +1,12 @@ +{ + "src/cli.js": { + "complexity": { + "count": 1 + } + }, + "src/codemods/transform-protect-to-show.cjs": { + "complexity": { + "count": 1 + } + } +} \ No newline at end of file From 78433f869c84d53b00313407e0b16aee71af93fb Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 14 Sep 2026 14:26:43 -0400 Subject: [PATCH 3/4] chore(repo): switch the complexity gate to cognitive complexity --- .claude/skills/clerk-monorepo/SKILL.md | 2 +- AGENTS.md | 2 +- eslint.config.mjs | 4 +- package.json | 1 + packages/backend/eslint-suppressions.json | 10 +- packages/clerk-js/eslint-suppressions.json | 41 +++++- packages/electron/eslint-suppressions.json | 12 ++ .../eslint-plugin/eslint-suppressions.json | 24 ++- packages/expo/eslint-suppressions.json | 9 +- packages/express/eslint-suppressions.json | 2 +- packages/fastify/eslint-suppressions.json | 7 + packages/headless/eslint-suppressions.json | 17 ++- packages/hono/eslint-suppressions.json | 2 +- packages/nextjs/eslint-suppressions.json | 12 -- .../react-router/eslint-suppressions.json | 7 - packages/react/eslint-suppressions.json | 12 ++ packages/shared/eslint-suppressions.json | 21 ++- .../eslint-suppressions.json | 7 - packages/testing/eslint-suppressions.json | 9 +- packages/ui/eslint-suppressions.json | 137 ++++++------------ packages/upgrade/eslint-suppressions.json | 39 ++++- packages/vue/eslint-suppressions.json | 7 + pnpm-lock.yaml | 77 +++++++++- 23 files changed, 298 insertions(+), 163 deletions(-) create mode 100644 packages/electron/eslint-suppressions.json create mode 100644 packages/fastify/eslint-suppressions.json delete mode 100644 packages/nextjs/eslint-suppressions.json delete mode 100644 packages/react-router/eslint-suppressions.json create mode 100644 packages/react/eslint-suppressions.json delete mode 100644 packages/tanstack-react-start/eslint-suppressions.json create mode 100644 packages/vue/eslint-suppressions.json diff --git a/.claude/skills/clerk-monorepo/SKILL.md b/.claude/skills/clerk-monorepo/SKILL.md index f1001c026e8..7ee23dab002 100644 --- a/.claude/skills/clerk-monorepo/SKILL.md +++ b/.claude/skills/clerk-monorepo/SKILL.md @@ -85,7 +85,7 @@ pnpm --filter @clerk/backend test:node path/to/file.test.ts pnpm lint pnpm format # workspace packages plus root files, docs/, integration/, scripts/ pnpm prettier --write '.claude/**/*.md' # pnpm format does not cover .claude/; format skill files this way -# Cyclomatic complexity (ESLint `complexity`, max 20, modified variant) is an error. Existing violations are +# Cognitive complexity (`sonarjs/cognitive-complexity`, max 15) is an error. Existing violations are # grandfathered in each package's eslint-suppressions.json. After simplifying one of those functions, drop its # stale entry (lint fails until you do) from inside that package: pnpm eslint src --prune-suppressions diff --git a/AGENTS.md b/AGENTS.md index d0483e3685e..cc0989e7b2c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,7 @@ Clerk's JavaScript SDK and library monorepo. - Use `pnpm` only. `npm` and `yarn` are blocked by `preinstall`. Node `>=24.15`, pnpm `>=10.33`. - Every PR needs a changeset. `pnpm changeset` for package changes, `pnpm changeset:empty` for tooling/repo-only. Empty changesets are two `---` delimiters with no body. A changeset is a changelog entry for users upgrading the package, not a summary of the work done in the PR. Describe the user-facing change (what changed for someone consuming the library and how it affects them) rather than the implementation details of the diff. If a change has no user-facing impact, use an empty changeset. - PR descriptions follow `.github/PULL_REQUEST_TEMPLATE.md` and add no sections of their own. Never add a "Testing" (or "Test plan" / "How to test") section summarizing the tests written or the checks run; the Checklist covers that and reviewers read the diff. Describe the change, not the work done on it. -- Keep functions under a cyclomatic complexity of 20 (ESLint `complexity`, modified variant, reported as an error). Pre-existing violations are grandfathered in each package's `eslint-suppressions.json`. A change that pushes a function past the limit must split it; do not add new suppressions or `eslint-disable` comments for this rule. When a grandfathered function is simplified, run `pnpm eslint src --prune-suppressions` in that package and commit the result. +- Keep functions under a cognitive complexity of 15 (`sonarjs/cognitive-complexity`, reported as an error). Pre-existing violations are grandfathered in each package's `eslint-suppressions.json`. A change that pushes a function past the limit must split it; do not add new suppressions or `eslint-disable` comments for this rule. When a grandfathered function is simplified, run `pnpm eslint src --prune-suppressions` in that package and commit the result. ## References diff --git a/eslint.config.mjs b/eslint.config.mjs index d695a8d919e..49e2a779ea8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -9,6 +9,7 @@ import pluginReact from 'eslint-plugin-react'; import pluginReactHooks from 'eslint-plugin-react-hooks'; import pluginStylex from '@stylexjs/eslint-plugin'; import pluginSimpleImportSort from 'eslint-plugin-simple-import-sort'; +import pluginSonarjs from 'eslint-plugin-sonarjs'; import pluginTurbo from 'eslint-plugin-turbo'; import pluginUnusedImports from 'eslint-plugin-unused-imports'; import pluginYml from 'eslint-plugin-yml'; @@ -330,6 +331,7 @@ export default tseslint.config([ }, }, 'simple-import-sort': pluginSimpleImportSort, + sonarjs: pluginSonarjs, 'unused-imports': pluginUnusedImports, turbo: pluginTurbo, }, @@ -398,7 +400,7 @@ export default tseslint.config([ 'sort-imports': 'off', ...pluginTurbo.configs['flat/recommended'].rules, 'unused-imports/no-unused-imports': 'error', - complexity: ['error', { max: 20, variant: 'modified' }], + 'sonarjs/cognitive-complexity': ['error', 15], // TYPESCRIPT RULE DISABLES '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unsafe-argument': 'off', diff --git a/package.json b/package.json index 836fd3ad975..1bb554441e9 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "eslint-plugin-react": "7.37.5", "eslint-plugin-react-hooks": "5.2.0", "eslint-plugin-simple-import-sort": "12.1.1", + "eslint-plugin-sonarjs": "4.2.0", "eslint-plugin-turbo": "2.5.5", "eslint-plugin-unused-imports": "4.4.1", "eslint-plugin-yml": "1.18.0", diff --git a/packages/backend/eslint-suppressions.json b/packages/backend/eslint-suppressions.json index 2a5b86259d8..9d3ae0fcdf0 100644 --- a/packages/backend/eslint-suppressions.json +++ b/packages/backend/eslint-suppressions.json @@ -1,22 +1,22 @@ { "src/api/request.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/proxy.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/tokens/authenticateContext.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/tokens/request.ts": { - "complexity": { - "count": 1 + "sonarjs/cognitive-complexity": { + "count": 2 } } } \ No newline at end of file diff --git a/packages/clerk-js/eslint-suppressions.json b/packages/clerk-js/eslint-suppressions.json index 648c457c400..9f84f8c22c8 100644 --- a/packages/clerk-js/eslint-suppressions.json +++ b/packages/clerk-js/eslint-suppressions.json @@ -1,26 +1,51 @@ { "src/core/clerk.ts": { - "complexity": { - "count": 4 + "sonarjs/cognitive-complexity": { + "count": 7 } }, "src/core/fapiClient.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/core/resources/Base.ts": { - "complexity": { + "src/core/fraudProtection.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/core/resources/User.ts": { - "complexity": { + "src/core/protect.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/core/protectSession.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/core/resources/Base.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/core/tokenCache.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/utils/billing.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/utils/captcha/turnstile.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/utils/mergePatch.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/electron/eslint-suppressions.json b/packages/electron/eslint-suppressions.json new file mode 100644 index 00000000000..2c5a0e3536b --- /dev/null +++ b/packages/electron/eslint-suppressions.json @@ -0,0 +1,12 @@ +{ + "src/main/create-clerk-bridge.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/storage/index.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/eslint-plugin/eslint-suppressions.json b/packages/eslint-plugin/eslint-suppressions.json index 7fb068dc704..5270fb7bb67 100644 --- a/packages/eslint-plugin/eslint-suppressions.json +++ b/packages/eslint-plugin/eslint-suppressions.json @@ -1,7 +1,27 @@ { - "src/next/require-auth-protection.ts": { - "complexity": { + "src/next/fix-auth-protection.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } + }, + "src/next/lib/exports.ts": { + "sonarjs/cognitive-complexity": { + "count": 3 + } + }, + "src/next/lib/fixers.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/next/lib/match-folders.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/next/lib/protection-checks.ts": { + "sonarjs/cognitive-complexity": { + "count": 2 + } } } \ No newline at end of file diff --git a/packages/expo/eslint-suppressions.json b/packages/expo/eslint-suppressions.json index b49f8d445be..a6794abf176 100644 --- a/packages/expo/eslint-suppressions.json +++ b/packages/expo/eslint-suppressions.json @@ -1,16 +1,11 @@ { "src/hooks/useSignInWithApple.ios.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/provider/nativeClientSync.tsx": { - "complexity": { - "count": 1 - } - }, - "src/provider/singleton/createClerkInstance.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/express/eslint-suppressions.json b/packages/express/eslint-suppressions.json index 758031e47c6..4f44c757bd2 100644 --- a/packages/express/eslint-suppressions.json +++ b/packages/express/eslint-suppressions.json @@ -1,6 +1,6 @@ { "src/authenticateRequest.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/fastify/eslint-suppressions.json b/packages/fastify/eslint-suppressions.json new file mode 100644 index 00000000000..faae7dda847 --- /dev/null +++ b/packages/fastify/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/withClerkMiddleware.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/headless/eslint-suppressions.json b/packages/headless/eslint-suppressions.json index 554a63dfe26..fe3050d1db7 100644 --- a/packages/headless/eslint-suppressions.json +++ b/packages/headless/eslint-suppressions.json @@ -1,6 +1,21 @@ { "src/primitives/drawer/use-drawer-drag.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/primitives/drawer/use-snap-points.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/primitives/otp/otp-input.tsx": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/utils/use-render.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/hono/eslint-suppressions.json b/packages/hono/eslint-suppressions.json index 8a6ec90cff2..430d5419109 100644 --- a/packages/hono/eslint-suppressions.json +++ b/packages/hono/eslint-suppressions.json @@ -1,6 +1,6 @@ { "src/clerkMiddleware.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/nextjs/eslint-suppressions.json b/packages/nextjs/eslint-suppressions.json deleted file mode 100644 index fc75cd6cd90..00000000000 --- a/packages/nextjs/eslint-suppressions.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "src/server/clerkMiddleware.ts": { - "complexity": { - "count": 1 - } - }, - "src/utils/mergeNextClerkPropsWithEnv.ts": { - "complexity": { - "count": 1 - } - } -} \ No newline at end of file diff --git a/packages/react-router/eslint-suppressions.json b/packages/react-router/eslint-suppressions.json deleted file mode 100644 index 867c44b80c8..00000000000 --- a/packages/react-router/eslint-suppressions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "src/server/loadOptions.ts": { - "complexity": { - "count": 1 - } - } -} \ No newline at end of file diff --git a/packages/react/eslint-suppressions.json b/packages/react/eslint-suppressions.json new file mode 100644 index 00000000000..197c8786e40 --- /dev/null +++ b/packages/react/eslint-suppressions.json @@ -0,0 +1,12 @@ +{ + "src/isomorphicClerk.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/utils/useCustomMenuItems.tsx": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/packages/shared/eslint-suppressions.json b/packages/shared/eslint-suppressions.json index 9c0dcf9de50..359658dfe79 100644 --- a/packages/shared/eslint-suppressions.json +++ b/packages/shared/eslint-suppressions.json @@ -1,26 +1,31 @@ { "src/authorization.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { + "count": 3 + } + }, + "src/internal/clerk-js/completeSignUpFlow.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/errors/parseError.ts": { - "complexity": { + "src/internal/clerk-js/url.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/internal/clerk-js/web3.ts": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/react/hooks/useBillingIsEnabled.ts": { - "complexity": { + "src/keys.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/react/hooks/useOrganizationDirectorySyncUsers.tsx": { - "complexity": { + "src/loadClerkJsScript.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/tanstack-react-start/eslint-suppressions.json b/packages/tanstack-react-start/eslint-suppressions.json deleted file mode 100644 index 867c44b80c8..00000000000 --- a/packages/tanstack-react-start/eslint-suppressions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "src/server/loadOptions.ts": { - "complexity": { - "count": 1 - } - } -} \ No newline at end of file diff --git a/packages/testing/eslint-suppressions.json b/packages/testing/eslint-suppressions.json index c35c061c571..59eb3126151 100644 --- a/packages/testing/eslint-suppressions.json +++ b/packages/testing/eslint-suppressions.json @@ -1,6 +1,11 @@ { - "src/common/setup.ts": { - "complexity": { + "src/common/helpers-utils.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/playwright/setupClerkTestingToken.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/ui/eslint-suppressions.json b/packages/ui/eslint-suppressions.json index 1b6aea25b65..2c97a39112e 100644 --- a/packages/ui/eslint-suppressions.json +++ b/packages/ui/eslint-suppressions.json @@ -1,166 +1,111 @@ { - "src/Components.tsx": { - "complexity": { - "count": 1 - } - }, "src/components/Checkout/CheckoutComplete.tsx": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/components/Checkout/CheckoutForm.tsx": { - "complexity": { - "count": 1 - } - }, - "src/components/ConfigureDirectorySync/steps/ConfigureStep.tsx": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx": { - "complexity": { + "src/components/ConfigureDirectorySync/SecurityDirectorySyncSection.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx": { - "complexity": { - "count": 1 - } - }, - "src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx": { - "complexity": { - "count": 1 - } - }, - "src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx": { - "complexity": { + "src/components/ConfigureSSO/elements/Wizard/reducer.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/components/OAuthConsent/OAuthConsent.tsx": { - "complexity": { - "count": 1 - } - }, - "src/components/OAuthDeviceVerification/OAuthDeviceVerification.tsx": { - "complexity": { - "count": 1 - } - }, - "src/components/OrganizationList/OrganizationListPage.tsx": { - "complexity": { - "count": 1 - } - }, - "src/components/OrganizationProfile/OrganizationMembers.tsx": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/components/PaymentAttempts/PaymentAttemptPage.tsx": { - "complexity": { + "src/components/OrganizationProfile/InviteMembersForm.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/components/PricingTable/PricingTableDefault.tsx": { - "complexity": { - "count": 1 - } - }, - "src/components/SessionTasks/tasks/TaskChooseOrganization/ChooseOrganizationScreen.tsx": { - "complexity": { - "count": 1 + "sonarjs/cognitive-complexity": { + "count": 2 } }, - "src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx": { - "complexity": { + "src/components/SignIn/SignInFactorOne.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/components/SignIn/SignInStart.tsx": { - "complexity": { - "count": 1 - } - }, - "src/components/SignUp/SignUpForm.tsx": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/components/SignUp/SignUpStart.tsx": { - "complexity": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/components/UserProfile/ConnectedAccountsSection.tsx": { - "complexity": { + "src/components/SignUp/signUpFormHelpers.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/components/UserProfile/ProfileForm.tsx": { - "complexity": { - "count": 1 + "src/components/SubscriptionDetails/index.tsx": { + "sonarjs/cognitive-complexity": { + "count": 2 } }, - "src/contexts/components/SignIn.ts": { - "complexity": { + "src/components/Subscriptions/SubscriptionsList.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/contexts/components/SignUp.ts": { - "complexity": { + "src/components/devPrompts/EnableOrganizationsPrompt/index.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/elements/FormControl.tsx": { - "complexity": { + "src/components/devPrompts/KeylessPrompt/index.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/elements/SocialButtons.tsx": { - "complexity": { + "src/contexts/components/Plans.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/elements/UserPreview.tsx": { - "complexity": { + "src/hooks/useProtectCheckRunner.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/hooks/__tests__/useCoreOrganization.test.tsx": { - "complexity": { - "count": 4 - } - }, - "src/mosaic/components/button/button.tsx": { - "complexity": { + "src/mosaic/user-button/user-button.view.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/mosaic/components/input/input.tsx": { - "complexity": { + "src/mosaic/user-profile/user-profile-security-panel.view.tsx": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/mosaic/user-button/user-button.model.tsx": { - "complexity": { - "count": 1 + "src/router/pathToRegexp.ts": { + "sonarjs/cognitive-complexity": { + "count": 4 } }, - "src/mosaic/user-profile/user-profile-account-section/user-profile-edit-name.view.tsx": { - "complexity": { + "src/utils/colors/legacy.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } }, - "src/router/pathToRegexp.ts": { - "complexity": { - "count": 3 - } - }, - "src/utils/passwordUtils.ts": { - "complexity": { + "src/utils/detectClerkStylesheetUsage.ts": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/upgrade/eslint-suppressions.json b/packages/upgrade/eslint-suppressions.json index 09580f5e85e..34e54bf2fd2 100644 --- a/packages/upgrade/eslint-suppressions.json +++ b/packages/upgrade/eslint-suppressions.json @@ -1,11 +1,46 @@ { "src/cli.js": { - "complexity": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/codemods/transform-align-experimental-unstable-prefixes.cjs": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/codemods/transform-protect-to-show-vue.cjs": { + "sonarjs/cognitive-complexity": { "count": 1 } }, "src/codemods/transform-protect-to-show.cjs": { - "complexity": { + "sonarjs/cognitive-complexity": { + "count": 3 + } + }, + "src/codemods/transform-remove-deprecated-props.cjs": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/config.js": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/runner.js": { + "sonarjs/cognitive-complexity": { + "count": 2 + } + }, + "src/util/detect-sdk.js": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + }, + "src/util/package-manager.js": { + "sonarjs/cognitive-complexity": { "count": 1 } } diff --git a/packages/vue/eslint-suppressions.json b/packages/vue/eslint-suppressions.json new file mode 100644 index 00000000000..cbf59b3b1fc --- /dev/null +++ b/packages/vue/eslint-suppressions.json @@ -0,0 +1,7 @@ +{ + "src/utils/useCustomMenuItems.ts": { + "sonarjs/cognitive-complexity": { + "count": 1 + } + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b3ecba5aa74..1fa42fc70ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -242,6 +242,9 @@ importers: eslint-plugin-simple-import-sort: specifier: 12.1.1 version: 12.1.1(eslint@9.31.0(jiti@2.7.0)) + eslint-plugin-sonarjs: + specifier: 4.2.0 + version: 4.2.0(eslint@9.31.0(jiti@2.7.0)) eslint-plugin-turbo: specifier: 2.5.5 version: 2.5.5(eslint@9.31.0(jiti@2.7.0))(turbo@2.10.12) @@ -7603,6 +7606,10 @@ packages: resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} engines: {node: '>=6.14.2'} + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -9016,6 +9023,11 @@ packages: peerDependencies: eslint: '>=5.0.0' + eslint-plugin-sonarjs@4.2.0: + resolution: {integrity: sha512-bqADfuNtTL7VK6RU29eoiFTtaaBKIpVPuX3bOl+rBpWSBa0zIBVZlqZNZQjfP6s4iXkAJokv5IsD8OsACkwApg==} + peerDependencies: + eslint: ^8.0.0 || ^9.0.0 || ^10.0.0 + eslint-plugin-turbo@2.5.5: resolution: {integrity: sha512-IlN65X6W7rgK88u5xl1xC+7FIGKA7eyaca0yxZQ9CBNV6keAaqtjZQLw8ZfXdv7T+MzTLYkYOeOHAv8yCRUx4Q==} peerDependencies: @@ -9637,6 +9649,9 @@ packages: resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} + functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} @@ -9793,6 +9808,10 @@ packages: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} + globals@17.12.0: + resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} + engines: {node: '>=18'} + globals@17.4.0: resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} engines: {node: '>=18'} @@ -10724,6 +10743,10 @@ packages: resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} engines: {'0': node >=0.6.0} + jsx-ast-utils-x@0.1.0: + resolution: {integrity: sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -13102,6 +13125,10 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} + refa@0.12.1: + resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -13131,6 +13158,10 @@ packages: regex@6.1.0: resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + regexp-ast-analysis@0.7.1: + resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} @@ -13427,6 +13458,10 @@ packages: resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} + scslre@0.3.0: + resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} + engines: {node: ^14.0.0 || >=16.0.0} + scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} @@ -22066,7 +22101,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 5.0.0(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.0)(@types/node@22.20.1)(@vitest/coverage-v8@5.0.0)(happy-dom@20.10.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@27.0.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.15.0(@types/node@22.20.1)(typescript@6.0.3))(vite@8.2.2(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.46.1)(tsx@4.20.6)(yaml@2.9.0)) + vitest: 5.0.0(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.0)(@types/node@22.20.1)(@vitest/coverage-v8@5.0.0)(happy-dom@20.10.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@27.0.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.15.0(@types/node@22.20.1)(typescript@6.0.3))(vite@8.2.2(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.46.1)(tsx@4.20.6)(yaml@2.9.0)) '@vitest/istanbul-lib-coverage@1.0.1': {} @@ -23296,6 +23331,8 @@ snapshots: node-gyp-build: 4.8.4 optional: true + builtin-modules@3.3.0: {} + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -24903,6 +24940,23 @@ snapshots: dependencies: eslint: 9.31.0(jiti@2.7.0) + eslint-plugin-sonarjs@4.2.0(eslint@9.31.0(jiti@2.7.0)): + dependencies: + '@eslint-community/regexpp': 4.12.2 + builtin-modules: 3.3.0 + bytes: 3.1.2 + eslint: 9.31.0(jiti@2.7.0) + functional-red-black-tree: 1.0.1 + globals: 17.12.0 + jsx-ast-utils-x: 0.1.0 + lodash.merge: 4.6.2 + minimatch: 10.2.6 + scslre: 0.3.0 + semver: 7.8.5 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + yaml: 2.9.0 + eslint-plugin-turbo@2.5.5(eslint@9.31.0(jiti@2.7.0))(turbo@2.10.12): dependencies: dotenv: 16.0.3 @@ -25780,6 +25834,8 @@ snapshots: hasown: 2.0.2 is-callable: 1.2.7 + functional-red-black-tree@1.0.1: {} + functions-have-names@1.2.3: {} fuse.js@7.5.0: {} @@ -25939,6 +25995,8 @@ snapshots: globals@15.15.0: {} + globals@17.12.0: {} + globals@17.4.0: {} globalthis@1.0.4: @@ -26996,6 +27054,8 @@ snapshots: json-schema: 0.4.0 verror: 1.10.0 + jsx-ast-utils-x@0.1.0: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -30059,6 +30119,10 @@ snapshots: dependencies: redis-errors: 1.2.0 + refa@0.12.1: + dependencies: + '@eslint-community/regexpp': 4.12.2 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -30097,6 +30161,11 @@ snapshots: dependencies: regex-utilities: 2.3.0 + regexp-ast-analysis@0.7.1: + dependencies: + '@eslint-community/regexpp': 4.12.2 + refa: 0.12.1 + regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -30496,6 +30565,12 @@ snapshots: ajv-keywords: 5.1.0(ajv@8.20.0) optional: true + scslre@0.3.0: + dependencies: + '@eslint-community/regexpp': 4.12.2 + refa: 0.12.1 + regexp-ast-analysis: 0.7.1 + scule@1.3.0: {} section-matter@1.0.0: From 007419aed38a3a458054f4ccd9a3e7ab9748ddf4 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 14 Sep 2026 15:12:02 -0400 Subject: [PATCH 4/4] chore(repo): drop the complexity rule from AGENTS.md --- AGENTS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index cc0989e7b2c..8e14f6f2746 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,6 @@ Clerk's JavaScript SDK and library monorepo. - Use `pnpm` only. `npm` and `yarn` are blocked by `preinstall`. Node `>=24.15`, pnpm `>=10.33`. - Every PR needs a changeset. `pnpm changeset` for package changes, `pnpm changeset:empty` for tooling/repo-only. Empty changesets are two `---` delimiters with no body. A changeset is a changelog entry for users upgrading the package, not a summary of the work done in the PR. Describe the user-facing change (what changed for someone consuming the library and how it affects them) rather than the implementation details of the diff. If a change has no user-facing impact, use an empty changeset. - PR descriptions follow `.github/PULL_REQUEST_TEMPLATE.md` and add no sections of their own. Never add a "Testing" (or "Test plan" / "How to test") section summarizing the tests written or the checks run; the Checklist covers that and reviewers read the diff. Describe the change, not the work done on it. -- Keep functions under a cognitive complexity of 15 (`sonarjs/cognitive-complexity`, reported as an error). Pre-existing violations are grandfathered in each package's `eslint-suppressions.json`. A change that pushes a function past the limit must split it; do not add new suppressions or `eslint-disable` comments for this rule. When a grandfathered function is simplified, run `pnpm eslint src --prune-suppressions` in that package and commit the result. ## References