UNPKG

@jsvfs/adapter-node-fs

Version:
227 lines 8.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NodeFSAdapter = void 0; /** * [[include:packages/adapter-node-fs/README.md]] * @packageDocumentation * @module @jsvfs/adapter-node-fs */ const extras_1 = require("@jsvfs/extras"); const fs_1 = require("fs"); const path_1 = require("path"); const { link, mkdir, readFile, readlink, rmdir, symlink, unlink, writeFile } = fs_1.promises; /** An adapter for NodeJS local filesystems using the `fs` module. */ class NodeFSAdapter { /** Creates an instance of Node file system adapter. * @param {NodeFSAdapterOpts} [opts] - Options for this instance. */ constructor(opts = {}) { this.root = typeof opts.cwd === 'string' ? path_1.resolve(opts.cwd) : process.cwd(); this.flushEnabled = typeof opts.flushEnabled === 'boolean' ? opts.flushEnabled : false; this.handle = 'node-fs'; this.journal = new extras_1.Journal(); } /** Snapshot of the underlying file system; an asynchronous iterable which returns an entry of path and data. * @param {string} [path='/'] - The current path as the tree is descended. * @param {boolean} [read=true] - Whether to retrieve the underlying data. * @returns {AsyncGenerator<[string, SnapshotEntry]>} The asynchronous iterable to get the snapshot. */ async *snapshot(path = '/') { let result = []; try { result = await fs_1.promises.readdir(path === '/' ? this.root : path_1.join(this.root, path), { withFileTypes: true }); } catch (error) { this.journal.push({ level: 'error', message: `Could not read directory '${path_1.join(this.root, path)}'.`, op: 'snapshot', error }); } for (const entry of result) { const newPath = path_1.posix.join(path, entry.name); try { switch (true) { case entry.isDirectory(): yield [newPath, { type: 'folder' }]; for await (const [path, data] of this.snapshot(newPath)) { yield [path, data]; } break; case entry.isFile(): yield [newPath, { type: 'file', contents: await readFile(path_1.join(this.root, newPath)) }]; break; case entry.isSymbolicLink(): yield [newPath, { type: 'softlink', contents: path_1.relative(path_1.join(this.root, newPath), await readlink(path_1.join(this.root, newPath), 'utf8')) .replace(this.root, '') .replace(/\\+|\/+/gu, '/') }]; break; } } catch (error) { this.journal.push({ level: 'error', message: `Could not get contents of '${path_1.join(this.root, newPath)}'.`, op: 'snapshot', error }); } } } /** Read a file from persistent storage; assumes 'utf8' file encoding. */ async read(path) { const newPath = path_1.join(this.root, path); try { return await readFile(newPath); } catch (error) { this.journal.push({ level: 'error', message: `Could not get contents of '${newPath}'.`, op: 'read', error }); return Buffer.alloc(0); } } /** Create a file or write the contents of a file to persistent storage. */ async write(path, contents) { const newPath = path_1.join(this.root, path); try { const parent = path_1.dirname(newPath); if (typeof contents === 'undefined') contents = Buffer.alloc(0); try { await mkdir(parent, { recursive: true }); } catch (error) { this.journal.push({ level: 'warn', message: `Could not create directory '${parent}'.`, op: 'write', error }); } await writeFile(newPath, contents); } catch (error) { this.journal.push({ level: 'error', message: `Could not get contents of '${newPath}'.`, op: 'write', error }); } } /** Make a directory or directory tree in persistent storage. */ async mkdir(path) { const newPath = path_1.join(this.root, path); try { await mkdir(newPath, { recursive: true }); } catch (error) { this.journal.push({ level: 'warn', message: `Could not create directory '${newPath}'.`, op: 'mkdir', error }); } } /** Create a link in persistent storage. */ async link(from, to, type) { const newFrom = path_1.join(this.root, from); const newTo = path_1.join(this.root, to); try { switch (type) { case 'hardlink': await link(newTo, newFrom); break; case 'softlink': await symlink(newTo, newFrom); break; } } catch (error) { this.journal.push({ level: 'error', message: `Could not create link from '${newFrom}' to '${newTo}'.`, op: 'link', error }); } } /** Remove items from persistent storage. */ async remove(path, type) { const newPath = path_1.join(this.root, path); try { switch (type) { case 'root': // Ignore root; removal of root is probably unintentional. break; case 'folder': await rmdir(newPath, { recursive: true }); break; default: await unlink(newPath); break; } } catch (error) { this.journal.push({ level: 'error', message: `Could not remove ${type} at path '${newPath}'.`, op: 'remove', error }); } } /** Flush the underlying file system to prepare for a commit. This is a destructive operation unless flush is disabled. */ async flush() { if (this.flushEnabled) { let result = []; try { result = await fs_1.promises.readdir(this.root, { withFileTypes: true }); } catch (error) { this.journal.push({ level: 'error', message: `Could not read directory '${this.root}'.`, op: 'flush', error }); } for (const entry of result) { const path = path_1.join(this.root, entry.name); try { switch (true) { case entry.isDirectory(): await rmdir(path, { recursive: true }); break; case entry.isFile(): case entry.isSymbolicLink(): await unlink(path); break; } } catch (error) { this.journal.push({ level: 'error', message: `Could not remove item at path '${path}'.`, op: 'remove', error }); } } } } } exports.NodeFSAdapter = NodeFSAdapter; //# sourceMappingURL=index.js.map