isomorphic-git / lightning-fs

A lean and fast 'fs' for the browser
MIT License
477 stars 47 forks source link

How to check if directory exists at all? #21

Closed kornerr closed 3 years ago

kornerr commented 4 years ago

Hi.

There are mkdir, readdir, but the docs don't mention any way to check if directory exists. Is there such a function?

Thanks.

100ideas commented 4 years ago

Here's an example: https://github.com/jcubic/fs-browser/blob/master/src/index.js#L63

This code hooks lightning-fs up to a jquery file-browser plugin so ignore those parts.

// https://github.com/jcubic/fs-browser/blob/5730cdb47a34effb41321679c78f687c0c5996ed/src/index.js#L63

    var fs = new LightningFS('fs').promises;
    // facade for RPC service used for editor
    const service = {
        is_writable: function() {
            return Promise.resolve(true);
        },
        file: function(path) {
            return fs.readFile(path, 'utf8');
        },
        filemtime: async function(path) {
            const stat = await fs.stat(path);
            return stat && stat.mtimeMs;
        },
        file_exists: async function(path) {
            try {
                await fs.stat(path);
                return true;
            } catch(e) {
                return false;
            }
        },
        save: function(path, content) {
            return fs.writeFile(path, content);
        }
    };
    var browser = $('<div/>').dialog({
        width: 600,
        height: 480
    }).browse({
        root: '/',
        separator: '/',
        contextmenu: true,
        menu: function(type) {
            if (type == 'li') {
                return {
                    'delete': function($li) {
                        alert(`delete "${ $li.text() }"`);
                    }
                };
            }  else {
                return true;
            }
        },
        dir: async function(path) {
            var names = await fs.readdir(path);
            var result = {files:[], dirs: []};
            for (let name of names) {
                const stat = await fs.stat(path + '/' + name);
                if (stat && stat.isDirectory()) {
                    result.dirs.push(name);
                } else {
                    result.files.push(name);
                }
            }
            return result;
        },
        exists: async function(path) {
            try {
                return await fs.stat(path);
            } catch (e) {
                return false;
            }
        },
billiegoose commented 4 years ago

Yup! Basically what @100ideas said. Here's the version I use in isomorphic-git

https://github.com/isomorphic-git/isomorphic-git/blob/2e6198df39279b3804ef6be7e3801b8a230557db/src/models/FileSystem.js#L60-L77

And yeah. Kinda annoying right, because there's a try/catch and everything? But then I looked at the source code for Node's fs.exists and yeah, it turns out that's how it's done.

https://github.com/nodejs/node/blob/b1e52fe2ea99a52ace6399e9f629c965f66a2643/lib/fs.js#L226-L245

At least until recently. It looks like ~2 years ago Node fs.exists switched from using stat under the hood to using access. I'm not sure how the new one works.

In theory, a native implementation of exists that doesn't throw an Error and immediately catch it might be ever-so-slightly faster. Not sure what the overhead of creating an Error object is... it can be "expensive" because it creates a stack trace - although I think most browsers compute the stack trace only if needed nowadays. Hmm.