UNPKG

@codesandbox/sandpack-client

Version:

<img style="width:100%" src="https://user-images.githubusercontent.com/4838076/143581035-ebee5ba2-9cb1-4fe8-a05b-2f44bd69bb4b.gif" alt="Component toolkit for live running code editing experiences" />

1,573 lines (1,518 loc) 756 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["BrowserFS"] = factory(); else root["BrowserFS"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./build/temp/library/rollup/browserfs.rollup.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./build/temp/library/rollup/browserfs.rollup.js": /*!*******************************************************!*\ !*** ./build/temp/library/rollup/browserfs.rollup.js ***! \*******************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Buffer, global, process) { Object.defineProperty(exports, '__esModule', { value: true }); var buffer = __webpack_require__(/*! buffer */ "./node_modules/buffer/index.js"); var path = __webpack_require__(/*! path */ "./node_modules/bfs-path/js/path.js"); /** * Standard libc error codes. Add more to this enum and ErrorStrings as they are * needed. * @url http://www.gnu.org/software/libc/manual/html_node/Error-Codes.html */ var ErrorCode; (function (ErrorCode) { ErrorCode[ErrorCode["EPERM"] = 1] = "EPERM"; ErrorCode[ErrorCode["ENOENT"] = 2] = "ENOENT"; ErrorCode[ErrorCode["EIO"] = 5] = "EIO"; ErrorCode[ErrorCode["EBADF"] = 9] = "EBADF"; ErrorCode[ErrorCode["EACCES"] = 13] = "EACCES"; ErrorCode[ErrorCode["EBUSY"] = 16] = "EBUSY"; ErrorCode[ErrorCode["EEXIST"] = 17] = "EEXIST"; ErrorCode[ErrorCode["ENOTDIR"] = 20] = "ENOTDIR"; ErrorCode[ErrorCode["EISDIR"] = 21] = "EISDIR"; ErrorCode[ErrorCode["EINVAL"] = 22] = "EINVAL"; ErrorCode[ErrorCode["EFBIG"] = 27] = "EFBIG"; ErrorCode[ErrorCode["ENOSPC"] = 28] = "ENOSPC"; ErrorCode[ErrorCode["EROFS"] = 30] = "EROFS"; ErrorCode[ErrorCode["ENOTEMPTY"] = 39] = "ENOTEMPTY"; ErrorCode[ErrorCode["ENOTSUP"] = 95] = "ENOTSUP"; })(ErrorCode || (ErrorCode = {})); /* tslint:disable:variable-name */ /** * Strings associated with each error code. * @hidden */ var ErrorStrings = {}; ErrorStrings[ErrorCode.EPERM] = 'Operation not permitted.'; ErrorStrings[ErrorCode.ENOENT] = 'No such file or directory.'; ErrorStrings[ErrorCode.EIO] = 'Input/output error.'; ErrorStrings[ErrorCode.EBADF] = 'Bad file descriptor.'; ErrorStrings[ErrorCode.EACCES] = 'Permission denied.'; ErrorStrings[ErrorCode.EBUSY] = 'Resource busy or locked.'; ErrorStrings[ErrorCode.EEXIST] = 'File exists.'; ErrorStrings[ErrorCode.ENOTDIR] = 'File is not a directory.'; ErrorStrings[ErrorCode.EISDIR] = 'File is a directory.'; ErrorStrings[ErrorCode.EINVAL] = 'Invalid argument.'; ErrorStrings[ErrorCode.EFBIG] = 'File is too big.'; ErrorStrings[ErrorCode.ENOSPC] = 'No space left on disk.'; ErrorStrings[ErrorCode.EROFS] = 'Cannot modify a read-only file system.'; ErrorStrings[ErrorCode.ENOTEMPTY] = 'Directory is not empty.'; ErrorStrings[ErrorCode.ENOTSUP] = 'Operation is not supported.'; /* tslint:enable:variable-name */ /** * Represents a BrowserFS error. Passed back to applications after a failed * call to the BrowserFS API. */ var ApiError = /*@__PURE__*/(function (Error) { function ApiError(type, message, path) { if ( message === void 0 ) message = ErrorStrings[type]; Error.call(this, message); // Unsupported. this.syscall = ""; this.errno = type; this.code = ErrorCode[type]; this.path = path; this.stack = new Error().stack; this.message = "Error: " + (this.code) + ": " + message + (this.path ? (", '" + (this.path) + "'") : ''); } if ( Error ) ApiError.__proto__ = Error; ApiError.prototype = Object.create( Error && Error.prototype ); ApiError.prototype.constructor = ApiError; /** * @return A friendly error message. */ ApiError.fromJSON = function fromJSON (json) { var err = new ApiError(0); err.errno = json.errno; err.code = json.code; err.path = json.path; err.stack = json.stack; err.message = json.message; return err; }; /** * Creates an ApiError object from a buffer. */ ApiError.fromBuffer = function fromBuffer (buffer, i) { if ( i === void 0 ) i = 0; return ApiError.fromJSON(JSON.parse(buffer.toString('utf8', i + 4, i + 4 + buffer.readUInt32LE(i)))); }; ApiError.FileError = function FileError (code, p) { return new ApiError(code, ErrorStrings[code], p); }; ApiError.ENOENT = function ENOENT (path) { return this.FileError(ErrorCode.ENOENT, path); }; ApiError.EEXIST = function EEXIST (path) { return this.FileError(ErrorCode.EEXIST, path); }; ApiError.EISDIR = function EISDIR (path) { return this.FileError(ErrorCode.EISDIR, path); }; ApiError.ENOTDIR = function ENOTDIR (path) { return this.FileError(ErrorCode.ENOTDIR, path); }; ApiError.EPERM = function EPERM (path) { return this.FileError(ErrorCode.EPERM, path); }; ApiError.ENOTEMPTY = function ENOTEMPTY (path) { return this.FileError(ErrorCode.ENOTEMPTY, path); }; ApiError.prototype.toString = function toString () { return this.message; }; ApiError.prototype.toJSON = function toJSON () { return { errno: this.errno, code: this.code, path: this.path, stack: this.stack, message: this.message }; }; /** * Writes the API error into a buffer. */ ApiError.prototype.writeToBuffer = function writeToBuffer (buffer, i) { if ( buffer === void 0 ) buffer = Buffer.alloc(this.bufferSize()); if ( i === void 0 ) i = 0; var bytesWritten = buffer.write(JSON.stringify(this.toJSON()), i + 4); buffer.writeUInt32LE(bytesWritten, i); return buffer; }; /** * The size of the API error in buffer-form in bytes. */ ApiError.prototype.bufferSize = function bufferSize () { // 4 bytes for string length. return 4 + Buffer.byteLength(JSON.stringify(this.toJSON())); }; return ApiError; }(Error)); var api_error = /*#__PURE__*/Object.freeze({ get ErrorCode () { return ErrorCode; }, ErrorStrings: ErrorStrings, ApiError: ApiError }); var ActionType; (function (ActionType) { // Indicates that the code should not do anything. ActionType[ActionType["NOP"] = 0] = "NOP"; // Indicates that the code should throw an exception. ActionType[ActionType["THROW_EXCEPTION"] = 1] = "THROW_EXCEPTION"; // Indicates that the code should truncate the file, but only if it is a file. ActionType[ActionType["TRUNCATE_FILE"] = 2] = "TRUNCATE_FILE"; // Indicates that the code should create the file. ActionType[ActionType["CREATE_FILE"] = 3] = "CREATE_FILE"; })(ActionType || (ActionType = {})); /** * Represents one of the following file flags. A convenience object. * * * `'r'` - Open file for reading. An exception occurs if the file does not exist. * * `'r+'` - Open file for reading and writing. An exception occurs if the file does not exist. * * `'rs'` - Open file for reading in synchronous mode. Instructs the filesystem to not cache writes. * * `'rs+'` - Open file for reading and writing, and opens the file in synchronous mode. * * `'w'` - Open file for writing. The file is created (if it does not exist) or truncated (if it exists). * * `'wx'` - Like 'w' but opens the file in exclusive mode. * * `'w+'` - Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists). * * `'wx+'` - Like 'w+' but opens the file in exclusive mode. * * `'a'` - Open file for appending. The file is created if it does not exist. * * `'ax'` - Like 'a' but opens the file in exclusive mode. * * `'a+'` - Open file for reading and appending. The file is created if it does not exist. * * `'ax+'` - Like 'a+' but opens the file in exclusive mode. * * Exclusive mode ensures that the file path is newly created. */ var FileFlag = function FileFlag(flagStr) { this.flagStr = flagStr; if (FileFlag.validFlagStrs.indexOf(flagStr) < 0) { throw new ApiError(ErrorCode.EINVAL, "Invalid flag: " + flagStr); } }; /** * Get the underlying flag string for this flag. */ FileFlag.getFileFlag = function getFileFlag (flagStr) { // Check cache first. if (FileFlag.flagCache.hasOwnProperty(flagStr)) { return FileFlag.flagCache[flagStr]; } return FileFlag.flagCache[flagStr] = new FileFlag(flagStr); }; FileFlag.prototype.getFlagString = function getFlagString () { return this.flagStr; }; /** * Returns true if the file is readable. */ FileFlag.prototype.isReadable = function isReadable () { return this.flagStr.indexOf('r') !== -1 || this.flagStr.indexOf('+') !== -1; }; /** * Returns true if the file is writeable. */ FileFlag.prototype.isWriteable = function isWriteable () { return this.flagStr.indexOf('w') !== -1 || this.flagStr.indexOf('a') !== -1 || this.flagStr.indexOf('+') !== -1; }; /** * Returns true if the file mode should truncate. */ FileFlag.prototype.isTruncating = function isTruncating () { return this.flagStr.indexOf('w') !== -1; }; /** * Returns true if the file is appendable. */ FileFlag.prototype.isAppendable = function isAppendable () { return this.flagStr.indexOf('a') !== -1; }; /** * Returns true if the file is open in synchronous mode. */ FileFlag.prototype.isSynchronous = function isSynchronous () { return this.flagStr.indexOf('s') !== -1; }; /** * Returns true if the file is open in exclusive mode. */ FileFlag.prototype.isExclusive = function isExclusive () { return this.flagStr.indexOf('x') !== -1; }; /** * Returns one of the static fields on this object that indicates the * appropriate response to the path existing. */ FileFlag.prototype.pathExistsAction = function pathExistsAction () { if (this.isExclusive()) { return ActionType.THROW_EXCEPTION; } else if (this.isTruncating()) { return ActionType.TRUNCATE_FILE; } else { return ActionType.NOP; } }; /** * Returns one of the static fields on this object that indicates the * appropriate response to the path not existing. */ FileFlag.prototype.pathNotExistsAction = function pathNotExistsAction () { if ((this.isWriteable() || this.isAppendable()) && this.flagStr !== 'r+') { return ActionType.CREATE_FILE; } else { return ActionType.THROW_EXCEPTION; } }; // Contains cached FileMode instances. FileFlag.flagCache = {}; // Array of valid mode strings. FileFlag.validFlagStrs = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+']; /** * Indicates the type of the given file. Applied to 'mode'. */ var FileType; (function (FileType) { FileType[FileType["FILE"] = 32768] = "FILE"; FileType[FileType["DIRECTORY"] = 16384] = "DIRECTORY"; FileType[FileType["SYMLINK"] = 40960] = "SYMLINK"; })(FileType || (FileType = {})); /** * Emulation of Node's `fs.Stats` object. * * Attribute descriptions are from `man 2 stat' * @see http://nodejs.org/api/fs.html#fs_class_fs_stats * @see http://man7.org/linux/man-pages/man2/stat.2.html */ var Stats = function Stats(itemType, size, mode, atimeMs, mtimeMs, ctimeMs, birthtimeMs) { /** * UNSUPPORTED ATTRIBUTES * I assume no one is going to need these details, although we could fake * appropriate values if need be. */ // ID of device containing file this.dev = 0; // inode number this.ino = 0; // device ID (if special file) this.rdev = 0; // number of hard links this.nlink = 1; // blocksize for file system I/O this.blksize = 4096; // @todo Maybe support these? atm, it's a one-user filesystem. // user ID of owner this.uid = 0; // group ID of owner this.gid = 0; // XXX: Some file systems stash data on stats objects. this.fileData = null; this.size = size; var currentTime = 0; if (typeof (atimeMs) !== 'number') { currentTime = Date.now(); atimeMs = currentTime; } if (typeof (mtimeMs) !== 'number') { if (!currentTime) { currentTime = Date.now(); } mtimeMs = currentTime; } if (typeof (ctimeMs) !== 'number') { if (!currentTime) { currentTime = Date.now(); } ctimeMs = currentTime; } if (typeof (birthtimeMs) !== 'number') { if (!currentTime) { currentTime = Date.now(); } birthtimeMs = currentTime; } this.atimeMs = atimeMs; this.ctimeMs = ctimeMs; this.mtimeMs = mtimeMs; this.birthtimeMs = birthtimeMs; if (!mode) { switch (itemType) { case FileType.FILE: this.mode = 0x1a4; break; case FileType.DIRECTORY: default: this.mode = 0x1ff; } } else { this.mode = mode; } // number of 512B blocks allocated this.blocks = Math.ceil(size / 512); // Check if mode also includes top-most bits, which indicate the file's // type. if (this.mode < 0x1000) { this.mode |= itemType; } }; var prototypeAccessors = { atime: { configurable: true },mtime: { configurable: true },ctime: { configurable: true },birthtime: { configurable: true } }; Stats.fromBuffer = function fromBuffer (buffer) { var size = buffer.readUInt32LE(0), mode = buffer.readUInt32LE(4), atime = buffer.readDoubleLE(8), mtime = buffer.readDoubleLE(16), ctime = buffer.readDoubleLE(24); return new Stats(mode & 0xF000, size, mode & 0xFFF, atime, mtime, ctime); }; /** * Clones the stats object. */ Stats.clone = function clone (s) { return new Stats(s.mode & 0xF000, s.size, s.mode & 0xFFF, s.atimeMs, s.mtimeMs, s.ctimeMs, s.birthtimeMs); }; prototypeAccessors.atime.get = function () { return new Date(this.atimeMs); }; prototypeAccessors.mtime.get = function () { return new Date(this.mtimeMs); }; prototypeAccessors.ctime.get = function () { return new Date(this.ctimeMs); }; prototypeAccessors.birthtime.get = function () { return new Date(this.birthtimeMs); }; Stats.prototype.toBuffer = function toBuffer () { var buffer = Buffer.alloc(32); buffer.writeUInt32LE(this.size, 0); buffer.writeUInt32LE(this.mode, 4); buffer.writeDoubleLE(this.atime.getTime(), 8); buffer.writeDoubleLE(this.mtime.getTime(), 16); buffer.writeDoubleLE(this.ctime.getTime(), 24); return buffer; }; /** * @return [Boolean] True if this item is a file. */ Stats.prototype.isFile = function isFile () { return (this.mode & 0xF000) === FileType.FILE; }; /** * @return [Boolean] True if this item is a directory. */ Stats.prototype.isDirectory = function isDirectory () { return (this.mode & 0xF000) === FileType.DIRECTORY; }; /** * @return [Boolean] True if this item is a symbolic link (only valid through lstat) */ Stats.prototype.isSymbolicLink = function isSymbolicLink () { return (this.mode & 0xF000) === FileType.SYMLINK; }; /** * Change the mode of the file. We use this helper function to prevent messing * up the type of the file, which is encoded in mode. */ Stats.prototype.chmod = function chmod (mode) { this.mode = (this.mode & 0xF000) | mode; }; // We don't support the following types of files. Stats.prototype.isSocket = function isSocket () { return false; }; Stats.prototype.isBlockDevice = function isBlockDevice () { return false; }; Stats.prototype.isCharacterDevice = function isCharacterDevice () { return false; }; Stats.prototype.isFIFO = function isFIFO () { return false; }; Object.defineProperties( Stats.prototype, prototypeAccessors ); /** * @hidden */ var toExport = typeof (window) !== 'undefined' ? window : typeof (self) !== 'undefined' ? self : global; /** * @hidden */ var bfsSetImmediate; if (typeof (setImmediate) !== "undefined") { bfsSetImmediate = setImmediate; } else { var gScope = toExport; var timeouts = []; var messageName = "zero-timeout-message"; var canUsePostMessage = function () { if (typeof gScope.importScripts !== 'undefined' || !gScope.postMessage) { return false; } var postMessageIsAsync = true; var oldOnMessage = gScope.onmessage; gScope.onmessage = function () { postMessageIsAsync = false; }; gScope.postMessage('', '*'); gScope.onmessage = oldOnMessage; return postMessageIsAsync; }; if (canUsePostMessage()) { bfsSetImmediate = function (fn) { var args = [], len = arguments.length - 1; while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; timeouts.push({ fn: fn, args: args }); gScope.postMessage(messageName, "*"); }; var handleMessage = function (event) { if (event.source === self && event.data === messageName) { if (event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } if (timeouts.length > 0) { var ref = timeouts.shift(); var fn = ref.fn; var args = ref.args; return fn.apply(void 0, args); } } }; if (gScope.addEventListener) { gScope.addEventListener('message', handleMessage, true); } else { gScope.attachEvent('onmessage', handleMessage); } } else if (gScope.MessageChannel) { // WebWorker MessageChannel var channel = new gScope.MessageChannel(); channel.port1.onmessage = function (event) { if (timeouts.length > 0) { var ref = timeouts.shift(); var fn = ref.fn; var args = ref.args; return fn.apply(void 0, args); } }; bfsSetImmediate = function (fn) { var args = [], len = arguments.length - 1; while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; timeouts.push({ fn: fn, args: args }); channel.port2.postMessage(''); }; } else { bfsSetImmediate = function (fn) { var args = [], len = arguments.length - 1; while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; return setTimeout.apply(void 0, [ fn, 0 ].concat( args )); }; } } var setImmediate$1 = bfsSetImmediate; var EventEmitter = __webpack_require__(/*! events */ "./node_modules/events/events.js"); var FileWatcher = function FileWatcher() { this.watchEntries = []; }; FileWatcher.prototype.triggerWatch = function triggerWatch (filename, event, newStats) { var this$1 = this; var validEntries = this.watchEntries.filter(function (entry) { if (entry.filename === filename) { return true; } if (entry.recursive && filename.startsWith(entry.filename)) { return true; } return false; }); validEntries.forEach(function (entry) { if (entry.callback) { entry.callback(event, filename); } var newStatsArg = newStats || entry.curr; var oldStatsArg = entry.curr || newStats; if (newStatsArg && oldStatsArg && entry.fileCallback) { entry.fileCallback(newStatsArg, oldStatsArg); entry.curr = newStatsArg; } entry.watcher.emit(event); if (!entry.persistent) { this$1.removeEntry(entry); } }); }; FileWatcher.prototype.watch = function watch (filename, arg2, listener) { var this$1 = this; if ( listener === void 0 ) listener = (function () { }); var watcher = new EventEmitter(); var watchEntry = { filename: filename, watcher: watcher, }; watcher.close = function () { this$1.removeEntry(watchEntry); }; if (typeof arg2 === 'object') { watchEntry.recursive = arg2.recursive; watchEntry.persistent = arg2.persistent === undefined ? true : arg2.persistent; watchEntry.callback = listener; } else if (typeof arg2 === 'function') { watchEntry.callback = arg2; } this.watchEntries.push(watchEntry); return watchEntry.watcher; }; FileWatcher.prototype.watchFile = function watchFile (curr, filename, arg2, listener) { var this$1 = this; if ( listener === void 0 ) listener = (function () { }); var watcher = new EventEmitter(); var watchEntry = { filename: filename, watcher: watcher, curr: curr, }; watcher.close = function () { this$1.removeEntry(watchEntry); }; if (typeof arg2 === 'object') { watchEntry.recursive = arg2.recursive; watchEntry.persistent = arg2.persistent === undefined ? true : arg2.persistent; watchEntry.fileCallback = listener; } else if (typeof arg2 === 'function') { watchEntry.fileCallback = arg2; } this.watchEntries.push(watchEntry); return watchEntry.watcher; }; FileWatcher.prototype.unwatchFile = function unwatchFile (filename, listener) { this.watchEntries = this.watchEntries.filter(function (entry) { return entry.filename !== filename && entry.fileCallback !== listener; }); }; FileWatcher.prototype.removeEntry = function removeEntry (watchEntry) { this.watchEntries = this.watchEntries.filter(function (en) { return en !== watchEntry; }); }; /** Used for unit testing. Defaults to a NOP. */ var wrapCbHook = function (cb, numArgs) { return cb; }; /** * Wraps a callback function, ensuring it is invoked through setImmediate. * @hidden */ function wrapCb(cb, numArgs) { if (typeof cb !== 'function') { throw new Error('Callback must be a function.'); } var hookedCb = wrapCbHook(cb, numArgs); // We could use `arguments`, but Function.call/apply is expensive. And we only // need to handle 1-3 arguments switch (numArgs) { case 1: return function (arg1) { setImmediate$1(function () { return hookedCb(arg1); }); }; case 2: return function (arg1, arg2) { setImmediate$1(function () { return hookedCb(arg1, arg2); }); }; case 3: return function (arg1, arg2, arg3) { setImmediate$1(function () { return hookedCb(arg1, arg2, arg3); }); }; default: throw new Error('Invalid invocation of wrapCb.'); } } /** * @hidden */ function assertRoot(fs) { if (fs) { return fs; } throw new ApiError(ErrorCode.EIO, "Initialize BrowserFS with a file system using BrowserFS.initialize(filesystem)"); } /** * @hidden */ function normalizeMode(mode, def) { switch (typeof mode) { case 'number': // (path, flag, mode, cb?) return mode; case 'string': // (path, flag, modeString, cb?) var trueMode = parseInt(mode, 8); if (!isNaN(trueMode)) { return trueMode; } // Invalid string. return def; default: return def; } } /** * @hidden */ function normalizeTime(time) { if (time instanceof Date) { return time; } if (typeof time === 'number') { return new Date(time * 1000); } throw new ApiError(ErrorCode.EINVAL, "Invalid time."); } /** * @hidden */ function normalizePath(p) { // Node doesn't allow null characters in paths. if (p.indexOf('\u0000') >= 0) { throw new ApiError(ErrorCode.EINVAL, 'Path must be a string without null bytes.'); } else if (p === '') { throw new ApiError(ErrorCode.EINVAL, 'Path must not be empty.'); } return path.resolve(p); } /** * @hidden */ function normalizeOptions(options, defEnc, defFlag, defMode) { // typeof null === 'object' so special-case handing is needed. switch (options === null ? 'null' : typeof options) { case 'object': return { encoding: typeof options.encoding !== 'undefined' ? options.encoding : defEnc, flag: typeof options.flag !== 'undefined' ? options.flag : defFlag, mode: normalizeMode(options.mode, defMode) }; case 'string': return { encoding: options, flag: defFlag, mode: defMode }; case 'null': case 'undefined': case 'function': return { encoding: defEnc, flag: defFlag, mode: defMode }; default: throw new TypeError(("\"options\" must be a string or an object, got " + (typeof options) + " instead.")); } } /** * The default callback is a NOP. * @hidden * @private */ function nopCb() { // NOP. } /** * The node frontend to all filesystems. * This layer handles: * * * Sanity checking inputs. * * Normalizing paths. * * Resetting stack depth for asynchronous operations which may not go through * the browser by wrapping all input callbacks using `setImmediate`. * * Performing the requested operation through the filesystem or the file * descriptor, as appropriate. * * Handling optional arguments and setting default arguments. * @see http://nodejs.org/api/fs.html */ var FS = function FS() { this.root = null; this.fdMap = {}; this.nextFd = 100; this.fileWatcher = new FileWatcher(); }; FS.prototype.initialize = function initialize (rootFS) { if (!rootFS.constructor.isAvailable()) { throw new ApiError(ErrorCode.EINVAL, 'Tried to instantiate BrowserFS with an unavailable file system.'); } return this.root = rootFS; }; /** * converts Date or number to a fractional UNIX timestamp * Grabbed from NodeJS sources (lib/fs.js) */ FS.prototype._toUnixTimestamp = function _toUnixTimestamp (time) { if (typeof time === 'number') { return time; } if (time instanceof Date) { return time.getTime() / 1000; } throw new Error("Cannot parse time: " + time); }; /** * **NONSTANDARD**: Grab the FileSystem instance that backs this API. * @return [BrowserFS.FileSystem | null] Returns null if the file system has * not been initialized. */ FS.prototype.getRootFS = function getRootFS () { if (this.root) { return this.root; } return null; }; // FILE OR DIRECTORY METHODS /** * Asynchronous rename. No arguments other than a possible exception are given * to the completion callback. * @param oldPath * @param newPath * @param callback */ FS.prototype.rename = function rename (oldPath, newPath, cb) { var this$1 = this; if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 1); try { setImmediate$1(function () { this$1.fileWatcher.triggerWatch(oldPath, 'rename'); this$1.stat(newPath, function (err, stat) { if (err) { return; } this$1.fileWatcher.triggerWatch(newPath, 'rename', stat); }); }); assertRoot(this.root).rename(normalizePath(oldPath), normalizePath(newPath), newCb); } catch (e) { newCb(e); } }; /** * Synchronous rename. * @param oldPath * @param newPath */ FS.prototype.renameSync = function renameSync (oldPath, newPath) { var this$1 = this; setImmediate$1(function () { this$1.fileWatcher.triggerWatch(oldPath, 'rename'); this$1.fileWatcher.triggerWatch(newPath, 'rename'); }); assertRoot(this.root).renameSync(normalizePath(oldPath), normalizePath(newPath)); }; /** * Test whether or not the given path exists by checking with the file system. * Then call the callback argument with either true or false. * @example Sample invocation * fs.exists('/etc/passwd', function (exists) { * util.debug(exists ? "it's there" : "no passwd!"); * }); * @param path * @param callback */ FS.prototype.exists = function exists (path, cb) { if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 1); try { return assertRoot(this.root).exists(normalizePath(path), newCb); } catch (e) { // Doesn't return an error. If something bad happens, we assume it just // doesn't exist. return newCb(false); } }; /** * Test whether or not the given path exists by checking with the file system. * @param path * @return [boolean] */ FS.prototype.existsSync = function existsSync (path) { try { return assertRoot(this.root).existsSync(normalizePath(path)); } catch (e) { // Doesn't return an error. If something bad happens, we assume it just // doesn't exist. return false; } }; /** * Asynchronous `stat`. * @param path * @param callback */ FS.prototype.stat = function stat (path, cb) { if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 2); try { return assertRoot(this.root).stat(normalizePath(path), false, newCb); } catch (e) { return newCb(e); } }; /** * Synchronous `stat`. * @param path * @return [BrowserFS.node.fs.Stats] */ FS.prototype.statSync = function statSync (path) { return assertRoot(this.root).statSync(normalizePath(path), false); }; /** * Asynchronous `lstat`. * `lstat()` is identical to `stat()`, except that if path is a symbolic link, * then the link itself is stat-ed, not the file that it refers to. * @param path * @param callback */ FS.prototype.lstat = function lstat (path, cb) { if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 2); try { return assertRoot(this.root).stat(normalizePath(path), true, newCb); } catch (e) { return newCb(e); } }; /** * Synchronous `lstat`. * `lstat()` is identical to `stat()`, except that if path is a symbolic link, * then the link itself is stat-ed, not the file that it refers to. * @param path * @return [BrowserFS.node.fs.Stats] */ FS.prototype.lstatSync = function lstatSync (path) { return assertRoot(this.root).statSync(normalizePath(path), true); }; FS.prototype.truncate = function truncate (path, arg2, cb) { var this$1 = this; if ( arg2 === void 0 ) arg2 = 0; if ( cb === void 0 ) cb = nopCb; var len = 0; if (typeof arg2 === 'function') { cb = arg2; } else if (typeof arg2 === 'number') { len = arg2; } var newCb = wrapCb(cb, 1); try { if (len < 0) { throw new ApiError(ErrorCode.EINVAL); } setImmediate$1(function () { this$1.stat(path, function (err, stat) { this$1.fileWatcher.triggerWatch(path, 'change', stat); }); }); return assertRoot(this.root).truncate(normalizePath(path), len, newCb); } catch (e) { return newCb(e); } }; /** * Synchronous `truncate`. * @param path * @param len */ FS.prototype.truncateSync = function truncateSync (path, len) { var this$1 = this; if ( len === void 0 ) len = 0; if (len < 0) { throw new ApiError(ErrorCode.EINVAL); } setImmediate$1(function () { this$1.stat(path, function (err, stat) { this$1.fileWatcher.triggerWatch(path, 'change', stat); }); }); return assertRoot(this.root).truncateSync(normalizePath(path), len); }; /** * Asynchronous `unlink`. * @param path * @param callback */ FS.prototype.unlink = function unlink (path, cb) { var this$1 = this; if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 1); try { setImmediate$1(function () { this$1.fileWatcher.triggerWatch(path, 'rename', new Stats(FileType.FILE, 0, undefined, 0, 0, 0, 0)); }); return assertRoot(this.root).unlink(normalizePath(path), newCb); } catch (e) { return newCb(e); } }; /** * Synchronous `unlink`. * @param path */ FS.prototype.unlinkSync = function unlinkSync (path) { var this$1 = this; setImmediate$1(function () { this$1.fileWatcher.triggerWatch(path, 'rename', new Stats(FileType.FILE, 0, undefined, 0, 0, 0, 0)); }); return assertRoot(this.root).unlinkSync(normalizePath(path)); }; FS.prototype.open = function open (path, flag, arg2, cb) { var this$1 = this; if ( cb === void 0 ) cb = nopCb; var mode = normalizeMode(arg2, 0x1a4); cb = typeof arg2 === 'function' ? arg2 : cb; var newCb = wrapCb(cb, 2); try { assertRoot(this.root).open(normalizePath(path), FileFlag.getFileFlag(flag), mode, function (e, file) { if (file) { newCb(e, this$1.getFdForFile(file)); } else { newCb(e); } }); } catch (e) { newCb(e); } }; /** * Synchronous file open. * @see http://www.manpagez.com/man/2/open/ * @param path * @param flags * @param mode defaults to `0644` * @return [BrowserFS.File] */ FS.prototype.openSync = function openSync (path, flag, mode) { if ( mode === void 0 ) mode = 0x1a4; return this.getFdForFile(assertRoot(this.root).openSync(normalizePath(path), FileFlag.getFileFlag(flag), normalizeMode(mode, 0x1a4))); }; FS.prototype.readFile = function readFile (filename, arg2, cb) { if ( arg2 === void 0 ) arg2 = {}; if ( cb === void 0 ) cb = nopCb; var options = normalizeOptions(arg2, null, 'r', null); cb = typeof arg2 === 'function' ? arg2 : cb; var newCb = wrapCb(cb, 2); try { var flag = FileFlag.getFileFlag(options.flag); if (!flag.isReadable()) { return newCb(new ApiError(ErrorCode.EINVAL, 'Flag passed to readFile must allow for reading.')); } return assertRoot(this.root).readFile(normalizePath(filename), options.encoding, flag, newCb); } catch (e) { return newCb(e); } }; FS.prototype.readFileSync = function readFileSync (filename, arg2) { if ( arg2 === void 0 ) arg2 = {}; var options = normalizeOptions(arg2, null, 'r', null); var flag = FileFlag.getFileFlag(options.flag); if (!flag.isReadable()) { throw new ApiError(ErrorCode.EINVAL, 'Flag passed to readFile must allow for reading.'); } return assertRoot(this.root).readFileSync(normalizePath(filename), options.encoding, flag); }; FS.prototype.writeFile = function writeFile (filename, data, arg3, cb) { var this$1 = this; if ( arg3 === void 0 ) arg3 = {}; if ( cb === void 0 ) cb = nopCb; var options = normalizeOptions(arg3, 'utf8', 'w', 0x1a4); cb = typeof arg3 === 'function' ? arg3 : cb; var newCb = wrapCb(cb, 1); try { var flag = FileFlag.getFileFlag(options.flag); if (!flag.isWriteable()) { return newCb(new ApiError(ErrorCode.EINVAL, 'Flag passed to writeFile must allow for writing.')); } assertRoot(this.root).writeFile(normalizePath(filename), data, options.encoding, flag, options.mode, function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; setImmediate$1(function () { this$1.stat(filename, function (_err, stat) { this$1.fileWatcher.triggerWatch(filename, 'change', stat); }); }); newCb.apply(void 0, args); }); } catch (e) { return newCb(e); } }; FS.prototype.writeFileSync = function writeFileSync (filename, data, arg3) { var this$1 = this; var options = normalizeOptions(arg3, 'utf8', 'w', 0x1a4); var flag = FileFlag.getFileFlag(options.flag); if (!flag.isWriteable()) { throw new ApiError(ErrorCode.EINVAL, 'Flag passed to writeFile must allow for writing.'); } setImmediate$1(function () { this$1.stat(filename, function (err, stat) { this$1.fileWatcher.triggerWatch(filename, 'change', stat); }); }); return assertRoot(this.root).writeFileSync(normalizePath(filename), data, options.encoding, flag, options.mode); }; FS.prototype.appendFile = function appendFile (filename, data, arg3, cb) { var this$1 = this; if ( cb === void 0 ) cb = nopCb; var options = normalizeOptions(arg3, 'utf8', 'a', 0x1a4); cb = typeof arg3 === 'function' ? arg3 : cb; var newCb = wrapCb(cb, 1); try { var flag = FileFlag.getFileFlag(options.flag); if (!flag.isAppendable()) { return newCb(new ApiError(ErrorCode.EINVAL, 'Flag passed to appendFile must allow for appending.')); } setImmediate$1(function () { this$1.stat(filename, function (err, stat) { this$1.fileWatcher.triggerWatch(filename, 'rename', stat); }); }); assertRoot(this.root).appendFile(normalizePath(filename), data, options.encoding, flag, options.mode, newCb); } catch (e) { newCb(e); } }; FS.prototype.appendFileSync = function appendFileSync (filename, data, arg3) { var this$1 = this; var options = normalizeOptions(arg3, 'utf8', 'a', 0x1a4); var flag = FileFlag.getFileFlag(options.flag); if (!flag.isAppendable()) { throw new ApiError(ErrorCode.EINVAL, 'Flag passed to appendFile must allow for appending.'); } setImmediate$1(function () { this$1.stat(filename, function (err, stat) { this$1.fileWatcher.triggerWatch(filename, 'change', stat); }); }); return assertRoot(this.root).appendFileSync(normalizePath(filename), data, options.encoding, flag, options.mode); }; // FILE DESCRIPTOR METHODS /** * Asynchronous `fstat`. * `fstat()` is identical to `stat()`, except that the file to be stat-ed is * specified by the file descriptor `fd`. * @param fd * @param callback */ FS.prototype.fstat = function fstat (fd, cb) { if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 2); try { var file = this.fd2file(fd); file.stat(newCb); } catch (e) { newCb(e); } }; /** * Synchronous `fstat`. * `fstat()` is identical to `stat()`, except that the file to be stat-ed is * specified by the file descriptor `fd`. * @param fd * @return [BrowserFS.node.fs.Stats] */ FS.prototype.fstatSync = function fstatSync (fd) { return this.fd2file(fd).statSync(); }; /** * Asynchronous close. * @param fd * @param callback */ FS.prototype.close = function close (fd, cb) { var this$1 = this; if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 1); try { this.fd2file(fd).close(function (e) { if (!e) { this$1.closeFd(fd); } newCb(e); }); } catch (e) { newCb(e); } }; /** * Synchronous close. * @param fd */ FS.prototype.closeSync = function closeSync (fd) { this.fd2file(fd).closeSync(); this.closeFd(fd); }; FS.prototype.ftruncate = function ftruncate (fd, arg2, cb) { if ( cb === void 0 ) cb = nopCb; var length = typeof arg2 === 'number' ? arg2 : 0; cb = typeof arg2 === 'function' ? arg2 : cb; var newCb = wrapCb(cb, 1); try { var file = this.fd2file(fd); if (length < 0) { throw new ApiError(ErrorCode.EINVAL); } file.truncate(length, newCb); } catch (e) { newCb(e); } }; /** * Synchronous ftruncate. * @param fd * @param len */ FS.prototype.ftruncateSync = function ftruncateSync (fd, len) { if ( len === void 0 ) len = 0; var file = this.fd2file(fd); if (len < 0) { throw new ApiError(ErrorCode.EINVAL); } file.truncateSync(len); }; /** * Asynchronous fsync. * @param fd * @param callback */ FS.prototype.fsync = function fsync (fd, cb) { if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 1); try { this.fd2file(fd).sync(newCb); } catch (e) { newCb(e); } }; /** * Synchronous fsync. * @param fd */ FS.prototype.fsyncSync = function fsyncSync (fd) { this.fd2file(fd).syncSync(); }; /** * Asynchronous fdatasync. * @param fd * @param callback */ FS.prototype.fdatasync = function fdatasync (fd, cb) { if ( cb === void 0 ) cb = nopCb; var newCb = wrapCb(cb, 1); try { this.fd2file(fd).datasync(newCb); } catch (e) { newCb(e); } }; /** * Synchronous fdatasync. * @param fd */ FS.prototype.fdatasyncSync = function fdatasyncSync (fd) { this.fd2file(fd).datasyncSync(); }; FS.prototype.write = function write (fd, arg2, arg3, arg4, arg5, cb) { if ( cb === void 0 ) cb = nopCb; var buffer; var offset; var length; var position = null; if (typeof arg2 === 'string') { // Signature 1: (fd, string, [position?, [encoding?]], cb?) var encoding = 'utf8'; switch (typeof arg3) { case 'function': // (fd, string, cb) cb = arg3; break; case 'number': // (fd, string, position, encoding?, cb?) position = arg3; encoding = typeof arg4 === 'string' ? arg4 : 'utf8'; cb = typeof arg5 === 'function' ? arg5 : cb; break; default: // ...try to find the callback and get out of here! cb = typeof arg4 === 'function' ? arg4 : typeof arg5 === 'function' ? arg5 : cb; return cb(new ApiError(ErrorCode.EINVAL, 'Invalid arguments.')); } buffer = Buffer.from(arg2, encoding); offset = 0; length = buffer.length; } else { // Signature 2: (fd, buffer, offset, length, position?, cb?) buffer = arg2; offset = arg3; length = arg4; position = typeof arg5 === 'number' ? arg5 : null; cb = typeof arg5 === 'function' ? arg5 : cb; } var newCb = wrapCb(cb, 3); try { var file = this.fd2file(fd); if (position === undefined || position === null) { position = file.getPos(); } file.write(buffer, offset, length, position, newCb); } catch (e) { newCb(e); } }; FS.prototype.writeSync = function writeSync (fd, arg2, arg3, arg4, arg5) { var buffer; var offset = 0; var length; var position; if (typeof arg2 === 'string') { // Signature 1: (fd, string, [position?, [encoding?]]) position = typeof arg3 === 'number' ? arg3 : null; var encoding = typeof arg4 === 'string' ? arg4 : 'utf8'; offset = 0; buffer = Buffer.from(arg2, encoding); length = buffer.length; } else { // Signature 2: (fd, buffer, offset, length, position?) buffer = arg2; offset = arg3; length = arg4; position = typeof arg5 === 'number' ? arg5 : null; } var file = this.fd2file(fd); if (position === undefined || position === null) { position = file.getPos(); } return file.writeSync(buffer, offset, length, position); }; FS.prototype.read = function read (fd, arg2, arg3, arg4, arg5, cb) { if ( cb === void 0 ) cb = nopCb; var position; var offset; var length; var buffer; var newCb; if (typeof arg2 === 'number') { // legacy interface // (fd, length, position, encoding, callback) length = arg2; position = arg3; var encoding = arg4; cb = typeof arg5 === 'function' ? arg5 : cb; offset = 0; buffer = Buffer.alloc(length); // XXX: Inefficient. // Wrap the cb so we shelter upper layers of the API from these // shenanigans. newCb = wrapCb(function (err, bytesRead, buf) { if (err) { return cb(err); } cb(err, buf.toString(encoding), bytesRead); }, 3); } else { buffer = arg2; offset = arg3; length = arg4; position = arg5; newCb = wrapCb(cb, 3); } try { var file = this.fd2file(fd); if (position === undefined || position === null) { position = file.getPos(); } file.read(buffer, offset, length, position, newCb); } catch (e) { newCb(e); } }; FS.prototype.readSync = function readSync (fd, arg2, arg3, arg4, arg5) { var shenanigans = false; var buffer; var offset; var length; var position; var encoding = 'utf8'; if (typeof arg2 === 'number') { length = arg2; position = arg3; encoding = arg4; offset = 0; buffer = Buffer.alloc(length); shenanigans = true; } else { buffer = arg2; offset = arg3; length = arg4; position = arg5; } var file = this.fd2file(fd); if (position === undefined || position === null) { position = file.getPos(); } var rv = file.readSync(buffer, offset, length, position); if (!shenanigans) { return rv; } return [buffer.toString(encoding), rv]; }; /** * Asynchronous `fchown`. * @param fd * @param uid * @param gid * @param callback */ FS.prototype.fchown = function fchown (fd, uid, gid, callback) { if ( callback === void 0 ) callback = nopCb; var newCb = wrapCb(callback, 1); try { this.fd2file(fd).chown(uid, gid, newCb); } catch (e) { newCb(e); } }; /** * Synchronous `fchown`. * @param fd * @param uid * @param gid */ FS.prototype.fchownSync = function fchownSync (fd, uid, gid) { this.fd2file(fd).chownSync(uid, gid); }; /** * Asynchronous `fchmod`. * @param fd * @param mode *