browserfs
Version:
A filesystem in your browser!
117 lines (107 loc) • 3.81 kB
text/typescript
import {BaseFileSystem, FileSystem} from '../core/file_system';
import path = require('path');
import {ApiError} from '../core/api_error';
/**
* The FolderAdapter file system wraps a file system, and scopes all interactions to a subfolder of that file system.
*/
export default class FolderAdapter extends BaseFileSystem implements FileSystem {
private _wrapped: FileSystem;
private _folder: string;
constructor(folder: string, wrapped: FileSystem) {
super();
this._folder = folder;
this._wrapped = wrapped;
}
/**
* Initialize the file system. Ensures that the wrapped file system
* has the given folder.
*/
public initialize(cb: (e?: ApiError) => void) {
this._wrapped.exists(this._folder, (exists: boolean) => {
if (exists) {
cb();
} else if (this._wrapped.isReadOnly()) {
cb(ApiError.ENOENT(this._folder));
} else {
this._wrapped.mkdir(this._folder, 0x1ff, cb);
}
});
}
public getName(): string { return this._wrapped.getName(); }
public isReadOnly(): boolean { return this._wrapped.isReadOnly(); }
public supportsProps(): boolean { return this._wrapped.supportsProps(); }
public supportsSynch(): boolean { return this._wrapped.supportsSynch(); }
public supportsLinks(): boolean { return false; }
public static isAvailable(): boolean {
return true;
}
}
function translateError(folder: string, e: any): any {
if (e !== null && typeof e === 'object') {
let err = <ApiError> e;
let p = err.path;
if (p) {
p = '/' + path.relative(folder, p);
err.message = err.message.replace(err.path, p);
err.path = p;
}
}
return e;
}
function wrapCallback(folder: string, cb: any): any {
if (typeof cb === 'function') {
return function(err) {
if (arguments.length > 0) {
arguments[0] = translateError(folder, err);
}
(<Function> cb).apply(null, arguments);
};
} else {
return cb;
}
}
function wrapFunction(name: string, wrapFirst: boolean, wrapSecond: boolean): Function {
if (name.slice(name.length - 4) !== 'Sync') {
// Async function. Translate error in callback.
return function() {
if (arguments.length > 0) {
if (wrapFirst) {
arguments[0] = path.join(this._folder, arguments[0]);
}
if (wrapSecond) {
arguments[1] = path.join(this._folder, arguments[1]);
}
arguments[arguments.length - 1] = wrapCallback(this._folder, arguments[arguments.length - 1]);
}
return this._wrapped[name].apply(this._wrapped, arguments);
};
} else {
// Sync function. Translate error in catch.
return function() {
try {
if (wrapFirst) {
arguments[0] = path.join(this._folder, arguments[0]);
}
if (wrapSecond) {
arguments[1] = path.join(this._folder, arguments[1]);
}
return this._wrapped[name].apply(this._wrapped, arguments);
} catch (e) {
throw translateError(this._folder, e);
}
};
}
}
// First argument is a path.
['diskSpace', 'stat', 'statSync', 'open', 'openSync', 'unlink', 'unlinkSync',
'rmdir', 'rmdirSync' ,'mkdir', 'mkdirSync', 'readdir', 'readdirSync', 'exists',
'existsSync', 'realpath', 'realpathSync', 'truncate', 'truncateSync', 'readFile',
'readFileSync', 'writeFile', 'writeFileSync', 'appendFile', 'appendFileSync',
'chmod', 'chmodSync', 'chown', 'chownSync', 'utimes', 'utimesSync', 'readlink',
'readlinkSync'].forEach((name: string) => {
FolderAdapter.prototype[name] = wrapFunction(name, true, false);
});
// First and second arguments are paths.
['rename', 'renameSync', 'link', 'linkSync', 'symlink', 'symlinkSync'].forEach((name: string) => {
FolderAdapter.prototype[name] = wrapFunction(name, true, true);
});