@rescript-tauri/plugin-fs¶
ReScript bindings for the Tauri 2.x filesystem
plugin. Single-shot
file IO (read / write / dir / stat) is covered; FileHandle,
watch, readTextFileLines, and the iOS-only security-scoped
APIs are deferred to a follow-up package iteration.
Note
This package is feature-complete in main. Its first npm publish is scheduled alongside the other packages. Until then, consume it via the source repository or a workspace link.
Install¶
pnpm add @rescript-tauri/plugin-fs @tauri-apps/plugin-fs
@rescript-tauri/plugin-fs declares both
@rescript-tauri/core and @tauri-apps/plugin-fs as
peerDependencies, so you control each upstream version.
Add the package to dependencies in your rescript.json:
{
"dependencies": [
"@rescript-tauri/core",
"@rescript-tauri/plugin-fs"
]
}
On the Rust side, add the plugin crate and register it on the builder:
# src-tauri/Cargo.toml
[dependencies]
tauri-plugin-fs = "2"
// src-tauri/src/main.rs
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_fs::init())
.run(tauri::generate_context!())
.expect("error while running app");
}
Capabilities¶
Tauri 2.x requires every filesystem operation to be granted a
capability. The minimal set used by
examples/plugin-fs-demo
is:
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default",
"fs:default",
"fs:allow-app-local-data-recursive"
]
}
fs:allow-app-local-data-recursive is a permission alias shipped
by the plugin that opens up the entire $APPLOCALDATA subtree.
Swap it for fs:allow-home-recursive,
fs:allow-document-recursive, etc. when you need a different
sandbox.
Minimal example¶
open RescriptTauriPluginFs
let baseDir = PluginFs.BaseDirectory.appLocalData
await PluginFs.mkdir(
"notes",
~options={baseDir: baseDir, recursive: true},
)
await PluginFs.writeTextFile(
"notes/hello.txt",
"Hello from rescript-tauri.\n",
~options={baseDir: baseDir, create: true},
)
let body = await PluginFs.readTextFile(
"notes/hello.txt",
~options={baseDir: baseDir},
)
Console.log(body)
Public API¶
All 14 single-shot IO functions are exposed under PluginFs:
Function |
Notes |
|---|---|
|
UTF-8 text read |
|
UTF-8 text write |
|
Raw bytes ( |
|
Raw bytes |
|
Path existence |
|
File or directory (set |
|
Move within the filesystem |
|
Directory creation ( |
|
Direct children of a directory |
|
Metadata, follows symlinks |
|
Metadata, does not follow symlinks |
|
Resize a file to N bytes |
|
Copy with optional cross-base-dir paths |
|
Plain byte count |
The associated option records (readFileOptions,
writeFileOptions, mkdirOptions, removeOptions,
renameOptions, copyFileOptions, statOptions,
existsOptions, readDirOptions, truncateOptions) are all
re-exported from PluginFs and the upstream
FileInfo
/ DirEntry
shapes are available as PluginFs.fileInfo /
PluginFs.dirEntry.
BaseDirectory is re-exported from
@rescript-tauri/core so you don’t need a separate import:
let baseDir = PluginFs.BaseDirectory.appLocalData
// equivalent to RescriptTauriCore.Path.BaseDirectory.appLocalData
Pitfalls¶
Single-field record punning¶
ReScript treats {baseDir} as a block with a single statement,
not a record with one punned field. Always spell out single-field
options as {baseDir: baseDir}:
// ❌ compile error: expected record, got block
await PluginFs.exists("foo.txt", ~options={baseDir})
// ✅
await PluginFs.exists("foo.txt", ~options={baseDir: baseDir})
Multi-field records ({baseDir, recursive: true}) work as
expected.
Uint8Array length¶
Uint8Array.t in @rescript/core is an alias for
TypedArray.t<int>. The length getter lives on the parent
module:
let bytes = await PluginFs.readFile("data.bin", ~options={baseDir: baseDir})
Console.log("byte count: " ++ Int.toString(TypedArray.length(bytes)))
Compatibility¶
Component |
Supported range |
|---|---|
Upstream |
|
Rust |
|
|
|
ReScript |
|
|
|
OS |
Linux / macOS / Windows |
See also¶
Live demo:
examples/plugin-fs-demoSource:
packages/plugin-fsUpstream docs: Tauri 2.x file-system plugin