210 lines
5.8 KiB
TypeScript
210 lines
5.8 KiB
TypeScript
import fs from "fs";
|
|
import os from "os";
|
|
import path from "path";
|
|
import { spawnSync } from "child_process";
|
|
|
|
let resolvedImageMagickBinaryPath = process.platform === "win32" ? "magick" : "convert";
|
|
|
|
function canExecute(command: string, args: string[]): boolean {
|
|
const result = spawnSync(command, args, {
|
|
stdio: "pipe",
|
|
windowsHide: true,
|
|
});
|
|
return result.status === 0;
|
|
}
|
|
|
|
function runCommand(command: string, args: string[]): string | null {
|
|
const result = spawnSync(command, args, {
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
encoding: "utf8",
|
|
windowsHide: true,
|
|
});
|
|
if (result.status !== 0) return null;
|
|
|
|
const stdout = (result.stdout ?? "").trim();
|
|
return stdout.length > 0 ? stdout : null;
|
|
}
|
|
|
|
function getWindowsInstallRootCandidates(): string[] {
|
|
const roots = new Set<string>();
|
|
|
|
if (process.env.LOCALAPPDATA) roots.add(process.env.LOCALAPPDATA);
|
|
if (process.env.ProgramFiles) roots.add(process.env.ProgramFiles);
|
|
if (process.env["ProgramFiles(x86)"]) {
|
|
roots.add(process.env["ProgramFiles(x86)"] as string);
|
|
}
|
|
roots.add(path.join(os.homedir(), "AppData", "Local"));
|
|
|
|
return Array.from(roots);
|
|
}
|
|
|
|
export function getWindowsImageMagickInstallDir(): string {
|
|
const localAppData =
|
|
process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local");
|
|
return path.join(localAppData, "Presenton", "runtime", "imagemagick");
|
|
}
|
|
|
|
function collectWindowsImageMagickBinaryCandidates(): string[] {
|
|
const candidates: string[] = [
|
|
path.join(getWindowsImageMagickInstallDir(), "magick.exe"),
|
|
];
|
|
|
|
for (const root of getWindowsInstallRootCandidates()) {
|
|
try {
|
|
const entries = fs.readdirSync(root, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory() || !/^ImageMagick/i.test(entry.name)) {
|
|
continue;
|
|
}
|
|
candidates.push(path.join(root, entry.name, "magick.exe"));
|
|
}
|
|
} catch {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return candidates;
|
|
}
|
|
|
|
function resolveBrewCommandPath(): string | null {
|
|
const candidates = ["brew", "/opt/homebrew/bin/brew", "/usr/local/bin/brew"];
|
|
for (const candidate of candidates) {
|
|
if (canExecute(candidate, ["--version"])) {
|
|
return candidate;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function collectDarwinBrewImageMagickCandidates(): string[] {
|
|
const candidates: string[] = [
|
|
"/opt/homebrew/bin/magick",
|
|
"/usr/local/bin/magick",
|
|
"/opt/homebrew/opt/imagemagick/bin/magick",
|
|
"/usr/local/opt/imagemagick/bin/magick",
|
|
];
|
|
|
|
const brewCommand = resolveBrewCommandPath();
|
|
if (!brewCommand) {
|
|
return candidates;
|
|
}
|
|
|
|
const brewPrefix = runCommand(brewCommand, ["--prefix", "imagemagick"]);
|
|
if (brewPrefix) {
|
|
candidates.push(path.join(brewPrefix, "bin", "magick"));
|
|
}
|
|
|
|
const brewCellar = runCommand(brewCommand, ["--cellar", "imagemagick"]);
|
|
if (brewCellar && fs.existsSync(brewCellar)) {
|
|
try {
|
|
const versions = fs
|
|
.readdirSync(brewCellar, { withFileTypes: true })
|
|
.filter((entry) => entry.isDirectory())
|
|
.map((entry) => entry.name)
|
|
.sort((a, b) =>
|
|
b.localeCompare(a, undefined, { numeric: true, sensitivity: "base" })
|
|
);
|
|
|
|
for (const version of versions) {
|
|
candidates.push(path.join(brewCellar, version, "bin", "magick"));
|
|
}
|
|
} catch {
|
|
// Ignore cellar enumeration errors and continue with other candidates.
|
|
}
|
|
}
|
|
|
|
return candidates;
|
|
}
|
|
|
|
function resolveImageMagickBinaryPath(): string | null {
|
|
const commandCandidates = process.platform === "win32" ? ["magick"] : ["magick", "convert"];
|
|
for (const candidate of commandCandidates) {
|
|
if (canExecute(candidate, ["-version"])) {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
if (process.platform === "win32") {
|
|
for (const candidate of collectWindowsImageMagickBinaryCandidates()) {
|
|
if (fs.existsSync(candidate) && canExecute(candidate, ["-version"])) {
|
|
return candidate;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
if (process.platform === "darwin") {
|
|
for (const candidate of collectDarwinBrewImageMagickCandidates()) {
|
|
if (fs.existsSync(candidate) && canExecute(candidate, ["-version"])) {
|
|
return candidate;
|
|
}
|
|
}
|
|
}
|
|
|
|
const unixCandidates = [
|
|
"/opt/homebrew/bin/magick",
|
|
"/usr/local/bin/magick",
|
|
"/opt/local/bin/magick",
|
|
"/usr/bin/magick",
|
|
"/usr/local/bin/convert",
|
|
"/usr/bin/convert",
|
|
];
|
|
|
|
for (const candidate of unixCandidates) {
|
|
if (fs.existsSync(candidate) && canExecute(candidate, ["-version"])) {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function isImageMagickInstalled(): boolean {
|
|
const resolved = resolveImageMagickBinaryPath();
|
|
if (!resolved) return false;
|
|
|
|
resolvedImageMagickBinaryPath = resolved;
|
|
return true;
|
|
}
|
|
|
|
export function getImageMagickBinaryPath(): string {
|
|
return resolvedImageMagickBinaryPath;
|
|
}
|
|
|
|
export function getImageMagickDownloadUrl(): string {
|
|
if (process.platform === "win32") {
|
|
return "https://imagemagick.org/archive/binaries/ImageMagick-7.1.2-18-Q16-HDRI-x64-dll.exe";
|
|
}
|
|
if (process.platform === "darwin") {
|
|
return "https://brew.sh/";
|
|
}
|
|
return "https://imagemagick.org/script/download.php#linux";
|
|
}
|
|
|
|
export function getImageMagickManualInstallCommands(): string[] {
|
|
if (process.platform === "win32") {
|
|
return [
|
|
"Download and run the installer:",
|
|
getImageMagickDownloadUrl(),
|
|
"Recommended install path:",
|
|
getWindowsImageMagickInstallDir(),
|
|
];
|
|
}
|
|
|
|
if (process.platform === "darwin") {
|
|
return [
|
|
"Install Homebrew:",
|
|
'/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
|
|
"Install ImageMagick:",
|
|
"brew install imagemagick",
|
|
"Verify detected binary path:",
|
|
"brew --prefix imagemagick",
|
|
];
|
|
}
|
|
|
|
return [
|
|
"Install ImageMagick:",
|
|
"sudo apt-get update",
|
|
"sudo apt-get install -y imagemagick",
|
|
];
|
|
}
|