From 7fdcf2055d449eea6fe1067db9767a96a0dd609d Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 1 Jun 2021 18:43:05 -0700 Subject: [PATCH 1/6] zip --- src/fileAttachment.js | 64 +++++++++++++++++++++++++++++++++++++------ src/zip.js | 3 ++ 2 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 src/zip.js diff --git a/src/fileAttachment.js b/src/fileAttachment.js index a1da5bb9..5e164daa 100644 --- a/src/fileAttachment.js +++ b/src/fileAttachment.js @@ -1,5 +1,6 @@ import {require as requireDefault} from "d3-require"; import sqlite, {SQLiteDatabaseClient} from "./sqlite.js"; +import jszip from "./zip.js"; async function remote_fetch(file) { const response = await fetch(await file.url()); @@ -14,15 +15,9 @@ async function dsv(file, delimiter, {array = false, typed = false} = {}) { : (array ? d3.csvParseRows : d3.csvParse))(text, typed && d3.autoType); } -class FileAttachment { - constructor(url, name) { - Object.defineProperties(this, { - _url: {value: url}, - name: {value: name, enumerable: true} - }); - } - async url() { - return (await this._url) + ""; +class AbstractFile { + constructor(name) { + Object.defineProperty(this, "name", {value: name, enumerable: true}); } async blob() { return (await remote_fetch(this)).blob(); @@ -62,6 +57,20 @@ class FileAttachment { const db = new SQL.Database(new Uint8Array(buffer)); return new SQLiteDatabaseClient(db); } + async zip() { + const [JSZip, buffer] = await Promise.all([jszip(requireDefault), this.arrayBuffer()]); + return new ZipArchive(await JSZip.loadAsync(buffer)); + } +} + +class FileAttachment extends AbstractFile { + constructor(url, name) { + super(name); + Object.defineProperty(this, "_url", {value: url}); + } + async url() { + return (await this._url) + ""; + } } export function NoFileAttachments(name) { @@ -78,3 +87,40 @@ export default function FileAttachments(resolve) { {prototype: FileAttachment.prototype} // instanceof ); } + +export class ZipArchive { + constructor(archive) { + Object.defineProperty(this, "_", {value: archive}); + } + files() { + return Object.keys(this._.files); + } + file(path) { + const object = this._.file(path); + if (!object || object.dir) throw new Error("file not found"); + return new ZipFile(object); + } +} + +class ZipFile extends AbstractFile { + constructor(object) { + super(object.name); + Object.defineProperty(this, "_", {value: object}); + Object.defineProperty(this, "_url", {writable: true}); + } + async url() { + return this._url || (this._url = this.blob().then(URL.createObjectURL)); + } + async blob() { + return this._.async("blob"); + } + async arrayBuffer() { + return this._.async("arraybuffer"); + } + async text() { + return this._.async("text"); + } + async json() { + return JSON.parse(await this.text()); + } +} diff --git a/src/zip.js b/src/zip.js new file mode 100644 index 00000000..8f8a77d9 --- /dev/null +++ b/src/zip.js @@ -0,0 +1,3 @@ +export default async function jszip(require) { + return await require("jszip@3.6.0/dist/jszip.min.js"); +} From 10f469812d13a4b9294b81babda13dab6550a274 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 1 Jun 2021 18:51:12 -0700 Subject: [PATCH 2/6] =?UTF-8?q?ZipFile=20=E2=86=A6=20ZipArchiveEntry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fileAttachment.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fileAttachment.js b/src/fileAttachment.js index 5e164daa..f3214937 100644 --- a/src/fileAttachment.js +++ b/src/fileAttachment.js @@ -98,11 +98,11 @@ export class ZipArchive { file(path) { const object = this._.file(path); if (!object || object.dir) throw new Error("file not found"); - return new ZipFile(object); + return new ZipArchiveEntry(object); } } -class ZipFile extends AbstractFile { +class ZipArchiveEntry extends AbstractFile { constructor(object) { super(object.name); Object.defineProperty(this, "_", {value: object}); From 1a7e47c998ce61d7f77c71616c387d6e816c741e Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 1 Jun 2021 19:26:41 -0700 Subject: [PATCH 3/6] =?UTF-8?q?files=20=E2=86=A6=20fileNames?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fileAttachment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fileAttachment.js b/src/fileAttachment.js index f3214937..1f5682fb 100644 --- a/src/fileAttachment.js +++ b/src/fileAttachment.js @@ -92,7 +92,7 @@ export class ZipArchive { constructor(archive) { Object.defineProperty(this, "_", {value: archive}); } - files() { + fileNames() { return Object.keys(this._.files); } file(path) { From c3d0c4a0da9ad83f94eff50fdd8745dabbecdf30 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 2 Jun 2021 07:26:45 -0700 Subject: [PATCH 4/6] archive.filenames --- src/fileAttachment.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/fileAttachment.js b/src/fileAttachment.js index 1f5682fb..ce7ca271 100644 --- a/src/fileAttachment.js +++ b/src/fileAttachment.js @@ -91,9 +91,7 @@ export default function FileAttachments(resolve) { export class ZipArchive { constructor(archive) { Object.defineProperty(this, "_", {value: archive}); - } - fileNames() { - return Object.keys(this._.files); + this.filenames = Object.keys(this._.files); } file(path) { const object = this._.file(path); From c20010a7ff8b553085c8908a0c2c7ea76409dd16 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 2 Jun 2021 07:27:02 -0700 Subject: [PATCH 5/6] better not found error --- src/fileAttachment.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fileAttachment.js b/src/fileAttachment.js index ce7ca271..52bff1f2 100644 --- a/src/fileAttachment.js +++ b/src/fileAttachment.js @@ -94,8 +94,8 @@ export class ZipArchive { this.filenames = Object.keys(this._.files); } file(path) { - const object = this._.file(path); - if (!object || object.dir) throw new Error("file not found"); + const object = this._.file(path += ""); + if (!object || object.dir) throw new Error(`file not found: ${path}`); return new ZipArchiveEntry(object); } } From faa710d0b7b75a0b41516861acc11c3d6f055901 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 2 Jun 2021 12:11:32 -0700 Subject: [PATCH 6/6] only include files in filenames --- src/fileAttachment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fileAttachment.js b/src/fileAttachment.js index 52bff1f2..56b5e242 100644 --- a/src/fileAttachment.js +++ b/src/fileAttachment.js @@ -91,7 +91,7 @@ export default function FileAttachments(resolve) { export class ZipArchive { constructor(archive) { Object.defineProperty(this, "_", {value: archive}); - this.filenames = Object.keys(this._.files); + this.filenames = Object.keys(archive.files).filter(name => !archive.files[name].dir); } file(path) { const object = this._.file(path += "");