diff --git a/.changeset/typescript-6-upgrade.md b/.changeset/typescript-6-upgrade.md new file mode 100644 index 00000000..05a6407c --- /dev/null +++ b/.changeset/typescript-6-upgrade.md @@ -0,0 +1,5 @@ +--- +"@clerk/cli-core": patch +--- + +Upgrade workspace and e2e fixtures to TypeScript 6 diff --git a/bun.lock b/bun.lock index fb60f693..24b1fee6 100644 --- a/bun.lock +++ b/bun.lock @@ -462,7 +462,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], + "typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], @@ -476,28 +476,8 @@ "@clerk/testing/@clerk/shared": ["@clerk/shared@4.4.0", "", { "dependencies": { "@tanstack/query-core": "5.90.16", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.9.0" }, "peerDependencies": { "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-3iBX7Svp2XrSIgFk4VtyVq5OZsGStkMGqVfTBbbiFCbSKQ745OfM8j/c2wgpq5QdyavesoeDA6YiMWlpZM9/ng=="], - "@inquirer/checkbox/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - - "@inquirer/confirm/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - - "@inquirer/editor/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - "@inquirer/editor/@inquirer/external-editor": ["@inquirer/external-editor@3.0.0", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-lDSwMgg+M5rq6JKBYaJwSX6T9e/HK2qqZ1oxmOwn4AQoJE5D+7TumsxLGC02PWS//rkIVqbZv3XA3ejsc9FYvg=="], - "@inquirer/expand/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - - "@inquirer/input/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - - "@inquirer/number/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - - "@inquirer/password/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - - "@inquirer/rawlist/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - - "@inquirer/search/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - - "@inquirer/select/@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="], - "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], diff --git a/package.json b/package.json index 7880cb0f..1d8030cf 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "scripts": { "build": "bun run --filter @clerk/cli-core build", "dev": "bun run --cwd packages/cli-core dev", + "typecheck": "bun run --filter @clerk/cli-core typecheck", "test": "bun run scripts/run-tests.ts --pattern 'packages/cli-core/src/**/*.test.ts' --pattern 'scripts/**/*.test.ts'", "test:e2e": "bun run scripts/run-tests.ts --pattern 'test/e2e/*.test.ts' --retries 1", "test:e2e:op": "bun run scripts/run-e2e-op.ts", diff --git a/packages/cli-core/mocks/bun.lock b/packages/cli-core/mocks/bun.lock index 9fa0c179..512895e9 100644 --- a/packages/cli-core/mocks/bun.lock +++ b/packages/cli-core/mocks/bun.lock @@ -1,10 +1,11 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "clerk-cli-mock-auth", "devDependencies": { - "typescript": "^5", + "typescript": "^6.0.2", "vite": "^6.3.5", }, }, @@ -134,7 +135,7 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="], "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], } diff --git a/packages/cli-core/mocks/package.json b/packages/cli-core/mocks/package.json index d7f08b02..6a401f75 100644 --- a/packages/cli-core/mocks/package.json +++ b/packages/cli-core/mocks/package.json @@ -6,7 +6,7 @@ "dev": "vite" }, "devDependencies": { - "typescript": "^5", + "typescript": "^6.0.2", "vite": "^6.3.5" } } diff --git a/packages/cli-core/mocks/src/main.ts b/packages/cli-core/mocks/src/main.ts index 0df3200a..ec7c5bd7 100644 --- a/packages/cli-core/mocks/src/main.ts +++ b/packages/cli-core/mocks/src/main.ts @@ -4,9 +4,9 @@ const app = document.getElementById("app")!; const params = new URLSearchParams(window.location.search); const callbackPort = params.get("callback_port"); -type Screen = "sign-in" | "sign-up" | "select-app" | "success-new" | "success-existing"; +type AppScreen = "sign-in" | "sign-up" | "select-app" | "success-new" | "success-existing"; -function render(screen: Screen) { +function render(screen: AppScreen) { switch (screen) { case "sign-in": return renderSignIn(); diff --git a/packages/cli-core/src/commands/api/index.test.ts b/packages/cli-core/src/commands/api/index.test.ts index d66b3613..4102d656 100644 --- a/packages/cli-core/src/commands/api/index.test.ts +++ b/packages/cli-core/src/commands/api/index.test.ts @@ -11,6 +11,7 @@ import { promptsStubs, stubFetch, } from "../../test/lib/stubs.ts"; +import { getPlapiBaseUrl } from "../../lib/environment.ts"; let mockStoredToken: string | null = null; mock.module("../../lib/credential-store.ts", () => ({ @@ -460,8 +461,7 @@ describe("api command", () => { }); await runApi("/v1/platform/applications", { platform: true }); - expect(capturedUrl).toContain("api.clerk.com"); - expect(capturedUrl).not.toContain("api.clerk.dev"); + expect(capturedUrl).toStartWith(`${getPlapiBaseUrl()}/`); expect(capturedHeaders?.get("Authorization")).toBe("Bearer plat_key_123"); }); diff --git a/packages/cli-core/src/commands/init/frameworks/react-router.test.ts b/packages/cli-core/src/commands/init/frameworks/react-router.test.ts index c97234cc..054db146 100644 --- a/packages/cli-core/src/commands/init/frameworks/react-router.test.ts +++ b/packages/cli-core/src/commands/init/frameworks/react-router.test.ts @@ -22,7 +22,7 @@ function makeCtx(overrides?: Partial): ProjectContext { packageManager: "npm", existingClerk: false, deps: { "react-router": "7.0.0" }, - envFile: ".env", + envFile: ".env.local", ...overrides, }; } diff --git a/packages/cli-core/src/commands/init/frameworks/react-router.ts b/packages/cli-core/src/commands/init/frameworks/react-router.ts index 27667aff..e4e5d43a 100644 --- a/packages/cli-core/src/commands/init/frameworks/react-router.ts +++ b/packages/cli-core/src/commands/init/frameworks/react-router.ts @@ -235,7 +235,8 @@ function ensureRouteImported(source: string): string { const importMatch = source.match( /import\s*\{([^}]*)\}\s*from\s*["']@react-router\/dev\/routes["']/, ); - if (!importMatch || /\broute\b/.test(importMatch[1]!)) return source; + const importedNames = importMatch?.[1]; + if (!importedNames || /\broute\b/.test(importedNames)) return source; return source.replace( /(\bimport\s*\{[^}]*)(\}\s*from\s*["']@react-router\/dev\/routes["'])/, @@ -311,7 +312,7 @@ function injectRouteEntries( const canonicalPattern = /export default \[([^\]]*)\]\s*satisfies\s*RouteConfig\s*;/s; const canonical = source.match(canonicalPattern); if (canonical) { - const innerContent = canonical[1]!.trimEnd(); + const innerContent = (canonical[1] ?? "").trimEnd(); const separator = innerContent.length > 0 && !innerContent.endsWith(",") ? "," : ""; const newInner = `${innerContent}${separator}\n ${newEntries},\n`; return source.replace(canonicalPattern, `export default [${newInner}] satisfies RouteConfig;`); @@ -321,7 +322,7 @@ function injectRouteEntries( const simplePattern = /(export\s+default\s+\[)([\s\S]*?)(\]\s*;)/; const simple = source.match(simplePattern); if (simple) { - const innerContent = simple[2]!.trimEnd(); + const innerContent = (simple[2] ?? "").trimEnd(); const separator = innerContent.length > 0 && !innerContent.endsWith(",") ? "," : ""; const newInner = `${innerContent}${separator}\n ${newEntries},\n`; return source.replace(simplePattern, `$1${newInner}$3`); diff --git a/packages/cli-core/src/commands/init/frameworks/react.test.ts b/packages/cli-core/src/commands/init/frameworks/react.test.ts index c0d82578..5b268fee 100644 --- a/packages/cli-core/src/commands/init/frameworks/react.test.ts +++ b/packages/cli-core/src/commands/init/frameworks/react.test.ts @@ -22,7 +22,7 @@ function makeCtx(overrides?: Partial): ProjectContext { packageManager: "npm", existingClerk: false, deps: {}, - envFile: ".env", + envFile: ".env.local", ...overrides, }; } diff --git a/packages/cli-core/src/commands/init/frameworks/tanstack-start.test.ts b/packages/cli-core/src/commands/init/frameworks/tanstack-start.test.ts index d3c384ee..069f3f08 100644 --- a/packages/cli-core/src/commands/init/frameworks/tanstack-start.test.ts +++ b/packages/cli-core/src/commands/init/frameworks/tanstack-start.test.ts @@ -22,7 +22,7 @@ function makeCtx(overrides?: Partial): ProjectContext { packageManager: "npm", existingClerk: false, deps: { "@tanstack/react-start": "1.0.0" }, - envFile: ".env", + envFile: ".env.local", ...overrides, }; } diff --git a/packages/cli-core/src/commands/init/frameworks/transformations.ts b/packages/cli-core/src/commands/init/frameworks/transformations.ts index 8eff9afb..dda2c970 100644 --- a/packages/cli-core/src/commands/init/frameworks/transformations.ts +++ b/packages/cli-core/src/commands/init/frameworks/transformations.ts @@ -121,7 +121,7 @@ export function wrapBodyWithProvider(content: string, provider: string): string const providerIndent = bodyIndent + " "; const contentIndent = providerIndent + " "; - const trimmedInner = inner.trim(); + const trimmedInner = (inner ?? "").trim(); const reindented = trimmedInner .split("\n") .map((line) => { diff --git a/packages/cli-core/src/commands/init/frameworks/vue.test.ts b/packages/cli-core/src/commands/init/frameworks/vue.test.ts index 4bba889f..7476a3f6 100644 --- a/packages/cli-core/src/commands/init/frameworks/vue.test.ts +++ b/packages/cli-core/src/commands/init/frameworks/vue.test.ts @@ -22,7 +22,7 @@ function makeCtx(overrides?: Partial): ProjectContext { packageManager: "npm", existingClerk: false, deps: {}, - envFile: ".env", + envFile: ".env.local", ...overrides, }; } @@ -149,7 +149,7 @@ test("creates entry file when none exists", async () => { // Auth pages and env should still be scaffolded expect(findAction(plan.actions, "src/views/sign-in.vue").type).toBe("create"); expect(findAction(plan.actions, "src/views/sign-up.vue").type).toBe("create"); - expect(findAction(plan.actions, ".env").type).toBe("modify"); + expect(findAction(plan.actions, ".env.local").type).toBe("modify"); // No post-instruction about entry file since it was created expect(plan.postInstructions.some((i) => i.includes("@clerk/vue"))).toBe(false); @@ -205,7 +205,7 @@ test("skips auth pages when they already exist", async () => { test("scaffolds env vars with VITE_ prefix", async () => { const plan = await vue.scaffold(makeCtx()); - const envAction = findAction(plan.actions, ".env"); + const envAction = findAction(plan.actions, ".env.local"); expect(envAction.type).toBe("modify"); if (envAction.type === "modify") { expect(envAction.content).toContain("VITE_CLERK_SIGN_IN_URL"); diff --git a/packages/cli-core/src/commands/init/heuristics.ts b/packages/cli-core/src/commands/init/heuristics.ts index fab992ba..9e2b6a7d 100644 --- a/packages/cli-core/src/commands/init/heuristics.ts +++ b/packages/cli-core/src/commands/init/heuristics.ts @@ -18,9 +18,15 @@ async function runPmInstall( label: string, { fromLockfile = false }: { fromLockfile?: boolean } = {}, ): Promise { - const pmBinary = addCmd.split(" ")[0]!; const manualCmd = `${addCmd} ${packages.join(" ")}`; + // The package manager is detected from lockfiles, which can exist without + // the actual binary being installed (e.g. teammate committed bun.lock, you + // only have npm). Fail fast with a useful message rather than a raw ENOENT. + const pmBinary = addCmd.split(" ")[0]; + if (!pmBinary) { + throw new Error(`Invalid package manager install command: ${addCmd}`); + } if (Bun.which(pmBinary) === null) { const hint = fromLockfile ? ` (detected from lockfile — install ${pmBinary} or switch package managers)` diff --git a/packages/cli-core/src/commands/init/index.test.ts b/packages/cli-core/src/commands/init/index.test.ts index 4e36b988..b03e997f 100644 --- a/packages/cli-core/src/commands/init/index.test.ts +++ b/packages/cli-core/src/commands/init/index.test.ts @@ -29,14 +29,14 @@ const FAKE_CTX = { name: "React", sdk: "@clerk/react", envVar: "VITE_CLERK_PUBLISHABLE_KEY", - envFile: ".env" as const, + envFile: ".env.local" as const, }, typescript: true, srcDir: false, packageManager: "npm" as const, existingClerk: true, deps: { react: "^19.0.0" }, - envFile: ".env", + envFile: ".env.local", }; const FAKE_BOOTSTRAP = { @@ -390,7 +390,7 @@ describe("init", () => { cwd: keylessCtx.cwd, createIfMissing: expect.any(String), }); - expect(pullMod.pull).toHaveBeenCalledWith({ file: ".env", cwd: keylessCtx.cwd }); + expect(pullMod.pull).toHaveBeenCalledWith({ file: ".env.local", cwd: keylessCtx.cwd }); }); test("agent mode with keyless framework uses linked profile as a real app target", async () => { @@ -414,7 +414,7 @@ describe("init", () => { expect(heuristics.printKeylessInfo).not.toHaveBeenCalled(); expect(linkMod.link).not.toHaveBeenCalled(); - expect(pullMod.pull).toHaveBeenCalledWith({ file: ".env", cwd: keylessCtx.cwd }); + expect(pullMod.pull).toHaveBeenCalledWith({ file: ".env.local", cwd: keylessCtx.cwd }); }); test("agent mode with keyless framework and --app uses real app flow", async () => { @@ -440,7 +440,7 @@ describe("init", () => { cwd: keylessCtx.cwd, createIfMissing: expect.any(String), }); - expect(pullMod.pull).toHaveBeenCalledWith({ file: ".env", cwd: keylessCtx.cwd }); + expect(pullMod.pull).toHaveBeenCalledWith({ file: ".env.local", cwd: keylessCtx.cwd }); }); test("agent mode with non-keyless framework and no app target prints manual setup", async () => { diff --git a/packages/cli-core/src/lib/auth-server.ts b/packages/cli-core/src/lib/auth-server.ts index ec6d8f02..fc855dbe 100644 --- a/packages/cli-core/src/lib/auth-server.ts +++ b/packages/cli-core/src/lib/auth-server.ts @@ -264,8 +264,13 @@ export function startAuthServer(expectedState: string): AuthServerResult { const activeServer = server; log.debug(`auth-server: listening on 127.0.0.1:${activeServer.port} for ${CALLBACK_PATH}`); + const port = server.port; + if (port === undefined) { + throw new Error("Failed to determine auth server port."); + } + return { - port: activeServer.port!, + port, waitForCallback: () => callbackPromise, stop: () => { clearTimeout(timeout); diff --git a/packages/cli-core/src/lib/dotenv.ts b/packages/cli-core/src/lib/dotenv.ts index 2b5d0cb2..8f14b1a2 100644 --- a/packages/cli-core/src/lib/dotenv.ts +++ b/packages/cli-core/src/lib/dotenv.ts @@ -42,8 +42,9 @@ export function parseEnvFile(content: string): EnvLine[] { if (line.trim() === "") return { type: "blank" }; const match = line.match(ENTRY_RE); if (!match) return { type: "comment", raw: line }; - const key = match[2]!; - let value = match[3]!; + const key = match[2]; + if (!key) return { type: "comment", raw: line }; + let value = match[3] ?? ""; // Strip surrounding quotes if ( (value.startsWith('"') && value.endsWith('"')) || @@ -59,7 +60,8 @@ export function mergeEnvVars(lines: EnvLine[], vars: Record): En const remaining = { ...vars }; const result = lines.map((line): EnvLine => { if (line.type !== "entry" || !(line.key in remaining)) return line; - const value = remaining[line.key]!; + const value = remaining[line.key]; + if (value === undefined) return line; delete remaining[line.key]; return { type: "entry", key: line.key, value, raw: `${line.key}=${value}` }; }); diff --git a/packages/cli-core/src/lib/help.ts b/packages/cli-core/src/lib/help.ts index ff4cb0c0..db984818 100644 --- a/packages/cli-core/src/lib/help.ts +++ b/packages/cli-core/src/lib/help.ts @@ -102,14 +102,15 @@ export function clerkHelpConfig(): Partial { c.argsStr ? c.name.padEnd(maxNameLen + 2) + c.argsStr : c.name, ); const termWidth = Math.max(...terms.map((t) => helper.displayWidth(t))); - const items = terms.map((term, i) => - helper.formatItem( + const items = terms.map((term, i) => { + const description = cmdData[i]?.description ?? ""; + return helper.formatItem( helper.styleSubcommandTerm(term), termWidth, - helper.styleSubcommandDescription(cmdData[i]!.description), + helper.styleSubcommandDescription(description), helper, - ), - ); + ); + }); output = output.concat(helper.formatItemList("Commands:", items, helper)); } diff --git a/packages/cli-core/src/lib/spinner.ts b/packages/cli-core/src/lib/spinner.ts index d3bebfdc..5d65dd53 100644 --- a/packages/cli-core/src/lib/spinner.ts +++ b/packages/cli-core/src/lib/spinner.ts @@ -62,7 +62,7 @@ function createSpinner() { } stream.write("\x1b[?25l"); // hide cursor timer = setInterval(() => { - const char = cyan(FRAMES[frame++ % FRAMES.length]!); + const char = cyan(FRAMES[frame++ % FRAMES.length] ?? FRAMES[0]!); stream.write(`\r\x1b[K${char} ${message}`); }, INTERVAL); }, diff --git a/test/e2e/fixtures/nextjs-app-router-next14/css.d.ts b/test/e2e/fixtures/nextjs-app-router-next14/css.d.ts new file mode 100644 index 00000000..ef6d741f --- /dev/null +++ b/test/e2e/fixtures/nextjs-app-router-next14/css.d.ts @@ -0,0 +1 @@ +declare module "*.css" {} diff --git a/test/e2e/fixtures/nextjs-app-router-next14/package.json b/test/e2e/fixtures/nextjs-app-router-next14/package.json index 0a673026..16e609a5 100644 --- a/test/e2e/fixtures/nextjs-app-router-next14/package.json +++ b/test/e2e/fixtures/nextjs-app-router-next14/package.json @@ -18,6 +18,6 @@ "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", - "typescript": "^5" + "typescript": "^6.0.2" } } diff --git a/test/e2e/fixtures/nextjs-app-router/package.json b/test/e2e/fixtures/nextjs-app-router/package.json index 18feeae3..02986a26 100644 --- a/test/e2e/fixtures/nextjs-app-router/package.json +++ b/test/e2e/fixtures/nextjs-app-router/package.json @@ -17,7 +17,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "typescript": "^5" + "typescript": "^6.0.2" }, "ignoreScripts": [ "sharp", diff --git a/test/e2e/fixtures/nextjs-pages-router/package.json b/test/e2e/fixtures/nextjs-pages-router/package.json index 0fc4932b..3e645fae 100644 --- a/test/e2e/fixtures/nextjs-pages-router/package.json +++ b/test/e2e/fixtures/nextjs-pages-router/package.json @@ -17,7 +17,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "typescript": "^5" + "typescript": "^6.0.2" }, "ignoreScripts": [ "sharp", diff --git a/test/e2e/fixtures/react-router/package.json b/test/e2e/fixtures/react-router/package.json index 4ca8115d..edb0a502 100644 --- a/test/e2e/fixtures/react-router/package.json +++ b/test/e2e/fixtures/react-router/package.json @@ -24,7 +24,7 @@ "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", "tailwindcss": "^4.1.13", - "typescript": "^5.9.2", + "typescript": "^6.0.2", "vite": "^7.1.7", "vite-tsconfig-paths": "^5.1.4" } diff --git a/test/e2e/fixtures/react-router/tsconfig.json b/test/e2e/fixtures/react-router/tsconfig.json index a6b90b7c..438d26f8 100644 --- a/test/e2e/fixtures/react-router/tsconfig.json +++ b/test/e2e/fixtures/react-router/tsconfig.json @@ -8,7 +8,6 @@ "moduleResolution": "bundler", "jsx": "react-jsx", "rootDirs": [".", "./.react-router/types"], - "baseUrl": ".", "paths": { "~/*": ["./app/*"] },