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, []); });});
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, []);
});
});