UNPKG

open-windows-file-dialog

Version:

Programmatically open a file dialog window (explorer) for picking files. Only works on Windows. Also see: open-finder-dialog, open-linux-file-dialog, and open-file-manager-dialog for other platforms.

205 lines (200 loc) 7.78 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // index.ts var open_windows_file_dialog_exports = {}; __export(open_windows_file_dialog_exports, { default: () => open_windows_file_dialog_default, openWindowsFileDialog: () => openWindowsFileDialog, openWindowsFileDialogSync: () => openWindowsFileDialogSync }); module.exports = __toCommonJS(open_windows_file_dialog_exports); var import_node_child_process = __toESM(require("child_process")); var import_node_path = __toESM(require("path")); var import_node_util = __toESM(require("util")); var execAsync = import_node_util.default.promisify(import_node_child_process.default.exec); var FileDialogError = class extends Error { static { __name(this, "FileDialogError"); } constructor(message) { super(message); this.name = "FileDialogError"; } }; var isCanceled = /* @__PURE__ */ __name((v) => /cancell?ed/i.test(v), "isCanceled"); var escapeForPowerShell = /* @__PURE__ */ __name((str) => { return str.replace(/\\/g, "\\\\").replace(/"/g, '`"').replace(/\$/g, "`$"); }, "escapeForPowerShell"); var buildFilterString = /* @__PURE__ */ __name((filter) => { if (filter === "*" || filter === "*.*") { return "All files (*.*)|*.*"; } if (filter.includes("|")) { return filter; } const ext = filter.replace("*", "").replace(".", ""); if (ext) { return `${ext.toUpperCase()} files (${filter})|${filter}`; } return "All files (*.*)|*.*"; }, "buildFilterString"); var checkPowerShellAvailable = /* @__PURE__ */ __name(async () => { try { const { stdout } = await execAsync('powershell.exe -Command "echo test"', { timeout: 5e3 }); return stdout.trim() === "test"; } catch { return false; } }, "checkPowerShellAvailable"); var openWindowsFileDialog = /* @__PURE__ */ __name(async (filepath = process.cwd(), options = {}) => { if (typeof filepath !== "string") { throw new FileDialogError("Filepath must be a string"); } const normalizedPath = import_node_path.default.resolve(filepath); const isPowerShellAvailable = await checkPowerShellAvailable(); if (!isPowerShellAvailable) { throw new FileDialogError("PowerShell is not available on this system"); } const opts = { multiple: true, checkFileExists: true, filter: "*.*", title: "Select File(s)", defaultExtension: "", maxTimeout: 5 * 60 * 1e3, // 5 minutes ...options }; const escapedPath = escapeForPowerShell(normalizedPath); const escapedTitle = escapeForPowerShell(opts.title); const filterString = buildFilterString(opts.filter); const pathSeparator = "|<<PATH_SEPARATOR>>|"; const powershellScript = ` try { Add-Type -AssemblyName System.Windows.Forms [System.Windows.Forms.Application]::EnableVisualStyles() $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog $OpenFileDialog.Multiselect = ${opts.multiple ? "$true" : "$false"} $OpenFileDialog.InitialDirectory = "${escapedPath}" $OpenFileDialog.Filter = "${escapeForPowerShell(filterString)}" $OpenFileDialog.CheckFileExists = ${opts.checkFileExists ? "$true" : "$false"} $OpenFileDialog.CheckPathExists = $true $OpenFileDialog.SupportMultiDottedExtensions = $true $OpenFileDialog.Title = "${escapedTitle}" $OpenFileDialog.RestoreDirectory = $true ${opts.defaultExtension ? `$OpenFileDialog.DefaultExt = "${escapeForPowerShell(opts.defaultExtension)}"` : ""} # Ensure dialog appears on top $OpenFileDialog.ShowHelp = $false $DialogResult = $OpenFileDialog.ShowDialog() if ($DialogResult -eq [System.Windows.Forms.DialogResult]::OK) { foreach ($FileName in $OpenFileDialog.FileNames) { Write-Host "$FileName${pathSeparator}" } } else { Write-Host "CANCELLED" } } catch { Write-Error $_.Exception.Message exit 1 } `; return new Promise((resolve, reject) => { const child = import_node_child_process.default.spawn( "powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", "-"], { windowsHide: true, shell: false } ); let stdoutData = ""; let stderrData = ""; let hasResolved = false; child.stdout.on("data", (data) => { stdoutData += data.toString(); }); child.stderr.on("data", (data) => { stderrData += data.toString(); }); child.on("error", (error) => { if (!hasResolved) { hasResolved = true; reject(new FileDialogError(`Failed to spawn PowerShell: ${error.message}`)); } }); child.on("close", (code) => { if (hasResolved) return; hasResolved = true; if (code !== 0 && stderrData) { const errorMessage = stderrData.trim(); reject(new FileDialogError(`PowerShell error: ${errorMessage}`)); return; } const output = stdoutData.trim(); if (isCanceled(output)) { resolve({ files: [], canceled: true }); return; } const files = output.split(pathSeparator).map((s) => s.trim()).filter((s) => s !== ""); const validFiles = files.filter((f) => import_node_path.default.isAbsolute(f)); if (validFiles.length !== files.length) { console.warn("Some file paths were not absolute and were filtered out"); } resolve({ files: validFiles, canceled: false }); }); if (opts.maxTimeout) { const timeout = setTimeout(() => { if (!hasResolved) { hasResolved = true; child.kill(); reject(new FileDialogError("Dialog timeout after 5 minutes")); } }, opts.maxTimeout); child.on("exit", () => { clearTimeout(timeout); }); } child.stdin.write(powershellScript); child.stdin.end(); }); }, "openWindowsFileDialog"); var openWindowsFileDialogSync = /* @__PURE__ */ __name(async (filepath, options) => { const result = await openWindowsFileDialog(filepath, options); return result.files; }, "openWindowsFileDialogSync"); var open_windows_file_dialog_default = openWindowsFileDialog; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { openWindowsFileDialog, openWindowsFileDialogSync }); //# sourceMappingURL=index.js.map