diff --git a/demo/localstorage.ts b/demo/localstorage.ts new file mode 100644 index 000000000..2b103343a --- /dev/null +++ b/demo/localstorage.ts @@ -0,0 +1,14 @@ +import {createVolume} from '../src/volume-localstorage'; + +const obj = {}; +const Volume = createVolume('default', obj); + +const vol = new Volume; +vol.fromJSON({'/foo': 'bar', '/foo2': 'bar2'}); +// vol.unlinkSync('/foo'); + +console.log(obj); +console.log(vol.toJSON()); + + + diff --git a/src/node.ts b/src/node.ts index 9cd93510f..e372666d8 100644 --- a/src/node.ts +++ b/src/node.ts @@ -337,6 +337,17 @@ export class Link extends EventEmitter { if(!link) return null; return link.walk(steps, stop, i + 1); } + + toJSON() { + for(let ch in this.children) { + console.log('ch', ch); + } + return { + steps: this.steps, + ino: this.ino, + children: Object.keys(this.children), + }; + } } diff --git a/src/volume-localstorage.ts b/src/volume-localstorage.ts index 62e45d80f..d7772d043 100644 --- a/src/volume-localstorage.ts +++ b/src/volume-localstorage.ts @@ -1,53 +1,52 @@ import {Volume} from "./volume"; import {Link, Node} from "./node"; -const LS = {}; -export class NodeLocalstorage extends Node { - key() { - return `memfs.ino.${this.ino}`; - } +export interface IStore { + setItem(key: string, json); + getItem(key: string); + removeItem(key: string); +} - sync() { - LS[this.key()] = this.toJSON(); - } - touch() { - super.touch(); - this.sync(); - } +export class ObjectStore { - del() { - delete LS[this.key()]; - } -} - -export class LinkLocalstorage extends Link { + obj: object; -} + constructor(obj) { + this.obj = obj; + } -export class VolumeLocalstorage extends Volume { - constructor() { - super({ - Node: NodeLocalstorage, - Link: LinkLocalstorage, - }); + setItem(key: string, json) { + this.obj[key] = JSON.stringify(json); + } + getItem(key: string) { + const data = this.obj[key]; + if(typeof data === void 0) return void 0; + return JSON.parse(data); + } + removeItem(key: string) { + delete this.obj[key]; } } -export function createVolume(namespace: string, LS = localStorage) { +export function createVolume(namespace: string, LS: Storage | object = localStorage): new (...args) => Volume { + const store = new ObjectStore(LS); const key = (type, id) => `memfs.${namespace}.${type}.${id}`; - export class NodeLocalstorage extends Node { - key() { - return key('ino', this.ino); + class NodeLocalStorage extends Node { + private _key: string; + + get Key(): string { + if(!this._key) this._key = key('ino', this.ino); + return this._key; } sync() { - LS[this.key()] = this.toJSON(); + store.setItem(this.Key, this.toJSON()); } touch() { @@ -57,22 +56,40 @@ export function createVolume(namespace: string, LS = localStorage) { del() { super.del(); - delete LS[this.key()]; + store.removeItem(this.Key); } } - export class LinkLocalstorage extends Link { + class LinkLocalStorage extends Link { + private _key: string; + + get Key(): string { + if(!this._key) this._key = key('link', this.getPath()); + return this._key; + } + sync() { + store.setItem(this,Key, this.toJSON()): + } } - export class VolumeLocalstorage extends Volume { + return class VolumeLocalStorage extends Volume { constructor() { super({ - Node: NodeLocalstorage, - Link: LinkLocalstorage, + Node: NodeLocalStorage, + Link: LinkLocalStorage, }); + } + createLink(parent?, name?, isDirectory?, perm?) { + const link = super.createLink(parent, name, isDirectory, perm); + store.setItem(key('link', link.getPath()), link.toJSON()); + return link; + } + deleteLink(link) { + store.removeItem(key('link', link.getPath())); + return super.deleteLink(link); } } } \ No newline at end of file diff --git a/src/volume.test.ts b/src/volume.test.ts index e19a447dc..f60ec3ce8 100644 --- a/src/volume.test.ts +++ b/src/volume.test.ts @@ -57,6 +57,17 @@ describe('volume', () => { expect(link1).to.equal(node2); }); }); + describe('i-nodes', () => { + it('i-node numbers are unique', () => { + const vol = Volume.fromJSON({ + '/1': 'foo', + '/2': 'bar', + }); + const stat1 = vol.statSync('/1'); + const stat2 = vol.statSync('/2'); + expect(stat1.ino === stat2.ino).to.be.false; + }); + }); describe('.toJSON()', () => { it('Single file', () => { const vol = new Volume; diff --git a/src/volume.ts b/src/volume.ts index ebd509c91..99959a967 100644 --- a/src/volume.ts +++ b/src/volume.ts @@ -507,9 +507,6 @@ export class Volume { return vol; } - // I-node number counter. - static ino: number = 0; - /** * Global file descriptor counter. UNIX file descriptors start from 0 and go sequentially * up, so here, in order not to conflict with them, we choose some big number and descrease @@ -526,6 +523,10 @@ export class Volume { // root: Node = new (this.NodeClass)(null, '', true); root: Link; + + // I-node number counter. + ino: number = 0; + // A mapping for i-node numbers to i-nodes (`Node`); inodes: {[ino: number]: Node} = {}; @@ -557,9 +558,9 @@ export class Volume { }; constructor(props = {}) { - this.props = extend(props, {Node, Link, File}); + this.props = extend({Node, Link, File}, props); - const root = new this.props.Link(this, null, ''); + const root = this.createLink(); root.setNode(this.createNode(true)); const self = this; @@ -599,8 +600,12 @@ export class Volume { this.root = root; } - createLink(parent: Link, name: string, isDirectory: boolean = false, perm?: number): Link { - return parent.createChild(name, this.createNode(isDirectory, perm)); + createLink(): Link; + createLink(parent: Link, name: string, isDirectory?: boolean, perm?: number): Link; + createLink(parent?: Link, name?: string, isDirectory: boolean = false, perm?: number): Link { + return parent + ? parent.createChild(name, this.createNode(isDirectory, perm)) + : new this.props.Link(this, null, ''); } deleteLink(link: Link): boolean { @@ -617,8 +622,8 @@ export class Volume { private newInoNumber(): number { if(this.releasedInos.length) return this.releasedInos.pop(); else { - Volume.ino = (Volume.ino++) % 0xFFFFFFFF; - return Volume.ino; + this.ino = (this.ino + 1) % 0xFFFFFFFF; + return this.ino; } } @@ -825,13 +830,14 @@ export class Volume { } reset() { + this.ino = 0; this.inodes = {}; this.releasedInos = []; this.fds = {}; this.releasedFds = []; this.openFiles = 0; - this.root = new this.props.Link(this, null, ''); + this.root = this.createLink(); this.root.setNode(this.createNode(true)); }