@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" />
363 lines • 13.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isDirInode = exports.isFileInode = exports.DirInode = exports.FileInode = exports.FileIndex = void 0;
var path = require("path");
var node_fs_stats_1 = require("../core/node_fs_stats");
/**
* A simple class for storing a filesystem index. Assumes that all paths passed
* to it are *absolute* paths.
*
* Can be used as a partial or a full index, although care must be taken if used
* for the former purpose, especially when directories are concerned.
*/
var FileIndex = /** @class */ (function () {
/**
* Constructs a new FileIndex.
*/
function FileIndex() {
// _index is a single-level key,value store that maps *directory* paths to
// DirInodes. File information is only contained in DirInodes themselves.
this._index = {};
// Create the root directory.
this.addPath('/', new DirInode());
}
/**
* Static method for constructing indices from a JSON listing.
* @param listing Directory listing generated by tools/XHRIndexer.coffee
* @return A new FileIndex object.
*/
FileIndex.fromListing = function (listing) {
var idx = new FileIndex();
// Add a root DirNode.
var rootInode = new DirInode();
idx._index['/'] = rootInode;
var queue = [['', listing, rootInode]];
while (queue.length > 0) {
var inode = void 0;
var next = queue.pop();
var pwd = next[0];
var tree = next[1];
var parent_1 = next[2];
for (var node in tree) {
if (tree.hasOwnProperty(node)) {
var children = tree[node];
var name_1 = "".concat(pwd, "/").concat(node);
if (children) {
idx._index[name_1] = inode = new DirInode();
queue.push([name_1, children, inode]);
}
else {
// This inode doesn't have correct size information, noted with -1.
inode = new FileInode(new node_fs_stats_1.default(node_fs_stats_1.FileType.FILE, -1, 0x16D));
}
if (parent_1) {
parent_1._ls[node] = inode;
}
}
}
}
return idx;
};
FileIndex.fromUnpkg = function (listing) {
var idx = new FileIndex();
function handleDir(dirPath, entry) {
var dirInode = new DirInode();
entry.files.forEach(function (child) {
var inode;
if (child.type === 'file') {
inode = new FileInode(new node_fs_stats_1.default(node_fs_stats_1.FileType.FILE, child.size));
// @ts-ignore
dirInode._ls[path.basename(child.path)] = inode;
}
else {
idx._index[child.path] = inode = handleDir(child.path, child);
}
});
return dirInode;
}
idx._index['/'] = handleDir('/', listing);
return idx;
};
FileIndex.fromJSDelivr = function (listing) {
var idx = new FileIndex();
listing.files.forEach(function (file) {
var inode = new FileInode(new node_fs_stats_1.default(node_fs_stats_1.FileType.FILE, file.size));
idx.addPathFast(file.name, inode);
});
return idx;
};
/**
* Runs the given function over all files in the index.
*/
FileIndex.prototype.fileIterator = function (cb) {
for (var path_1 in this._index) {
if (this._index.hasOwnProperty(path_1)) {
var dir = this._index[path_1];
var files = dir.getListing();
for (var _i = 0, files_1 = files; _i < files_1.length; _i++) {
var file = files_1[_i];
var item = dir.getItem(file);
if (isFileInode(item)) {
cb(item.getData(), path_1 + '/' + file);
}
}
}
}
};
/**
* Adds the given absolute path to the index if it is not already in the index.
* Creates any needed parent directories.
* @param path The path to add to the index.
* @param inode The inode for the
* path to add.
* @return 'True' if it was added or already exists, 'false' if there
* was an issue adding it (e.g. item in path is a file, item exists but is
* different).
* @todo If adding fails and implicitly creates directories, we do not clean up
* the new empty directories.
*/
FileIndex.prototype.addPath = function (path, inode) {
if (!inode) {
throw new Error('Inode must be specified');
}
if (path[0] !== '/') {
throw new Error('Path must be absolute, got: ' + path);
}
// Check if it already exists.
if (this._index.hasOwnProperty(path)) {
return this._index[path] === inode;
}
var splitPath = this._split_path(path);
var dirpath = splitPath[0];
var itemname = splitPath[1];
// Try to add to its parent directory first.
var parent = this._index[dirpath];
if (parent === undefined && path !== '/') {
// Create parent.
parent = new DirInode();
if (!this.addPath(dirpath, parent)) {
return false;
}
}
// Add myself to my parent.
if (path !== '/') {
if (!parent.addItem(itemname, inode)) {
return false;
}
}
// If I'm a directory, add myself to the index.
if (isDirInode(inode)) {
this._index[path] = inode;
}
return true;
};
/**
* Adds the given absolute path to the index if it is not already in the index.
* The path is added without special treatment (no joining of adjacent separators, etc).
* Creates any needed parent directories.
* @param path The path to add to the index.
* @param inode The inode for the
* path to add.
* @return 'True' if it was added or already exists, 'false' if there
* was an issue adding it (e.g. item in path is a file, item exists but is
* different).
* @todo If adding fails and implicitly creates directories, we do not clean up
* the new empty directories.
*/
FileIndex.prototype.addPathFast = function (path, inode) {
var itemNameMark = path.lastIndexOf('/');
var parentPath = itemNameMark === 0 ? "/" : path.substring(0, itemNameMark);
var itemName = path.substring(itemNameMark + 1);
// Try to add to its parent directory first.
var parent = this._index[parentPath];
if (parent === undefined) {
// Create parent.
parent = new DirInode();
this.addPathFast(parentPath, parent);
}
if (!parent.addItem(itemName, inode)) {
return false;
}
// If adding a directory, add to the index as well.
if (inode.isDir()) {
this._index[path] = inode;
}
return true;
};
/**
* Removes the given path. Can be a file or a directory.
* @return The removed item,
* or null if it did not exist.
*/
FileIndex.prototype.removePath = function (path) {
var splitPath = this._split_path(path);
var dirpath = splitPath[0];
var itemname = splitPath[1];
// Try to remove it from its parent directory first.
var parent = this._index[dirpath];
if (parent === undefined) {
return null;
}
// Remove myself from my parent.
var inode = parent.remItem(itemname);
if (inode === null) {
return null;
}
// If I'm a directory, remove myself from the index, and remove my children.
if (isDirInode(inode)) {
var children = inode.getListing();
for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
var child = children_1[_i];
this.removePath(path + '/' + child);
}
// Remove the directory from the index, unless it's the root.
if (path !== '/') {
delete this._index[path];
}
}
return inode;
};
/**
* Retrieves the directory listing of the given path.
* @return An array of files in the given path, or 'null' if it does not exist.
*/
FileIndex.prototype.ls = function (path) {
var item = this._index[path];
if (item === undefined) {
return null;
}
return item.getListing();
};
/**
* Returns the inode of the given item.
* @return Returns null if the item does not exist.
*/
FileIndex.prototype.getInode = function (path) {
var splitPath = this._split_path(path);
var dirpath = splitPath[0];
var itemname = splitPath[1];
// Retrieve from its parent directory.
var parent = this._index[dirpath];
if (parent === undefined) {
return null;
}
// Root case
if (dirpath === path) {
return parent;
}
return parent.getItem(itemname);
};
/**
* Split into a (directory path, item name) pair
*/
FileIndex.prototype._split_path = function (p) {
var dirpath = path.dirname(p);
var itemname = p.substr(dirpath.length + (dirpath === "/" ? 0 : 1));
return [dirpath, itemname];
};
return FileIndex;
}());
exports.FileIndex = FileIndex;
/**
* Inode for a file. Stores an arbitrary (filesystem-specific) data payload.
*/
var FileInode = /** @class */ (function () {
function FileInode(data) {
this.data = data;
}
FileInode.prototype.isFile = function () { return true; };
FileInode.prototype.isDir = function () { return false; };
FileInode.prototype.getData = function () { return this.data; };
FileInode.prototype.setData = function (data) { this.data = data; };
return FileInode;
}());
exports.FileInode = FileInode;
/**
* Inode for a directory. Currently only contains the directory listing.
*/
var DirInode = /** @class */ (function () {
/**
* Constructs an inode for a directory.
*/
function DirInode(data) {
if (data === void 0) { data = null; }
this.data = data;
this._ls = {};
}
DirInode.prototype.isFile = function () {
return false;
};
DirInode.prototype.isDir = function () {
return true;
};
DirInode.prototype.getData = function () { return this.data; };
/**
* Return a Stats object for this inode.
* @todo Should probably remove this at some point. This isn't the
* responsibility of the FileIndex.
*/
DirInode.prototype.getStats = function () {
return new node_fs_stats_1.default(node_fs_stats_1.FileType.DIRECTORY, 4096, 0x16D);
};
/**
* Returns the directory listing for this directory. Paths in the directory are
* relative to the directory's path.
* @return The directory listing for this directory.
*/
DirInode.prototype.getListing = function () {
return Object.keys(this._ls);
};
/**
* Returns the inode for the indicated item, or null if it does not exist.
* @param p Name of item in this directory.
*/
DirInode.prototype.getItem = function (p) {
var item = this._ls[p];
return item && this._ls.hasOwnProperty(p) ? item : null;
};
/**
* Add the given item to the directory listing. Note that the given inode is
* not copied, and will be mutated by the DirInode if it is a DirInode.
* @param p Item name to add to the directory listing.
* @param inode The inode for the
* item to add to the directory inode.
* @return True if it was added, false if it already existed.
*/
DirInode.prototype.addItem = function (p, inode) {
if (p in this._ls) {
return false;
}
this._ls[p] = inode;
return true;
};
/**
* Removes the given item from the directory listing.
* @param p Name of item to remove from the directory listing.
* @return Returns the item
* removed, or null if the item did not exist.
*/
DirInode.prototype.remItem = function (p) {
var item = this._ls[p];
if (item === undefined) {
return null;
}
delete this._ls[p];
return item;
};
return DirInode;
}());
exports.DirInode = DirInode;
/**
* @hidden
*/
function isFileInode(inode) {
return !!inode && inode.isFile();
}
exports.isFileInode = isFileInode;
/**
* @hidden
*/
function isDirInode(inode) {
return !!inode && inode.isDir();
}
exports.isDirInode = isDirInode;