External Editor is a Node.js library that enables applications to launch external text editors (using $VISUAL or $EDITOR environment variables) for user text input. It handles temporary file creation, editor process management, and content retrieval with comprehensive error handling.
npm install external-editorimport { edit, editAsync, ExternalEditor, CreateFileError, LaunchEditorError, ReadFileError, RemoveFileError } from "external-editor";For CommonJS:
const { edit, editAsync, ExternalEditor, CreateFileError, LaunchEditorError, ReadFileError, RemoveFileError } = require("external-editor");import { edit } from "external-editor";
// Simple synchronous editing
const result = edit("Initial text content");
console.log(result); // User's edited text
// With file options
const result2 = edit("# Please write your message", {
postfix: ".md",
prefix: "message-"
});External Editor provides two primary usage patterns:
edit() and editAsync() for simple, one-off text editingExternalEditor class for more control over the editing lifecycleSimple function for immediate text editing with automatic cleanup.
/**
* Edit text using the user's preferred external editor synchronously
* @param text - Initial text content (defaults to empty string)
* @param fileOptions - Optional temporary file configuration
* @returns Edited text content
* @throws CreateFileError, LaunchEditorError, ReadFileError, RemoveFileError
*/
function edit(text?: string, fileOptions?: IFileOptions): string;Asynchronous function for text editing with callback-based result handling.
/**
* Edit text using the user's preferred external editor asynchronously
* @param text - Initial text content (defaults to empty string)
* @param callback - Callback function for handling results
* @param fileOptions - Optional temporary file configuration
*/
function editAsync(text?: string, callback: StringCallback, fileOptions?: IFileOptions): void;Full-featured class providing complete control over the editing lifecycle.
/**
* External editor class providing complete control over editing lifecycle
*/
class ExternalEditor {
/** Current text content */
text: string;
/** Path to temporary file */
tempFile: string;
/** Editor configuration (binary and arguments) */
editor: IEditorParams;
/** Last exit status from editor process */
lastExitStatus: number;
/** @deprecated Use tempFile instead */
readonly temp_file: string;
/** @deprecated Use lastExitStatus instead */
readonly last_exit_status: number;
/**
* Create a new ExternalEditor instance
* @param text - Initial text content (defaults to empty string)
* @param fileOptions - Optional temporary file configuration
* @throws CreateFileError
*/
constructor(text?: string, fileOptions?: IFileOptions);
/**
* Run editor synchronously and read result
* @returns Edited text content
* @throws LaunchEditorError, ReadFileError
*/
run(): string;
/**
* Run editor asynchronously
* @param callback - Callback function for handling results
*/
runAsync(callback: StringCallback): void;
/**
* Clean up temporary file
* @throws RemoveFileError
*/
cleanup(): void;
}Usage Examples:
import { ExternalEditor } from "external-editor";
// Class-based usage with full control
const editor = new ExternalEditor("Initial content");
try {
const result = editor.run();
if (editor.lastExitStatus !== 0) {
console.warn("Editor exited with non-zero status");
}
console.log("Edited text:", result);
} catch (error) {
console.error("Editor failed:", error.message);
} finally {
editor.cleanup();
}
// Async usage
const editor2 = new ExternalEditor("Start here...");
editor2.runAsync((error, result) => {
if (error) {
console.error("Error:", error.message);
} else {
console.log("Result:", result);
}
editor2.cleanup();
});Comprehensive error types for different failure scenarios.
/**
* Error thrown when temporary file creation fails
*/
class CreateFileError extends Error {
/** Original underlying error */
originalError: Error;
constructor(originalError: Error);
}
/**
* Error thrown when editor launch fails
*/
class LaunchEditorError extends Error {
/** Original underlying error */
originalError: Error;
constructor(originalError: Error);
}
/**
* Error thrown when temporary file reading fails
*/
class ReadFileError extends Error {
/** Original underlying error */
originalError: Error;
constructor(originalError: Error);
}
/**
* Error thrown when temporary file removal fails
*/
class RemoveFileError extends Error {
/** Original underlying error */
originalError: Error;
constructor(originalError: Error);
}Error Handling Example:
import { ExternalEditor, CreateFileError, LaunchEditorError, ReadFileError, RemoveFileError } from "external-editor";
try {
const editor = new ExternalEditor();
const result = editor.run();
editor.cleanup();
} catch (error) {
if (error instanceof CreateFileError) {
console.error("Failed to create temporary file:", error.originalError);
} else if (error instanceof LaunchEditorError) {
console.error("Failed to launch editor:", error.originalError);
} else if (error instanceof ReadFileError) {
console.error("Failed to read temporary file:", error.originalError);
} else if (error instanceof RemoveFileError) {
console.error("Failed to remove temporary file:", error.originalError);
} else {
throw error;
}
}/**
* Configuration for the external editor command
*/
interface IEditorParams {
/** Command line arguments for the editor */
args: string[];
/** Path to editor binary */
bin: string;
}
/**
* Options for temporary file creation
*/
interface IFileOptions {
/** File name prefix */
prefix?: string;
/** File name postfix (useful for extensions like .md) */
postfix?: string;
/** File permission mode (e.g., 0o644) */
mode?: number;
/** File name template (see tmp package documentation) */
template?: string;
/** Directory for temporary file */
dir?: string;
}
/**
* Callback function type for async operations with string results
*/
type StringCallback = (err: Error, result: string) => void;
/**
* Callback function type for void async operations
*/
type VoidCallback = () => void;