From 8ddc33946fb3e1ae12592b73190d8b111728bcba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 16:21:14 +0200 Subject: [PATCH 01/10] Bring in vitest types for ui tests --- .changeset/ui-test-jest-dom-types.md | 2 ++ packages/ui/src/test/jest-dom.d.ts | 1 + 2 files changed, 3 insertions(+) create mode 100644 .changeset/ui-test-jest-dom-types.md create mode 100644 packages/ui/src/test/jest-dom.d.ts diff --git a/.changeset/ui-test-jest-dom-types.md b/.changeset/ui-test-jest-dom-types.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/ui-test-jest-dom-types.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/ui/src/test/jest-dom.d.ts b/packages/ui/src/test/jest-dom.d.ts new file mode 100644 index 00000000000..bb02c60cd05 --- /dev/null +++ b/packages/ui/src/test/jest-dom.d.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest'; From 0f9e87d424f9c0eedc481f320c653b0d9cf509c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 16:24:41 +0200 Subject: [PATCH 02/10] Fix more ui test TS errors --- packages/ui/src/test/mock-helpers.ts | 4 ++-- packages/ui/tsconfig.test.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/test/mock-helpers.ts b/packages/ui/src/test/mock-helpers.ts index 5ff677f038a..0323cf8ac12 100644 --- a/packages/ui/src/test/mock-helpers.ts +++ b/packages/ui/src/test/mock-helpers.ts @@ -1,13 +1,13 @@ import { __createClerkTestQueryClient } from '@clerk/shared/react'; import type { ActiveSessionResource, LoadedClerk } from '@clerk/shared/types'; -import { type Mocked, vi } from 'vitest'; +import { type Mock, vi } from 'vitest'; import type { RouteContextValue } from '@/ui/router'; type FunctionLike = (...args: any) => any; type DeepVitestMocked = T extends FunctionLike - ? Mocked + ? Mock : T extends object ? { [k in keyof T]: DeepVitestMocked; diff --git a/packages/ui/tsconfig.test.json b/packages/ui/tsconfig.test.json index 2f48da331b8..8090187b6d6 100644 --- a/packages/ui/tsconfig.test.json +++ b/packages/ui/tsconfig.test.json @@ -16,7 +16,8 @@ "src/**/__tests__/**/*.ts", "src/**/__tests__/**/*.tsx", "src/test/**/*.ts", - "src/test/**/*.tsx" + "src/test/**/*.tsx", + "src/global.d.ts" ], "exclude": ["node_modules", "dist"] } From 2b41b700b40a14bfb516f1dcb2a073027670243b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 16:31:48 +0200 Subject: [PATCH 03/10] Stop relying on typechecking clerk-js in ui tests --- .../UserProfile/__tests__/utils.test.ts | 310 ++---------------- packages/ui/src/test/core-shims/clerk.ts | 4 + packages/ui/src/test/core-shims/resources.ts | 13 + packages/ui/src/test/fixture-helpers.ts | 2 +- packages/ui/tsconfig.base.json | 4 +- packages/ui/tsconfig.mosaic.json | 2 +- 6 files changed, 55 insertions(+), 280 deletions(-) create mode 100644 packages/ui/src/test/core-shims/clerk.ts create mode 100644 packages/ui/src/test/core-shims/resources.ts diff --git a/packages/ui/src/components/UserProfile/__tests__/utils.test.ts b/packages/ui/src/components/UserProfile/__tests__/utils.test.ts index 95d67d29ce1..9e7cd95b662 100644 --- a/packages/ui/src/components/UserProfile/__tests__/utils.test.ts +++ b/packages/ui/src/components/UserProfile/__tests__/utils.test.ts @@ -1,10 +1,20 @@ -import type { VerificationJSON } from '@clerk/shared/types'; +import type { EmailAddressResource, PhoneNumberResource } from '@clerk/shared/types'; import { describe, expect, it } from 'vitest'; -import { EmailAddress, PhoneNumber } from '@/core/resources'; - import { sortIdentificationBasedOnVerification } from '../utils'; +const email = (id: string, status: string, expireAtMs = 0) => + ({ + id, + verification: { status, expireAt: new Date(expireAtMs) }, + }) as EmailAddressResource; + +const phone = (id: string, status: string, expireAtMs = 0) => + ({ + id, + verification: { status, expireAt: new Date(expireAtMs) }, + }) as PhoneNumberResource; + describe('UserProfile utils', () => { describe('sortIdentificationBasedOnVerification', () => { it('should return an empty array if the input is null or undefined', () => { @@ -22,71 +32,11 @@ describe('UserProfile utils', () => { `1) primary, 2) verified (sorted alphabetically by id), 3) unverified (sorted by expiresAt verification property)`, () => { const input = [ - new EmailAddress( - { - id: '1', - email_address: 'test@clerk.com', - verification: { - strategy: 'email_code', - status: 'unverified', - attempts: 0, - expire_at: 200, - } as VerificationJSON, - }, - '', - ), - new EmailAddress( - { - id: '2', - email_address: 'test@clerk.com', - verification: { - strategy: 'email_code', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new EmailAddress( - { - id: '3', - email_address: 'test@clerk.com', - verification: { - strategy: 'email_code', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new EmailAddress( - { - id: '4', - email_address: 'test@clerk.com', - verification: { - strategy: 'email_code', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new EmailAddress( - { - id: '5', - email_address: 'test@clerk.com', - verification: { - strategy: 'email_code', - status: 'unverified', - attempts: 0, - expire_at: 100, - } as VerificationJSON, - }, - '', - ), + email('1', 'unverified', 200), + email('2', 'verified'), + email('3', 'verified'), + email('4', 'verified'), + email('5', 'unverified', 100), ]; const result = sortIdentificationBasedOnVerification(input, '3'); expect(result[0].id).toEqual('3'); @@ -102,71 +52,11 @@ describe('UserProfile utils', () => { `1) primary, 2) verified (sorted alphabetically by id), 3) unverified (sorted by expiresAt verification property)`, () => { const input = [ - new PhoneNumber( - { - id: '1', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'unverified', - attempts: 0, - expire_at: 200, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '2', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '3', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '4', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '5', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'unverified', - attempts: 0, - expire_at: 100, - } as VerificationJSON, - }, - '', - ), + phone('1', 'unverified', 200), + phone('2', 'verified'), + phone('3', 'verified'), + phone('4', 'verified'), + phone('5', 'unverified', 100), ]; const result = sortIdentificationBasedOnVerification(input, '3'); @@ -180,71 +70,11 @@ describe('UserProfile utils', () => { it('should return the correct order if the primaryId is not in the array', () => { const input = [ - new PhoneNumber( - { - id: '1', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'unverified', - attempts: 0, - expire_at: 200, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '2', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '3', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '4', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '5', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'unverified', - attempts: 0, - expire_at: 100, - } as VerificationJSON, - }, - '', - ), + phone('1', 'unverified', 200), + phone('2', 'verified'), + phone('3', 'verified'), + phone('4', 'verified'), + phone('5', 'unverified', 100), ]; const result = sortIdentificationBasedOnVerification(input, '10'); @@ -257,84 +87,12 @@ describe('UserProfile utils', () => { it('should return last the item without verification status', () => { const input = [ - new PhoneNumber( - { - id: '1', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'unverified', - attempts: 0, - expire_at: 200, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '2', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '3', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '4', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'verified', - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '5', - phone_number: '+1234567890', - verification: { - strategy: 'sms', - status: 'unverified', - attempts: 0, - expire_at: 100, - } as VerificationJSON, - }, - '', - ), - new PhoneNumber( - { - id: '6', - phone_number: '+1234567890', - verification: { - strategy: '', - status: '' as any, - attempts: 0, - expire_at: 0, - } as VerificationJSON, - }, - '', - ), + phone('1', 'unverified', 200), + phone('2', 'verified'), + phone('3', 'verified'), + phone('4', 'verified'), + phone('5', 'unverified', 100), + phone('6', ''), ]; const result = sortIdentificationBasedOnVerification(input, '3'); diff --git a/packages/ui/src/test/core-shims/clerk.ts b/packages/ui/src/test/core-shims/clerk.ts new file mode 100644 index 00000000000..cc93b325868 --- /dev/null +++ b/packages/ui/src/test/core-shims/clerk.ts @@ -0,0 +1,4 @@ +export class Clerk { + constructor(_publishableKey?: string) {} + async load() {} +} diff --git a/packages/ui/src/test/core-shims/resources.ts b/packages/ui/src/test/core-shims/resources.ts new file mode 100644 index 00000000000..8c164162cfb --- /dev/null +++ b/packages/ui/src/test/core-shims/resources.ts @@ -0,0 +1,13 @@ +export class Client { + static getOrCreateInstance() { + return { fetch: async () => new Client() }; + } + constructor(_data?: unknown) {} +} + +export class Environment { + static getInstance() { + return { fetch: async () => new Environment() }; + } + constructor(_data?: unknown) {} +} diff --git a/packages/ui/src/test/fixture-helpers.ts b/packages/ui/src/test/fixture-helpers.ts index a35c7f8adb6..c9684669c8b 100644 --- a/packages/ui/src/test/fixture-helpers.ts +++ b/packages/ui/src/test/fixture-helpers.ts @@ -1,3 +1,4 @@ +import { SIGN_UP_MODES } from '@clerk/shared/internal/clerk-js/constants'; import type { ClientJSON, DisplayConfigJSON, @@ -16,7 +17,6 @@ import type { VerificationJSON, } from '@clerk/shared/types'; -import { SIGN_UP_MODES } from '@/core/constants'; import type { OrgParams } from '@/test/core-fixtures'; import { createUser, getOrganizationId } from '@/test/core-fixtures'; diff --git a/packages/ui/tsconfig.base.json b/packages/ui/tsconfig.base.json index acdf124e912..b787838a2a9 100644 --- a/packages/ui/tsconfig.base.json +++ b/packages/ui/tsconfig.base.json @@ -17,8 +17,8 @@ "allowJs": true, "verbatimModuleSyntax": true, "paths": { - // Test-only imports; vitest.config.mts aliases these to clerk-js at runtime. - "@/core/*": ["../clerk-js/src/core/*"], + // Test-only; tsserver stays in-package. vitest.config.mts aliases these to clerk-js at runtime. + "@/core/*": ["./src/test/core-shims/*"], "@/*": ["./src/*"], // Adding this to avoid changes in the ui files // in order to make git merges easier diff --git a/packages/ui/tsconfig.mosaic.json b/packages/ui/tsconfig.mosaic.json index 05fde7579f6..a0653f841f2 100644 --- a/packages/ui/tsconfig.mosaic.json +++ b/packages/ui/tsconfig.mosaic.json @@ -10,7 +10,7 @@ "@clerk/headless/utils": ["../headless/src/utils/index.ts"], "@clerk/headless/*": ["../headless/src/*"], // Preserve the base config's test-only aliases (extends replaces `paths` wholesale). - "@/core/*": ["../clerk-js/src/core/*"], + "@/core/*": ["./src/test/core-shims/*"], "@/*": ["./src/*"], "@/ui*": ["./src/*"] } From 74801b92e824780826b4db6ce270123c1c66c39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 16:49:48 +0200 Subject: [PATCH 04/10] fix(ui): typecheck tests through a source project reference --- eslint.config.mjs | 2 +- packages/ui/package.json | 4 ++-- packages/ui/tsconfig.json | 22 ++-------------------- packages/ui/tsconfig.mosaic.json | 3 ++- packages/ui/tsconfig.src.json | 24 ++++++++++++++++++++++++ packages/ui/tsconfig.test.json | 4 ++-- packages/ui/tsdown.config.mts | 1 + packages/ui/turbo.json | 1 + 8 files changed, 35 insertions(+), 26 deletions(-) create mode 100644 packages/ui/tsconfig.src.json diff --git a/eslint.config.mjs b/eslint.config.mjs index 0edfe0d6b60..b6e779f50ed 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -339,7 +339,7 @@ export default tseslint.config([ node: true, typescript: { alwaysTryTypes: true, - project: ['packages/*/tsconfig.json', 'integration/tsconfig.json'], + project: ['packages/*/tsconfig.json', 'packages/*/tsconfig.src.json', 'integration/tsconfig.json'], }, }, }, diff --git a/packages/ui/package.json b/packages/ui/package.json index 82e3ae346a4..47c5a287918 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -100,11 +100,11 @@ "lint": "eslint src", "lint:attw": "attw --pack . --exclude-entrypoints themes/shadcn.css --profile esm-only", "lint:publint": "publint", - "showerrors": "tsc", + "showerrors": "tsc -p tsconfig.src.json", "test": "vitest run", "test:ci": "vitest run --maxWorkers=70%", "test:coverage": "vitest --collectCoverage && open coverage/lcov-report/index.html", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit -p tsconfig.src.json" }, "dependencies": { "@clerk/localizations": "workspace:^", diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 05dc93a5b78..1047f4776eb 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -1,22 +1,4 @@ { - "extends": "./tsconfig.base.json", - "compilerOptions": { - "rootDir": "src", - "preserveWatchOutput": true, - "outDir": "dist", - "declaration": true, - "declarationMap": true, - "types": ["@rspack/core/module"] - }, - "exclude": [ - "node_modules", - "**/*.test.ts", - "**/*.test.tsx", - "**/*.spec.ts", - "**/*.spec.tsx", - "**/__tests__/**", - "./src/test/**" - ], - "include": ["src", "src/global.d.ts"], - "references": [{ "path": "./tsconfig.test.json" }] + "files": [], + "references": [{ "path": "./tsconfig.src.json" }, { "path": "./tsconfig.test.json" }] } diff --git a/packages/ui/tsconfig.mosaic.json b/packages/ui/tsconfig.mosaic.json index a0653f841f2..e8f3f160eb7 100644 --- a/packages/ui/tsconfig.mosaic.json +++ b/packages/ui/tsconfig.mosaic.json @@ -1,6 +1,7 @@ { - "extends": "./tsconfig.json", + "extends": "./tsconfig.src.json", "compilerOptions": { + "composite": false, "jsxImportSource": "react", "paths": { // Resolve the private, unpublished `@clerk/headless` to its SOURCE for the Mosaic diff --git a/packages/ui/tsconfig.src.json b/packages/ui/tsconfig.src.json new file mode 100644 index 00000000000..86e4191133b --- /dev/null +++ b/packages/ui/tsconfig.src.json @@ -0,0 +1,24 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "preserveWatchOutput": true, + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "node_modules/.cache/tsconfig-src", + "tsBuildInfoFile": "node_modules/.cache/tsconfig-src.tsbuildinfo", + "types": ["@rspack/core/module"] + }, + "exclude": [ + "node_modules", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.spec.ts", + "**/*.spec.tsx", + "**/__tests__/**", + "./src/test/**" + ], + "include": ["src", "src/global.d.ts"] +} diff --git a/packages/ui/tsconfig.test.json b/packages/ui/tsconfig.test.json index 8090187b6d6..9f7f40e1d90 100644 --- a/packages/ui/tsconfig.test.json +++ b/packages/ui/tsconfig.test.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.base.json", "compilerOptions": { - // composite so tsserver can load this via tsconfig.json references (tsc --noEmit requires it). "composite": true, "declaration": true, "emitDeclarationOnly": true, @@ -19,5 +18,6 @@ "src/test/**/*.tsx", "src/global.d.ts" ], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist"], + "references": [{ "path": "./tsconfig.src.json" }] } diff --git a/packages/ui/tsdown.config.mts b/packages/ui/tsdown.config.mts index f5b8a74e396..aa920485e31 100644 --- a/packages/ui/tsdown.config.mts +++ b/packages/ui/tsdown.config.mts @@ -6,6 +6,7 @@ import uiPackage from './package.json' with { type: 'json' }; export default defineConfig(({ watch }) => { const common = { + tsconfig: './tsconfig.src.json', dts: true, sourcemap: true, clean: false, diff --git a/packages/ui/turbo.json b/packages/ui/turbo.json index d001d7daa95..731fbef4e40 100644 --- a/packages/ui/turbo.json +++ b/packages/ui/turbo.json @@ -11,6 +11,7 @@ "*.d.ts", "src/**", "tsconfig.json", + "tsconfig.src.json", "tsconfig.mosaic.json", "rspack.config.js", "tsdown.mosaic.config.mts", From 73e05d885450167fc12db8696cd21e5bbcef0736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 17:11:30 +0200 Subject: [PATCH 05/10] Small config tweaks --- packages/ui/tsconfig.json | 1 + packages/ui/turbo.json | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 1047f4776eb..5f9e2a21b8b 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -1,4 +1,5 @@ { "files": [], + "include": [], "references": [{ "path": "./tsconfig.src.json" }, { "path": "./tsconfig.test.json" }] } diff --git a/packages/ui/turbo.json b/packages/ui/turbo.json index 731fbef4e40..52949c19d26 100644 --- a/packages/ui/turbo.json +++ b/packages/ui/turbo.json @@ -11,6 +11,7 @@ "*.d.ts", "src/**", "tsconfig.json", + "tsconfig.base.json", "tsconfig.src.json", "tsconfig.mosaic.json", "rspack.config.js", From e0172d8cae85e9a754463ad70548a3ddc112ee22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 18:43:30 +0200 Subject: [PATCH 06/10] Add test tsconfig definitions to eslint import resolver --- eslint.config.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index b6e779f50ed..3aaba7730ad 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -339,7 +339,12 @@ export default tseslint.config([ node: true, typescript: { alwaysTryTypes: true, - project: ['packages/*/tsconfig.json', 'packages/*/tsconfig.src.json', 'integration/tsconfig.json'], + project: [ + 'packages/*/tsconfig.json', + 'packages/*/tsconfig.src.json', + 'packages/*/tsconfig.test.json', + 'integration/tsconfig.json', + ], }, }, }, From a821f0a712668660d81269ddfceb744112eaf6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 19:01:44 +0200 Subject: [PATCH 07/10] Add tsdown.config.mts to turbo.json --- packages/ui/turbo.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui/turbo.json b/packages/ui/turbo.json index 52949c19d26..e4784386d91 100644 --- a/packages/ui/turbo.json +++ b/packages/ui/turbo.json @@ -15,6 +15,7 @@ "tsconfig.src.json", "tsconfig.mosaic.json", "rspack.config.js", + "tsdown.config.mts", "tsdown.mosaic.config.mts", "!**/*.test.*", From fe3bc94d6b38491a8ab3db4e6a46372ab1186e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 19:22:41 +0200 Subject: [PATCH 08/10] Fix broken linting --- eslint.config.mjs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 3aaba7730ad..9607a144d65 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -20,7 +20,14 @@ import { CUSTOM_BLOCK_TAGS, CUSTOM_MODIFIER_TAGS } from './.typedoc/custom-tags. const ECMA_VERSION = 2021, JAVASCRIPT_FILES = ['**/*.cjs', '**/*.js', '**/*.jsx', '**/*.mjs'], TEST_FILES = ['**/*.test.js', '**/*.test.jsx', '**/*.test.ts', '**/*.test.tsx', '**/test/**', '**/__tests__/**'], - TYPESCRIPT_FILES = ['**/*.cts', '**/*.mts', '**/*.ts', '**/*.tsx']; + TYPESCRIPT_FILES = ['**/*.cts', '**/*.mts', '**/*.ts', '**/*.tsx'], + // turbo lint runs `eslint src` from each package cwd; these must be repo-absolute + IMPORT_RESOLVER_TSCONFIGS = [ + `${import.meta.dirname}/packages/*/tsconfig.json`, + `${import.meta.dirname}/packages/*/tsconfig.src.json`, + `${import.meta.dirname}/packages/*/tsconfig.test.json`, + `${import.meta.dirname}/integration/tsconfig.json`, + ]; const noNavigateUseClerk = { meta: { @@ -339,12 +346,7 @@ export default tseslint.config([ node: true, typescript: { alwaysTryTypes: true, - project: [ - 'packages/*/tsconfig.json', - 'packages/*/tsconfig.src.json', - 'packages/*/tsconfig.test.json', - 'integration/tsconfig.json', - ], + project: IMPORT_RESOLVER_TSCONFIGS, }, }, }, @@ -530,6 +532,25 @@ export default tseslint.config([ 'custom-rules/no-unstable-methods': 'error', }, }, + { + // Don't reuse the repo-wide tsconfig list. clerk-js defines the same `@/*` + // alias, and the resolver will apply it to UI files and break `@/ui/*` imports. + name: 'packages/ui/import-resolver', + files: ['packages/ui/**/*.{ts,tsx}'], + settings: { + 'import/resolver': { + node: true, + typescript: { + alwaysTryTypes: true, + project: [ + `${import.meta.dirname}/packages/ui/tsconfig.src.json`, + `${import.meta.dirname}/packages/ui/tsconfig.test.json`, + `${import.meta.dirname}/packages/ui/tsconfig.mosaic.json`, + ], + }, + }, + }, + }, { name: 'packages/ui', files: ['packages/ui/src/**/*'], From 1469780abe6d3ee7d1ea8c26dbe642fb72e56fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 20:40:48 +0200 Subject: [PATCH 09/10] More robust lint fix --- eslint.config.mjs | 61 ++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 9607a144d65..b1e6bdcc4fe 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,3 +1,6 @@ +import fs from 'node:fs'; +import path from 'node:path'; + import eslint from '@eslint/js'; import configPrettier from 'eslint-config-prettier'; import configTurbo from 'eslint-config-turbo/flat'; @@ -17,17 +20,43 @@ import tseslint from 'typescript-eslint'; import { CUSTOM_BLOCK_TAGS, CUSTOM_MODIFIER_TAGS } from './.typedoc/custom-tags.mjs'; +const REPO_ROOT = import.meta.dirname; const ECMA_VERSION = 2021, JAVASCRIPT_FILES = ['**/*.cjs', '**/*.js', '**/*.jsx', '**/*.mjs'], TEST_FILES = ['**/*.test.js', '**/*.test.jsx', '**/*.test.ts', '**/*.test.tsx', '**/test/**', '**/__tests__/**'], TYPESCRIPT_FILES = ['**/*.cts', '**/*.mts', '**/*.ts', '**/*.tsx'], // turbo lint runs `eslint src` from each package cwd; these must be repo-absolute - IMPORT_RESOLVER_TSCONFIGS = [ - `${import.meta.dirname}/packages/*/tsconfig.json`, - `${import.meta.dirname}/packages/*/tsconfig.src.json`, - `${import.meta.dirname}/packages/*/tsconfig.test.json`, - `${import.meta.dirname}/integration/tsconfig.json`, - ]; + IMPORT_RESOLVER_TSCONFIGS = [`${REPO_ROOT}/integration/tsconfig.json`], + IMPORT_RESOLVER_TSCONFIG_NAMES = ['tsconfig.json', 'tsconfig.src.json', 'tsconfig.test.json', 'tsconfig.mosaic.json']; + +// Each package must see only its own tsconfigs. Several packages define `@/*`, and a +// shared project list lets the resolver apply clerk-js's alias to UI or shared files. +const PACKAGE_IMPORT_RESOLVER_CONFIGS = fs + .readdirSync(path.join(REPO_ROOT, 'packages'), { withFileTypes: true }) + .filter(entry => entry.isDirectory() && fs.existsSync(path.join(REPO_ROOT, 'packages', entry.name, 'package.json'))) + .flatMap(entry => { + const project = IMPORT_RESOLVER_TSCONFIG_NAMES.map(name => + path.join(REPO_ROOT, 'packages', entry.name, name), + ).filter(file => fs.existsSync(file)); + if (project.length === 0) { + return []; + } + return [ + { + name: `packages/${entry.name}/import-resolver`, + files: [`packages/${entry.name}/**/*.{ts,tsx,js,jsx,mts,cts}`], + settings: { + 'import/resolver': { + node: true, + typescript: { + alwaysTryTypes: true, + project, + }, + }, + }, + }, + ]; + }); const noNavigateUseClerk = { meta: { @@ -524,6 +553,7 @@ export default tseslint.config([ 'react-hooks/rules-of-hooks': 'warn', }, }, + ...PACKAGE_IMPORT_RESOLVER_CONFIGS, { name: 'packages/clerk-js', files: ['packages/clerk-js/src/ui/**/*'], @@ -532,25 +562,6 @@ export default tseslint.config([ 'custom-rules/no-unstable-methods': 'error', }, }, - { - // Don't reuse the repo-wide tsconfig list. clerk-js defines the same `@/*` - // alias, and the resolver will apply it to UI files and break `@/ui/*` imports. - name: 'packages/ui/import-resolver', - files: ['packages/ui/**/*.{ts,tsx}'], - settings: { - 'import/resolver': { - node: true, - typescript: { - alwaysTryTypes: true, - project: [ - `${import.meta.dirname}/packages/ui/tsconfig.src.json`, - `${import.meta.dirname}/packages/ui/tsconfig.test.json`, - `${import.meta.dirname}/packages/ui/tsconfig.mosaic.json`, - ], - }, - }, - }, - }, { name: 'packages/ui', files: ['packages/ui/src/**/*'], From 2409a2829872ef21ab489fc8ddbb10e9f2469df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Mon, 7 Sep 2026 21:12:10 +0200 Subject: [PATCH 10/10] Fix broken UI build --- packages/ui/tsdown.config.mts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/ui/tsdown.config.mts b/packages/ui/tsdown.config.mts index aa920485e31..881f3ea3ebf 100644 --- a/packages/ui/tsdown.config.mts +++ b/packages/ui/tsdown.config.mts @@ -7,7 +7,17 @@ import uiPackage from './package.json' with { type: 'json' }; export default defineConfig(({ watch }) => { const common = { tsconfig: './tsconfig.src.json', - dts: true, + dts: { + // The .src. file is for typechecking, this overrides the relevant parts for build output + compilerOptions: { + composite: false, + declaration: true, + declarationMap: true, + emitDeclarationOnly: false, + incremental: false, + outDir: 'dist', + }, + }, sourcemap: true, clean: false, target: 'es2022',