@epubook/core 0.0.3 → 0.0.5

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
@@ -1,8 +1,8 @@
1
1
  # @epubook/core
2
2
 
3
- The fundamental module of [epubook](https://github.com/yjl9903/epubook) for generating EPUB books.
3
+ [![version](https://img.shields.io/npm/v/@epubook/core?color=rgb%2850%2C203%2C86%29&label=@epubook/core)](https://www.npmjs.com/package/@epubook/theme-core) [![CI](https://github.com/yjl9903/epubook/actions/workflows/ci.yml/badge.svg)](https://github.com/yjl9903/epubook/actions/workflows/ci.yml)
4
4
 
5
- > 👷‍♂️ Still work in progress.
5
+ The fundamental module of [epubook](https://github.com/yjl9903/epubook). It provides low-level API for generating EPUB books.
6
6
 
7
7
  ## Installation
8
8
 
package/dist/index.cjs CHANGED
@@ -4,9 +4,9 @@ const path = require('node:path');
4
4
  const node_crypto = require('node:crypto');
5
5
  const defu$1 = require('defu');
6
6
  const path$1 = require('pathe');
7
- const node_fs = require('node:fs');
8
7
  const fflate = require('fflate');
9
8
  const fastXmlParser = require('fast-xml-parser');
9
+ const node_fs = require('node:fs');
10
10
 
11
11
  function _interopNamespaceDefault(e) {
12
12
  const n = Object.create(null);
@@ -22,6 +22,19 @@ function _interopNamespaceDefault(e) {
22
22
  const path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
23
23
  const path__namespace$1 = /*#__PURE__*/_interopNamespaceDefault(path$1);
24
24
 
25
+ const Fragment = "Fragment";
26
+ function h(tag, attrs = {}, ...children) {
27
+ const sub = children.flatMap(
28
+ (c) => typeof c === "object" && !Array.isArray(c) && c.tag === Fragment ? c.children ?? [] : c
29
+ ).filter((c) => c !== void 0 && c !== null && c !== false);
30
+ const o = {
31
+ tag,
32
+ attrs: attrs ?? {},
33
+ children: sub
34
+ };
35
+ return o;
36
+ }
37
+
25
38
  const MIMETYPE = "application/epub+zip";
26
39
  const ImageGif = "image/gif";
27
40
  const ImageJpeg = "image/jpeg";
@@ -49,19 +62,6 @@ function getImageMediaType(file) {
49
62
  }
50
63
  }
51
64
 
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
65
  class ManifestItem {
66
66
  constructor(href, id) {
67
67
  this.optional = {};
@@ -215,59 +215,68 @@ const builder = new fastXmlParser.XMLBuilder({
215
215
  class XHTML extends Item {
216
216
  constructor(file, meta, content) {
217
217
  super(file, TextXHTML);
218
- this.meta = meta;
219
- this.content = content;
218
+ this._meta = meta;
219
+ this._content = content;
220
+ }
221
+ meta() {
222
+ return this._meta;
220
223
  }
221
224
  title() {
222
- return this.meta.title;
225
+ return this._meta.title;
223
226
  }
224
227
  language() {
225
- return this.meta.language;
228
+ return this._meta.language;
229
+ }
230
+ content() {
231
+ return this._content;
226
232
  }
227
233
  async bundle() {
228
- return fflate.strToU8(this.content);
234
+ return fflate.strToU8(this._content);
229
235
  }
230
236
  }
231
237
  class XHTMLBuilder {
232
238
  constructor(filename) {
233
- this.meta = {
239
+ this._meta = {
234
240
  language: "en",
235
241
  title: ""
236
242
  };
237
243
  this._head = [];
238
244
  this._body = [];
245
+ this._bodyAttrs = {};
239
246
  this._filename = filename;
240
- this.meta.title = path__namespace$1.basename(filename);
247
+ this._meta.title = path__namespace$1.basename(filename);
241
248
  }
242
249
  language(value) {
243
- this.meta.language = value;
250
+ this._meta.language = value;
244
251
  return this;
245
252
  }
246
253
  title(value) {
247
- this.meta.title = value;
254
+ this._meta.title = value;
248
255
  return this;
249
256
  }
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
- });
257
+ style(...list) {
258
+ for (const href of list) {
259
+ if (typeof href === "string") {
260
+ this._head.push({
261
+ tag: "link",
262
+ attrs: {
263
+ href,
264
+ rel: "stylesheet",
265
+ type: TextCSS
266
+ },
267
+ children: [""]
268
+ });
269
+ } else {
270
+ this._head.push({
271
+ tag: "link",
272
+ attrs: {
273
+ href: href.relative(this._filename),
274
+ rel: "stylesheet",
275
+ type: TextCSS
276
+ },
277
+ children: [""]
278
+ });
279
+ }
271
280
  }
272
281
  return this;
273
282
  }
@@ -279,21 +288,32 @@ class XHTMLBuilder {
279
288
  this._body.push(...node);
280
289
  return this;
281
290
  }
291
+ bodyAttrs(attrs = {}) {
292
+ const a = Object.entries(attrs).map(([key, value]) => [`@_${key}`, value]);
293
+ this._bodyAttrs = {
294
+ ...this._bodyAttrs,
295
+ ...Object.fromEntries(a)
296
+ };
297
+ return this;
298
+ }
282
299
  build() {
283
300
  const content = builder.build({
284
301
  html: {
285
302
  "@_xmlns": "http://www.w3.org/1999/xhtml",
286
303
  "@_xmlns:epub": "http://www.idpf.org/2007/ops",
287
- "@_lang": this.meta.language,
288
- "@_xml:lang": this.meta.language,
304
+ "@_lang": this._meta.language,
305
+ "@_xml:lang": this._meta.language,
289
306
  head: {
290
- title: this.meta.title,
307
+ title: this._meta.title,
291
308
  ...list(this._head)
292
309
  },
293
- body: list(this._body)
310
+ body: {
311
+ ...this._bodyAttrs,
312
+ ...list(this._body)
313
+ }
294
314
  }
295
315
  });
296
- return new XHTML(this._filename, this.meta, content);
316
+ return new XHTML(this._filename, this._meta, content);
297
317
  function build(node) {
298
318
  const attrs = Object.fromEntries(
299
319
  Object.entries(node.attrs ?? {}).map(([key, value]) => ["@_" + key, value])
@@ -311,10 +331,17 @@ class XHTMLBuilder {
311
331
  }
312
332
  return obj;
313
333
  }
314
- function list(nodes) {
334
+ function list(list2) {
315
335
  const obj = {};
336
+ const nodes = list2.flatMap((n) => n.tag === Fragment ? n.children ?? [] : [n]);
316
337
  for (const c of nodes) {
317
- if (c.tag in obj) {
338
+ if (typeof c === "string") {
339
+ if (obj["#text"]) {
340
+ obj["#text"] += c;
341
+ } else {
342
+ obj["#text"] = c;
343
+ }
344
+ } else if (c.tag in obj) {
318
345
  obj[c.tag].push(build(c));
319
346
  } else {
320
347
  obj[c.tag] = [build(c)];
@@ -325,14 +352,18 @@ class XHTMLBuilder {
325
352
  }
326
353
  }
327
354
 
328
- class Toc extends Item {
329
- constructor(file) {
330
- super(file, TextXHTML);
331
- this._builder = new XHTMLBuilder(this.filename());
355
+ class Toc extends XHTML {
356
+ constructor(file, meta, content) {
357
+ super(file, meta, content);
332
358
  this.update({ properties: "nav" });
333
359
  }
334
- generate(nav, { title = "Nav", heading = 1, titleAttrs = {}, head = [], body = [] } = {}) {
335
- const builder = new XHTMLBuilder(this.filename());
360
+ static from(xhtml) {
361
+ return new Toc(xhtml.filename(), xhtml.meta(), xhtml.content());
362
+ }
363
+ static generate(file, nav, { title = "Nav", heading = 1, titleAttrs = {}, builder } = {}) {
364
+ if (!builder) {
365
+ builder = new XHTMLBuilder(file);
366
+ }
336
367
  const root = {
337
368
  tag: "nav",
338
369
  attrs: {
@@ -342,17 +373,13 @@ class Toc extends Item {
342
373
  };
343
374
  root.children.push(h("h" + heading, titleAttrs, title));
344
375
  root.children.push(/* @__PURE__ */ h("ol", null, list(nav)));
345
- builder.title(title).body(root).head(...head).body(...body);
346
- return this._builder = builder;
376
+ return builder.body(root);
347
377
  function list(items) {
348
378
  return items.map(
349
379
  (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
380
  );
351
381
  }
352
382
  }
353
- async bundle() {
354
- return this._builder.build().bundle();
355
- }
356
383
  }
357
384
 
358
385
  const defu = defu$1.createDefu((obj, key, value) => {
@@ -440,9 +467,11 @@ class PackageDocument {
440
467
  return this._toc;
441
468
  }
442
469
  setToc(nav, option = {}) {
443
- const toc = new Toc("nav.xhtml");
444
- toc.generate(nav, option).language(this._metadata.language).build();
445
- this._toc = toc;
470
+ const toc = Toc.generate("nav.xhtml", nav, option);
471
+ if (!option.builder) {
472
+ toc.title(option.title ?? "Nav").language(this._metadata.language);
473
+ }
474
+ this._toc = Toc.from(toc.build());
446
475
  return this;
447
476
  }
448
477
  // --- identifier ---
package/dist/index.d.ts CHANGED
@@ -1,261 +1,268 @@
1
1
  import { Plugin } from 'rollup';
2
2
  import { BuildConfig } from 'unbuild';
3
3
 
4
- declare const MIMETYPE = "application/epub+zip";
5
- declare const ImageGif = "image/gif";
6
- declare const ImageJpeg = "image/jpeg";
7
- declare const ImagePng = "image/png";
8
- declare const ImageSvg = "image/svg+xml";
9
- declare const ImageWebp = "image/webp";
10
- type ImageMediaType = typeof ImageGif | typeof ImageJpeg | typeof ImagePng | typeof ImageSvg | typeof ImageWebp;
11
- type ImageExtension = 'gif' | 'jpg' | 'jpeg' | 'png' | 'svg' | 'webp';
12
- declare const TextCSS = "text/css";
13
- declare const TextXHTML = "application/xhtml+xml";
14
- type MediaType = ImageMediaType | typeof TextCSS | typeof TextXHTML;
4
+ declare const MIMETYPE = "application/epub+zip";
5
+ declare const ImageGif = "image/gif";
6
+ declare const ImageJpeg = "image/jpeg";
7
+ declare const ImagePng = "image/png";
8
+ declare const ImageSvg = "image/svg+xml";
9
+ declare const ImageWebp = "image/webp";
10
+ type ImageMediaType = typeof ImageGif | typeof ImageJpeg | typeof ImagePng | typeof ImageSvg | typeof ImageWebp;
11
+ type ImageExtension = 'gif' | 'jpg' | 'jpeg' | 'png' | 'svg' | 'webp';
12
+ declare const TextCSS = "text/css";
13
+ declare const TextXHTML = "application/xhtml+xml";
14
+ type MediaType = ImageMediaType | typeof TextCSS | typeof TextXHTML;
15
15
  declare function getImageMediaType(file: string): ImageMediaType | undefined;
16
16
 
17
- declare class ManifestItem {
18
- private _href;
19
- private _id;
20
- private optional;
21
- constructor(href: string, id: string);
22
- update(info: typeof this$1.optional): this;
23
- href(): string;
24
- id(): string;
25
- fallback(): string | undefined;
26
- mediaOverlay(): string | undefined;
27
- mediaType(): MediaType | undefined;
28
- properties(): string | undefined;
29
- ref(): ManifestItemRef;
30
- }
31
- declare class ManifestItemRef {
32
- private _idref;
33
- private optional;
34
- constructor(idref: string);
35
- update(info: typeof this$1.optional): this;
36
- idref(): string;
37
- id(): string | undefined;
38
- linear(): string | undefined;
39
- properties(): string | undefined;
17
+ declare class ManifestItem {
18
+ private _href;
19
+ private _id;
20
+ private optional;
21
+ constructor(href: string, id: string);
22
+ update(info: typeof this$1.optional): this;
23
+ href(): string;
24
+ id(): string;
25
+ fallback(): string | undefined;
26
+ mediaOverlay(): string | undefined;
27
+ mediaType(): MediaType | undefined;
28
+ properties(): string | undefined;
29
+ ref(): ManifestItemRef;
30
+ }
31
+ declare class ManifestItemRef {
32
+ private _idref;
33
+ private optional;
34
+ constructor(idref: string);
35
+ update(info: typeof this$1.optional): this;
36
+ idref(): string;
37
+ id(): string | undefined;
38
+ linear(): string | undefined;
39
+ properties(): string | undefined;
40
40
  }
41
41
 
42
- declare abstract class Item {
43
- private readonly file;
44
- private readonly mediaType;
45
- private _properties?;
46
- constructor(file: string, mediaType: MediaType);
47
- filename(): string;
48
- relative(from: string): string;
49
- update(info: Partial<{
50
- properties: string;
51
- }>): this;
52
- id(): string;
53
- manifest(): ManifestItem;
54
- itemref(): ManifestItemRef;
55
- abstract bundle(): Promise<Uint8Array>;
56
- }
57
- declare class Style extends Item {
58
- private content;
59
- constructor(file: string, content: string);
60
- static read(src: string, dst: string): Promise<Style | undefined>;
61
- bundle(): Promise<Uint8Array>;
62
- }
63
- declare class Image extends Item {
64
- private data;
65
- constructor(file: string, type: ImageMediaType, data: Uint8Array);
66
- static read(file: string, src: string): Promise<Image | undefined>;
67
- bundle(): Promise<Uint8Array>;
68
- }
69
- declare class HTML extends Item {
70
- private content;
71
- constructor(file: string, content: string);
72
- static read(src: string, dst: string): Promise<HTML | undefined>;
73
- bundle(): Promise<Uint8Array>;
42
+ declare abstract class Item {
43
+ private readonly file;
44
+ private readonly mediaType;
45
+ private _properties?;
46
+ constructor(file: string, mediaType: MediaType);
47
+ filename(): string;
48
+ relative(from: string): string;
49
+ update(info: Partial<{
50
+ properties: string;
51
+ }>): this;
52
+ id(): string;
53
+ manifest(): ManifestItem;
54
+ itemref(): ManifestItemRef;
55
+ abstract bundle(): Promise<Uint8Array>;
56
+ }
57
+ declare class Style extends Item {
58
+ private content;
59
+ constructor(file: string, content: string);
60
+ static read(src: string, dst: string): Promise<Style | undefined>;
61
+ bundle(): Promise<Uint8Array>;
62
+ }
63
+ declare class Image extends Item {
64
+ private data;
65
+ constructor(file: string, type: ImageMediaType, data: Uint8Array);
66
+ static read(file: string, src: string): Promise<Image | undefined>;
67
+ bundle(): Promise<Uint8Array>;
68
+ }
69
+ declare class HTML extends Item {
70
+ private content;
71
+ constructor(file: string, content: string);
72
+ static read(src: string, dst: string): Promise<HTML | undefined>;
73
+ bundle(): Promise<Uint8Array>;
74
74
  }
75
75
 
76
- type Prettify<T> = {
77
- [K in keyof T]: T[K];
76
+ type Prettify<T> = {
77
+ [K in keyof T]: T[K];
78
78
  } & {};
79
79
 
80
- declare global {
81
- namespace JSX {
82
- interface IntrinsicElements extends EpubIntrinsicElements {
83
- }
84
- }
85
- }
86
- interface XHTMLNode {
87
- tag: string;
88
- attrs?: Record<string, string>;
89
- children?: Array<string | XHTMLNode>;
90
- }
91
- interface EpubIntrinsicElements {
92
- nav: {};
93
- ul: {};
94
- ol: {};
95
- li: {};
96
- h1: {};
97
- h2: {};
98
- h3: {};
99
- h4: {};
100
- h5: {};
101
- h6: {};
102
- p: {};
103
- pre: {};
104
- span: {};
105
- code: {};
106
- a: {
107
- href: string;
108
- };
80
+ declare global {
81
+ namespace JSX {
82
+ interface IntrinsicElements extends EpubIntrinsicElements {
83
+ }
84
+ }
85
+ }
86
+ interface XHTMLNode {
87
+ tag: string;
88
+ attrs?: Record<string, string>;
89
+ children?: Array<string | XHTMLNode>;
90
+ }
91
+ interface EpubIntrinsicElements {
92
+ nav: {};
93
+ ul: {};
94
+ ol: {};
95
+ li: {};
96
+ h1: {};
97
+ h2: {};
98
+ h3: {};
99
+ h4: {};
100
+ h5: {};
101
+ h6: {};
102
+ p: {};
103
+ pre: {};
104
+ span: {};
105
+ code: {};
106
+ a: {
107
+ href: string;
108
+ };
109
109
  }
110
110
 
111
- declare const Fragment = "Fragment";
112
- declare function h(tag: string, attrs?: Record<string, string>, ...children: Array<string | XHTMLNode | Array<string | XHTMLNode>>): {
113
- tag: string;
114
- attrs: Record<string, string>;
115
- children: (string | XHTMLNode)[];
111
+ declare const Fragment = "Fragment";
112
+ declare function h(tag: string, attrs?: Record<string, string>, ...children: Array<string | XHTMLNode | Array<string | XHTMLNode>>): {
113
+ tag: string;
114
+ attrs: Record<string, string>;
115
+ children: (string | XHTMLNode)[];
116
116
  };
117
117
 
118
- interface HTMLMeta {
119
- language: string;
120
- title: string;
121
- }
122
- declare class XHTML extends Item {
123
- private meta;
124
- private content;
125
- constructor(file: string, meta: HTMLMeta, content: string);
126
- title(): string;
127
- language(): string;
128
- bundle(): Promise<Uint8Array>;
129
- }
130
- declare class XHTMLBuilder {
131
- private meta;
132
- private _filename;
133
- private _head;
134
- private _body;
135
- constructor(filename: string);
136
- language(value: string): this;
137
- title(value: string): this;
138
- style(href: string | Style): this;
139
- head(...node: XHTMLNode[]): this;
140
- body(...node: XHTMLNode[]): this;
141
- build(): XHTML;
118
+ interface HTMLMeta {
119
+ language: string;
120
+ title: string;
121
+ }
122
+ declare class XHTML extends Item {
123
+ private _meta;
124
+ private _content;
125
+ constructor(file: string, meta: HTMLMeta, content: string);
126
+ meta(): HTMLMeta;
127
+ title(): string;
128
+ language(): string;
129
+ content(): string;
130
+ bundle(): Promise<Uint8Array>;
131
+ }
132
+ declare class XHTMLBuilder {
133
+ private _meta;
134
+ private _filename;
135
+ private _head;
136
+ private _body;
137
+ private _bodyAttrs;
138
+ constructor(filename: string);
139
+ language(value: string): this;
140
+ title(value: string): this;
141
+ style(...list: Array<string | Style>): this;
142
+ head(...node: XHTMLNode[]): this;
143
+ body(...node: XHTMLNode[]): this;
144
+ bodyAttrs(attrs?: Record<string, string>): this;
145
+ build(): XHTML;
142
146
  }
143
147
 
144
- type NavItem = {
145
- title: string;
146
- attrs?: Record<string, string>;
147
- };
148
- type NavLink = Prettify<NavItem & {
149
- page: HTML | XHTML;
150
- }>;
151
- type NavSubList = Prettify<NavItem & {
152
- list: NavLink[];
153
- }>;
154
- type NavList = Array<NavLink | NavSubList>;
155
- interface NavOption {
156
- title: string;
157
- heading: 1 | 2 | 3 | 4 | 5 | 6;
158
- titleAttrs: Record<string, string>;
159
- head: XHTMLNode[];
160
- body: XHTMLNode[];
161
- }
162
- declare class Toc extends Item {
163
- private _builder;
164
- constructor(file: string);
165
- generate(nav: NavList, { title, heading, titleAttrs, head, body }?: Partial<NavOption>): XHTMLBuilder;
166
- bundle(): Promise<Uint8Array>;
148
+ type NavItem = {
149
+ title: string;
150
+ attrs?: Record<string, string>;
151
+ };
152
+ type NavLink = Prettify<NavItem & {
153
+ page: HTML | XHTML;
154
+ }>;
155
+ type NavSubList = Prettify<NavItem & {
156
+ list: NavLink[];
157
+ }>;
158
+ type NavList = Array<NavLink | NavSubList>;
159
+ interface NavOption {
160
+ title: string;
161
+ heading: 1 | 2 | 3 | 4 | 5 | 6;
162
+ titleAttrs: Record<string, string>;
163
+ builder?: XHTMLBuilder;
164
+ }
165
+ declare class Toc extends XHTML {
166
+ constructor(file: string, meta: HTMLMeta, content: string);
167
+ static from(xhtml: XHTML): Toc;
168
+ static generate(file: string, nav: NavList, { title, heading, titleAttrs, builder }?: Partial<NavOption>): XHTMLBuilder;
167
169
  }
168
170
 
169
- interface Author {
170
- name: string;
171
- fileAs?: string;
172
- role?: string;
173
- uid?: string;
174
- }
175
- interface PackageDocumentMeta {
176
- title: string;
177
- language: string;
178
- contributor: Author[];
179
- coverage: string;
180
- creator: Author;
181
- date: Date;
182
- description: string;
183
- format: string;
184
- publisher: string;
185
- relation: string;
186
- rights: string;
187
- source: string;
188
- subject: string;
189
- type: string;
190
- lastModified: Date;
191
- }
192
- declare class PackageDocument {
193
- private readonly file;
194
- private readonly SpecVersion;
195
- private _uniqueIdentifier;
196
- private _identifier;
197
- private _metadata;
198
- private _toc;
199
- private _items;
200
- private _spine;
201
- constructor(file: string);
202
- filename(): string;
203
- version(): "3.0";
204
- update(info: Partial<PackageDocumentMeta>): this;
205
- title(): string;
206
- language(): string;
207
- creator(): Author;
208
- metadata(): PackageDocumentMeta;
209
- addItem(item: Item): this;
210
- items(): Item[];
211
- manifest(): ManifestItem[];
212
- spine(): ManifestItemRef[];
213
- setSpine(items: Item[]): this;
214
- toc(): Toc | undefined;
215
- setToc(nav: NavList, option?: Partial<NavOption>): this;
216
- uniqueIdentifier(): string;
217
- identifier(): string;
218
- setIdentifier(identifier: string, uniqueIdentifier?: string): void;
171
+ interface Author {
172
+ name: string;
173
+ fileAs?: string;
174
+ role?: string;
175
+ uid?: string;
176
+ }
177
+ interface PackageDocumentMeta {
178
+ title: string;
179
+ language: string;
180
+ contributor: Author[];
181
+ coverage: string;
182
+ creator: Author;
183
+ date: Date;
184
+ description: string;
185
+ format: string;
186
+ publisher: string;
187
+ relation: string;
188
+ rights: string;
189
+ source: string;
190
+ subject: string;
191
+ type: string;
192
+ lastModified: Date;
193
+ }
194
+ declare class PackageDocument {
195
+ private readonly file;
196
+ private readonly SpecVersion;
197
+ private _uniqueIdentifier;
198
+ private _identifier;
199
+ private _metadata;
200
+ private _toc;
201
+ private _items;
202
+ private _spine;
203
+ constructor(file: string);
204
+ filename(): string;
205
+ version(): "3.0";
206
+ update(info: Partial<PackageDocumentMeta>): this;
207
+ title(): string;
208
+ language(): string;
209
+ creator(): Author;
210
+ metadata(): PackageDocumentMeta;
211
+ addItem(item: Item): this;
212
+ items(): Item[];
213
+ manifest(): ManifestItem[];
214
+ spine(): ManifestItemRef[];
215
+ setSpine(items: Item[]): this;
216
+ toc(): Toc | undefined;
217
+ setToc(nav: NavList, option?: Partial<NavOption>): this;
218
+ uniqueIdentifier(): string;
219
+ identifier(): string;
220
+ setIdentifier(identifier: string, uniqueIdentifier?: string): void;
219
221
  }
220
222
 
221
- declare class Epub {
222
- /**
223
- * See: https://www.w3.org/TR/epub-33/#sec-package-doc
224
- *
225
- * Now, it only supports single opf (OEBPS/content.opf)
226
- *
227
- * @returns list of package documents
228
- */
229
- private opfs;
230
- constructor(meta?: Partial<PackageDocumentMeta>);
231
- packages(): PackageDocument[];
232
- main(): PackageDocument;
233
- item(...items: Item[]): this;
234
- toc(nav: NavList, option?: Partial<NavOption>): this;
235
- spine(...items: Item[]): this;
236
- bundle(): Promise<Uint8Array>;
237
- writeFile(file: string): Promise<void>;
223
+ declare class Epub {
224
+ /**
225
+ * See: https://www.w3.org/TR/epub-33/#sec-package-doc
226
+ *
227
+ * Now, it only supports single opf (OEBPS/content.opf)
228
+ *
229
+ * @returns list of package documents
230
+ */
231
+ private opfs;
232
+ constructor(meta?: Partial<PackageDocumentMeta>);
233
+ packages(): PackageDocument[];
234
+ main(): PackageDocument;
235
+ item(...items: Item[]): this;
236
+ toc(nav: NavList, option?: Partial<NavOption>): this;
237
+ spine(...items: Item[]): this;
238
+ bundle(): Promise<Uint8Array>;
239
+ writeFile(file: string): Promise<void>;
238
240
  }
239
241
 
240
- declare class BundleError extends Error {
241
- constructor(msg: string);
242
+ declare class BundleError extends Error {
243
+ constructor(msg: string);
242
244
  }
243
245
 
244
- type PageTemplate<T = any> = (file: string, props: T) => XHTMLBuilder;
245
- interface Theme<P extends Record<string, PageTemplate<any>>> {
246
- name: string;
247
- styles: string[];
248
- images: string[];
249
- pages: Prettify<{
250
- cover(file: string, props: {
251
- image: Image;
252
- }): XHTMLBuilder;
253
- } & P>;
246
+ type PageTemplate<T = any> = (file: string, props: T) => XHTMLBuilder;
247
+ interface Theme<P extends Record<string, PageTemplate<any>>> {
248
+ name: string;
249
+ styles: string[];
250
+ images: string[];
251
+ pages: Prettify<{
252
+ cover(file: string, props: {
253
+ image: Image;
254
+ title?: string;
255
+ }): XHTMLBuilder;
256
+ nav(file: string, props: {
257
+ nav: NavList;
258
+ option: Partial<NavOption>;
259
+ }): XHTMLBuilder;
260
+ } & P>;
254
261
  }
255
262
 
256
- declare const UnbuildPreset: (options?: {
257
- inject?: boolean;
258
- }) => BuildConfig;
263
+ declare const UnbuildPreset: (options?: {
264
+ inject?: boolean;
265
+ }) => BuildConfig;
259
266
  declare function Rollup(): Plugin;
260
267
 
261
- export { Author, BundleError, Epub, EpubIntrinsicElements, Fragment, HTML, Image, ImageExtension, ImageGif, ImageJpeg, ImageMediaType, ImagePng, ImageSvg, ImageWebp, Item, MIMETYPE, ManifestItem, ManifestItemRef, MediaType, NavList, NavOption, PackageDocument, PackageDocumentMeta, PageTemplate, Rollup, Style, TextCSS, TextXHTML, Theme, Toc, UnbuildPreset, XHTML, XHTMLBuilder, XHTMLNode, getImageMediaType, h };
268
+ export { Author, BundleError, Epub, EpubIntrinsicElements, Fragment, HTML, HTMLMeta, Image, ImageExtension, ImageGif, ImageJpeg, ImageMediaType, ImagePng, ImageSvg, ImageWebp, Item, MIMETYPE, ManifestItem, ManifestItemRef, MediaType, NavList, NavOption, PackageDocument, PackageDocumentMeta, PageTemplate, Rollup, Style, TextCSS, TextXHTML, Theme, Toc, UnbuildPreset, XHTML, XHTMLBuilder, XHTMLNode, getImageMediaType, h };
package/dist/index.mjs CHANGED
@@ -2,9 +2,22 @@ import * as path from 'node:path';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { createDefu } from 'defu';
4
4
  import * as path$1 from 'pathe';
5
- import { promises, existsSync, mkdirSync } from 'node:fs';
6
5
  import { strToU8 } from 'fflate';
7
6
  import { XMLBuilder } from 'fast-xml-parser';
7
+ import { promises, existsSync, mkdirSync } from 'node:fs';
8
+
9
+ const Fragment = "Fragment";
10
+ function h(tag, attrs = {}, ...children) {
11
+ const sub = children.flatMap(
12
+ (c) => typeof c === "object" && !Array.isArray(c) && c.tag === Fragment ? c.children ?? [] : c
13
+ ).filter((c) => c !== void 0 && c !== null && c !== false);
14
+ const o = {
15
+ tag,
16
+ attrs: attrs ?? {},
17
+ children: sub
18
+ };
19
+ return o;
20
+ }
8
21
 
9
22
  const MIMETYPE = "application/epub+zip";
10
23
  const ImageGif = "image/gif";
@@ -33,19 +46,6 @@ function getImageMediaType(file) {
33
46
  }
34
47
  }
35
48
 
36
- const Fragment = "Fragment";
37
- function h(tag, attrs = {}, ...children) {
38
- const sub = children.flatMap(
39
- (c) => typeof c === "object" && !Array.isArray(c) && c.tag === Fragment ? c.children ?? [] : c
40
- ).filter((c) => c !== void 0 && c !== null && c !== false);
41
- const o = {
42
- tag,
43
- attrs: attrs ?? {},
44
- children: sub
45
- };
46
- return o;
47
- }
48
-
49
49
  class ManifestItem {
50
50
  constructor(href, id) {
51
51
  this.optional = {};
@@ -199,59 +199,68 @@ const builder = new XMLBuilder({
199
199
  class XHTML extends Item {
200
200
  constructor(file, meta, content) {
201
201
  super(file, TextXHTML);
202
- this.meta = meta;
203
- this.content = content;
202
+ this._meta = meta;
203
+ this._content = content;
204
+ }
205
+ meta() {
206
+ return this._meta;
204
207
  }
205
208
  title() {
206
- return this.meta.title;
209
+ return this._meta.title;
207
210
  }
208
211
  language() {
209
- return this.meta.language;
212
+ return this._meta.language;
213
+ }
214
+ content() {
215
+ return this._content;
210
216
  }
211
217
  async bundle() {
212
- return strToU8(this.content);
218
+ return strToU8(this._content);
213
219
  }
214
220
  }
215
221
  class XHTMLBuilder {
216
222
  constructor(filename) {
217
- this.meta = {
223
+ this._meta = {
218
224
  language: "en",
219
225
  title: ""
220
226
  };
221
227
  this._head = [];
222
228
  this._body = [];
229
+ this._bodyAttrs = {};
223
230
  this._filename = filename;
224
- this.meta.title = path$1.basename(filename);
231
+ this._meta.title = path$1.basename(filename);
225
232
  }
226
233
  language(value) {
227
- this.meta.language = value;
234
+ this._meta.language = value;
228
235
  return this;
229
236
  }
230
237
  title(value) {
231
- this.meta.title = value;
238
+ this._meta.title = value;
232
239
  return this;
233
240
  }
234
- style(href) {
235
- if (typeof href === "string") {
236
- this._head.push({
237
- tag: "link",
238
- attrs: {
239
- href,
240
- rel: "stylesheet",
241
- type: TextCSS
242
- },
243
- children: [""]
244
- });
245
- } else {
246
- this._head.push({
247
- tag: "link",
248
- attrs: {
249
- href: href.relative(this._filename),
250
- rel: "stylesheet",
251
- type: TextCSS
252
- },
253
- children: [""]
254
- });
241
+ style(...list) {
242
+ for (const href of list) {
243
+ if (typeof href === "string") {
244
+ this._head.push({
245
+ tag: "link",
246
+ attrs: {
247
+ href,
248
+ rel: "stylesheet",
249
+ type: TextCSS
250
+ },
251
+ children: [""]
252
+ });
253
+ } else {
254
+ this._head.push({
255
+ tag: "link",
256
+ attrs: {
257
+ href: href.relative(this._filename),
258
+ rel: "stylesheet",
259
+ type: TextCSS
260
+ },
261
+ children: [""]
262
+ });
263
+ }
255
264
  }
256
265
  return this;
257
266
  }
@@ -263,21 +272,32 @@ class XHTMLBuilder {
263
272
  this._body.push(...node);
264
273
  return this;
265
274
  }
275
+ bodyAttrs(attrs = {}) {
276
+ const a = Object.entries(attrs).map(([key, value]) => [`@_${key}`, value]);
277
+ this._bodyAttrs = {
278
+ ...this._bodyAttrs,
279
+ ...Object.fromEntries(a)
280
+ };
281
+ return this;
282
+ }
266
283
  build() {
267
284
  const content = builder.build({
268
285
  html: {
269
286
  "@_xmlns": "http://www.w3.org/1999/xhtml",
270
287
  "@_xmlns:epub": "http://www.idpf.org/2007/ops",
271
- "@_lang": this.meta.language,
272
- "@_xml:lang": this.meta.language,
288
+ "@_lang": this._meta.language,
289
+ "@_xml:lang": this._meta.language,
273
290
  head: {
274
- title: this.meta.title,
291
+ title: this._meta.title,
275
292
  ...list(this._head)
276
293
  },
277
- body: list(this._body)
294
+ body: {
295
+ ...this._bodyAttrs,
296
+ ...list(this._body)
297
+ }
278
298
  }
279
299
  });
280
- return new XHTML(this._filename, this.meta, content);
300
+ return new XHTML(this._filename, this._meta, content);
281
301
  function build(node) {
282
302
  const attrs = Object.fromEntries(
283
303
  Object.entries(node.attrs ?? {}).map(([key, value]) => ["@_" + key, value])
@@ -295,10 +315,17 @@ class XHTMLBuilder {
295
315
  }
296
316
  return obj;
297
317
  }
298
- function list(nodes) {
318
+ function list(list2) {
299
319
  const obj = {};
320
+ const nodes = list2.flatMap((n) => n.tag === Fragment ? n.children ?? [] : [n]);
300
321
  for (const c of nodes) {
301
- if (c.tag in obj) {
322
+ if (typeof c === "string") {
323
+ if (obj["#text"]) {
324
+ obj["#text"] += c;
325
+ } else {
326
+ obj["#text"] = c;
327
+ }
328
+ } else if (c.tag in obj) {
302
329
  obj[c.tag].push(build(c));
303
330
  } else {
304
331
  obj[c.tag] = [build(c)];
@@ -309,14 +336,18 @@ class XHTMLBuilder {
309
336
  }
310
337
  }
311
338
 
312
- class Toc extends Item {
313
- constructor(file) {
314
- super(file, TextXHTML);
315
- this._builder = new XHTMLBuilder(this.filename());
339
+ class Toc extends XHTML {
340
+ constructor(file, meta, content) {
341
+ super(file, meta, content);
316
342
  this.update({ properties: "nav" });
317
343
  }
318
- generate(nav, { title = "Nav", heading = 1, titleAttrs = {}, head = [], body = [] } = {}) {
319
- const builder = new XHTMLBuilder(this.filename());
344
+ static from(xhtml) {
345
+ return new Toc(xhtml.filename(), xhtml.meta(), xhtml.content());
346
+ }
347
+ static generate(file, nav, { title = "Nav", heading = 1, titleAttrs = {}, builder } = {}) {
348
+ if (!builder) {
349
+ builder = new XHTMLBuilder(file);
350
+ }
320
351
  const root = {
321
352
  tag: "nav",
322
353
  attrs: {
@@ -326,17 +357,13 @@ class Toc extends Item {
326
357
  };
327
358
  root.children.push(h("h" + heading, titleAttrs, title));
328
359
  root.children.push(/* @__PURE__ */ h("ol", null, list(nav)));
329
- builder.title(title).body(root).head(...head).body(...body);
330
- return this._builder = builder;
360
+ return builder.body(root);
331
361
  function list(items) {
332
362
  return items.map(
333
363
  (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
334
364
  );
335
365
  }
336
366
  }
337
- async bundle() {
338
- return this._builder.build().bundle();
339
- }
340
367
  }
341
368
 
342
369
  const defu = createDefu((obj, key, value) => {
@@ -424,9 +451,11 @@ class PackageDocument {
424
451
  return this._toc;
425
452
  }
426
453
  setToc(nav, option = {}) {
427
- const toc = new Toc("nav.xhtml");
428
- toc.generate(nav, option).language(this._metadata.language).build();
429
- this._toc = toc;
454
+ const toc = Toc.generate("nav.xhtml", nav, option);
455
+ if (!option.builder) {
456
+ toc.title(option.title ?? "Nav").language(this._metadata.language);
457
+ }
458
+ this._toc = Toc.from(toc.build());
430
459
  return this;
431
460
  }
432
461
  // --- identifier ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epubook/core",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "ebook",