macotron.fs
Read, write, list, and rename files, open them or show them in Finder, move them to the Trash, and watch a file or folder for changes.
// Give new screenshots on the Desktop a short name.
for (const name of macotron.fs.list("~/Desktop")) {
const m = name.match(/^Screenshot (\d{4}-\d\d-\d\d) at (.+)\.png$/);
if (!m) continue;
const dest = "~/Desktop/shot-" + m[1] + ".png";
if (!macotron.fs.exists(dest)) macotron.fs.rename("~/Desktop/" + name, dest);
}
Paths
Every call expands a leading ~ to your home folder. Macotron is not sandboxed and applies no path rules of its own: a plugin can read and write anything your user account can, outside the workdir too. macOS privacy rules still apply. Folders such as Desktop, Documents, and Downloads can make macOS ask once whether Macotron may open them, and a refused folder makes the call throw.
Use absolute paths or paths that start with ~. Relative paths do not resolve against the plugin's folder.
All calls are synchronous. When a call fails, it throws an error whose message starts with the call name, for example fs.read failed: …. open, reveal, and trash do not throw; they report failure in their return value.
Reading
read(path: string): string
The whole file as UTF-8 text. Throws when the file is missing or is not valid UTF-8.
readBytes(path: string): string
The whole file as a base64 string. Use this for images and other binary files.
exists(path: string): boolean
Whether a file or folder exists at path. Never throws.
list(path: string): string[]
The names of the entries in a folder, not full paths. Hidden files such as .DS_Store are included. The order is not sorted. Throws when path is not a folder.
Writing
write(path: string, content: string): void
Writes content as UTF-8, replacing the file if it exists. Missing parent folders are created. The write is atomic: Macotron writes a temporary file and moves it into place, so a reader never sees half a file.
rename(from: string, to: string): void
Moves or renames a file or folder. Throws if to already exists, so check with exists() first.
Opening and the Trash
open(path: string, options?: { app?: string }): boolean
Opens the file or folder in its default app, as a double-click in Finder does. app is a bundle ID to open it with instead, such as "com.apple.TextEdit". Returns false when the path does not exist or no app has that bundle ID.
reveal(path: string | string[]): boolean
Opens a Finder window with the items selected. Paths that do not exist are skipped. Returns false when none exist.
trash(path: string | string[]): { trashed: string[]; failed: Array<{ path: string; error: string }> }
Moves each item to the Trash, where the user can still put it back. Paths come back as you passed them, so you can match them against your own list.
const { trashed, failed } = macotron.fs.trash(["~/Desktop/old.png", "~/Desktop/older.png"]);
for (const f of failed) console.error(f.path, f.error);
Watching
watch(path: string, callback: (event: { path: string; type: string }) => void): () => void
Calls callback when something changes, and returns a function that stops the watch. Throws when path does not exist. Changes are grouped and delivered about 0.3 seconds after they happen.
event.path is the full path of the item that changed. event.type is "created", "modified", or "deleted". A rename reports "created", for both the old and the new name.
Watching a folder reports changes to everything inside it, subfolders included. Watching a file watches its parent folder, so the callback also gets events for the file's neighbors. Compare event.path when you only care about one file.
const stop = macotron.fs.watch("~/Downloads", ({ path, type }) => {
if (type === "created" && path.endsWith(".heic")) console.log("New photo:", path);
});
// later: stop();
Watches stop by themselves when Macotron reloads plugins.
See also
- macotron.image to convert images, and macotron.plist to read property lists.
- macotron.files to search for files by name.
- The
batch-rename.jsandscreenshot-rename.jsplugins.