@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/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 util = __toESM(require("node:util"), 1);
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 import_yauzl = __toESM(require("yauzl"), 1);
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 = new import_fast_xml_parser.XMLParser({
134
- allowBooleanAttributes: true,
135
- alwaysCreateTextNode: true,
136
- preserveOrder: true,
137
- ignoreAttributes: false,
138
- htmlEntities: true,
139
- trimValues: false,
140
- stopNodes: ["*.pre", "*.script"],
141
- parseTagValue: false,
142
- updateTag(_tagName, _jPath, attrs) {
143
- if (attrs && "@_/" in attrs) {
144
- delete attrs["@_/"];
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
- return true;
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=${language.toString()} version="3.0">
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
- var _stack = [];
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
- const extractPath = (0, import_path.join)(
361
- (0, import_node_os.tmpdir)(),
362
- `storyteller-platform-epub-${(0, import_node_crypto.randomUUID)()}.epub`
363
- );
364
- const zipfile = typeof pathOrData === "string" ? await unzipFromPath(pathOrData, { lazyEntries: true }) : await unzipFromBuffer(Buffer.from(pathOrData), { lazyEntries: true });
365
- const stack = __using(_stack, new DisposableStack());
366
- stack.defer(() => {
367
- zipfile.close();
368
- });
369
- const { promise, resolve: resolve2 } = Promise.withResolvers();
370
- zipfile.on("end", () => {
371
- resolve2();
372
- });
373
- const openReadStream = util.promisify(zipfile.openReadStream.bind(zipfile));
374
- zipfile.readEntry();
375
- zipfile.on("entry", async (entry) => {
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
- await promise;
395
- return new this(
396
- extractPath,
397
- typeof pathOrData === "string" ? pathOrData : void 0
398
- );
399
- } catch (_) {
400
- var _error = _, _hasError = true;
401
- } finally {
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 * as util from "node:util";
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 yauzl from "yauzl";
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 = new XMLParser({
101
- allowBooleanAttributes: true,
102
- alwaysCreateTextNode: true,
103
- preserveOrder: true,
104
- ignoreAttributes: false,
105
- htmlEntities: true,
106
- trimValues: false,
107
- stopNodes: ["*.pre", "*.script"],
108
- parseTagValue: false,
109
- updateTag(_tagName, _jPath, attrs) {
110
- if (attrs && "@_/" in attrs) {
111
- delete attrs["@_/"];
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
- return true;
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=${language.toString()} version="3.0">
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
- var _stack = [];
307
+ const extractPath = join(
308
+ tmpdir(),
309
+ `storyteller-platform-epub-${randomUUID()}.epub`
310
+ );
326
311
  try {
327
- const extractPath = join(
328
- tmpdir(),
329
- `storyteller-platform-epub-${randomUUID()}.epub`
330
- );
331
- const zipfile = typeof pathOrData === "string" ? await unzipFromPath(pathOrData, { lazyEntries: true }) : await unzipFromBuffer(Buffer.from(pathOrData), { lazyEntries: true });
332
- const stack = __using(_stack, new DisposableStack());
333
- stack.defer(() => {
334
- zipfile.close();
335
- });
336
- const { promise, resolve: resolve2 } = Promise.withResolvers();
337
- zipfile.on("end", () => {
338
- resolve2();
339
- });
340
- const openReadStream = util.promisify(zipfile.openReadStream.bind(zipfile));
341
- zipfile.readEntry();
342
- zipfile.on("entry", async (entry) => {
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
- await promise;
362
- return new this(
363
- extractPath,
364
- typeof pathOrData === "string" ? pathOrData : void 0
365
- );
366
- } catch (_) {
367
- var _error = _, _hasError = true;
368
- } finally {
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.2",
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.3",
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.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": "^3.2.0",
57
+ "yauzl-promise": "^4.0.0",
56
58
  "yazl": "^3.3.1"
57
59
  },
58
60
  "publishConfig": {