@storyteller-platform/epub 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +263 -193
- package/dist/index.cjs +85 -128
- package/dist/index.d.cts +41 -31
- package/dist/index.d.ts +41 -31
- package/dist/index.js +85 -134
- package/dist/node.cjs +11 -12
- package/dist/node.d.cts +6 -10
- package/dist/node.d.ts +6 -10
- package/dist/node.js +14 -13
- package/package.json +29 -29
package/dist/index.js
CHANGED
|
@@ -1,45 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
Uint8ArrayReader,
|
|
4
|
-
Uint8ArrayWriter,
|
|
5
|
-
ZipReader,
|
|
6
|
-
ZipWriter
|
|
7
|
-
} from "@zip.js/zip.js";
|
|
1
|
+
import { PortablePath, ppath } from "@yarnpkg/fslib";
|
|
2
|
+
import { DEFAULT_COMPRESSION_LEVEL, ZipFS } from "@yarnpkg/libzip";
|
|
8
3
|
import { Mutex } from "async-mutex";
|
|
9
4
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
10
5
|
import memoize from "mem";
|
|
11
6
|
import { lookup } from "mime-types";
|
|
12
7
|
import { nanoid } from "nanoid";
|
|
13
|
-
import { dirname, resolve } from "@storyteller-platform/path";
|
|
14
|
-
class EpubEntry {
|
|
15
|
-
filename;
|
|
16
|
-
entry = null;
|
|
17
|
-
data = null;
|
|
18
|
-
async getData() {
|
|
19
|
-
if (this.data) return this.data;
|
|
20
|
-
const writer = new Uint8ArrayWriter();
|
|
21
|
-
const data = await this.entry.getData(writer);
|
|
22
|
-
this.data = data;
|
|
23
|
-
return this.data;
|
|
24
|
-
}
|
|
25
|
-
setData(data) {
|
|
26
|
-
this.data = data;
|
|
27
|
-
}
|
|
28
|
-
constructor(entry) {
|
|
29
|
-
this.filename = entry.filename;
|
|
30
|
-
if ("data" in entry) {
|
|
31
|
-
this.data = entry.data;
|
|
32
|
-
} else {
|
|
33
|
-
this.entry = entry;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
8
|
class Epub {
|
|
38
|
-
constructor(
|
|
39
|
-
this.
|
|
40
|
-
this.onClose = onClose;
|
|
41
|
-
this.dataWriter = new Uint8ArrayWriter();
|
|
42
|
-
this.zipWriter = new ZipWriter(this.dataWriter);
|
|
9
|
+
constructor(zipFs) {
|
|
10
|
+
this.zipFs = zipFs;
|
|
43
11
|
this.readXhtmlItemContents = memoize(
|
|
44
12
|
this.readXhtmlItemContents.bind(this),
|
|
45
13
|
// This isn't unnecessary, the generic here just isn't handling the
|
|
@@ -194,21 +162,10 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
194
162
|
static isXmlTextNode(node) {
|
|
195
163
|
return "#text" in node;
|
|
196
164
|
}
|
|
197
|
-
zipWriter;
|
|
198
|
-
dataWriter;
|
|
199
165
|
rootfile = null;
|
|
200
166
|
manifest = null;
|
|
201
167
|
spine = null;
|
|
202
168
|
packageMutex = new Mutex();
|
|
203
|
-
/**
|
|
204
|
-
* Close the Epub. Must be called before the Epub goes out
|
|
205
|
-
* of scope/is garbage collected.
|
|
206
|
-
*/
|
|
207
|
-
async close() {
|
|
208
|
-
var _a;
|
|
209
|
-
await ((_a = this.onClose) == null ? void 0 : _a.call(this));
|
|
210
|
-
await this.zipWriter.close();
|
|
211
|
-
}
|
|
212
169
|
/**
|
|
213
170
|
* Construct an Epub instance, optionally beginning
|
|
214
171
|
* with the provided metadata.
|
|
@@ -226,7 +183,7 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
226
183
|
creators,
|
|
227
184
|
contributors
|
|
228
185
|
}, additionalMetadata = []) {
|
|
229
|
-
const
|
|
186
|
+
const zipFs = new ZipFS();
|
|
230
187
|
const encoder = new TextEncoder();
|
|
231
188
|
const container = encoder.encode(`<?xml version="1.0"?>
|
|
232
189
|
<container>
|
|
@@ -235,8 +192,10 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
235
192
|
</rootfiles>
|
|
236
193
|
</container>
|
|
237
194
|
`);
|
|
238
|
-
|
|
239
|
-
|
|
195
|
+
await zipFs.mkdirpPromise(ppath.join(PortablePath.root, "META-INF"));
|
|
196
|
+
await zipFs.writeFilePromise(
|
|
197
|
+
ppath.join(PortablePath.root, "META-INF", "container.xml"),
|
|
198
|
+
container
|
|
240
199
|
);
|
|
241
200
|
const packageDocument = encoder.encode(`<?xml version="1.0"?>
|
|
242
201
|
<package unique-identifier="pub-id" dir="${language.textInfo.direction}" xml:lang=${language.toString()} version="3.0">
|
|
@@ -248,10 +207,12 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
248
207
|
</spine>
|
|
249
208
|
</package>
|
|
250
209
|
`);
|
|
251
|
-
|
|
252
|
-
|
|
210
|
+
await zipFs.mkdirpPromise(ppath.join(PortablePath.root, "OEBPS"));
|
|
211
|
+
await zipFs.writeFilePromise(
|
|
212
|
+
ppath.join(PortablePath.root, "OEBPS", "content.opf"),
|
|
213
|
+
packageDocument
|
|
253
214
|
);
|
|
254
|
-
const epub = new this(
|
|
215
|
+
const epub = new this(zipFs);
|
|
255
216
|
const metadata = [
|
|
256
217
|
{
|
|
257
218
|
id: "pub-id",
|
|
@@ -287,42 +248,30 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
287
248
|
* path to an EPUB file on disk, or a Uint8Array representing
|
|
288
249
|
* the data of the EPUB publication.
|
|
289
250
|
*/
|
|
251
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
290
252
|
static async from(pathOrData) {
|
|
291
253
|
if (typeof pathOrData === "string") {
|
|
292
254
|
throw new Error("Import from /node to construct from a file");
|
|
293
255
|
}
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const epubEntries = zipEntries.map((entry) => new EpubEntry(entry));
|
|
299
|
-
const epub = new this(epubEntries, () => zipReader.close());
|
|
256
|
+
const epub = new this(
|
|
257
|
+
// TODO: Is this cast chill?
|
|
258
|
+
new ZipFS(pathOrData)
|
|
259
|
+
);
|
|
300
260
|
return epub;
|
|
301
261
|
}
|
|
302
|
-
getEntry(path) {
|
|
303
|
-
return this.entries.find((entry) => entry.filename === path);
|
|
304
|
-
}
|
|
305
262
|
async removeEntry(href) {
|
|
306
263
|
const rootfile = await this.getRootfile();
|
|
307
264
|
const filename = this.resolveHref(rootfile, href);
|
|
308
|
-
|
|
309
|
-
if (index === -1) return;
|
|
310
|
-
this.entries.splice(index, 1);
|
|
265
|
+
await this.zipFs.removePromise(filename);
|
|
311
266
|
}
|
|
312
267
|
async getFileData(path, encoding) {
|
|
313
|
-
|
|
314
|
-
if (!containerEntry)
|
|
315
|
-
throw new Error(
|
|
316
|
-
`Could not get file data for entry ${path}: entry not found`
|
|
317
|
-
);
|
|
318
|
-
const containerContents = await containerEntry.getData();
|
|
319
|
-
return encoding === "utf-8" ? new TextDecoder("utf-8").decode(containerContents) : containerContents;
|
|
268
|
+
return await this.zipFs.readFilePromise(path, encoding);
|
|
320
269
|
}
|
|
321
270
|
async getRootfile() {
|
|
322
271
|
var _a;
|
|
323
272
|
if (this.rootfile !== null) return this.rootfile;
|
|
324
273
|
const containerString = await this.getFileData(
|
|
325
|
-
"META-INF
|
|
274
|
+
ppath.join(PortablePath.root, "META-INF", "container.xml"),
|
|
326
275
|
"utf-8"
|
|
327
276
|
);
|
|
328
277
|
if (!containerString)
|
|
@@ -349,11 +298,12 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
349
298
|
return !Epub.isXmlTextNode(node) && ((_a2 = node[":@"]) == null ? void 0 : _a2["@_media-type"]) === "application/oebps-package+xml";
|
|
350
299
|
}
|
|
351
300
|
);
|
|
352
|
-
|
|
301
|
+
const fullPath = (_a = rootfile == null ? void 0 : rootfile[":@"]) == null ? void 0 : _a["@_full-path"];
|
|
302
|
+
if (!fullPath)
|
|
353
303
|
throw new Error(
|
|
354
304
|
"Failed to parse EPUB container.xml: Found no rootfile element"
|
|
355
305
|
);
|
|
356
|
-
this.rootfile =
|
|
306
|
+
this.rootfile = ppath.resolve(PortablePath.root, fullPath);
|
|
357
307
|
return this.rootfile;
|
|
358
308
|
}
|
|
359
309
|
migratePackageDocument(packageDocument) {
|
|
@@ -416,7 +366,7 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
416
366
|
produced ?? packageDocument
|
|
417
367
|
);
|
|
418
368
|
const rootfile = await this.getRootfile();
|
|
419
|
-
this.writeEntryContents(rootfile, updatedPackageDocument, "utf-8");
|
|
369
|
+
await this.writeEntryContents(rootfile, updatedPackageDocument, "utf-8");
|
|
420
370
|
});
|
|
421
371
|
}
|
|
422
372
|
/**
|
|
@@ -555,6 +505,34 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
555
505
|
const metadata = metadataElement.metadata.map((node) => Epub.parseMetadataItem(node)).filter((node) => !!node);
|
|
556
506
|
return metadata;
|
|
557
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* Retrieve the identifier from the dc:identifier element
|
|
510
|
+
* in the EPUB metadata.
|
|
511
|
+
*
|
|
512
|
+
* If there is no dc:identifier element, returns null.
|
|
513
|
+
*
|
|
514
|
+
* @link https://www.w3.org/TR/epub-33/#sec-opf-dcidentifier
|
|
515
|
+
*/
|
|
516
|
+
async getIdentifier() {
|
|
517
|
+
const metadata = await this.getMetadata();
|
|
518
|
+
const entry = metadata.find(({ type }) => type === "dc:identifier");
|
|
519
|
+
return (entry == null ? void 0 : entry.value) ?? null;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Set the dc:identifier metadata element with the provided string.
|
|
523
|
+
*
|
|
524
|
+
* Updates the existing dc:identifier element if one exists.
|
|
525
|
+
* Otherwise creates a new element
|
|
526
|
+
*
|
|
527
|
+
* @link https://www.w3.org/TR/epub-33/#sec-opf-dcidentifier
|
|
528
|
+
*/
|
|
529
|
+
async setIdentifier(identifier) {
|
|
530
|
+
await this.replaceMetadata(({ type }) => type === "dc:identifier", {
|
|
531
|
+
type: "dc:identifier",
|
|
532
|
+
properties: {},
|
|
533
|
+
value: identifier
|
|
534
|
+
});
|
|
535
|
+
}
|
|
558
536
|
/**
|
|
559
537
|
* Even "EPUB 3" publications sometimes still only use the
|
|
560
538
|
* EPUB 2 specification for identifying the cover image.
|
|
@@ -1448,9 +1426,8 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1448
1426
|
* Returns a Zip Entry path for an HREF
|
|
1449
1427
|
*/
|
|
1450
1428
|
resolveHref(from, href) {
|
|
1451
|
-
const startPath = dirname(from);
|
|
1452
|
-
|
|
1453
|
-
return resolve(absoluteStartPath, href).slice(1);
|
|
1429
|
+
const startPath = ppath.dirname(from);
|
|
1430
|
+
return ppath.resolve(PortablePath.root, startPath, href);
|
|
1454
1431
|
}
|
|
1455
1432
|
async readItemContents(id, encoding) {
|
|
1456
1433
|
const rootfile = await this.getRootfile();
|
|
@@ -1495,11 +1472,9 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1495
1472
|
const body = Epub.getXhtmlBody(xml);
|
|
1496
1473
|
return Epub.getXhtmlTextContent(body);
|
|
1497
1474
|
}
|
|
1498
|
-
writeEntryContents(path, contents, encoding) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
if (!entry) throw new Error(`Could not find file at ${path} in EPUB`);
|
|
1502
|
-
entry.setData(data);
|
|
1475
|
+
async writeEntryContents(path, contents, encoding) {
|
|
1476
|
+
await this.zipFs.mkdirpPromise(ppath.dirname(path));
|
|
1477
|
+
await this.zipFs.writeFilePromise(path, contents, encoding);
|
|
1503
1478
|
}
|
|
1504
1479
|
async writeItemContents(id, contents, encoding) {
|
|
1505
1480
|
const rootfile = await this.getRootfile();
|
|
@@ -1510,9 +1485,9 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1510
1485
|
memoize.clear(this.readXhtmlItemContents);
|
|
1511
1486
|
const href = this.resolveHref(rootfile, manifestItem.href);
|
|
1512
1487
|
if (encoding === "utf-8") {
|
|
1513
|
-
this.writeEntryContents(href, contents, encoding);
|
|
1488
|
+
await this.writeEntryContents(href, contents, encoding);
|
|
1514
1489
|
} else {
|
|
1515
|
-
this.writeEntryContents(href, contents);
|
|
1490
|
+
await this.writeEntryContents(href, contents);
|
|
1516
1491
|
}
|
|
1517
1492
|
}
|
|
1518
1493
|
/**
|
|
@@ -1575,7 +1550,9 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1575
1550
|
...item.mediaType && { "media-type": item.mediaType },
|
|
1576
1551
|
...item.fallback && { fallback: item.fallback },
|
|
1577
1552
|
...item.mediaOverlay && { "media-overlay": item.mediaOverlay },
|
|
1578
|
-
...item.properties && {
|
|
1553
|
+
...item.properties && {
|
|
1554
|
+
properties: item.properties.join(" ")
|
|
1555
|
+
}
|
|
1579
1556
|
})
|
|
1580
1557
|
);
|
|
1581
1558
|
});
|
|
@@ -1587,7 +1564,8 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1587
1564
|
contents
|
|
1588
1565
|
)
|
|
1589
1566
|
) : contents;
|
|
1590
|
-
this.
|
|
1567
|
+
await this.zipFs.mkdirpPromise(ppath.dirname(filename));
|
|
1568
|
+
await this.zipFs.writeFilePromise(filename, data);
|
|
1591
1569
|
}
|
|
1592
1570
|
/**
|
|
1593
1571
|
* Update the manifest entry for an existing item.
|
|
@@ -1737,19 +1715,21 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1737
1715
|
}
|
|
1738
1716
|
});
|
|
1739
1717
|
}
|
|
1718
|
+
discardAndClose() {
|
|
1719
|
+
this.zipFs.discardAndClose();
|
|
1720
|
+
this.rootfile = null;
|
|
1721
|
+
this.manifest = null;
|
|
1722
|
+
this.spine = null;
|
|
1723
|
+
}
|
|
1740
1724
|
/**
|
|
1741
1725
|
* Write the current contents of the Epub to a new
|
|
1742
1726
|
* Uint8Array.
|
|
1743
1727
|
*
|
|
1744
|
-
* This _does not_ close the Epub. It can continue to
|
|
1745
|
-
* be modified after it has been written to disk. Use
|
|
1746
|
-
* `epub.close()` to close the Epub.
|
|
1747
|
-
*
|
|
1748
1728
|
* When this method is called, the "dcterms:modified"
|
|
1749
1729
|
* meta tag is automatically updated to the current UTC
|
|
1750
1730
|
* timestamp.
|
|
1751
1731
|
*/
|
|
1752
|
-
async
|
|
1732
|
+
async getArrayAndClose() {
|
|
1753
1733
|
await this.replaceMetadata(
|
|
1754
1734
|
(entry) => entry.properties["property"] === "dcterms:modified",
|
|
1755
1735
|
{
|
|
@@ -1759,48 +1739,19 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1759
1739
|
value: (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+/, "")
|
|
1760
1740
|
}
|
|
1761
1741
|
);
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
});
|
|
1768
|
-
this.entries.push(mimetypeEntry);
|
|
1769
|
-
}
|
|
1770
|
-
const mimetypeReader = new Uint8ArrayReader(await mimetypeEntry.getData());
|
|
1771
|
-
try {
|
|
1772
|
-
await this.zipWriter.add(mimetypeEntry.filename, mimetypeReader, {
|
|
1773
|
-
level: 0,
|
|
1774
|
-
extendedTimestamp: false
|
|
1775
|
-
});
|
|
1776
|
-
} catch (e) {
|
|
1777
|
-
if (e instanceof Error && e.message === ERR_DUPLICATED_NAME) {
|
|
1778
|
-
throw new Error(
|
|
1779
|
-
`Failed to add file "${mimetypeEntry.filename}" to zip archive: ${e.message}`
|
|
1780
|
-
);
|
|
1781
|
-
}
|
|
1782
|
-
throw e;
|
|
1783
|
-
}
|
|
1784
|
-
await Promise.all(
|
|
1785
|
-
this.entries.map(async (entry) => {
|
|
1786
|
-
if (entry.filename === "mimetype") return;
|
|
1787
|
-
const reader = new Uint8ArrayReader(await entry.getData());
|
|
1788
|
-
try {
|
|
1789
|
-
return await this.zipWriter.add(entry.filename, reader);
|
|
1790
|
-
} catch (e) {
|
|
1791
|
-
if (e instanceof Error && e.message === ERR_DUPLICATED_NAME) {
|
|
1792
|
-
throw new Error(
|
|
1793
|
-
`Failed to add file "${entry.filename}" to zip archive: ${e.message}`
|
|
1794
|
-
);
|
|
1795
|
-
}
|
|
1796
|
-
throw e;
|
|
1797
|
-
}
|
|
1798
|
-
})
|
|
1742
|
+
this.zipFs.level = 0;
|
|
1743
|
+
await this.zipFs.writeFilePromise(
|
|
1744
|
+
ppath.join(PortablePath.root, "mimetype"),
|
|
1745
|
+
"application/epub+zip",
|
|
1746
|
+
"utf-8"
|
|
1799
1747
|
);
|
|
1800
|
-
|
|
1801
|
-
this.
|
|
1802
|
-
|
|
1803
|
-
|
|
1748
|
+
this.zipFs.level = DEFAULT_COMPRESSION_LEVEL;
|
|
1749
|
+
return this.zipFs.getBufferAndClose();
|
|
1750
|
+
}
|
|
1751
|
+
[Symbol.dispose]() {
|
|
1752
|
+
if (this.zipFs.ready) {
|
|
1753
|
+
this.discardAndClose();
|
|
1754
|
+
}
|
|
1804
1755
|
}
|
|
1805
1756
|
}
|
|
1806
1757
|
export {
|
package/dist/node.cjs
CHANGED
|
@@ -23,14 +23,21 @@ __export(node_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(node_exports);
|
|
24
24
|
var import_promises = require("node:fs/promises");
|
|
25
25
|
var import_node_path = require("node:path");
|
|
26
|
+
var import_fslib = require("@yarnpkg/fslib");
|
|
27
|
+
var import_libzip = require("@yarnpkg/libzip");
|
|
26
28
|
var import_fs = require("@storyteller-platform/fs");
|
|
27
29
|
var import_index = require("./index.cjs");
|
|
28
30
|
class Epub extends import_index.Epub {
|
|
29
|
-
static create(
|
|
30
|
-
return super.create(
|
|
31
|
+
static create(dc, additionalMetadata = []) {
|
|
32
|
+
return super.create(dc, additionalMetadata);
|
|
31
33
|
}
|
|
32
34
|
static async from(...args) {
|
|
33
35
|
const pathOrData = args[0];
|
|
36
|
+
if (typeof pathOrData === "string") {
|
|
37
|
+
return new Epub(
|
|
38
|
+
new import_libzip.ZipFS(import_fslib.npath.toPortablePath(import_fslib.npath.resolve(pathOrData)))
|
|
39
|
+
);
|
|
40
|
+
}
|
|
34
41
|
const fileData = typeof pathOrData === "string" ? await (0, import_fs.streamFile)(pathOrData) : pathOrData;
|
|
35
42
|
return super.from(fileData);
|
|
36
43
|
}
|
|
@@ -38,10 +45,6 @@ class Epub extends import_index.Epub {
|
|
|
38
45
|
* Write the current contents of the Epub to a new
|
|
39
46
|
* EPUB archive on disk.
|
|
40
47
|
*
|
|
41
|
-
* This _does not_ close the Epub. It can continue to
|
|
42
|
-
* be modified after it has been written to disk. Use
|
|
43
|
-
* `epub.close()` to close the Epub.
|
|
44
|
-
*
|
|
45
48
|
* When this method is called, the "dcterms:modified"
|
|
46
49
|
* meta tag is automatically updated to the current UTC
|
|
47
50
|
* timestamp.
|
|
@@ -50,13 +53,9 @@ class Epub extends import_index.Epub {
|
|
|
50
53
|
* parent directory does not need te exist -- the path will be
|
|
51
54
|
* recursively created.
|
|
52
55
|
*/
|
|
53
|
-
async
|
|
54
|
-
const data = await this.writeToArray();
|
|
55
|
-
if (!data.length)
|
|
56
|
-
throw new Error(
|
|
57
|
-
"Failed to write zip archive to file; writer returned no data"
|
|
58
|
-
);
|
|
56
|
+
async saveAndClose(path) {
|
|
59
57
|
await (0, import_promises.mkdir)((0, import_node_path.dirname)(path), { recursive: true });
|
|
58
|
+
const data = await this.getArrayAndClose();
|
|
60
59
|
await (0, import_promises.writeFile)(path, data);
|
|
61
60
|
}
|
|
62
61
|
}
|
package/dist/node.d.cts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import { Epub as Epub$1 } from './index.cjs';
|
|
2
|
-
export { AlternateScript, Collection, DcCreator, DcSubject,
|
|
3
|
-
import '@
|
|
1
|
+
import { Epub as Epub$1, DublinCore, EpubMetadata } from './index.cjs';
|
|
2
|
+
export { AlternateScript, Collection, DcCreator, DcSubject, ElementName, ManifestItem, MetadataEntry, ParsedXml, XmlElement, XmlNode, XmlTextNode } from './index.cjs';
|
|
3
|
+
import '@yarnpkg/libzip';
|
|
4
4
|
import 'fast-xml-parser';
|
|
5
5
|
|
|
6
6
|
declare class Epub extends Epub$1 {
|
|
7
|
-
static create(
|
|
7
|
+
static create(dc: DublinCore, additionalMetadata?: EpubMetadata): Promise<Epub>;
|
|
8
8
|
static from(...args: Parameters<typeof Epub$1.from>): Promise<Epub>;
|
|
9
9
|
/**
|
|
10
10
|
* Write the current contents of the Epub to a new
|
|
11
11
|
* EPUB archive on disk.
|
|
12
12
|
*
|
|
13
|
-
* This _does not_ close the Epub. It can continue to
|
|
14
|
-
* be modified after it has been written to disk. Use
|
|
15
|
-
* `epub.close()` to close the Epub.
|
|
16
|
-
*
|
|
17
13
|
* When this method is called, the "dcterms:modified"
|
|
18
14
|
* meta tag is automatically updated to the current UTC
|
|
19
15
|
* timestamp.
|
|
@@ -22,7 +18,7 @@ declare class Epub extends Epub$1 {
|
|
|
22
18
|
* parent directory does not need te exist -- the path will be
|
|
23
19
|
* recursively created.
|
|
24
20
|
*/
|
|
25
|
-
|
|
21
|
+
saveAndClose(path: string): Promise<void>;
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
export { Epub };
|
|
24
|
+
export { DublinCore, Epub, EpubMetadata };
|
package/dist/node.d.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import { Epub as Epub$1 } from './index.js';
|
|
2
|
-
export { AlternateScript, Collection, DcCreator, DcSubject,
|
|
3
|
-
import '@
|
|
1
|
+
import { Epub as Epub$1, DublinCore, EpubMetadata } from './index.js';
|
|
2
|
+
export { AlternateScript, Collection, DcCreator, DcSubject, ElementName, ManifestItem, MetadataEntry, ParsedXml, XmlElement, XmlNode, XmlTextNode } from './index.js';
|
|
3
|
+
import '@yarnpkg/libzip';
|
|
4
4
|
import 'fast-xml-parser';
|
|
5
5
|
|
|
6
6
|
declare class Epub extends Epub$1 {
|
|
7
|
-
static create(
|
|
7
|
+
static create(dc: DublinCore, additionalMetadata?: EpubMetadata): Promise<Epub>;
|
|
8
8
|
static from(...args: Parameters<typeof Epub$1.from>): Promise<Epub>;
|
|
9
9
|
/**
|
|
10
10
|
* Write the current contents of the Epub to a new
|
|
11
11
|
* EPUB archive on disk.
|
|
12
12
|
*
|
|
13
|
-
* This _does not_ close the Epub. It can continue to
|
|
14
|
-
* be modified after it has been written to disk. Use
|
|
15
|
-
* `epub.close()` to close the Epub.
|
|
16
|
-
*
|
|
17
13
|
* When this method is called, the "dcterms:modified"
|
|
18
14
|
* meta tag is automatically updated to the current UTC
|
|
19
15
|
* timestamp.
|
|
@@ -22,7 +18,7 @@ declare class Epub extends Epub$1 {
|
|
|
22
18
|
* parent directory does not need te exist -- the path will be
|
|
23
19
|
* recursively created.
|
|
24
20
|
*/
|
|
25
|
-
|
|
21
|
+
saveAndClose(path: string): Promise<void>;
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
export { Epub };
|
|
24
|
+
export { DublinCore, Epub, EpubMetadata };
|
package/dist/node.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
+
import { npath } from "@yarnpkg/fslib";
|
|
4
|
+
import { ZipFS } from "@yarnpkg/libzip";
|
|
3
5
|
import { streamFile } from "@storyteller-platform/fs";
|
|
4
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
Epub as BaseEpub
|
|
8
|
+
} from "./index.js";
|
|
5
9
|
class Epub extends BaseEpub {
|
|
6
|
-
static create(
|
|
7
|
-
return super.create(
|
|
10
|
+
static create(dc, additionalMetadata = []) {
|
|
11
|
+
return super.create(dc, additionalMetadata);
|
|
8
12
|
}
|
|
9
13
|
static async from(...args) {
|
|
10
14
|
const pathOrData = args[0];
|
|
15
|
+
if (typeof pathOrData === "string") {
|
|
16
|
+
return new Epub(
|
|
17
|
+
new ZipFS(npath.toPortablePath(npath.resolve(pathOrData)))
|
|
18
|
+
);
|
|
19
|
+
}
|
|
11
20
|
const fileData = typeof pathOrData === "string" ? await streamFile(pathOrData) : pathOrData;
|
|
12
21
|
return super.from(fileData);
|
|
13
22
|
}
|
|
@@ -15,10 +24,6 @@ class Epub extends BaseEpub {
|
|
|
15
24
|
* Write the current contents of the Epub to a new
|
|
16
25
|
* EPUB archive on disk.
|
|
17
26
|
*
|
|
18
|
-
* This _does not_ close the Epub. It can continue to
|
|
19
|
-
* be modified after it has been written to disk. Use
|
|
20
|
-
* `epub.close()` to close the Epub.
|
|
21
|
-
*
|
|
22
27
|
* When this method is called, the "dcterms:modified"
|
|
23
28
|
* meta tag is automatically updated to the current UTC
|
|
24
29
|
* timestamp.
|
|
@@ -27,13 +32,9 @@ class Epub extends BaseEpub {
|
|
|
27
32
|
* parent directory does not need te exist -- the path will be
|
|
28
33
|
* recursively created.
|
|
29
34
|
*/
|
|
30
|
-
async
|
|
31
|
-
const data = await this.writeToArray();
|
|
32
|
-
if (!data.length)
|
|
33
|
-
throw new Error(
|
|
34
|
-
"Failed to write zip archive to file; writer returned no data"
|
|
35
|
-
);
|
|
35
|
+
async saveAndClose(path) {
|
|
36
36
|
await mkdir(dirname(path), { recursive: true });
|
|
37
|
+
const data = await this.getArrayAndClose();
|
|
37
38
|
await writeFile(path, data);
|
|
38
39
|
}
|
|
39
40
|
}
|
package/package.json
CHANGED
|
@@ -1,34 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyteller-platform/epub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"exports": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"types": "./dist/index.d.ts",
|
|
14
|
-
"default": "./dist/index.js"
|
|
15
|
-
},
|
|
16
|
-
"require": {
|
|
17
|
-
"types": "./dist/index.d.cts",
|
|
18
|
-
"default": "./dist/index.cjs"
|
|
19
|
-
}
|
|
9
|
+
"import": {
|
|
10
|
+
"types": "./dist/node.d.ts",
|
|
11
|
+
"default": "./dist/node.js",
|
|
12
|
+
"browser": "./dist/index.js"
|
|
20
13
|
},
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"types": "./dist/node.d.ts",
|
|
26
|
-
"default": "./dist/node.js"
|
|
27
|
-
},
|
|
28
|
-
"require": {
|
|
29
|
-
"types": "./dist/node.d.cts",
|
|
30
|
-
"default": "./dist/node.cjs"
|
|
31
|
-
}
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/node.d.cts",
|
|
16
|
+
"default": "./dist/node.cjs",
|
|
17
|
+
"browser": "./dist/index.cjs"
|
|
32
18
|
}
|
|
33
19
|
},
|
|
34
20
|
"files": [
|
|
@@ -47,19 +33,21 @@
|
|
|
47
33
|
"@storyteller-platform/tsup": "^0.1.0",
|
|
48
34
|
"@tsconfig/strictest": "^2.0.5",
|
|
49
35
|
"@types/mime-types": "^2",
|
|
50
|
-
"@types/node": "^
|
|
36
|
+
"@types/node": "^24.0.0",
|
|
51
37
|
"markdown-toc": "^1.2.0",
|
|
52
38
|
"remark-toc": "^9.0.0",
|
|
53
39
|
"tsup": "^8.5.0",
|
|
54
40
|
"tsx": "^4.19.2",
|
|
55
|
-
"typedoc": "^0.28.
|
|
41
|
+
"typedoc": "^0.28.13",
|
|
56
42
|
"typedoc-plugin-markdown": "^4.6.3",
|
|
57
43
|
"typedoc-plugin-remark": "^1.2.0",
|
|
58
|
-
"typescript": "^5.
|
|
44
|
+
"typescript": "^5.9.2"
|
|
59
45
|
},
|
|
60
46
|
"dependencies": {
|
|
61
|
-
"@storyteller-platform/fs": "^0.1.
|
|
62
|
-
"@storyteller-platform/path": "^0.1.
|
|
47
|
+
"@storyteller-platform/fs": "^0.1.3",
|
|
48
|
+
"@storyteller-platform/path": "^0.1.1",
|
|
49
|
+
"@yarnpkg/fslib": "^3.1.3",
|
|
50
|
+
"@yarnpkg/libzip": "^3.2.2",
|
|
63
51
|
"@zip.js/zip.js": "^2.0.0",
|
|
64
52
|
"async-mutex": "^0.5.0",
|
|
65
53
|
"fast-xml-parser": "^4.0.0",
|
|
@@ -68,6 +56,18 @@
|
|
|
68
56
|
"nanoid": "^5.1.5"
|
|
69
57
|
},
|
|
70
58
|
"publishConfig": {
|
|
71
|
-
"access": "public"
|
|
59
|
+
"access": "public",
|
|
60
|
+
"exports": {
|
|
61
|
+
"import": {
|
|
62
|
+
"types": "./dist/node.d.ts",
|
|
63
|
+
"default": "./dist/node.js",
|
|
64
|
+
"browser": "./dist/index.js"
|
|
65
|
+
},
|
|
66
|
+
"require": {
|
|
67
|
+
"types": "./dist/node.d.cts",
|
|
68
|
+
"default": "./dist/node.cjs",
|
|
69
|
+
"browser": "./dist/index.cjs"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
72
|
}
|
|
73
73
|
}
|