wp-epub-gen 0.2.3 → 0.4.0

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/build/index.js CHANGED
@@ -1,62 +1,311 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const mime = require("mime");
4
- const os = require("os");
5
- const path = require("path");
6
- const uuid = require("uuid");
7
- const cheerio = require("cheerio");
8
- const diacritics = require("diacritics");
9
- const uslug = require("uslug");
10
- const archiver = require("archiver");
11
- const fs$1 = require("fs-extra");
12
- const request = require("superagent");
13
- const fs = require("fs");
14
- const util = require("util");
15
- const ejs = require("ejs");
16
- const entities = require("entities");
17
- function _interopNamespaceDefault(e) {
18
- const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
19
- if (e) {
20
- for (const k in e) {
21
- if (k !== "default") {
22
- const d = Object.getOwnPropertyDescriptor(e, k);
23
- Object.defineProperty(n, k, d.get ? d : {
24
- enumerable: true,
25
- get: () => e[k]
26
- });
27
- }
28
- }
29
- }
30
- n.default = e;
31
- return Object.freeze(n);
32
- }
33
- const mime__namespace = /* @__PURE__ */ _interopNamespaceDefault(mime);
34
- const os__namespace = /* @__PURE__ */ _interopNamespaceDefault(os);
35
- const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
36
- const fs__namespace$1 = /* @__PURE__ */ _interopNamespaceDefault(fs$1);
37
- const request__namespace = /* @__PURE__ */ _interopNamespaceDefault(request);
38
- const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
39
- const ejs__namespace = /* @__PURE__ */ _interopNamespaceDefault(ejs);
40
- const entities__namespace = /* @__PURE__ */ _interopNamespaceDefault(entities);
1
+ import os from "os";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { v4 } from "uuid";
5
+ import * as cheerio from "cheerio";
6
+ import { remove } from "diacritics";
7
+ import uslug from "uslug";
8
+ import archiver from "archiver";
9
+ import fs$1 from "fs-extra";
10
+ import request from "superagent";
11
+ import fs from "fs";
12
+ import { promisify } from "util";
13
+ import ejs from "ejs";
14
+ import * as entities from "entities";
41
15
  const errors = {
42
16
  no_output_path: "No output path!",
43
17
  no_title: "Title is required.",
44
18
  no_content: "Content is required."
45
19
  };
