@epubook/core 0.0.2 → 0.0.3

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 CHANGED
@@ -13,7 +13,7 @@ npm i @epubook/core
13
13
  ## Usage
14
14
 
15
15
  ```ts
16
- import { Epub } from '@epubook/core'
16
+ import { Epub, HTML } from '@epubook/core'
17
17
 
18
18
  const book = new Epub({
19
19
  title: 'Test Book',
@@ -24,7 +24,9 @@ const book = new Epub({
24
24
  source: 'imagine'
25
25
  })
26
26
 
27
- // ...
27
+ const page = new HTML('start.xhtml', '...')
28
+
29
+ book.item(page).spine(page).toc([{ title: 'Start', page }])
28
30
 
29
31
  await book.writeFile('./test.epub')
30
32
  ```
@@ -53,24 +55,16 @@ Currently, it supports use [unbuild](https://github.com/unjs/unbuild) to transfo
53
55
 
54
56
  import { defineBuildConfig } from 'unbuild';
55
57
 
56
- import { EsbuildOptions, Rollup } from '@epubook/core';
58
+ import { UnbuildPreset } from '@epubook/core';
57
59
 
58
60
  export default defineBuildConfig({
59
61
  entries: ['src/index'],
60
62
  declaration: true,
61
63
  clean: true,
62
64
  rollup: {
63
- emitCJS: true,
64
- esbuild: EsbuildOptions
65
+ emitCJS: true
65
66
  },
66
- hooks: {
67
- 'rollup:options'(_options, config) {
68
- const plugins = config.plugins;
69
- if (Array.isArray(plugins)) {
70
- plugins.push(Rollup());
71
- }
72
- }
73
- }
67
+ preset: UnbuildPreset()
74
68
  });
75
69
  ```
76
70
 
@@ -3,10 +3,11 @@
3
3
  const path = require('pathe');
4
4
  const fflate = require('fflate');
5
5
  const fastXmlParser = require('fast-xml-parser');
6
- const index = require('../shared/core.2c388ac3.cjs');
7
- require('node:fs');
6
+ const index = require('../index.cjs');
7
+ require('node:path');
8
8
  require('node:crypto');
9
9
  require('defu');
10
+ require('node:fs');
10
11
 
11
12
  function _interopNamespaceDefault(e) {
12
13
  const n = Object.create(null);
@@ -146,7 +147,7 @@ function makePackageDocument(opf) {
146
147
  {
147
148
  "@_refines": "#" + opf.creator().uid,
148
149
  "@_property": "file-as",
149
- "#text": opf.creator()?.fileAs ?? ""
150
+ "#text": opf.creator()?.fileAs ?? opf.creator().name
150
151
  }
151
152
  ]
152
153
  };
@@ -1,10 +1,11 @@
1
1
  import * as path from 'pathe';
2
2
  import * as fflate from 'fflate';
3
3
  import { XMLBuilder } from 'fast-xml-parser';
4
- import { M as MIMETYPE, B as BundleError } from '../shared/core.c365bf12.mjs';
5
- import 'node:fs';
4
+ import { MIMETYPE, BundleError } from '../index.mjs';
5
+ import 'node:path';
6
6
  import 'node:crypto';
7
7
  import 'defu';
8
+ import 'node:fs';
8
9
 
9
10
  function toISO8601String(date) {
10
11
  function pad(n) {
@@ -130,7 +131,7 @@ function makePackageDocument(opf) {
130
131
  {
131
132
  "@_refines": "#" + opf.creator().uid,
132
133
  "@_property": "file-as",
133
- "#text": opf.creator()?.fileAs ?? ""
134
+ "#text": opf.creator()?.fileAs ?? opf.creator().name
134
135
  }
135
136
  ]
136
137
  };
package/dist/index.cjs CHANGED
@@ -1,26 +1,573 @@
1
1
  'use strict';
2
2
 
3
- const index = require('./shared/core.2c388ac3.cjs');
4
- require('fast-xml-parser');
5
- require('pathe');
6
- require('node:fs');
7
- require('node:crypto');
8
- require('defu');
9
- require('fflate');
10
-
11
-
12
-
13
- exports.BundleError = index.BundleError;
14
- exports.Epub = index.Epub;
15
- exports.EsbuildOptions = index.EsbuildOptions;
16
- exports.Fragment = index.Fragment;
17
- exports.Html = index.Html;
18
- exports.Image = index.Image;
19
- exports.Item = index.Item;
20
- exports.ManifestItem = index.ManifestItem;
21
- exports.ManifestItemRef = index.ManifestItemRef;
22
- exports.PackageDocument = index.PackageDocument;
23
- exports.Rollup = index.Rollup;
24
- exports.Style = index.Style;
25
- exports.XHTMLBuilder = index.XHTMLBuilder;
26
- exports.h = index.h;
3
+ const path = require('node:path');
4
+ const node_crypto = require('node:crypto');
5
+ const defu$1 = require('defu');
6
+ const path$1 = require('pathe');
7
+ const node_fs = require('node:fs');
8
+ const fflate = require('fflate');
9
+ const fastXmlParser = require('fast-xml-parser');
10
+
11
+ function _interopNamespaceDefault(e) {
12
+ const n = Object.create(null);
13
+ if (e) {
14
+ for (const k in e) {
15
+ n[k] = e[k];
16
+ }
17
+ }
18
+ n.default = e;
19
+ return n;
20
+ }
21
+
22
+ const path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
23
+ const path__namespace$1 = /*#__PURE__*/_interopNamespaceDefault(path$1);
24
+
25
+ const MIMETYPE = "application/epub+zip";
26
+ const ImageGif = "image/gif";
27
+ const ImageJpeg = "image/jpeg";
28
+ const ImagePng = "image/png";
29
+ const ImageSvg = "image/svg+xml";
30
+ const ImageWebp = "image/webp";
31
+ const TextCSS = "text/css";
32
+ const TextXHTML = "application/xhtml+xml";
33
+ function getImageMediaType(file) {
34
+ const ext = path__namespace.extname(file);
35
+ switch (ext) {
36
+ case ".gif":
37
+ return ImageGif;
38
+ case ".jpg":
39
+ case ".jpeg":
40
+ return ImageJpeg;
41
+ case ".png":
42
+ return ImagePng;
43
+ case ".svg":
44
+ return ImageSvg;
45
+ case ".webp":
46
+ return ImageWebp;
47
+ default:
48
+ return void 0;
49
+ }
50
+ }
51
+
52
+ const Fragment = "Fragment";
53
+ function h(tag, attrs = {}, ...children) {
54
+ const sub = children.flatMap(
55
+ (c) => typeof c === "object" && !Array.isArray(c) && c.tag === Fragment ? c.children ?? [] : c
56
+ ).filter((c) => c !== void 0 && c !== null && c !== false);
57
+ const o = {
58
+ tag,
59
+ attrs: attrs ?? {},
60
+ children: sub
61
+ };
62
+ return o;
63
+ }
64
+
65
+ class ManifestItem {
66
+ constructor(href, id) {
67
+ this.optional = {};
68
+ this._href = href;
69
+ this._id = id;
70
+ }
71
+ update(info) {
72
+ for (const [key, value] of Object.entries(info)) {
73
+ if (!!value) {
74
+ this.optional[key] = value;
75
+ }
76
+ }
77
+ return this;
78
+ }
79
+ href() {
80
+ return this._href;
81
+ }
82
+ id() {
83
+ return this._id;
84
+ }
85
+ fallback() {
86
+ return this.optional.fallback;
87
+ }
88
+ mediaOverlay() {
89
+ return this.optional.mediaOverlay;
90
+ }
91
+ mediaType() {
92
+ return this.optional.mediaType;
93
+ }
94
+ properties() {
95
+ return this.optional.properties;
96
+ }
97
+ ref() {
98
+ return new ManifestItemRef(this._id);
99
+ }
100
+ }
101
+ class ManifestItemRef {
102
+ constructor(idref) {
103
+ this.optional = {};
104
+ this._idref = idref;
105
+ }
106
+ update(info) {
107
+ for (const [key, value] of Object.entries(info)) {
108
+ if (!!value) {
109
+ this.optional[key] = value;
110
+ }
111
+ }
112
+ return this;
113
+ }
114
+ idref() {
115
+ return this._idref;
116
+ }
117
+ id() {
118
+ return this.optional.id;
119
+ }
120
+ linear() {
121
+ return this.optional.linear;
122
+ }
123
+ properties() {
124
+ return this.optional.properties;
125
+ }
126
+ }
127
+
128
+ class Item {
129
+ constructor(file, mediaType) {
130
+ this.file = file;
131
+ this.mediaType = mediaType;
132
+ }
133
+ filename() {
134
+ return this.file;
135
+ }
136
+ relative(from) {
137
+ return path__namespace$1.relative(path__namespace$1.dirname(from), this.file);
138
+ }
139
+ update(info) {
140
+ if (info.properties) {
141
+ this._properties = info.properties;
142
+ }
143
+ return this;
144
+ }
145
+ id() {
146
+ return this.file.replace(/\/|\\/g, "_").replace(/\.[\w]+$/, "");
147
+ }
148
+ manifest() {
149
+ return new ManifestItem(this.file, this.id()).update({
150
+ mediaType: this.mediaType,
151
+ properties: this._properties
152
+ });
153
+ }
154
+ itemref() {
155
+ return this.manifest().ref();
156
+ }
157
+ }
158
+ class Style extends Item {
159
+ constructor(file, content) {
160
+ super(file, TextCSS);
161
+ this.content = content;
162
+ }
163
+ static async read(src, dst) {
164
+ if (!src.endsWith(".css")) {
165
+ return void 0;
166
+ }
167
+ const content = await node_fs.promises.readFile(src, "utf-8");
168
+ return new Style(dst, content);
169
+ }
170
+ async bundle() {
171
+ return fflate.strToU8(this.content);
172
+ }
173
+ }
174
+ class Image extends Item {
175
+ constructor(file, type, data) {
176
+ super(file, type);
177
+ this.data = data;
178
+ }
179
+ static async read(file, src) {
180
+ const content = await node_fs.promises.readFile(src);
181
+ const media = getImageMediaType(src);
182
+ if (media) {
183
+ return new Image(file, media, content);
184
+ } else {
185
+ return void 0;
186
+ }
187
+ }
188
+ async bundle() {
189
+ return this.data;
190
+ }
191
+ }
192
+ class HTML extends Item {
193
+ constructor(file, content) {
194
+ super(file, TextXHTML);
195
+ this.content = content;
196
+ }
197
+ static async read(src, dst) {
198
+ if (!src.endsWith(".xhtml")) {
199
+ return void 0;
200
+ }
201
+ const content = await node_fs.promises.readFile(src, "utf-8");
202
+ return new HTML(dst, content);
203
+ }
204
+ async bundle() {
205
+ return fflate.strToU8(this.content);
206
+ }
207
+ }
208
+
209
+ const builder = new fastXmlParser.XMLBuilder({
210
+ format: true,
211
+ ignoreAttributes: false,
212
+ suppressUnpairedNode: false,
213
+ unpairedTags: ["link"]
214
+ });
215
+ class XHTML extends Item {
216
+ constructor(file, meta, content) {
217
+ super(file, TextXHTML);
218
+ this.meta = meta;
219
+ this.content = content;
220
+ }
221
+ title() {
222
+ return this.meta.title;
223
+ }
224
+ language() {
225
+ return this.meta.language;
226
+ }
227
+ async bundle() {
228
+ return fflate.strToU8(this.content);
229
+ }
230
+ }
231
+ class XHTMLBuilder {
232
+ constructor(filename) {
233
+ this.meta = {
234
+ language: "en",
235
+ title: ""
236
+ };
237
+ this._head = [];
238
+ this._body = [];
239
+ this._filename = filename;
240
+ this.meta.title = path__namespace$1.basename(filename);
241
+ }
242
+ language(value) {
243
+ this.meta.language = value;
244
+ return this;
245
+ }
246
+ title(value) {
247
+ this.meta.title = value;
248
+ return this;
249
+ }
250
+ style(href) {
251
+ if (typeof href === "string") {
252
+ this._head.push({
253
+ tag: "link",
254
+ attrs: {
255
+ href,
256
+ rel: "stylesheet",
257
+ type: TextCSS
258
+ },
259
+ children: [""]
260
+ });
261
+ } else {
262
+ this._head.push({
263
+ tag: "link",
264
+ attrs: {
265
+ href: href.relative(this._filename),
266
+ rel: "stylesheet",
267
+ type: TextCSS
268
+ },
269
+ children: [""]
270
+ });
271
+ }
272
+ return this;
273
+ }
274
+ head(...node) {
275
+ this._head.push(...node);
276
+ return this;
277
+ }
278
+ body(...node) {
279
+ this._body.push(...node);
280
+ return this;
281
+ }
282
+ build() {
283
+ const content = builder.build({
284
+ html: {
285
+ "@_xmlns": "http://www.w3.org/1999/xhtml",
286
+ "@_xmlns:epub": "http://www.idpf.org/2007/ops",
287
+ "@_lang": this.meta.language,
288
+ "@_xml:lang": this.meta.language,
289
+ head: {
290
+ title: this.meta.title,
291
+ ...list(this._head)
292
+ },
293
+ body: list(this._body)
294
+ }
295
+ });
296
+ return new XHTML(this._filename, this.meta, content);
297
+ function build(node) {
298
+ const attrs = Object.fromEntries(
299
+ Object.entries(node.attrs ?? {}).map(([key, value]) => ["@_" + key, value])
300
+ );
301
+ const obj = {
302
+ ...attrs
303
+ };
304
+ if (Array.isArray(node.children)) {
305
+ const text = node.children.filter((c) => typeof c === "string");
306
+ const nodes = node.children.filter((c) => typeof c !== "string");
307
+ if (text.length > 0) {
308
+ obj["#text"] = text[0];
309
+ }
310
+ Object.assign(obj, list(nodes));
311
+ }
312
+ return obj;
313
+ }
314
+ function list(nodes) {
315
+ const obj = {};
316
+ for (const c of nodes) {
317
+ if (c.tag in obj) {
318
+ obj[c.tag].push(build(c));
319
+ } else {
320
+ obj[c.tag] = [build(c)];
321
+ }
322
+ }
323
+ return obj;
324
+ }
325
+ }
326
+ }
327
+
328
+ class Toc extends Item {
329
+ constructor(file) {
330
+ super(file, TextXHTML);
331
+ this._builder = new XHTMLBuilder(this.filename());
332
+ this.update({ properties: "nav" });
333
+ }
334
+ generate(nav, { title = "Nav", heading = 1, titleAttrs = {}, head = [], body = [] } = {}) {
335
+ const builder = new XHTMLBuilder(this.filename());
336
+ const root = {
337
+ tag: "nav",
338
+ attrs: {
339
+ "epub:type": "toc"
340
+ },
341
+ children: []
342
+ };
343
+ root.children.push(h("h" + heading, titleAttrs, title));
344
+ root.children.push(/* @__PURE__ */ h("ol", null, list(nav)));
345
+ builder.title(title).body(root).head(...head).body(...body);
346
+ return this._builder = builder;
347
+ function list(items) {
348
+ return items.map(
349
+ (i) => "page" in i ? /* @__PURE__ */ h("li", { ...i.attrs }, /* @__PURE__ */ h("a", { href: i.page.filename() }, i.title)) : "list" in i ? /* @__PURE__ */ h("li", { ...i.attrs }, /* @__PURE__ */ h("span", null, i.title), /* @__PURE__ */ h("ol", null, list(i.list))) : false
350
+ );
351
+ }
352
+ }
353
+ async bundle() {
354
+ return this._builder.build().bundle();
355
+ }
356
+ }
357
+
358
+ const defu = defu$1.createDefu((obj, key, value) => {
359
+ if (obj[key] instanceof Date && value instanceof Date) {
360
+ obj[key] = value;
361
+ return true;
362
+ }
363
+ });
364
+ class PackageDocument {
365
+ constructor(file) {
366
+ this.SpecVersion = "3.0";
367
+ this._uniqueIdentifier = "uuid";
368
+ this._identifier = node_crypto.randomUUID();
369
+ this._metadata = {
370
+ title: "",
371
+ language: "zh-CN",
372
+ contributor: [],
373
+ coverage: "",
374
+ creator: {
375
+ name: "unknown",
376
+ uid: "creator"
377
+ },
378
+ date: /* @__PURE__ */ new Date(),
379
+ description: "",
380
+ format: "",
381
+ publisher: "",
382
+ relation: "",
383
+ rights: "",
384
+ source: "",
385
+ subject: "",
386
+ type: "",
387
+ lastModified: /* @__PURE__ */ new Date()
388
+ };
389
+ this._items = [];
390
+ this._spine = [];
391
+ this.file = file;
392
+ }
393
+ filename() {
394
+ return this.file;
395
+ }
396
+ version() {
397
+ return this.SpecVersion;
398
+ }
399
+ // --- metadata ---
400
+ update(info) {
401
+ this._metadata = defu(info, this._metadata);
402
+ return this;
403
+ }
404
+ title() {
405
+ return this._metadata.title;
406
+ }
407
+ language() {
408
+ return this._metadata.language;
409
+ }
410
+ creator() {
411
+ return this._metadata.creator;
412
+ }
413
+ metadata() {
414
+ return this._metadata;
415
+ }
416
+ // --- manifest ---
417
+ addItem(item) {
418
+ this._items.push(item);
419
+ return this;
420
+ }
421
+ items() {
422
+ const l = [...this._items];
423
+ if (this._toc) {
424
+ l.push(this._toc);
425
+ }
426
+ return l;
427
+ }
428
+ manifest() {
429
+ return this.items().map((i) => i.manifest());
430
+ }
431
+ // --- navigation ---
432
+ spine() {
433
+ return this._spine;
434
+ }
435
+ setSpine(items) {
436
+ this._spine.splice(0, this._spine.length, ...items.map((i) => i.itemref()));
437
+ return this;
438
+ }
439
+ toc() {
440
+ return this._toc;
441
+ }
442
+ setToc(nav, option = {}) {
443
+ const toc = new Toc("nav.xhtml");
444
+ toc.generate(nav, option).language(this._metadata.language).build();
445
+ this._toc = toc;
446
+ return this;
447
+ }
448
+ // --- identifier ---
449
+ uniqueIdentifier() {
450
+ return this._uniqueIdentifier;
451
+ }
452
+ identifier() {
453
+ return this._identifier;
454
+ }
455
+ setIdentifier(identifier, uniqueIdentifier = "uuid") {
456
+ this._identifier = identifier;
457
+ this._uniqueIdentifier = uniqueIdentifier;
458
+ }
459
+ }
460
+
461
+ class Epub {
462
+ constructor(meta = {}) {
463
+ /**
464
+ * See: https://www.w3.org/TR/epub-33/#sec-package-doc
465
+ *
466
+ * Now, it only supports single opf (OEBPS/content.opf)
467
+ *
468
+ * @returns list of package documents
469
+ */
470
+ this.opfs = [new PackageDocument("OEBPS/content.opf")];
471
+ this.opfs[0].update(meta);
472
+ }
473
+ packages() {
474
+ return this.opfs;
475
+ }
476
+ main() {
477
+ return this.opfs[0];
478
+ }
479
+ item(...items) {
480
+ for (const item of items) {
481
+ this.opfs[0].addItem(item);
482
+ }
483
+ return this;
484
+ }
485
+ toc(nav, option = {}) {
486
+ this.opfs[0].setToc(nav, option);
487
+ return this;
488
+ }
489
+ spine(...items) {
490
+ this.opfs[0].setSpine(items);
491
+ return this;
492
+ }
493
+ async bundle() {
494
+ const { bundle } = await import('./chunks/index.cjs');
495
+ return await bundle(this);
496
+ }
497
+ async writeFile(file) {
498
+ const buffer = await this.bundle();
499
+ const dir = path__namespace$1.dirname(file);
500
+ if (!node_fs.existsSync(dir)) {
501
+ node_fs.mkdirSync(dir, { recursive: true });
502
+ }
503
+ await node_fs.promises.writeFile(file, buffer);
504
+ }
505
+ }
506
+
507
+ class BundleError extends Error {
508
+ constructor(msg) {
509
+ super(msg);
510
+ }
511
+ }
512
+
513
+ const UnbuildPreset = ({
514
+ inject = true
515
+ } = {}) => ({
516
+ rollup: {
517
+ esbuild: {
518
+ jsxFactory: inject ? "__epubook_core.h" : "h",
519
+ jsxFragment: inject ? "__epubook_core.Fragment" : "Fragment",
520
+ loaders: {
521
+ ".js": "js",
522
+ ".ts": "ts",
523
+ ".jsx": "jsx",
524
+ ".tsx": "tsx"
525
+ }
526
+ }
527
+ },
528
+ hooks: {
529
+ "rollup:options"(_options, config) {
530
+ const plugins = config.plugins;
531
+ if (inject && Array.isArray(plugins)) {
532
+ plugins.push(Rollup());
533
+ }
534
+ }
535
+ }
536
+ });
537
+ function Rollup() {
538
+ return {
539
+ name: "epubook:inject-tsx",
540
+ transform(code, id) {
541
+ if (id.endsWith(".tsx")) {
542
+ return `import * as __epubook_core from '@epubook/core';
543
+ ` + code;
544
+ }
545
+ }
546
+ };
547
+ }
548
+
549
+ exports.BundleError = BundleError;
550
+ exports.Epub = Epub;
551
+ exports.Fragment = Fragment;
552
+ exports.HTML = HTML;
553
+ exports.Image = Image;
554
+ exports.ImageGif = ImageGif;
555
+ exports.ImageJpeg = ImageJpeg;
556
+ exports.ImagePng = ImagePng;
557
+ exports.ImageSvg = ImageSvg;
558
+ exports.ImageWebp = ImageWebp;
559
+ exports.Item = Item;
560
+ exports.MIMETYPE = MIMETYPE;
561
+ exports.ManifestItem = ManifestItem;
562
+ exports.ManifestItemRef = ManifestItemRef;
563
+ exports.PackageDocument = PackageDocument;
564
+ exports.Rollup = Rollup;
565
+ exports.Style = Style;
566
+ exports.TextCSS = TextCSS;
567
+ exports.TextXHTML = TextXHTML;
568
+ exports.Toc = Toc;
569
+ exports.UnbuildPreset = UnbuildPreset;
570
+ exports.XHTML = XHTML;
571
+ exports.XHTMLBuilder = XHTMLBuilder;
572
+ exports.getImageMediaType = getImageMediaType;
573
+ exports.h = h;