@storyteller-platform/epub 0.4.2 → 0.4.6
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 +125 -125
- package/dist/index.cjs +60 -86
- package/dist/index.js +60 -86
- package/package.json +7 -5
package/dist/index.cjs
CHANGED
|
@@ -80,38 +80,15 @@ var import_node_crypto = require("node:crypto");
|
|
|
80
80
|
var import_node_fs = require("node:fs");
|
|
81
81
|
var import_promises = require("node:fs/promises");
|
|
82
82
|
var import_node_os = require("node:os");
|
|
83
|
-
var
|
|
83
|
+
var import_promises2 = require("node:stream/promises");
|
|
84
84
|
var import_async_mutex = require("async-mutex");
|
|
85
85
|
var import_fast_xml_parser = require("fast-xml-parser");
|
|
86
86
|
var import_mem = __toESM(require("mem"), 1);
|
|
87
87
|
var import_mime_types = require("mime-types");
|
|
88
88
|
var import_nanoid = require("nanoid");
|
|
89
|
-
var
|
|
89
|
+
var import_yauzl_promise = require("yauzl-promise");
|
|
90
90
|
var import_yazl = require("yazl");
|
|
91
91
|
var import_path = require("@storyteller-platform/path");
|
|
92
|
-
function promisify(api) {
|
|
93
|
-
return function(arg, options) {
|
|
94
|
-
return new Promise(function(resolve2, reject) {
|
|
95
|
-
api(arg, options, function(err, response) {
|
|
96
|
-
if (err) {
|
|
97
|
-
reject(err);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
resolve2(response);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
const unzipFromBuffer = promisify(
|
|
106
|
-
(arg, options, callback) => {
|
|
107
|
-
import_yauzl.default.fromBuffer(arg, options, callback);
|
|
108
|
-
}
|
|
109
|
-
);
|
|
110
|
-
const unzipFromPath = promisify(
|
|
111
|
-
(arg, options, callback) => {
|
|
112
|
-
import_yauzl.default.open(arg, options, callback);
|
|
113
|
-
}
|
|
114
|
-
);
|
|
115
92
|
class Epub {
|
|
116
93
|
constructor(extractPath, inputPath) {
|
|
117
94
|
this.extractPath = extractPath;
|
|
@@ -130,22 +107,27 @@ class Epub {
|
|
|
130
107
|
ignoreAttributes: false,
|
|
131
108
|
parseTagValue: false
|
|
132
109
|
});
|
|
133
|
-
static xhtmlParser =
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
110
|
+
static xhtmlParser = (() => {
|
|
111
|
+
const parser = new import_fast_xml_parser.XMLParser({
|
|
112
|
+
allowBooleanAttributes: true,
|
|
113
|
+
alwaysCreateTextNode: true,
|
|
114
|
+
preserveOrder: true,
|
|
115
|
+
ignoreAttributes: false,
|
|
116
|
+
htmlEntities: true,
|
|
117
|
+
trimValues: false,
|
|
118
|
+
stopNodes: ["*.pre", "*.script"],
|
|
119
|
+
parseTagValue: false,
|
|
120
|
+
updateTag(_tagName, _jPath, attrs) {
|
|
121
|
+
if (attrs && "@_/" in attrs) {
|
|
122
|
+
delete attrs["@_/"];
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
145
125
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
126
|
+
});
|
|
127
|
+
parser.addEntity("nbsp", "\xA0");
|
|
128
|
+
parser.addEntity("#160", "\xA0");
|
|
129
|
+
return parser;
|
|
130
|
+
})();
|
|
149
131
|
static xmlBuilder = new import_fast_xml_parser.XMLBuilder({
|
|
150
132
|
preserveOrder: true,
|
|
151
133
|
format: true,
|
|
@@ -297,7 +279,7 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
297
279
|
);
|
|
298
280
|
const encoder = new TextEncoder();
|
|
299
281
|
const container = encoder.encode(`<?xml version="1.0"?>
|
|
300
|
-
<container>
|
|
282
|
+
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
|
301
283
|
<rootfiles>
|
|
302
284
|
<rootfile media-type="application/oebps-package+xml" full-path="OEBPS/content.opf"/>
|
|
303
285
|
</rootfiles>
|
|
@@ -306,7 +288,7 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
306
288
|
await (0, import_promises.mkdir)((0, import_path.join)(extractPath, "META-INF"), { recursive: true });
|
|
307
289
|
await (0, import_promises.writeFile)((0, import_path.join)(extractPath, "META-INF", "container.xml"), container);
|
|
308
290
|
const packageDocument = encoder.encode(`<?xml version="1.0"?>
|
|
309
|
-
<package unique-identifier="pub-id" dir="${language.textInfo.direction}" xml:lang
|
|
291
|
+
<package unique-identifier="pub-id" dir="${language.textInfo.direction}" xml:lang="${language.toString()}" version="3.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
310
292
|
<metadata>
|
|
311
293
|
</metadata>
|
|
312
294
|
<manifest>
|
|
@@ -355,52 +337,42 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
355
337
|
*/
|
|
356
338
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
357
339
|
static async from(pathOrData) {
|
|
358
|
-
|
|
340
|
+
const extractPath = (0, import_path.join)(
|
|
341
|
+
(0, import_node_os.tmpdir)(),
|
|
342
|
+
`storyteller-platform-epub-${(0, import_node_crypto.randomUUID)()}.epub`
|
|
343
|
+
);
|
|
359
344
|
try {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
zipfile
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (entry.fileName.endsWith("/")) {
|
|
377
|
-
zipfile.readEntry();
|
|
378
|
-
} else {
|
|
379
|
-
const writePath = (0, import_path.join)(extractPath, entry.fileName);
|
|
380
|
-
const readStream = await openReadStream(entry);
|
|
381
|
-
await (0, import_promises.mkdir)((0, import_path.dirname)(writePath), { recursive: true });
|
|
382
|
-
await new Promise((resolvePipe) => {
|
|
383
|
-
const writePath2 = (0, import_path.join)(extractPath, entry.fileName);
|
|
384
|
-
const writeStream = (0, import_node_fs.createWriteStream)(writePath2);
|
|
385
|
-
writeStream.on("finish", () => {
|
|
386
|
-
resolvePipe();
|
|
387
|
-
});
|
|
388
|
-
readStream.pipe(writeStream);
|
|
389
|
-
}).finally(() => {
|
|
390
|
-
zipfile.readEntry();
|
|
391
|
-
});
|
|
345
|
+
var _stack = [];
|
|
346
|
+
try {
|
|
347
|
+
const zipfile = typeof pathOrData === "string" ? await (0, import_yauzl_promise.open)(pathOrData) : await (0, import_yauzl_promise.fromBuffer)(Buffer.from(pathOrData));
|
|
348
|
+
const stack = __using(_stack, new AsyncDisposableStack(), true);
|
|
349
|
+
stack.defer(async () => {
|
|
350
|
+
await zipfile.close();
|
|
351
|
+
});
|
|
352
|
+
for await (const entry of zipfile) {
|
|
353
|
+
if (entry.filename.endsWith("/")) {
|
|
354
|
+
} else {
|
|
355
|
+
const writePath = (0, import_path.join)(extractPath, entry.filename);
|
|
356
|
+
const readStream = await entry.openReadStream();
|
|
357
|
+
await (0, import_promises.mkdir)((0, import_path.dirname)(writePath), { recursive: true });
|
|
358
|
+
const writeStream = (0, import_node_fs.createWriteStream)(writePath);
|
|
359
|
+
await (0, import_promises2.pipeline)(readStream, writeStream);
|
|
360
|
+
}
|
|
392
361
|
}
|
|
393
|
-
})
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
} catch (
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
__callDispose(_stack, _error, _hasError);
|
|
362
|
+
} catch (_) {
|
|
363
|
+
var _error = _, _hasError = true;
|
|
364
|
+
} finally {
|
|
365
|
+
var _promise = __callDispose(_stack, _error, _hasError);
|
|
366
|
+
_promise && await _promise;
|
|
367
|
+
}
|
|
368
|
+
} catch (error) {
|
|
369
|
+
(0, import_node_fs.rmSync)(extractPath, { force: true, recursive: true });
|
|
370
|
+
throw error;
|
|
403
371
|
}
|
|
372
|
+
return new this(
|
|
373
|
+
extractPath,
|
|
374
|
+
typeof pathOrData === "string" ? pathOrData : void 0
|
|
375
|
+
);
|
|
404
376
|
}
|
|
405
377
|
async removeEntry(href) {
|
|
406
378
|
const rootfile = await this.getRootfile();
|
|
@@ -1597,7 +1569,9 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1597
1569
|
async createXhtmlDocument(body, head, language) {
|
|
1598
1570
|
const lang = language ?? await this.getLanguage();
|
|
1599
1571
|
return [
|
|
1600
|
-
Epub.createXmlElement("?xml", { version: "1.0", encoding: "UTF-8" }
|
|
1572
|
+
Epub.createXmlElement("?xml", { version: "1.0", encoding: "UTF-8" }, [
|
|
1573
|
+
{ "#text": "" }
|
|
1574
|
+
]),
|
|
1601
1575
|
Epub.createXmlElement(
|
|
1602
1576
|
"html",
|
|
1603
1577
|
{
|
package/dist/index.js
CHANGED
|
@@ -47,38 +47,15 @@ import { randomUUID } from "node:crypto";
|
|
|
47
47
|
import { createWriteStream, rmSync } from "node:fs";
|
|
48
48
|
import { cp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
49
49
|
import { tmpdir } from "node:os";
|
|
50
|
-
import
|
|
50
|
+
import { pipeline } from "node:stream/promises";
|
|
51
51
|
import { Mutex } from "async-mutex";
|
|
52
52
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
53
53
|
import memoize from "mem";
|
|
54
54
|
import { lookup } from "mime-types";
|
|
55
55
|
import { nanoid } from "nanoid";
|
|
56
|
-
import
|
|
56
|
+
import { fromBuffer, open } from "yauzl-promise";
|
|
57
57
|
import { ZipFile } from "yazl";
|
|
58
58
|
import { dirname, join, resolve } from "@storyteller-platform/path";
|
|
59
|
-
function promisify(api) {
|
|
60
|
-
return function(arg, options) {
|
|
61
|
-
return new Promise(function(resolve2, reject) {
|
|
62
|
-
api(arg, options, function(err, response) {
|
|
63
|
-
if (err) {
|
|
64
|
-
reject(err);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
resolve2(response);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
const unzipFromBuffer = promisify(
|
|
73
|
-
(arg, options, callback) => {
|
|
74
|
-
yauzl.fromBuffer(arg, options, callback);
|
|
75
|
-
}
|
|
76
|
-
);
|
|
77
|
-
const unzipFromPath = promisify(
|
|
78
|
-
(arg, options, callback) => {
|
|
79
|
-
yauzl.open(arg, options, callback);
|
|
80
|
-
}
|
|
81
|
-
);
|
|
82
59
|
class Epub {
|
|
83
60
|
constructor(extractPath, inputPath) {
|
|
84
61
|
this.extractPath = extractPath;
|
|
@@ -97,22 +74,27 @@ class Epub {
|
|
|
97
74
|
ignoreAttributes: false,
|
|
98
75
|
parseTagValue: false
|
|
99
76
|
});
|
|
100
|
-
static xhtmlParser =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
77
|
+
static xhtmlParser = (() => {
|
|
78
|
+
const parser = new XMLParser({
|
|
79
|
+
allowBooleanAttributes: true,
|
|
80
|
+
alwaysCreateTextNode: true,
|
|
81
|
+
preserveOrder: true,
|
|
82
|
+
ignoreAttributes: false,
|
|
83
|
+
htmlEntities: true,
|
|
84
|
+
trimValues: false,
|
|
85
|
+
stopNodes: ["*.pre", "*.script"],
|
|
86
|
+
parseTagValue: false,
|
|
87
|
+
updateTag(_tagName, _jPath, attrs) {
|
|
88
|
+
if (attrs && "@_/" in attrs) {
|
|
89
|
+
delete attrs["@_/"];
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
112
92
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
93
|
+
});
|
|
94
|
+
parser.addEntity("nbsp", "\xA0");
|
|
95
|
+
parser.addEntity("#160", "\xA0");
|
|
96
|
+
return parser;
|
|
97
|
+
})();
|
|
116
98
|
static xmlBuilder = new XMLBuilder({
|
|
117
99
|
preserveOrder: true,
|
|
118
100
|
format: true,
|
|
@@ -264,7 +246,7 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
264
246
|
);
|
|
265
247
|
const encoder = new TextEncoder();
|
|
266
248
|
const container = encoder.encode(`<?xml version="1.0"?>
|
|
267
|
-
<container>
|
|
249
|
+
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
|
268
250
|
<rootfiles>
|
|
269
251
|
<rootfile media-type="application/oebps-package+xml" full-path="OEBPS/content.opf"/>
|
|
270
252
|
</rootfiles>
|
|
@@ -273,7 +255,7 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
273
255
|
await mkdir(join(extractPath, "META-INF"), { recursive: true });
|
|
274
256
|
await writeFile(join(extractPath, "META-INF", "container.xml"), container);
|
|
275
257
|
const packageDocument = encoder.encode(`<?xml version="1.0"?>
|
|
276
|
-
<package unique-identifier="pub-id" dir="${language.textInfo.direction}" xml:lang
|
|
258
|
+
<package unique-identifier="pub-id" dir="${language.textInfo.direction}" xml:lang="${language.toString()}" version="3.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
277
259
|
<metadata>
|
|
278
260
|
</metadata>
|
|
279
261
|
<manifest>
|
|
@@ -322,52 +304,42 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
322
304
|
*/
|
|
323
305
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
324
306
|
static async from(pathOrData) {
|
|
325
|
-
|
|
307
|
+
const extractPath = join(
|
|
308
|
+
tmpdir(),
|
|
309
|
+
`storyteller-platform-epub-${randomUUID()}.epub`
|
|
310
|
+
);
|
|
326
311
|
try {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
zipfile
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if (entry.fileName.endsWith("/")) {
|
|
344
|
-
zipfile.readEntry();
|
|
345
|
-
} else {
|
|
346
|
-
const writePath = join(extractPath, entry.fileName);
|
|
347
|
-
const readStream = await openReadStream(entry);
|
|
348
|
-
await mkdir(dirname(writePath), { recursive: true });
|
|
349
|
-
await new Promise((resolvePipe) => {
|
|
350
|
-
const writePath2 = join(extractPath, entry.fileName);
|
|
351
|
-
const writeStream = createWriteStream(writePath2);
|
|
352
|
-
writeStream.on("finish", () => {
|
|
353
|
-
resolvePipe();
|
|
354
|
-
});
|
|
355
|
-
readStream.pipe(writeStream);
|
|
356
|
-
}).finally(() => {
|
|
357
|
-
zipfile.readEntry();
|
|
358
|
-
});
|
|
312
|
+
var _stack = [];
|
|
313
|
+
try {
|
|
314
|
+
const zipfile = typeof pathOrData === "string" ? await open(pathOrData) : await fromBuffer(Buffer.from(pathOrData));
|
|
315
|
+
const stack = __using(_stack, new AsyncDisposableStack(), true);
|
|
316
|
+
stack.defer(async () => {
|
|
317
|
+
await zipfile.close();
|
|
318
|
+
});
|
|
319
|
+
for await (const entry of zipfile) {
|
|
320
|
+
if (entry.filename.endsWith("/")) {
|
|
321
|
+
} else {
|
|
322
|
+
const writePath = join(extractPath, entry.filename);
|
|
323
|
+
const readStream = await entry.openReadStream();
|
|
324
|
+
await mkdir(dirname(writePath), { recursive: true });
|
|
325
|
+
const writeStream = createWriteStream(writePath);
|
|
326
|
+
await pipeline(readStream, writeStream);
|
|
327
|
+
}
|
|
359
328
|
}
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
} catch (
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
__callDispose(_stack, _error, _hasError);
|
|
329
|
+
} catch (_) {
|
|
330
|
+
var _error = _, _hasError = true;
|
|
331
|
+
} finally {
|
|
332
|
+
var _promise = __callDispose(_stack, _error, _hasError);
|
|
333
|
+
_promise && await _promise;
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
rmSync(extractPath, { force: true, recursive: true });
|
|
337
|
+
throw error;
|
|
370
338
|
}
|
|
339
|
+
return new this(
|
|
340
|
+
extractPath,
|
|
341
|
+
typeof pathOrData === "string" ? pathOrData : void 0
|
|
342
|
+
);
|
|
371
343
|
}
|
|
372
344
|
async removeEntry(href) {
|
|
373
345
|
const rootfile = await this.getRootfile();
|
|
@@ -1564,7 +1536,9 @@ ${JSON.stringify(element, null, 2)}`
|
|
|
1564
1536
|
async createXhtmlDocument(body, head, language) {
|
|
1565
1537
|
const lang = language ?? await this.getLanguage();
|
|
1566
1538
|
return [
|
|
1567
|
-
Epub.createXmlElement("?xml", { version: "1.0", encoding: "UTF-8" }
|
|
1539
|
+
Epub.createXmlElement("?xml", { version: "1.0", encoding: "UTF-8" }, [
|
|
1540
|
+
{ "#text": "" }
|
|
1541
|
+
]),
|
|
1568
1542
|
Epub.createXmlElement(
|
|
1569
1543
|
"html",
|
|
1570
1544
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyteller-platform/epub",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -28,11 +28,14 @@
|
|
|
28
28
|
"prepack": "yarn build"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
+
"@storyteller-platform/eslint": "0.1.0",
|
|
31
32
|
"@storyteller-platform/tsup": "^0.1.0",
|
|
32
33
|
"@tsconfig/strictest": "^2.0.5",
|
|
33
34
|
"@types/mime-types": "^2",
|
|
34
35
|
"@types/node": "^24.0.0",
|
|
36
|
+
"@types/yauzl-promise": "^4",
|
|
35
37
|
"@types/yazl": "^3",
|
|
38
|
+
"eslint": "^8.0.0",
|
|
36
39
|
"markdown-toc": "^1.2.0",
|
|
37
40
|
"remark-toc": "^9.0.0",
|
|
38
41
|
"tsup": "^8.5.0",
|
|
@@ -43,16 +46,15 @@
|
|
|
43
46
|
"typescript": "^5.9.2"
|
|
44
47
|
},
|
|
45
48
|
"dependencies": {
|
|
46
|
-
"@storyteller-platform/fs": "^0.1.
|
|
49
|
+
"@storyteller-platform/fs": "^0.1.4",
|
|
47
50
|
"@storyteller-platform/path": "^0.1.1",
|
|
48
|
-
"@types/yauzl": "^2.10.3",
|
|
49
51
|
"@zip.js/zip.js": "^2.0.0",
|
|
50
52
|
"async-mutex": "^0.5.0",
|
|
51
|
-
"fast-xml-parser": "^4.0.
|
|
53
|
+
"fast-xml-parser": "^4.0.1",
|
|
52
54
|
"mem": "^8.0.0",
|
|
53
55
|
"mime-types": "^3.0.1",
|
|
54
56
|
"nanoid": "^5.1.5",
|
|
55
|
-
"yauzl": "^
|
|
57
|
+
"yauzl-promise": "^4.0.0",
|
|
56
58
|
"yazl": "^3.3.1"
|
|
57
59
|
},
|
|
58
60
|
"publishConfig": {
|