From a492147c8c945d24a17193a4b0a477bdf2752935 Mon Sep 17 00:00:00 2001 From: "James P." Date: Thu, 23 May 2024 09:28:48 -0500 Subject: [PATCH] Fixed #7 (subdirectories) Added nested directory tests --- src/ZipFS.ts | 48 ++++++++++++++++++++++------------------------ tests/data.zip | Bin 310 -> 630 bytes tests/zip.test.ts | 16 ++++++++++++---- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/ZipFS.ts b/src/ZipFS.ts index 3247b31..bbfff17 100644 --- a/src/ZipFS.ts +++ b/src/ZipFS.ts @@ -55,7 +55,8 @@ export interface ZipOptions { * This isn't that bad, so we might do this at a later date. */ export class ZipFS extends Readonly(Sync(FileSystem)) { - protected entries: Map = new Map(); + protected files: Map = new Map(); + protected directories: Map = new Map(); protected _time = Date.now(); @@ -121,9 +122,20 @@ export class ZipFS extends Readonly(Sync(FileSystem)) { } // Strip the trailing '/' if it exists const name = cd.name.endsWith('/') ? cd.name.slice(0, -1) : cd.name; - this.entries.set('/' + name, cd); + this.files.set('/' + name, cd); ptr += cd.size; } + + // Parse directories + for (const entry of this.files.keys()) { + const { dir, base } = parse(entry); + + if (!this.directories.has(dir)) { + this.directories.set(dir, []); + } + + this.directories.get(dir).push(base); + } } public metadata(): FileSystemMetadata { @@ -136,15 +148,15 @@ export class ZipFS extends Readonly(Sync(FileSystem)) { } public get numberOfCentralDirectoryEntries(): number { - return this.entries.size; + return this.files.size; } public statSync(path: string): Stats { - // The EOCD/Header does not track '/', so it does not exist in `entries` - if (path == '/') { + // The EOCD/Header does not track directories, so it does not exist in `entries` + if (this.directories.has(path)) { return new Stats({ mode: 0o555 | FileType.DIRECTORY, - size: [...this.entries.values()].reduce((size, entry) => size + entry.uncompressedSize, 0), + size: 4096, mtimeMs: this._time, ctimeMs: this._time, atimeMs: Date.now(), @@ -152,13 +164,11 @@ export class ZipFS extends Readonly(Sync(FileSystem)) { }); } - const entry = this.entries.get(path); - - if (!entry) { - throw ErrnoError.With('ENOENT', path, 'stat'); + if (this.files.has(path)) { + return this.files.get(path).stats; } - return entry.stats; + throw ErrnoError.With('ENOENT', path, 'stat'); } public openFileSync(path: string, flag: string, cred: Cred): NoSyncFile { @@ -173,19 +183,7 @@ export class ZipFS extends Readonly(Sync(FileSystem)) { throw ErrnoError.With('EACCES', path, 'openFile'); } - return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.entries.get(path).data); - } - - protected dirEntries(path: string): string[] { - const entries = []; - - for (const entry of this.entries.keys()) { - const { dir, base } = parse(entry); - if (path == dir) { - entries.push(base); - } - } - return entries; + return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.files.get(path).data); } public readdirSync(path: string): string[] { @@ -195,7 +193,7 @@ export class ZipFS extends Readonly(Sync(FileSystem)) { throw ErrnoError.With('ENOTDIR', path, 'readdir'); } - return this.dirEntries(path); + return this.directories.get(path); } } diff --git a/tests/data.zip b/tests/data.zip index 76f8ddd1197b75ae1b89751a55cb35565d716590..585e5a422eab7ae04344bb91fdedeb822abd0b2a 100644 GIT binary patch literal 630 zcmWIWW@h1H00B+!?Ga!GlwfC&VaQ7@E=f(%4-MgDU_KY4w^GysJ85C-Jurt6hdlz`1S05szora2)QnZ*h~l&Ao)R3R-hCsh$) zx@zvWi0$w1G&2HC24Rr%^7B&R#=6<~VKdecVS*9RgtV?xfvC4?&<|pb*3`jFk}{wHO&De`NGwbee3( Tq{YX?z{D_%fq|h7h#43Fn`00_ diff --git a/tests/zip.test.ts b/tests/zip.test.ts index 1736abc..c1e3531 100644 --- a/tests/zip.test.ts +++ b/tests/zip.test.ts @@ -5,7 +5,7 @@ import { Zip } from '../dist/ZipFS.js'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; -describe('Basic zip file', () => { +describe('Basic ZIP operations', () => { test('Configure', async () => { const buffer = readFileSync(dirname(fileURLToPath(import.meta.url)) + '/data.zip'); const data = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); @@ -17,14 +17,22 @@ describe('Basic zip file', () => { }); test('readdir /', () => { - expect(fs.readdirSync('/').length).toBe(2); + expect(fs.readdirSync('/').length).toBe(3); }); - test('read #1', () => { + test('read /one.txt', () => { expect(fs.readFileSync('/one.txt', 'utf8')).toBe('1'); }); - test('read #2', () => { + test('read /two.txt', () => { expect(fs.readFileSync('/two.txt', 'utf8')).toBe('two'); }); + + test('readdir /nested', () => { + expect(fs.readdirSync('/nested').length).toBe(1); + }); + + test('readdir /nested/omg.txt', () => { + expect(fs.readFileSync('/nested/omg.txt', 'utf8')).toBe('This is a nested file!'); + }); });