413 lines · 13.3 KB · TypeScript
RawDownload
import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, it } from "@effect/vitest";import { assertSuccess } from "@effect/vitest/utils";import { FileSystem, Path, Effect } from "effect"; import {  isCommandAvailable,  launchDetached,  resolveAvailableEditors,  resolveEditorLaunch,} from "./open.ts"; it.layer(NodeServices.layer)("resolveEditorLaunch", (it) => {  it.effect("returns commands for command-based editors", () =>    Effect.gen(function* () {      const antigravityLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "antigravity" },        "darwin",        { PATH: "" },      );      assert.deepEqual(antigravityLaunch, {        command: "agy",        args: ["/tmp/workspace"],      });       const cursorLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "cursor" },        "darwin",        { PATH: "" },      );      assert.deepEqual(cursorLaunch, {        command: "cursor",        args: ["/tmp/workspace"],      });       const traeLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "trae" },        "darwin",      );      assert.deepEqual(traeLaunch, {        command: "trae",        args: ["/tmp/workspace"],      });       const kiroLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "kiro" },        "darwin",        { PATH: "" },      );      assert.deepEqual(kiroLaunch, {        command: "kiro",        args: ["ide", "/tmp/workspace"],      });       const vscodeLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "vscode" },        "darwin",        { PATH: "" },      );      assert.deepEqual(vscodeLaunch, {        command: "code",        args: ["/tmp/workspace"],      });       const vscodeInsidersLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "vscode-insiders" },        "darwin",      );      assert.deepEqual(vscodeInsidersLaunch, {        command: "code-insiders",        args: ["/tmp/workspace"],      });       const vscodiumLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "vscodium" },        "darwin",      );      assert.deepEqual(vscodiumLaunch, {        command: "codium",        args: ["/tmp/workspace"],      });       const zedLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "zed" },        "darwin",        { PATH: "" },      );      assert.deepEqual(zedLaunch, {        command: "zed",        args: ["/tmp/workspace"],      });       const ideaLaunch = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "idea" },        "darwin",      );      assert.deepEqual(ideaLaunch, {        command: "idea",        args: ["/tmp/workspace"],      });    }),  );   it.effect("applies launch-style-specific navigation arguments", () =>    Effect.gen(function* () {      const lineOnly = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/AGENTS.md:48", editor: "cursor" },        "darwin",        { PATH: "" },      );      assert.deepEqual(lineOnly, {        command: "cursor",        args: ["--goto", "/tmp/workspace/AGENTS.md:48"],      });       const lineAndColumn = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "cursor" },        "darwin",        { PATH: "" },      );      assert.deepEqual(lineAndColumn, {        command: "cursor",        args: ["--goto", "/tmp/workspace/src/open.ts:71:5"],      });       const traeLineAndColumn = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "trae" },        "darwin",      );      assert.deepEqual(traeLineAndColumn, {        command: "trae",        args: ["--goto", "/tmp/workspace/src/open.ts:71:5"],      });       const kiroLineAndColumn = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "kiro" },        "darwin",        { PATH: "" },      );      assert.deepEqual(kiroLineAndColumn, {        command: "kiro",        args: ["ide", "--goto", "/tmp/workspace/src/open.ts:71:5"],      });       const vscodeLineAndColumn = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "vscode" },        "darwin",        { PATH: "" },      );      assert.deepEqual(vscodeLineAndColumn, {        command: "code",        args: ["--goto", "/tmp/workspace/src/open.ts:71:5"],      });       const vscodeInsidersLineAndColumn = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "vscode-insiders" },        "darwin",      );      assert.deepEqual(vscodeInsidersLineAndColumn, {        command: "code-insiders",        args: ["--goto", "/tmp/workspace/src/open.ts:71:5"],      });       const vscodiumLineAndColumn = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "vscodium" },        "darwin",      );      assert.deepEqual(vscodiumLineAndColumn, {        command: "codium",        args: ["--goto", "/tmp/workspace/src/open.ts:71:5"],      });       const zedLineAndColumn = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "zed" },        "darwin",        { PATH: "" },      );      assert.deepEqual(zedLineAndColumn, {        command: "zed",        args: ["/tmp/workspace/src/open.ts:71:5"],      });       const zedLineOnly = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/AGENTS.md:48", editor: "zed" },        "darwin",        { PATH: "" },      );      assert.deepEqual(zedLineOnly, {        command: "zed",        args: ["/tmp/workspace/AGENTS.md:48"],      });       const ideaLineOnly = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/AGENTS.md:48", editor: "idea" },        "darwin",      );      assert.deepEqual(ideaLineOnly, {        command: "idea",        args: ["--line", "48", "/tmp/workspace/AGENTS.md"],      });       const ideaLineAndColumn = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "idea" },        "darwin",      );      assert.deepEqual(ideaLineAndColumn, {        command: "idea",        args: ["--line", "71", "--column", "5", "/tmp/workspace/src/open.ts"],      });    }),  );   it.effect("falls back to zeditor when zed is not installed", () =>    Effect.gen(function* () {      const fs = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });      yield* fs.writeFileString(path.join(dir, "zeditor"), "#!/bin/sh\nexit 0\n");      yield* fs.chmod(path.join(dir, "zeditor"), 0o755);       const result = yield* resolveEditorLaunch({ cwd: "/tmp/workspace", editor: "zed" }, "linux", {        PATH: dir,      });       assert.deepEqual(result, {        command: "zeditor",        args: ["/tmp/workspace"],      });    }),  );   it.effect("falls back to the primary command when no alias is installed", () =>    Effect.gen(function* () {      const result = yield* resolveEditorLaunch({ cwd: "/tmp/workspace", editor: "zed" }, "linux", {        PATH: "",      });      assert.deepEqual(result, {        command: "zed",        args: ["/tmp/workspace"],      });    }),  );   it.effect("maps file-manager editor to OS open commands", () =>    Effect.gen(function* () {      const launch1 = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "file-manager" },        "darwin",        { PATH: "" },      );      assert.deepEqual(launch1, {        command: "open",        args: ["/tmp/workspace"],      });       const launch2 = yield* resolveEditorLaunch(        { cwd: "C:\\workspace", editor: "file-manager" },        "win32",        { PATH: "" },      );      assert.deepEqual(launch2, {        command: "explorer",        args: ["C:\\workspace"],      });       const launch3 = yield* resolveEditorLaunch(        { cwd: "/tmp/workspace", editor: "file-manager" },        "linux",        { PATH: "" },      );      assert.deepEqual(launch3, {        command: "xdg-open",        args: ["/tmp/workspace"],      });    }),  );}); it.layer(NodeServices.layer)("launchDetached", (it) => {  it.effect("resolves when command can be spawned", () =>    Effect.gen(function* () {      const result = yield* launchDetached({        command: process.execPath,        args: ["-e", "process.exit(0)"],      }).pipe(Effect.result);      assertSuccess(result, undefined);    }),  );   it.effect("rejects when command does not exist", () =>    Effect.gen(function* () {      const result = yield* launchDetached({        command: `t3code-no-such-command-${Date.now()}`,        args: [],      }).pipe(Effect.result);      assert.equal(result._tag, "Failure");    }),  );}); it.layer(NodeServices.layer)("isCommandAvailable", (it) => {  it.effect("resolves win32 commands with PATHEXT", () =>    Effect.gen(function* () {      const fs = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });      yield* fs.writeFileString(path.join(dir, "code.CMD"), "@echo off\r\n");      const env = {        PATH: dir,        PATHEXT: ".COM;.EXE;.BAT;.CMD",      } satisfies NodeJS.ProcessEnv;      assert.equal(isCommandAvailable("code", { platform: "win32", env }), true);    }),  );   it("returns false when a command is not on PATH", () => {    const env = {      PATH: "",      PATHEXT: ".COM;.EXE;.BAT;.CMD",    } satisfies NodeJS.ProcessEnv;    assert.equal(isCommandAvailable("definitely-not-installed", { platform: "win32", env }), false);  });   it.effect("does not treat bare files without executable extension as available on win32", () =>    Effect.gen(function* () {      const fs = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });      yield* fs.writeFileString(path.join(dir, "npm"), "echo nope\r\n");      const env = {        PATH: dir,        PATHEXT: ".COM;.EXE;.BAT;.CMD",      } satisfies NodeJS.ProcessEnv;      assert.equal(isCommandAvailable("npm", { platform: "win32", env }), false);    }),  );   it.effect("appends PATHEXT for commands with non-executable extensions on win32", () =>    Effect.gen(function* () {      const fs = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });      yield* fs.writeFileString(path.join(dir, "my.tool.CMD"), "@echo off\r\n");      const env = {        PATH: dir,        PATHEXT: ".COM;.EXE;.BAT;.CMD",      } satisfies NodeJS.ProcessEnv;      assert.equal(isCommandAvailable("my.tool", { platform: "win32", env }), true);    }),  );   it.effect("uses platform-specific PATH delimiter for platform overrides", () =>    Effect.gen(function* () {      const fs = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const firstDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });      const secondDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });      yield* fs.writeFileString(path.join(firstDir, "code.CMD"), "@echo off\r\n");      yield* fs.writeFileString(path.join(secondDir, "code.CMD"), "MZ");      const env = {        PATH: `${firstDir};${secondDir}`,        PATHEXT: ".COM;.EXE;.BAT;.CMD",      } satisfies NodeJS.ProcessEnv;      assert.equal(isCommandAvailable("code", { platform: "win32", env }), true);    }),  );}); it.layer(NodeServices.layer)("resolveAvailableEditors", (it) => {  it.effect("returns installed editors for command launches", () =>    Effect.gen(function* () {      const fs = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-editors-" });       yield* fs.writeFileString(path.join(dir, "trae.CMD"), "@echo off\r\n");      yield* fs.writeFileString(path.join(dir, "kiro.CMD"), "@echo off\r\n");      yield* fs.writeFileString(path.join(dir, "code-insiders.CMD"), "@echo off\r\n");      yield* fs.writeFileString(path.join(dir, "codium.CMD"), "@echo off\r\n");      yield* fs.writeFileString(path.join(dir, "explorer.CMD"), "MZ");      const editors = resolveAvailableEditors("win32", {        PATH: dir,        PATHEXT: ".COM;.EXE;.BAT;.CMD",      });      assert.deepEqual(editors, ["trae", "kiro", "vscode-insiders", "vscodium", "file-manager"]);    }),  );   it.effect("includes zed when only the zeditor command is installed", () =>    Effect.gen(function* () {      const fs = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-editors-" });       yield* fs.writeFileString(path.join(dir, "zeditor"), "#!/bin/sh\nexit 0\n");      yield* fs.writeFileString(path.join(dir, "xdg-open"), "#!/bin/sh\nexit 0\n");      yield* fs.chmod(path.join(dir, "zeditor"), 0o755);      yield* fs.chmod(path.join(dir, "xdg-open"), 0o755);       const editors = resolveAvailableEditors("linux", {        PATH: dir,      });      assert.deepEqual(editors, ["zed", "file-manager"]);    }),  );   it("omits file-manager when the platform opener is unavailable", () => {    const editors = resolveAvailableEditors("linux", {      PATH: "",    });    assert.deepEqual(editors, []);  });});