From 73d354c2ec518c16b56578becf9308b51f0b5c19 Mon Sep 17 00:00:00 2001 From: Kaylee <65376239+KayleeWilliams@users.noreply.github.com> Date: Mon, 11 May 2026 19:16:02 -0700 Subject: [PATCH] Fix Leadtype CLI symlinked bin execution --- .changeset/fix-symlinked-cli-direct-run.md | 5 +++ packages/leadtype/src/cli.test.ts | 45 ++++++++++++++++++++-- packages/leadtype/src/cli.ts | 21 ++++++++-- 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 .changeset/fix-symlinked-cli-direct-run.md diff --git a/.changeset/fix-symlinked-cli-direct-run.md b/.changeset/fix-symlinked-cli-direct-run.md new file mode 100644 index 0000000..4b7dcc9 --- /dev/null +++ b/.changeset/fix-symlinked-cli-direct-run.md @@ -0,0 +1,5 @@ +--- +"leadtype": patch +--- + +Fix the CLI direct-run check so package-manager bin shims and symlinked workspace installs correctly run `leadtype generate`. diff --git a/packages/leadtype/src/cli.test.ts b/packages/leadtype/src/cli.test.ts index 4774b1a..0050196 100644 --- a/packages/leadtype/src/cli.test.ts +++ b/packages/leadtype/src/cli.test.ts @@ -1,11 +1,18 @@ import { existsSync } from "node:fs"; -import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { + mkdir, + mkdtemp, + readFile, + rm, + symlink, + writeFile, +} from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; -import { fileURLToPath } from "node:url"; +import { fileURLToPath, pathToFileURL } from "node:url"; import { glob as fg } from "tinyglobby"; import { afterEach, describe, expect, it } from "vitest"; -import { runCli } from "./cli"; +import { isDirectRun, runCli } from "./cli"; const tempDirs: string[] = []; const repoRoot = path.resolve( @@ -88,6 +95,38 @@ afterEach(async () => { }); describe("leadtype CLI", () => { + it("treats symlinked package-manager bin paths as direct runs", async () => { + const fixtureDir = await createTempDir(); + const realCliPath = path.join(fixtureDir, "packages", "leadtype", "dist"); + const linkedPackagePath = path.join( + fixtureDir, + "node_modules", + "leadtype", + "dist" + ); + const linkType = process.platform === "win32" ? "junction" : "dir"; + const binPath = path.join(fixtureDir, "node_modules", ".bin", "leadtype"); + const cliPath = path.join(realCliPath, "cli.js"); + + await mkdir(realCliPath, { recursive: true }); + await mkdir(path.dirname(binPath), { recursive: true }); + await writeFile(cliPath, "#!/usr/bin/env node\n"); + await symlink( + path.join(fixtureDir, "packages", "leadtype"), + path.join(fixtureDir, "node_modules", "leadtype"), + linkType + ); + await symlink(path.join(linkedPackagePath, "cli.js"), binPath); + + expect(isDirectRun(binPath, pathToFileURL(cliPath).href)).toBe(true); + expect( + isDirectRun( + path.join(linkedPackagePath, "cli.js"), + pathToFileURL(cliPath).href + ) + ).toBe(true); + }); + it("prints the command list", async () => { const capture = createCapture(); diff --git a/packages/leadtype/src/cli.ts b/packages/leadtype/src/cli.ts index c306474..0c835d0 100644 --- a/packages/leadtype/src/cli.ts +++ b/packages/leadtype/src/cli.ts @@ -1,5 +1,6 @@ +import { realpathSync } from "node:fs"; import { resolve } from "node:path"; -import { pathToFileURL } from "node:url"; +import { fileURLToPath } from "node:url"; import { getGenerateUsage, runGenerateCommand } from "./cli/generate"; import { logger, setLogStreams } from "./internal/logger"; import { getLintUsage, runLintCommand } from "./lint/cli"; @@ -61,9 +62,21 @@ export async function runCli( return 2; } -function isDirectRun(): boolean { - const entry = process.argv[1]; - return entry ? import.meta.url === pathToFileURL(resolve(entry)).href : false; +function resolveRealPath(filePath: string): string { + try { + return realpathSync.native(resolve(filePath)); + } catch { + return resolve(filePath); + } +} + +export function isDirectRun( + entry = process.argv[1], + moduleUrl = import.meta.url +): boolean { + return entry + ? resolveRealPath(entry) === resolveRealPath(fileURLToPath(moduleUrl)) + : false; } if (isDirectRun()) {