MrOtherGuy / fx-autoconfig

Load custom javascript in browser context
Mozilla Public License 2.0
164 stars 10 forks source link

Replace old file-system methods with new API #28

Closed MrOtherGuy closed 11 months ago

MrOtherGuy commented 1 year ago

This is something that's been bothering me for a while. The old methods to interact with filesystem are really all over the place. It would be great to have a totally separate filesystem namespace where all filesystem stuff can live.

But replacing the old ways is going to be a breaking change. The loader itself interacts with various filesystem related functions too so it's going to be a pretty big change. The new interface could look like this:

// import - can also be used on @backgroundmodule scripts
// easy to switch to .mjs module when time comes for that
const { FileSystem } = ChromeUtils.import("chrome://userchromejs/fs.jsm"); 

// can be accessed throug _ucUtils like this
_ucUtils.fs.readFile()

// These methods would all return a FileSystemResult object
.readFile()
.readSync()
.readJSON()
.getEntry()

// writeFile() behaves the same as before
.writeFile()

// ... and various other helpers

The FileSystemResult object is basically a wrapper - a common interface which represents one of:

// This variant represents a file of that name in resources directory
let fsResult = FileSystem.getEntry("some.txt");
fsResult.isFile()
// >> true

// This variant will represent non-existent file
let fsResult = FileSystem.getEntry("nonexistent.txt");
fsResult.isFile()
// >> false
fsResult.isError()
// >> true

// same thing for Content and Directory

To get the actual data out of this you would use one of few methods:

let fsResult = await FileSystem.readFile("some.txt");
fsResult.content()
// >> "Hello worlds!"

let fsResult = FileSystem.getEntry("some.txt");
let nativeFile = fsResult.entry() // nsIFile object

// These both internally call FileSystem.readFile{Sync} and unwrap the result
let fsResult = FileSystem.getEntry("some.txt");
let content = await fsResult.read() // async read 
let content = fsResult.readSync() // sync read

let fsResult = FileSystem.getEntry("mydir/");
for(let entry of fsResult){ // or explicitly fsResult.entries()
  // Note: entries() produces native nsIFile objects, because it
  // is looping through files in the directory, so we know they exist
  console.log(entry.leafName)
}
>> "some.txt"

In essence, the operations that can be given a String (a filename) would produce FileSystemResult objects, but methods on those will produce native data be it content or nsIFile or throw an exception.

However, it's debatable if Error should be one variant or is it better to just throw an error. Perhaps non-existent file should produce a variant but other kind of "errors" should throw?

MrOtherGuy commented 1 year ago

The new filesystem API is now live. I preserved the old helpers for backwards compatibility for now, they should have the same behavior as before although now they are just wrappers for underlying fs. methods.

I've added a version number for this build 0.7 and the plan is that those compatibility wrappers will be removed with the version in which the loader module is transformed to ES6 module. That will happen along with Firefox ESR 115 later this year.

MrOtherGuy commented 11 months ago

This can be closed because old methods have been removed.