Skip to content

Commit a57ee14

Browse files
committed
vfs: add archive provider backed by ZipBuffer/ZipFile
Adds an ArchiveProvider to node:vfs that mounts a ZipBuffer or ZipFile as a virtual file system, built on the writable ZipBuffer/ZipFile support added to node:zlib. provider.readonly reflects the underlying archive's own writability. Directories are recognized both explicitly (an entry name ending in /) and implicitly (any entry name starting with "<dir>/"). Since a ZIP member can't be edited or read in place, writes are buffered in memory and only committed - as a new archive entry - when the file handle is closed. Every method has a synchronous counterpart backed by ZipBuffer/ZipFile's own synchronous surface, documented as event-loop-blocking like the rest of node:vfs's providers.
1 parent c7a3f01 commit a57ee14

4 files changed

Lines changed: 824 additions & 0 deletions

File tree

doc/api/vfs.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,55 @@ added: v26.4.0
304304

305305
The resolved absolute path used as the root.
306306

307+
## Class: `ArchiveProvider`
308+
309+
<!-- YAML
310+
added: REPLACEME
311+
-->
312+
313+
A provider that exposes the entries of a ZIP archive - either a
314+
[`zlib.ZipBuffer`][] (in memory) or a [`zlib.ZipFile`][] (on disk) - through
315+
the VFS API. `provider.readonly` reflects the archive's own
316+
[`zipFile.writable`][] flag: a `ZipBuffer` is always writable, and a
317+
`ZipFile` is writable only when opened with `{ writable: true }`.
318+
319+
Directories are recognized both explicitly (an entry whose name ends in `/`)
320+
and implicitly (any entry name starting with `"<dir>/"`). `readdir()` does
321+
not support `{ recursive: true }`. Because a ZIP member cannot be edited or
322+
read in place - only fully written or fully decompressed - a file opened for
323+
writing only commits its content (as a new archive entry) when the handle is
324+
closed.
325+
326+
Every method has a synchronous counterpart (`openSync()`, `statSync()`,
327+
`readdirSync()`, and so on), backed by the equally complete synchronous
328+
surface [`zlib.ZipBuffer`][]/[`zlib.ZipFile`][] expose. As with those, the
329+
synchronous methods here block the Node.js event loop and further JavaScript
330+
execution until the operation - including any deflate/inflate pass -
331+
completes.
332+
333+
```cjs
334+
const vfs = require('node:vfs');
335+
const zlib = require('node:zlib');
336+
const { readFileSync } = require('node:fs');
337+
338+
async function main() {
339+
const zip = new zlib.ZipBuffer(readFileSync('archive.zip'));
340+
const archiveVfs = vfs.create(new vfs.ArchiveProvider(zip));
341+
342+
console.log(await archiveVfs.promises.readdir('/'));
343+
await archiveVfs.promises.writeFile('/new.txt', 'hello');
344+
}
345+
main();
346+
```
347+
348+
### `new ArchiveProvider(source)`
349+
350+
<!-- YAML
351+
added: REPLACEME
352+
-->
353+
354+
* `source` {zlib.ZipBuffer|zlib.ZipFile} An already-open archive.
355+
307356
## Implementation details
308357

309358
### `Stats` objects
@@ -318,10 +367,14 @@ fields use synthetic but stable values:
318367
* `blocks` is `Math.ceil(size / 512)`.
319368
* Times default to the moment the entry was created/last modified.
320369

370+
[`ArchiveProvider`]: #class-archiveprovider
321371
[`MemoryProvider`]: #class-memoryprovider
322372
[`RealFSProvider`]: #class-realfsprovider
323373
[`VirtualFileSystem`]: #class-virtualfilesystem
324374
[`VirtualProvider`]: #class-virtualprovider
325375
[`fs.BigIntStats`]: fs.md#class-fsbigintstats
326376
[`fs.Stats`]: fs.md#class-fsstats
327377
[`node:fs`]: fs.md
378+
[`zipFile.writable`]: zlib.md#zipfilewritable
379+
[`zlib.ZipBuffer`]: zlib.md#class-zlibzipbuffer
380+
[`zlib.ZipFile`]: zlib.md#class-zlibzipfile

0 commit comments

Comments
 (0)