20
+ class GlobalLogger {
21
+ constructor() {
22
+ this.customLogger = null;
23
+ }
24
+ /**
25
+ * 获取 Logger 单例实例
26
+ */
27
+ static getInstance() {
28
+ if (!GlobalLogger.instance) {
29
+ GlobalLogger.instance = new GlobalLogger();
30
+ }
31
+ return GlobalLogger.instance;
32
+ }
33
+ /**
34
+ * 设置自定义 logger
35
+ * @param logger 自定义 logger 实例
36
+ */
37
+ setLogger(logger2) {
38
+ this.customLogger = logger2;
39
+ }
40
+ /**
41
+ * 获取当前使用的 logger
42
+ */
43
+ getLogger() {
44
+ return this.customLogger || {
45
+ log: (msg) => console.log(msg),
46
+ info: (msg) => console.info(msg),
47
+ error: (msg) => console.error(msg),
48
+ warn: (msg) => console.warn(msg)
49
+ };
50
+ }
51
+ /**
52
+ * 输出普通日志
53
+ */
54
+ log(msg) {
55
+ this.getLogger().log(msg);
56
+ }
57
+ /**
58
+ * 输出信息日志
59
+ */
60
+ info(msg) {
61
+ this.getLogger().info(msg);
62
+ }
63
+ /**
64
+ * 输出错误日志
65
+ */
66
+ error(msg) {
67
+ this.getLogger().error(msg);
68
+ }
69
+ /**
70
+ * 输出警告日志
71
+ */
72
+ warn(msg) {
73
+ this.getLogger().warn(msg);
74
+ }
75
+ }
76
+ const logger = GlobalLogger.getInstance();
46
77
  function safeFineName(name) {
47
78
  return name.replace(/[/\\?%*:|"<>\t\r\n]/g, "_");
48
79
  }
49
- function parseContent(content, index, epubConfigs) {
50
- let chapter = { ...content };
80
+ const mimeModule$1 = require("mime/lite");
81
+ const mime$1 = mimeModule$1.default || mimeModule$1;
82
+ const ALLOWED_ATTRIBUTES = [
83
+ "about",
84
+ "accesskey",
85
+ "alt",
86
+ "aria-activedescendant",
87
+ "aria-atomic",
88
+ "aria-autocomplete",
89
+ "aria-busy",
90
+ "aria-checked",
91
+ "aria-controls",
92
+ "aria-describedat",
93
+ "aria-describedby",
94
+ "aria-disabled",
95
+ "aria-dropeffect",
96
+ "aria-expanded",
97
+ "aria-flowto",
98
+ "aria-grabbed",
99
+ "aria-haspopup",
100
+ "aria-hidden",
101
+ "aria-invalid",
102
+ "aria-label",
103
+ "aria-labelledby",
104
+ "aria-level",
105
+ "aria-live",
106
+ "aria-multiline",
107
+ "aria-multiselectable",
108
+ "aria-orientation",
109
+ "aria-owns",
110
+ "aria-posinset",
111
+ "aria-pressed",
112
+ "aria-readonly",
113
+ "aria-relevant",
114
+ "aria-required",
115
+ "aria-selected",
116
+ "aria-setsize",
117
+ "aria-sort",
118
+ "aria-valuemax",
119
+ "aria-valuemin",
120
+ "aria-valuenow",
121
+ "aria-valuetext",
122
+ "class",
123
+ "colspan",
124
+ "content",
125
+ // 去除重复
126
+ "contenteditable",
127
+ "contextmenu",
128
+ "datatype",
129
+ "dir",
130
+ "draggable",
131
+ "dropzone",
132
+ "epub:prefix",
133
+ "epub:type",
134
+ "hidden",
135
+ "href",
136
+ "hreflang",
137
+ "id",
138
+ // 去除重复
139
+ "inlist",
140
+ "itemid",
141
+ "itemref",
142
+ "itemscope",
143
+ "itemtype",
144
+ "lang",
145
+ "media",
146
+ "ns1:type",
147
+ "ns2:alphabet",
148
+ "ns2:ph",
149
+ "onabort",
150
+ "onblur",
151
+ "oncanplay",
152
+ "oncanplaythrough",
153
+ "onchange",
154
+ "onclick",
155
+ "oncontextmenu",
156
+ "ondblclick",
157
+ "ondrag",
158
+ "ondragend",
159
+ "ondragenter",
160
+ "ondragleave",
161
+ "ondragover",
162
+ "ondragstart",
163
+ "ondrop",
164
+ "ondurationchange",
165
+ "onemptied",
166
+ "onended",
167
+ "onerror",
168
+ "onfocus",
169
+ "oninput",
170
+ "oninvalid",
171
+ "onkeydown",
172
+ "onkeypress",
173
+ "onkeyup",
174
+ "onload",
175
+ "onloadeddata",
176
+ "onloadedmetadata",
177
+ "onloadstart",
178
+ "onmousedown",
179
+ "onmousemove",
180
+ "onmouseout",
181
+ "onmouseover",
182
+ "onmouseup",
183
+ "onmousewheel",
184
+ "onpause",
185
+ "onplay",
186
+ "onplaying",
187
+ "onprogress",
188
+ "onratechange",
189
+ "onreadystatechange",
190
+ "onreset",
191
+ "onscroll",
192
+ "onseeked",
193
+ "onseeking",
194
+ "onselect",
195
+ "onshow",
196
+ "onstalled",
197
+ "onsubmit",
198
+ "onsuspend",
199
+ "ontimeupdate",
200
+ "onvolumechange",
201
+ "onwaiting",
202
+ "prefix",
203
+ "property",
204
+ "rel",
205
+ "resource",
206
+ "rev",
207
+ "role",
208
+ "rowspan",
209
+ "spellcheck",
210
+ "src",
211
+ "style",
212
+ "tabindex",
213
+ "target",
214
+ "title",
215
+ // 去除重复
216
+ "type",
217
+ "typeof",
218
+ "vocab",
219
+ "xml:base",
220
+ "xml:lang",
221
+ "xml:space"
222
+ ];
223
+ const ALLOWED_XHTML11_TAGS = [
224
+ "a",
225
+ "abbr",
226
+ "acronym",
227
+ "address",
228
+ "applet",
229
+ "b",
230
+ "bar",
231
+ "basefont",
232
+ "bdo",
233
+ "big",
234
+ "blockquote",
235
+ "br",
236
+ "caption",
237
+ "center",
238
+ "cite",
239
+ "code",
240
+ "col",
241
+ "colgroup",
242
+ "dd",
243
+ "del",
244
+ "dfn",
245
+ "div",
246
+ "dl",
247
+ "dt",
248
+ "em",
249
+ "embed",
250
+ "font",
251
+ "h1",
252
+ "h2",
253
+ "h3",
254
+ "h4",
255
+ "h5",
256
+ "h6",
257
+ "hr",
258
+ "i",
259
+ "iframe",
260
+ "img",
261
+ "ins",
262
+ "kbd",
263
+ "li",
264
+ "map",
265
+ "noscript",
266
+ "ns:svg",
267
+ "object",
268
+ // 去除重复
269
+ "ol",
270
+ "p",
271
+ "param",
272
+ "pre",
273
+ "q",
274
+ "s",
275
+ "samp",
276
+ "script",
277
+ "small",
278
+ "span",
279
+ "strike",
280
+ "strong",
281
+ "sub",
282
+ "sup",
283
+ "table",
284
+ "tbody",
285
+ "td",
286
+ "tfoot",
287
+ "th",
288
+ "thead",
289
+ "tr",
290
+ "tt",
291
+ "u",
292
+ "ul",
293
+ "var"
294
+ ];
295
+ const ALLOWED_ATTRIBUTES_SET = new Set(ALLOWED_ATTRIBUTES);
296
+ const ALLOWED_XHTML11_TAGS_SET = new Set(ALLOWED_XHTML11_TAGS);
297
+ const SELF_CLOSING_TAGS = /* @__PURE__ */ new Set(["img", "br", "hr"]);
298
+ function initializeChapterInfo(content, index, epubConfigs) {
299
+ const chapter = { ...content };
51
300
  let { filename } = chapter;
52
301
  if (!filename) {
53
- let titleSlug = uslug(diacritics.remove(chapter.title || "no title"));
54
- titleSlug = titleSlug.replace(/[\/\\]/g, "_");
302
+ let titleSlug = uslug(remove(chapter.title || "no title"));
303
+ titleSlug = titleSlug.replace(/[/\\]/g, "_");
55
304
  chapter.href = `${index}_${titleSlug}.xhtml`;
56
305
  chapter.filePath = path.join(epubConfigs.dir, "OEBPS", chapter.href);
57
306
  } else {
58
307
  filename = safeFineName(filename);
59
- let is_xhtml = filename.endsWith(".xhtml");
308
+ const is_xhtml = filename.endsWith(".xhtml");
60
309
  chapter.href = is_xhtml ? filename : `${filename}.xhtml`;
61
310
  if (is_xhtml) {
62
311
  chapter.filePath = path.join(epubConfigs.dir, "OEBPS", filename);
@@ -68,311 +317,258 @@ function parseContent(content, index, epubConfigs) {
68
317
  chapter.dir = path.dirname(chapter.filePath);
69
318
  chapter.excludeFromToc = chapter.excludeFromToc || false;
70
319
  chapter.beforeToc = chapter.beforeToc || false;
320
+ return chapter;
321
+ }
322
+ function normalizeAuthorInfo(chapter) {
71
323
  if (chapter.author && typeof chapter.author === "string") {
72
324
  chapter.author = [chapter.author];
73
325
  } else if (!chapter.author || !Array.isArray(chapter.author)) {
74
326
  chapter.author = [];
75
327
  }
76
- let allowedAttributes = [
77
- "content",
78
- "alt",
79
- "id",
80
- "title",
81
- "src",
82
- "href",
83
- "about",
84
- "accesskey",
85
- "aria-activedescendant",
86
- "aria-atomic",
87
- "aria-autocomplete",
88
- "aria-busy",
89
- "aria-checked",
90
- "aria-controls",
91
- "aria-describedat",
92
- "aria-describedby",
93
- "aria-disabled",
94
- "aria-dropeffect",
95
- "aria-expanded",
96
- "aria-flowto",
97
- "aria-grabbed",
98
- "aria-haspopup",
99
- "aria-hidden",
100
- "aria-invalid",
101
- "aria-label",
102
- "aria-labelledby",
103
- "aria-level",
104
- "aria-live",
105
- "aria-multiline",
106
- "aria-multiselectable",
107
- "aria-orientation",
108
- "aria-owns",
109
- "aria-posinset",
110
- "aria-pressed",
111
- "aria-readonly",
112
- "aria-relevant",
113
- "aria-required",
114
- "aria-selected",
115
- "aria-setsize",
116
- "aria-sort",
117
- "aria-valuemax",
118
- "aria-valuemin",
119
- "aria-valuenow",
120
- "aria-valuetext",
121
- "class",
122
- "content",
123
- "contenteditable",
124
- "contextmenu",
125
- "datatype",
126
- "dir",
127
- "draggable",
128
- "dropzone",
129
- "hidden",
130
- "hreflang",
131
- "id",
132
- "inlist",
133
- "itemid",
134
- "itemref",
135
- "itemscope",
136
- "itemtype",
137
- "lang",
138
- "media",
139
- "ns1:type",
140
- "ns2:alphabet",
141
- "ns2:ph",
142
- "onabort",
143
- "onblur",
144
- "oncanplay",
145
- "oncanplaythrough",
146
- "onchange",
147
- "onclick",
148
- "oncontextmenu",
149
- "ondblclick",
150
- "ondrag",
151
- "ondragend",
152
- "ondragenter",
153
- "ondragleave",
154
- "ondragover",
155
- "ondragstart",
156
- "ondrop",
157
- "ondurationchange",
158
- "onemptied",
159
- "onended",
160
- "onerror",
161
- "onfocus",
162
- "oninput",
163
- "oninvalid",
164
- "onkeydown",
165
- "onkeypress",
166
- "onkeyup",
167
- "onload",
168
- "onloadeddata",
169
- "onloadedmetadata",
170
- "onloadstart",
171
- "onmousedown",
172
- "onmousemove",
173
- "onmouseout",
174
- "onmouseover",
175
- "onmouseup",
176
- "onmousewheel",
177
- "onpause",
178
- "onplay",
179
- "onplaying",
180
- "onprogress",
181
- "onratechange",
182
- "onreadystatechange",
183
- "onreset",
184
- "onscroll",
185
- "onseeked",
186
- "onseeking",
187
- "onselect",
188
- "onshow",
189
- "onstalled",
190
- "onsubmit",
191
- "onsuspend",
192
- "ontimeupdate",
193
- "onvolumechange",
194
- "onwaiting",
195
- "prefix",
196
- "property",
197
- "rel",
198
- "resource",
199
- "rev",
200
- "role",
201
- "spellcheck",
202
- "style",
203
- "tabindex",
204
- "target",
205
- "title",
206
- "type",
207
- "typeof",
208
- "vocab",
209
- "xml:base",
210
- "xml:lang",
211
- "xml:space",
212
- "colspan",
213
- "rowspan",
214
- "epub:type",
215
- "epub:prefix"
216
- ];
217
- let allowedXhtml11Tags = [
218
- "div",
219
- "p",
220
- "h1",
221
- "h2",
222
- "h3",
223
- "h4",
224
- "h5",
225
- "h6",
226
- "ul",
227
- "ol",
228
- "li",
229
- "dl",
230
- "dt",
231
- "dd",
232
- "address",
233
- "hr",
234
- "pre",
235
- "blockquote",
236
- "center",
237
- "ins",
238
- "del",
239
- "a",
240
- "span",
241
- "bdo",
242
- "br",
243
- "em",
244
- "strong",
245
- "dfn",
246
- "code",
247
- "samp",
248
- "kbd",
249
- "bar",
250
- "cite",
251
- "abbr",
252
- "acronym",
253
- "q",
254
- "sub",
255
- "sup",
256
- "tt",
257
- "i",
258
- "b",
259
- "big",
260
- "small",
261
- "u",
262
- "s",
263
- "strike",
264
- "basefont",
265
- "font",
266
- "object",
267
- "param",
268
- "img",
269
- "table",
270
- "caption",
271
- "colgroup",
272
- "col",
273
- "thead",
274
- "tfoot",
275
- "tbody",
276
- "tr",
277
- "th",
278
- "td",
279
- "embed",
280
- "applet",
281
- "iframe",
282
- "img",
283
- "map",
284
- "noscript",
285
- "ns:svg",
286
- "object",
287
- "script",
288
- "table",
289
- "tt",
290
- "var"
291
- ];
292
- let $ = cheerio.load(chapter.data, {
293
- lowerCaseTags: true,
294
- recognizeSelfClosing: true
295
- });
296
- let body = $("body");
297
- if (body.length) {
298
- let html = body.html();
299
- if (html) {
300
- $ = cheerio.load(html, {
328
+ }
329
+ function getAllowedAttributes() {
330
+ return ALLOWED_ATTRIBUTES;
331
+ }
332
+ function getAllowedXhtml11Tags() {
333
+ return ALLOWED_XHTML11_TAGS;
334
+ }
335
+ function loadAndProcessHtml(data) {
336
+ if (!data || typeof data !== "string") {
337
+ throw new Error("Invalid HTML data: data must be a non-empty string");
338
+ }
339
+ const trimmedData = data.trim();
340
+ if (trimmedData.length === 0) {
341
+ throw new Error("Invalid HTML data: data cannot be empty or whitespace only");
342
+ }
343
+ try {
344
+ let $ = cheerio.load(trimmedData, {
345
+ xml: {
301
346
  lowerCaseTags: true,
302
347
  recognizeSelfClosing: true
303
- });
348
+ }
349
+ });
350
+ const body = $("body");
351
+ if (body.length) {
352
+ const html = body.html();
353
+ if (html) {
354
+ $ = cheerio.load(html, {
355
+ xml: {
356
+ lowerCaseTags: true,
357
+ recognizeSelfClosing: true
358
+ }
359
+ });
360
+ }
304
361
  }
362
+ return $;
363
+ } catch (error) {
364
+ throw new Error(
365
+ `Failed to parse HTML content: ${error instanceof Error ? error.message : "Unknown error"}`
366
+ );
305
367
  }
368
+ }
369
+ function processHtmlElements($, allowedAttributes, allowedXhtml11Tags, epubConfigs, index) {
370
+ const allowedAttrsSet = ALLOWED_ATTRIBUTES_SET;
371
+ const allowedTagsSet = ALLOWED_XHTML11_TAGS_SET;
372
+ const selfClosingTags = SELF_CLOSING_TAGS;
306
373
  $($("*").get().reverse()).each(function(elemIndex, elem) {
307
- let attrs = elem.attribs;
308
- let that = this;
309
- let tags = ["img", "br", "hr"];
310
- if (tags.includes(that.name)) {
311
- if (that.name === "img" && !$(that).attr("alt")) {
312
- $(that).attr("alt", "image-placeholder");
374
+ const attrs = elem.attribs || {};
375
+ const $elem = $(elem);
376
+ const tagName = elem.name;
377
+ if (selfClosingTags.has(tagName)) {
378
+ if (tagName === "img" && !$elem.attr("alt")) {
379
+ $elem.attr("alt", "image-placeholder");
313
380
  }
314
381
  }
315
- Object.entries(attrs).map(([k, v]) => {
316
- if (allowedAttributes.includes(k)) {
317
- if (k === "type" && that.name !== "script") {
318
- $(that).removeAttr(k);
382
+ const attrsToRemove = [];
383
+ for (const [attrName] of Object.entries(attrs)) {
384
+ if (allowedAttrsSet.has(attrName)) {
385
+ if (attrName === "type" && tagName !== "script") {
386
+ attrsToRemove.push(attrName);
319
387
  }
320
388
  } else {
321
- $(that).removeAttr(k);
389
+ attrsToRemove.push(attrName);
322
390
  }
323
- });
391
+ }
392
+ for (const attrName of attrsToRemove) {
393
+ $elem.removeAttr(attrName);
394
+ }
324
395
  if (epubConfigs.version === 2) {
325
- if (!allowedXhtml11Tags.includes(that.name)) {
396
+ if (!allowedTagsSet.has(tagName)) {
326
397
  if (epubConfigs.verbose) {
327
- console.log(
328
- "Warning (content[" + index + "]):",
329
- that.name,
330
- "tag isn't allowed on EPUB 2/XHTML 1.1 DTD."
398
+ logger.warn(
399
+ `Warning (content[${index}]): ${tagName} tag isn't allowed on EPUB 2/XHTML 1.1 DTD.`
331
400
  );
332
401
  }
333
- let child = $(that).html();
334
- $(that).replaceWith($("<div>" + child + "</div>"));
402
+ const child = $elem.html();
403
+ $elem.replaceWith($("<div>" + child + "</div>"));
335
404
  }
336
405
  }
337
406
  });
338
- $("img").each((index2, elem) => {
339
- let url = $(elem).attr("src") || "";
340
- let image = epubConfigs.images.find((el) => el.url === url);
407
+ }
408
+ function processImages($, chapter, epubConfigs) {
409
+ $("img").each((index, elem) => {
410
+ const url = $(elem).attr("src") || "";
411
+ if (!url || url.trim().length === 0) {
412
+ logger.warn(`Image at index ${index} in chapter has empty src attribute, removing element`);
413
+ $(elem).remove();
414
+ return;
415
+ }
416
+ const trimmedUrl = url.trim();
417
+ try {
418
+ if (!trimmedUrl.match(/^(https?:\/\/|data:|\.\/|\/)/)) {
419
+ logger.warn(`Image URL "${trimmedUrl}" appears to be invalid, but processing anyway`);
420
+ }
421
+ } catch (error) {
422
+ logger.error(`Error validating image URL "${trimmedUrl}": ${error}`);
423
+ }
424
+ const image = epubConfigs.images.find((el) => el.url === trimmedUrl);
341
425
  let id;
342
426
  let extension;
343
427
  if (image) {
344
428
  id = image.id;
345
429
  extension = image.extension;
346
430
  } else {
347
- id = uuid.v4();
348
- let mediaType = mime.getType(url.replace(/\?.*/, "")) || "";
349
- extension = mime.getExtension(mediaType) || "";
350
- let dir = chapter.dir || "";
351
- let img = { id, url, dir, mediaType, extension };
431
+ id = v4();
432
+ let mediaType = "";
433
+ try {
434
+ const cleanUrl = trimmedUrl.replace(/\?.*/, "");
435
+ mediaType = mime$1.getType(cleanUrl) || "";
436
+ if (!mediaType) {
437
+ const urlExtension = cleanUrl.split(".").pop()?.toLowerCase();
438
+ if (urlExtension && ["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(urlExtension)) {
439
+ mediaType = `image/${urlExtension === "jpg" ? "jpeg" : urlExtension}`;
440
+ logger.warn(
441
+ `Could not determine MIME type for "${trimmedUrl}", inferred as "${mediaType}"`
442
+ );
443
+ } else {
444
+ logger.warn(
445
+ `Could not determine MIME type for "${trimmedUrl}", defaulting to image/jpeg`
446
+ );
447
+ mediaType = "image/jpeg";
448
+ }
449
+ }
450
+ } catch (error) {
451
+ logger.error(`Error determining MIME type for "${trimmedUrl}": ${error}`);
452
+ mediaType = "image/jpeg";
453
+ }
454
+ try {
455
+ extension = mime$1.getExtension(mediaType) || "jpg";
456
+ } catch (error) {
457
+ logger.error(`Error getting extension for MIME type "${mediaType}": ${error}`);
458
+ extension = "jpg";
459
+ }
460
+ const dir = chapter.dir || "";
461
+ const img = { id, url: trimmedUrl, dir, mediaType, extension };
352
462
  epubConfigs.images.push(img);
463
+ if (epubConfigs.verbose) {
464
+ logger.info(`Added image: ${trimmedUrl} -> images/${id}.${extension} (${mediaType})`);
465
+ }
353
466
  }
354
- $(elem).attr("src", `images/${id}.${extension}`);
467
+ try {
468
+ $(elem).attr("src", `images/${id}.${extension}`);
469
+ } catch (error) {
470
+ logger.error(`Error setting src attribute for image ${id}: ${error}`);
471
+ $(elem).remove();
472
+ }
473
+ });
474
+ }
475
+ function extractAndCleanHtmlContent($, originalData) {
476
+ let data;
477
+ if ($("body").length) {
478
+ data = $("body").html() || "";
479
+ } else {
480
+ data = $.root().html() || "";
481
+ }
482
+ if (!originalData) {
483
+ return data.replace(
484
+ /<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)><\/\1>/gi,
485
+ "<$1$2/>"
486
+ ).replace(
487
+ new RegExp("<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)(?<!\\/)>", "gi"),
488
+ "<$1$2/>"
489
+ );
490
+ }
491
+ const entityMap = /* @__PURE__ */ new Map();
492
+ const entityRegex = /&[a-zA-Z][a-zA-Z0-9]*;|&#[0-9]+;|&#x[0-9a-fA-F]+;/g;
493
+ const matches = Array.from(originalData.matchAll(entityRegex));
494
+ let processedOriginal = originalData;
495
+ const timestamp = Date.now();
496
+ const randomId = Math.random().toString(36).substring(2, 8);
497
+ const placeholderPrefix = `__ENTITY_${timestamp}_${randomId}_`;
498
+ for (let i = matches.length - 1; i >= 0; i--) {
499
+ const match = matches[i];
500
+ const placeholder = `${placeholderPrefix}${i}__`;
501
+ entityMap.set(placeholder, match[0]);
502
+ processedOriginal = processedOriginal.substring(0, match.index) + placeholder + processedOriginal.substring(match.index + match[0].length);
503
+ }
504
+ const $temp = cheerio.load(processedOriginal, {
505
+ xmlMode: false
355
506
  });
356
- chapter.data = $.xml();
507
+ let tempData;
508
+ if ($temp("body").length) {
509
+ tempData = $temp("body").html() || "";
510
+ } else {
511
+ tempData = $temp.root().html() || "";
512
+ }
513
+ for (const [placeholder, entity] of entityMap) {
514
+ tempData = tempData.replace(new RegExp(placeholder, "g"), entity);
515
+ }
516
+ return tempData.replace(
517
+ /<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)><\/\1>/gi,
518
+ "<$1$2/>"
519
+ ).replace(
520
+ new RegExp("<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)(?<!\\/)>", "gi"),
521
+ "<$1$2/>"
522
+ );
523
+ }
524
+ function processChildrenChapters(chapter, index, epubConfigs) {
357
525
  if (Array.isArray(chapter.children)) {
358
526
  chapter.children = chapter.children.map(
359
- (content2, idx) => parseContent(content2, `${index}_${idx}`, epubConfigs)
527
+ (content, idx) => parseContent(content, `${index}_${idx}`, epubConfigs)
360
528
  );
361
529
  }
530
+ }
531
+ function parseContent(content, index, epubConfigs) {
532
+ if (!content) {
533
+ throw new Error("Content cannot be null or undefined");
534
+ }
535
+ if (!content.data) {
536
+ logger.warn(`Chapter at index ${index} has no data, using empty string`);
537
+ content.data = "";
538
+ }
539
+ const chapter = initializeChapterInfo(content, index, epubConfigs);
540
+ normalizeAuthorInfo(chapter);
541
+ const allowedAttributes = getAllowedAttributes();
542
+ const allowedXhtml11Tags = getAllowedXhtml11Tags();
543
+ if (!chapter.data || chapter.data.trim().length === 0) {
544
+ logger.warn(`Chapter at index ${index} has empty data, setting empty content`);
545
+ chapter.data = "";
546
+ } else {
547
+ let $;
548
+ try {
549
+ $ = loadAndProcessHtml(chapter.data);
550
+ } catch (error) {
551
+ logger.error(`Failed to process HTML for chapter ${index}: ${error}`);
552
+ $ = cheerio.load(`<div>${chapter.data}</div>`);
553
+ }
554
+ processHtmlElements($, allowedAttributes, allowedXhtml11Tags, epubConfigs, index);
555
+ processImages($, chapter, epubConfigs);
556
+ chapter.data = extractAndCleanHtmlContent($, content.data);
557
+ }
558
+ processChildrenChapters(chapter, index, epubConfigs);
362
559
  return chapter;
363
560
  }
364
- const readFile = util.promisify(fs__namespace.readFile);
365
- const writeFile = util.promisify(fs__namespace.writeFile);
561
+ promisify(fs.readFile);
562
+ const writeFile = promisify(fs.writeFile);
366
563
  const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36";
367
564
  const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
368
565
  async function fileIsStable(filename, max_wait = 3e4) {
369
- let start_time = (/* @__PURE__ */ new Date()).getTime();
370
- let last_size = fs__namespace.statSync(filename).size;
566
+ const start_time = (/* @__PURE__ */ new Date()).getTime();
567
+ let last_size = fs.statSync(filename).size;
371
568
  while ((/* @__PURE__ */ new Date()).getTime() - start_time <= max_wait) {
372
569
  await wait(1e3);
373
- let size = fs__namespace.statSync(filename).size;
374
- if (size === last_size)
375
- return true;
570
+ const size = fs.statSync(filename).size;
571
+ if (size === last_size) return true;
376
572
  last_size = size;
377
573
  }
378
574
  return false;
@@ -382,26 +578,31 @@ function simpleMinifier(xhtml) {
382
578
  return xhtml;
383
579
  }
384
580
  const downloadImage = async (epubData, options) => {
385
- let { url } = options;
386
- let { log } = epubData;
387
- let epub_dir = epubData.dir;
581
+ const { url } = options;
582
+ const { log } = epubData;
583
+ const epub_dir = epubData.dir;
388
584
  if (!url) {
389
585
  return;
390
586
  }
391
- let image_dir = path__namespace.join(epub_dir, "OEBPS", "images");
392
- fs__namespace$1.ensureDirSync(image_dir);
393
- let filename = path__namespace.join(image_dir, options.id + "." + options.extension);
587
+ const image_dir = path.join(epub_dir, "OEBPS", "images");
588
+ fs$1.ensureDirSync(image_dir);
589
+ const filename = path.join(image_dir, options.id + "." + options.extension);
394
590
  if (url.startsWith("file://") || url.startsWith("/")) {
395
- let auxPath = url.replace(/^file:\/\//i, "");
591
+ let aux_path = url.replace(/^file:\/\//i, "");
592
+ try {
593
+ aux_path = decodeURIComponent(aux_path);
594
+ } catch {
595
+ log(`[URL Decode Warning] Failed to decode path: ${aux_path}`);
596
+ }
396
597
  if (process.platform === "win32") {
397
- if (auxPath.match(/^\/[a-zA-Z]:/)) {
398
- auxPath = auxPath.replace(/^\//, "");
598
+ if (aux_path.match(/^\/[a-zA-Z]:/)) {
599
+ aux_path = aux_path.replace(/^\//, "");
399
600
  }
400
601
  }
401
- log(`[Copy 1] '${auxPath}' to '${filename}'`);
402
- if (fs__namespace$1.existsSync(auxPath)) {
602
+ log(`[Copy 1] '${aux_path}' to '${filename}'`);
603
+ if (fs$1.existsSync(aux_path)) {
403
604
  try {
404
- fs__namespace$1.copySync(auxPath, filename);
605
+ fs$1.copySync(aux_path, filename);
405
606
  } catch (e) {
406
607
  log("[Copy 1 Error] " + e.message);
407
608
  }
@@ -412,18 +613,18 @@ const downloadImage = async (epubData, options) => {
412
613
  }
413
614
  let requestAction;
414
615
  if (url.startsWith("http")) {
415
- requestAction = request__namespace.get(url).set({ "User-Agent": USER_AGENT });
416
- requestAction.pipe(fs__namespace$1.createWriteStream(filename));
616
+ requestAction = request.get(url).set({ "User-Agent": USER_AGENT });
617
+ requestAction.pipe(fs$1.createWriteStream(filename));
417
618
  } else {
418
619
  log(`[Copy 2] '${url}' to '${filename}'`);
419
- requestAction = fs__namespace$1.createReadStream(path__namespace.join(options.dir || "", url));
420
- requestAction.pipe(fs__namespace$1.createWriteStream(filename));
620
+ requestAction = fs$1.createReadStream(path.join(options.dir || "", url));
621
+ requestAction.pipe(fs$1.createWriteStream(filename));
421
622
  }
422
- return new Promise((resolve, reject) => {
623
+ return new Promise((resolve, _reject) => {
423
624
  requestAction.on("error", (err) => {
424
625
  log("[Download Error] Error while downloading: " + url);
425
626
  log(err);
426
- fs__namespace$1.unlinkSync(filename);
627
+ fs$1.unlinkSync(filename);
427
628
  resolve();
428
629
  });
429
630
  requestAction.on("end", () => {
@@ -433,43 +634,339 @@ const downloadImage = async (epubData, options) => {
433
634
  });
434
635
  };
435
636
  const downloadAllImages = async (epubData) => {
436
- let { images } = epubData;
437
- if (images.length === 0)
438
- return;
439
- fs__namespace$1.ensureDirSync(path__namespace.join(epubData.dir, "OEBPS", "images"));
440
- for (let image of images) {
637
+ const { images } = epubData;
638
+ if (images.length === 0) return;
639
+ fs$1.ensureDirSync(path.join(epubData.dir, "OEBPS", "images"));
640
+ for (const image of images) {
441
641
  await downloadImage(epubData, image);
442
642
  }
443
643
  };
644
+ const epub2_content_opf_ejs = `<?xml version="1.0" encoding="UTF-8"?>
645
+ <package xmlns="http://www.idpf.org/2007/opf"
646
+ version="2.0"
647
+ unique-identifier="BookId">
648
+
649
+ <metadata xmlns:dc="http://purl.org/dc/elements/1.1/"
650
+ xmlns:opf="http://www.idpf.org/2007/opf">
651
+
652
+ <dc:identifier id="BookId" opf:scheme="URN"><%= id %></dc:identifier>
653
+ <dc:title><%= title %></dc:title>
654
+ <dc:description><%= description %></dc:description>
655
+ <dc:publisher><%= publisher || "anonymous" %></dc:publisher>
656
+ <dc:creator opf:role="aut" opf:file-as="<%= author.length ? author.join(",") : author %>"><%= author.length ? author.join(",") : author %></dc:creator>
657
+ <dc:date opf:event="modification"><%= date %></dc:date>
658
+ <dc:language><%= lang || "en" %></dc:language>
659
+ <meta name="cover" content="image_cover"/>
660
+ <meta name="generator" content="epub-gen"/>
661
+
662
+ </metadata>
663
+
664
+ <manifest>
665
+ <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
666
+ <item id="toc" href="toc.xhtml" media-type="application/xhtml+xml"/>
667
+ <item id="css" href="style.css" media-type="text/css"/>
668
+
669
+ <% if(locals.cover) { %>
670
+ <item id="image_cover" href="cover.<%= _coverExtension %>" media-type="<%= _coverMediaType %>"/>
671
+ <% } %>
672
+
673
+ <% images.forEach(function(image, index){ %>
674
+ <item id="image_<%= index %>" href="images/<%= image.id %>.<%= image.extension %>" media-type="<%= image.mediaType %>"/>
675
+ <% }) %>
676
+
677
+ <% content.forEach(function(content, index){ %>
678
+ <item id="content_<%= index %>_<%= content.id %>" href="<%= content.href %>" media-type="application/xhtml+xml"/>
679
+ <% }) %>
680
+
681
+ <% fonts.forEach(function(font, index) { %>
682
+ <item id="font_<%= index %>" href="fonts/<%= font %>" media-type="application/x-font-ttf"/>
683
+ <% }) %>
684
+ </manifest>
685
+
686
+ <spine toc="ncx">
687
+ <% content.forEach(function(content, index){ %>
688
+ <% if(content.beforeToc && !content.excludeFromToc){ %>
689
+ <itemref idref="content_<%= index %>_<%= content.id %>"/>
690
+ <% } %>
691
+ <% }) %>
692
+ <itemref idref="toc"/>
693
+ <% content.forEach(function(content, index){ %>
694
+ <% if(!content.beforeToc && !content.excludeFromToc){ %>
695
+ <itemref idref="content_<%= index %>_<%= content.id %>"/>
696
+ <% } %>
697
+ <% }) %>
698
+ </spine>
699
+ <guide/>
700
+ </package>
701
+ `;
702
+ const epub2_toc_xhtml_ejs = `<?xml version="1.0" encoding="UTF-8"?>
703
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
704
+ <html xml:lang="<%- lang %>" xmlns="http://www.w3.org/1999/xhtml">
705
+ <head>
706
+ <title><%= title %></title>
707
+ <meta charset="UTF-8"/>
708
+ <link rel="stylesheet" type="text/css" href="style.css"/>
709
+ </head>
710
+ <body>
711
+ <h1 class="h1"><%= tocTitle %></h1>
712
+ <% content.forEach(function(content, index){ %>
713
+ <% if(!content.excludeFromToc){ %>
714
+ <p class="table-of-content">
715
+ <a href="<%= content.href %>"><%= (1 + index) + ". " + (content.title || "Chapter " + (1 + index)) %>
716
+ <% if(content.author.length){ %>
717
+ - <small class="toc-author"><%= content.author.join(",") %></small>
718
+ <% } %>
719
+ <% if(content.url){ %><span class="toc-link"><%= content.url %></span>
720
+ <% }else{ %><span class="toc-link"></span>
721
+ <% } %>
722
+ </a>
723
+ </p>
724
+ <% } %>
725
+ <% }) %>
726
+ </body>
727
+ </html>
728
+ `;
729
+ const epub3_content_opf_ejs = `<?xml version="1.0" encoding="UTF-8"?>
730
+ <package xmlns="http://www.idpf.org/2007/opf"
731
+ version="3.0"
732
+ unique-identifier="BookId"
733
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
734
+ xmlns:dcterms="http://purl.org/dc/terms/"
735
+ xml:lang="en"
736
+ xmlns:media="http://www.idpf.org/epub/vocab/overlays/#"
737
+ prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/">
738
+
739
+ <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
740
+
741
+ <dc:identifier id="BookId"><%= id %></dc:identifier>
742
+ <meta refines="#BookId" property="identifier-type" scheme="onix:codelist5">22</meta>
743
+ <meta property="dcterms:identifier" id="meta-identifier">BookId</meta>
744
+ <dc:title><%= title %></dc:title>
745
+ <meta property="dcterms:title" id="meta-title"><%= title %></meta>
746
+ <dc:language><%= lang || "en" %></dc:language>
747
+ <meta property="dcterms:language" id="meta-language"><%= lang || "en" %></meta>
748
+ <meta property="dcterms:modified"><%= (new Date()).toISOString().split(".")[0] + "Z" %></meta>
749
+ <dc:creator id="creator"><%= author.length ? author.join(",") : author %></dc:creator>
750
+ <meta refines="#creator" property="file-as"><%= author.length ? author.join(",") : author %></meta>
751
+ <meta property="dcterms:publisher"><%= publisher || "anonymous" %></meta>
752
+ <dc:publisher><%= publisher || "anonymous" %></dc:publisher>
753
+ <% var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var stringDate = "" + year + "-" + month + "-" + day; %>
754
+ <meta property="dcterms:date"><%= stringDate %></meta>
755
+ <dc:date><%= stringDate %></dc:date>
756
+ <meta property="dcterms:rights">All rights reserved</meta>
757
+ <dc:rights>Copyright &#x00A9; <%= (new Date()).getFullYear() %> by <%= publisher || "anonymous" %></dc:rights>
758
+ <% if(locals.cover) { %>
759
+ <meta name="cover" content="image_cover" />
760
+ <% } %>
761
+ <meta name="generator" content="epub-gen" />
762
+ <meta property="ibooks:specified-fonts">true</meta>
763
+ </metadata>
764
+
765
+ <manifest>
766
+ <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
767
+ <item id="toc" href="toc.xhtml" media-type="application/xhtml+xml" properties="nav" />
768
+ <item id="css" href="style.css" media-type="text/css" />
769
+
770
+ <% if(locals.cover) { %>
771
+ <item id="image_cover" href="cover.<%= _coverExtension %>" media-type="<%= _coverMediaType %>" />
772
+ <% } %>
773
+
774
+ <% images.forEach(function(image, index){ %>
775
+ <item id="image_<%= index %>" href="images/<%= image.id %>.<%= image.extension %>" media-type="<%= image.mediaType %>" />
776
+ <% }) %>
777
+
778
+ <% function renderContentItem(content) { %>
779
+ <% content.forEach(function(content){ %>
780
+ <item id="content_<%= content.id %>" href="<%= content.href %>" media-type="application/xhtml+xml" />
781
+ <% if (Array.isArray(content.children)) { %>
782
+ <% renderContentItem(content.children) %>
783
+ <% } %>
784
+ <% }) %>
785
+ <% } %>
786
+ <% renderContentItem(content) %>
787
+
788
+ <% fonts.forEach(function(font, index){ %>
789
+ <item id="font_<%= index %>" href="fonts/<%= font %>" media-type="application/x-font-ttf" />
790
+ <% }) %>
791
+ </manifest>
792
+
793
+ <spine toc="ncx">
794
+ <% var nodes_1 = content.filter(item => !item.excludeFromToc && item.beforeToc) %>
795
+ <% var nodes_2 = content.filter(item => !item.excludeFromToc && !item.beforeToc) %>
796
+ <% function renderToc(nodes) { %>
797
+ <% nodes.forEach(function(content){ %>
798
+ <itemref idref="content_<%= content.id %>" />
799
+ <% if (Array.isArray(content.children)) { %>
800
+ <% renderToc(content.children) %>
801
+ <% } %>
802
+ <% }) %>
803
+ <% } %>
804
+ <% renderToc(nodes_1) %>
805
+ <itemref idref="toc" />
806
+ <% renderToc(nodes_2) %>
807
+ </spine>
808
+ <guide>
809
+ <reference type="text" title="Table of Content" href="toc.xhtml" />
810
+ </guide>
811
+ </package>
812
+ `;
813
+ const epub3_toc_xhtml_ejs = `<?xml version="1.0" encoding="UTF-8"?>
814
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
815
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="<%- lang %>"
816
+ lang="<%- lang %>">
817
+ <head>
818
+ <title><%= title %></title>
819
+ <meta charset="UTF-8" />
820
+ <link rel="stylesheet" type="text/css" href="style.css" />
821
+ </head>
822
+ <body>
823
+ <h1 class="h1"><%= tocTitle %></h1>
824
+ <nav id="toc" class="TOC" epub:type="toc">
825
+ <% var nodes_1 = content.filter(item => !item.excludeFromToc && item.beforeToc) %>
826
+ <% var nodes_2 = content.filter(item => !item.excludeFromToc && !item.beforeToc) %>
827
+ <% function renderToc(nodes, indent = 0) { %>
828
+ <ol>
829
+ <% nodes.forEach(function(content, index){ %>
830
+ <li class="table-of-content">
831
+ <a href="<%= content.href %>"><%= (content.title || "Chapter " + (1 + index)) %>
832
+ <% if(content.author.length){ %> - <small
833
+ class="toc-author"><%= content.author.join(",") %></small>
834
+ <% } %>
835
+ <% if(content.url){ %><span class="toc-link"><%= content.url %></span>
836
+ <% } %>
837
+ </a>
838
+ <% if (Array.isArray(content.children) && content.children.length > 0) { %>
839
+ <% renderToc(content.children, indent + 1) %>
840
+ <% } %>
841
+ </li>
842
+ <% }) %>
843
+ </ol>
844
+ <% } %>
845
+ <% renderToc(nodes_1) %>
846
+ <% renderToc(nodes_2) %>
847
+ </nav>
848
+
849
+ </body>
850
+ </html>
851
+ `;
852
+ const template_css = `.epub-author {
853
+ color: #555;
854
+ }
855
+
856
+ .epub-link {
857
+ margin-bottom: 30px;
858
+ }
859
+
860
+ .epub-link a {
861
+ color: #666;
862
+ font-size: 90%;
863
+ }
864
+
865
+ .toc-author {
866
+ font-size: 90%;
867
+ color: #555;
868
+ }
869
+
870
+ .toc-link {
871
+ color: #999;
872
+ font-size: 85%;
873
+ display: block;
874
+ }
875
+
876
+ hr {
877
+ border: 0;
878
+ border-bottom: 1px solid #dedede;
879
+ margin: 60px 10%;
880
+ }
881
+
882
+ .TOC > ol {
883
+ margin: 0;
884
+ padding: 0;
885
+ }
886
+
887
+ .TOC > ol ol {
888
+ padding: 0;
889
+ margin-left: 2em;
890
+ }
891
+
892
+ .TOC li {
893
+ font-size: 16px;
894
+ list-style: none;
895
+ margin: 0 auto;
896
+ padding: 0;
897
+ }
898
+ `;
899
+ const toc_ncx_ejs = `<?xml version="1.0" encoding="UTF-8"?>
900
+ <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
901
+ <head>
902
+ <meta name="dtb:uid" content="<%= id %>"/>
903
+ <meta name="dtb:generator" content="epub-gen"/>
904
+ <meta name="dtb:depth" content="<%= (toc_depth || 1)%>"/>
905
+ <meta name="dtb:totalPageCount" content="0"/>
906
+ <meta name="dtb:maxPageNumber" content="0"/>
907
+ </head>
908
+ <docTitle>
909
+ <text><%= title %></text>
910
+ </docTitle>
911
+ <docAuthor>
912
+ <text><%= author %></text>
913
+ </docAuthor>
914
+ <navMap>
915
+ <% var _index = 1; %>
916
+ <% var nodes_1 = content.filter(c => !c.excludeFromToc && c.beforeToc) %>
917
+ <% var nodes_2 = content.filter(c => !c.excludeFromToc && !c.beforeToc) %>
918
+ <% function renderToc(nodes) { %>
919
+ <% nodes.forEach(function(content, index){ %>
920
+ <navPoint id="content_<%= content.id %>" playOrder="<%= _index++ %>" class="chapter">
921
+ <navLabel>
922
+ <text><%= (tocAutoNumber ? ((1 + index) + ". ") : "") + (content.title || "Chapter " + (1 + index)) %></text>
923
+ </navLabel>
924
+ <content src="<%= content.href %>"/>
925
+ <% if (Array.isArray(content.children)) { %>
926
+ <% renderToc(content.children) %>
927
+ <% } %>
928
+ </navPoint>
929
+ <% }) %>
930
+ <% } %>
931
+
932
+ <% renderToc(nodes_1) %>
933
+
934
+ <navPoint id="toc" playOrder="<%= _index++ %>" class="chapter">
935
+ <navLabel>
936
+ <text><%= tocTitle %></text>
937
+ </navLabel>
938
+ <content src="toc.xhtml"/>
939
+ </navPoint>
940
+
941
+ <% renderToc(nodes_2) %>
942
+ </navMap>
943
+ </ncx>
944
+ `;
444
945
  const generateTempFile = async (epubData) => {
445
- var _a;
446
- let { log } = epubData;
447
- let oebps_dir = path__namespace.join(epubData.dir, "OEBPS");
448
- await fs__namespace$1.ensureDir(oebps_dir);
449
- const templates_dir = path__namespace.join(epubData.baseDir, "templates");
450
- epubData.css = epubData.css || await readFile(path__namespace.join(templates_dir, "template.css"), "utf-8");
451
- await writeFile(path__namespace.join(oebps_dir, "style.css"), epubData.css, "utf-8");
452
- if ((_a = epubData.fonts) == null ? void 0 : _a.length) {
453
- let fonts_dir = path__namespace.join(oebps_dir, "fonts");
454
- await fs__namespace$1.ensureDir(fonts_dir);
946
+ const { log } = epubData;
947
+ const oebps_dir = path.join(epubData.dir, "OEBPS");
948
+ await fs$1.ensureDir(oebps_dir);
949
+ epubData.css = epubData.css || template_css;
950
+ await writeFile(path.join(oebps_dir, "style.css"), epubData.css, "utf-8");
951
+ if (epubData.fonts?.length) {
952
+ const fonts_dir = path.join(oebps_dir, "fonts");
953
+ await fs$1.ensureDir(fonts_dir);
455
954
  epubData.fonts = epubData.fonts.map((font) => {
456
- let filename = path__namespace.basename(font);
457
- if (!fs__namespace$1.existsSync(font)) {
955
+ const filename = path.basename(font);
956
+ if (!fs$1.existsSync(font)) {
458
957
  log(`Custom font not found at '${font}'.`);
459
958
  } else {
460
- fs__namespace$1.copySync(font, path__namespace.join(fonts_dir, filename));
959
+ fs$1.copySync(font, path.join(fonts_dir, filename));
461
960
  }
462
961
  return filename;
463
962
  });
464
963
  }
465
964
  const isAppendTitle = (global_append, local_append) => {
466
- if (typeof local_append === "boolean")
467
- return local_append;
965
+ if (typeof local_append === "boolean") return local_append;
468
966
  return !!global_append;
469
967
  };
470
968
  const saveContentToFile = (content) => {
471
- var _a2;
472
- let title = entities__namespace.encodeXML(content.title || "");
969
+ const title = entities.encodeXML(content.title || "");
473
970
  let html = `${epubData.docHeader}
474
971
  <head>
475
972
  <meta charset="UTF-8" />
@@ -481,21 +978,21 @@ const generateTempFile = async (epubData) => {
481
978
  if (content.title && isAppendTitle(epubData.appendChapterTitles, content.appendChapterTitle)) {
482
979
  html += `<h1>${title}</h1>`;
483
980
  }
484
- html += content.title && content.author && ((_a2 = content.author) == null ? void 0 : _a2.length) ? `<p class='epub-author'>${entities__namespace.encodeXML(content.author.join(", "))}</p>` : "";
981
+ html += content.title && content.author && content.author?.length ? `<p class='epub-author'>${entities.encodeXML(content.author.join(", "))}</p>` : "";
485
982
  html += content.title && content.url ? `<p class="epub-link"><a href="${content.url}">${content.url}</a></p>` : "";
486
983
  html += `${content.data}`;
487
984
  html += "\n</body>\n</html>";
488
- fs__namespace$1.ensureDirSync(path__namespace.dirname(content.filePath));
489
- fs__namespace$1.writeFileSync(content.filePath, html, "utf-8");
985
+ fs$1.ensureDirSync(path.dirname(content.filePath));
986
+ fs$1.writeFileSync(content.filePath, html, "utf-8");
490
987
  if (Array.isArray(content.children)) {
491
988
  content.children.map(saveContentToFile);
492
989
  }
493
990
  };
494
991
  epubData.content.map(saveContentToFile);
495
- let metainf_dir = path__namespace.join(epubData.dir, "META-INF");
496
- fs__namespace$1.ensureDirSync(metainf_dir);
497
- fs__namespace$1.writeFileSync(
498
- path__namespace.join(metainf_dir, "container.xml"),
992
+ const metainf_dir = path.join(epubData.dir, "META-INF");
993
+ fs$1.ensureDirSync(metainf_dir);
994
+ fs$1.writeFileSync(
995
+ path.join(metainf_dir, "container.xml"),
499
996
  `<?xml version="1.0" encoding="UTF-8" ?>
500
997
  <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
501
998
  <rootfiles><rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/></rootfiles>
@@ -503,8 +1000,8 @@ const generateTempFile = async (epubData) => {
503
1000
  "utf-8"
504
1001
  );
505
1002
  if (epubData.version === 2) {
506
- let fn = path__namespace.join(metainf_dir, "com.apple.ibooks.display-options.xml");
507
- fs__namespace$1.writeFileSync(
1003
+ const fn = path.join(metainf_dir, "com.apple.ibooks.display-options.xml");
1004
+ fs$1.writeFileSync(
508
1005
  fn,
509
1006
  `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
510
1007
  <display_options>
@@ -516,50 +1013,53 @@ const generateTempFile = async (epubData) => {
516
1013
  "utf-8"
517
1014
  );
518
1015
  }
519
- let opfPath = epubData.customOpfTemplatePath || path__namespace.join(templates_dir, `epub${epubData.version}`, "content.opf.ejs");
520
- if (!fs__namespace$1.existsSync(opfPath)) {
521
- throw new Error("Custom file to OPF template not found.");
1016
+ let opfTemplate;
1017
+ let ncxTocTemplate;
1018
+ let htmlTocTemplate;
1019
+ if (epubData.customOpfTemplatePath && fs$1.existsSync(epubData.customOpfTemplatePath)) {
1020
+ const { readFile } = require("./libs/utils");
1021
+ opfTemplate = await readFile(epubData.customOpfTemplatePath, "utf-8");
1022
+ } else {
1023
+ opfTemplate = epubData.version === 2 ? epub2_content_opf_ejs : epub3_content_opf_ejs;
522
1024
  }
523
- let ncxTocPath = epubData.customNcxTocTemplatePath || path__namespace.join(templates_dir, `toc.ncx.ejs`);
524
- if (!fs__namespace$1.existsSync(ncxTocPath)) {
525
- throw new Error("Custom file the NCX toc template not found.");
1025
+ if (epubData.customNcxTocTemplatePath && fs$1.existsSync(epubData.customNcxTocTemplatePath)) {
1026
+ const { readFile } = require("./libs/utils");
1027
+ ncxTocTemplate = await readFile(epubData.customNcxTocTemplatePath, "utf-8");
1028
+ } else {
1029
+ ncxTocTemplate = toc_ncx_ejs;
526
1030
  }
527
- let htmlTocPath = epubData.customHtmlTocTemplatePath || path__namespace.join(templates_dir, `epub${epubData.version}`, "toc.xhtml.ejs");
528
- if (!fs__namespace$1.existsSync(htmlTocPath)) {
529
- throw new Error("Custom file to HTML toc template not found.");
1031
+ if (epubData.customHtmlTocTemplatePath && fs$1.existsSync(epubData.customHtmlTocTemplatePath)) {
1032
+ const { readFile } = require("./libs/utils");
1033
+ htmlTocTemplate = await readFile(epubData.customHtmlTocTemplatePath, "utf-8");
1034
+ } else {
1035
+ htmlTocTemplate = epubData.version === 2 ? epub2_toc_xhtml_ejs : epub3_toc_xhtml_ejs;
530
1036
  }
531
- let toc_depth = 1;
532
- fs__namespace$1.writeFileSync(
533
- path__namespace.join(oebps_dir, "content.opf"),
534
- await ejs__namespace.renderFile(opfPath, epubData),
535
- "utf-8"
536
- );
537
- fs__namespace$1.writeFileSync(
538
- path__namespace.join(oebps_dir, "toc.ncx"),
539
- await ejs__namespace.renderFile(ncxTocPath, { ...epubData, toc_depth }),
1037
+ const toc_depth = 1;
1038
+ fs$1.writeFileSync(path.join(oebps_dir, "content.opf"), ejs.render(opfTemplate, epubData), "utf-8");
1039
+ fs$1.writeFileSync(
1040
+ path.join(oebps_dir, "toc.ncx"),
1041
+ ejs.render(ncxTocTemplate, { ...epubData, toc_depth }),
540
1042
  "utf-8"
541
1043
  );
542
- fs__namespace$1.writeFileSync(
543
- path__namespace.join(oebps_dir, "toc.xhtml"),
544
- simpleMinifier(await ejs__namespace.renderFile(htmlTocPath, epubData)),
1044
+ fs$1.writeFileSync(
1045
+ path.join(oebps_dir, "toc.xhtml"),
1046
+ simpleMinifier(ejs.render(htmlTocTemplate, epubData)),
545
1047
  "utf-8"
546
1048
  );
547
1049
  };
548
1050
  async function makeCover(data) {
549
- let { cover, _coverExtension, log } = data;
550
- if (!cover)
551
- return;
552
- let destPath = path__namespace.join(data.dir, "OEBPS", `cover.${_coverExtension}`);
1051
+ const { cover, _coverExtension, log } = data;
1052
+ if (!cover) return;
1053
+ const destPath = path.join(data.dir, "OEBPS", `cover.${_coverExtension}`);
553
1054
  let writeStream = null;
554
1055
  if (cover.startsWith("http")) {
555
- writeStream = request__namespace.get(cover).set({ "User-Agent": USER_AGENT });
556
- writeStream.pipe(fs__namespace$1.createWriteStream(destPath));
1056
+ writeStream = request.get(cover).set({ "User-Agent": USER_AGENT });
1057
+ writeStream.pipe(fs$1.createWriteStream(destPath));
557
1058
  } else {
558
- if (!fs__namespace$1.existsSync(cover))
559
- return;
1059
+ if (!fs$1.existsSync(cover)) return;
560
1060
  log("local cover image: " + cover);
561
- writeStream = fs__namespace$1.createReadStream(cover);
562
- writeStream.pipe(fs__namespace$1.createWriteStream(destPath));
1061
+ writeStream = fs$1.createReadStream(cover);
1062
+ writeStream.pipe(fs$1.createWriteStream(destPath));
563
1063
  }
564
1064
  return new Promise((resolve) => {
565
1065
  writeStream.on("end", () => {
@@ -569,9 +1069,9 @@ async function makeCover(data) {
569
1069
  writeStream.on("error", (e) => {
570
1070
  log("[Error] cover image error: " + e.message);
571
1071
  log("destPath: " + destPath);
572
- if (fs__namespace$1.existsSync(destPath)) {
1072
+ if (fs$1.existsSync(destPath)) {
573
1073
  try {
574
- fs__namespace$1.unlinkSync(destPath);
1074
+ fs$1.unlinkSync(destPath);
575
1075
  log("destPath removed.");
576
1076
  } catch (e2) {
577
1077
  log("[Error] remove cover image error: " + e2.message);
@@ -582,7 +1082,7 @@ async function makeCover(data) {
582
1082
  });
583
1083
  }
584
1084
  async function render(data) {
585
- let { log } = data;
1085
+ const { log } = data;
586
1086
  log("Generating Template Files...");
587
1087
  await generateTempFile(data);
588
1088
  log("Downloading Images...");
@@ -599,14 +1099,14 @@ async function render(data) {
599
1099
  }
600
1100
  }
601
1101
  async function genEpub(epubData) {
602
- let { log, dir, output } = epubData;
603
- let archive = archiver("zip", { zlib: { level: 9 } });
604
- let outputStream = fs$1.createWriteStream(epubData.output);
1102
+ const { log, dir, output } = epubData;
1103
+ const archive = archiver("zip", { zlib: { level: 9 } });
1104
+ const outputStream = fs$1.createWriteStream(epubData.output);
605
1105
  log("Zipping temp dir to " + output);
606
1106
  return new Promise((resolve, reject) => {
607
1107
  archive.on("end", async () => {
608
1108
  log("Done zipping, clearing temp dir...");
609
- let stable = await fileIsStable(epubData.output);
1109
+ const stable = await fileIsStable(epubData.output);
610
1110
  if (!stable) {
611
1111
  log("Output epub file is not stable!");
612
1112
  }
@@ -635,14 +1135,18 @@ async function genEpub(epubData) {
635
1135
  archive.finalize();
636
1136
  });
637
1137
  }
638
- const baseDir = path__namespace.dirname(__dirname);
1138
+ const mimeModule = require("mime/lite");
1139
+ const mime = mimeModule.default || mimeModule;
1140
+ const __filename = fileURLToPath(import.meta.url);
1141
+ const __dirname = path.dirname(__filename);
1142
+ const baseDir = __dirname;
639
1143
  function result(success, message, options) {
640
1144
  if (options && options.verbose) {
641
1145
  if (!success) {
642
- console.error(new Error(message));
1146
+ logger.error(new Error(message));
643
1147
  }
644
1148
  }
645
- let out = {
1149
+ const out = {
646
1150
  success
647
1151
  };
648
1152
  if (typeof message === "string") {
@@ -666,9 +1170,9 @@ function check(options) {
666
1170
  return result(true, void 0, options);
667
1171
  }
668
1172
  function parseOptions(options) {
669
- let tmpDir = options.tmpDir || os__namespace.tmpdir();
670
- let id = uuid.v4();
671
- let data = {
1173
+ const tmpDir = options.tmpDir || os.tmpdir();
1174
+ const id = v4();
1175
+ const data = {
672
1176
  description: options.title,
673
1177
  publisher: "anonymous",
674
1178
  author: ["anonymous"],
@@ -685,12 +1189,12 @@ function parseOptions(options) {
685
1189
  ...options,
686
1190
  id,
687
1191
  tmpDir,
688
- dir: path__namespace.resolve(tmpDir, id),
1192
+ dir: path.resolve(tmpDir, id),
689
1193
  baseDir,
690
1194
  docHeader: "",
691
1195
  images: [],
692
1196
  content: [],
693
- log: (msg) => options.verbose && console.log(msg)
1197
+ log: (msg) => options.verbose && logger.log(msg)
694
1198
  };
695
1199
  if (data.version === 2) {
696
1200
  data.docHeader = `<?xml version="1.0" encoding="UTF-8"?>
@@ -699,7 +1203,7 @@ function parseOptions(options) {
699
1203
  `;
700
1204
  } else {
701
1205
  data.docHeader = `<?xml version="1.0" encoding="UTF-8"?>
702
- <!DOCTYPE html>
1206
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
703
1207
  <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="#{self.options.lang}">
704
1208
  `;
705
1209
  }
@@ -711,48 +1215,46 @@ function parseOptions(options) {
711
1215
  }
712
1216
  data.content = options.content.map((content, index) => parseContent(content, index, data));
713
1217
  if (data.cover) {
714
- data._coverMediaType = mime__namespace.getType(data.cover) || "";
715
- data._coverExtension = mime__namespace.getExtension(data._coverMediaType) || "";
1218
+ data._coverMediaType = mime.getType(data.cover) || "";
1219
+ data._coverExtension = mime.getExtension(data._coverMediaType) || "";
716
1220
  }
717
1221
  return data;
718
1222
  }
719
- async function epubGen(options, output) {
720
- options = { ...options };
721
- if (output) {
722
- options.output = output;
1223
+ async function epubGen(options, configs) {
1224
+ if (configs?.logger) {
1225
+ logger.setLogger(configs.logger);
723
1226
  }
724
- let o = check(options);
725
- let verbose = options.verbose !== false;
1227
+ options = { ...options };
1228
+ const o = check(options);
1229
+ const verbose = options.verbose !== false;
726
1230
  if (!o.success) {
727
- if (verbose)
728
- console.error(o.message);
1231
+ if (verbose) logger.error(o.message);
729
1232
  return o;
730
1233
  }
731
1234
  let t;
732
1235
  try {
733
- let data = parseOptions(options);
734
- let timeoutSeconds = data.timeoutSeconds || 0;
1236
+ const data = parseOptions(options);
1237
+ const timeoutSeconds = data.timeoutSeconds || 0;
735
1238
  if (timeoutSeconds > 0) {
736
- if (verbose)
737
- console.log(`TIMEOUT: ${timeoutSeconds}s`);
1239
+ if (verbose) logger.log(`TIMEOUT: ${timeoutSeconds}s`);
738
1240
  t = setTimeout(() => {
739
1241
  throw new Error("timeout!");
740
1242
  }, timeoutSeconds * 1e3);
741
1243
  } else {
742
- if (verbose)
743
- console.log(`TIMEOUT: N/A`);
1244
+ if (verbose) logger.log(`TIMEOUT: N/A`);
744
1245
  }
745
1246
  await render(data);
746
1247
  return result(true, void 0, data);
747
1248
  } catch (e) {
748
- if (verbose)
749
- console.error(e);
1249
+ if (verbose) logger.error(e);
750
1250
  return result(false, e.message, options);
751
1251
  } finally {
752
1252
  clearTimeout(t);
753
1253
  }
754
1254
  }
755
1255
  const gen = epubGen;
756
- exports.epubGen = epubGen;
757
- exports.errors = errors;
758
- exports.gen = gen;
1256
+ export {
1257
+ epubGen,
1258
+ errors,
1259
+ gen
1260
+ };