@storyteller-platform/epub 0.8.1 → 1.0.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/dist/upgrade.js CHANGED
@@ -1,6 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@storyteller-platform/epub/jsx-runtime";
1
2
  import { nanoid } from "nanoid";
2
3
  import { MediaType } from "@storyteller-platform/media-types";
3
- import { Epub, } from "./index.js";
4
+ import { findXmlChildByNameNS, findXmlDescendantByName, findXmlDescendantByNameNS, insert, NS, replace, serialize, text, x, } from "./xml.js";
4
5
  const GUIDE_TO_EPUBTYPE = {
5
6
  acknowledgements: "acknowledgments",
6
7
  "other.afterword": "afterword",
@@ -50,54 +51,52 @@ const GUIDE_TO_EPUBTYPE = {
50
51
  const XHTML_MEDIA_TYPES = new Set([MediaType.XHTML, MediaType.HTML]);
51
52
  // xml helpers
52
53
  function getMetadataElement(pkg) {
53
- return Epub.findXmlChildByName("metadata", Epub.getXmlChildren(pkg));
54
+ return findXmlChildByNameNS(NS.opf, "metadata", pkg);
54
55
  }
55
56
  function getManifestElement(pkg) {
56
- return Epub.findXmlChildByName("manifest", Epub.getXmlChildren(pkg));
57
+ return findXmlChildByNameNS(NS.opf, "manifest", pkg);
57
58
  }
58
59
  function getSpineElement(pkg) {
59
- return Epub.findXmlChildByName("spine", Epub.getXmlChildren(pkg));
60
+ return findXmlChildByNameNS(NS.opf, "spine", pkg);
61
+ }
62
+ /**
63
+ * Replace an outdated document type declaration with `<!DOCTYPE html>`.
64
+ *
65
+ * EPUB 3 XHTML content documents must conform to the HTML5 conformance
66
+ * criteria, which require `<!DOCTYPE html>`.
67
+ */
68
+ export function normalizeDoctype(doc) {
69
+ const doctype = doc.doctype;
70
+ if (doctype && doctype.publicId === "" && doctype.systemId === "")
71
+ return;
72
+ if (doctype)
73
+ doc.removeChild(doctype);
74
+ doc.insertBefore(doc.implementation.createDocumentType("html", "", ""), doc.documentElement);
75
+ return;
60
76
  }
61
77
  function ensureId(element) {
62
- const existing = element[":@"]?.["@_id"];
78
+ const existing = element.getAttribute("id");
63
79
  if (existing)
64
80
  return existing;
65
81
  const id = `id-${nanoid(8)}`;
66
- element[":@"] ??= {};
67
- element[":@"]["@_id"] = id;
82
+ element.setAttribute("id", id);
68
83
  return id;
69
84
  }
70
- function findAllByName(name, xml) {
71
- return xml.filter((node) => !Epub.isXmlTextNode(node) && name in node);
72
- }
73
- function hasElementDeep(xml, name) {
74
- for (const node of xml) {
75
- if (Epub.isXmlTextNode(node))
76
- continue;
77
- if (Epub.getXmlElementName(node) === name)
78
- return true;
79
- if (hasElementDeep(Epub.getXmlChildren(node), name))
80
- return true;
81
- }
82
- return false;
83
- }
84
- function textContentOf(element) {
85
- return Epub.getXhtmlTextContent(Epub.getXmlChildren(element)).trim();
85
+ function findAllByNameNS(namespaceUri, name, element) {
86
+ return element.children.filter((node) => (!namespaceUri || node.namespaceURI === namespaceUri) &&
87
+ node.localName === name);
86
88
  }
87
- function removeFromArray(array, item) {
88
- const idx = array.indexOf(item);
89
- if (idx !== -1)
90
- array.splice(idx, 1);
89
+ function findAllByName(name, element) {
90
+ return findAllByNameNS(null, name, element);
91
91
  }
92
92
  // ---------------------------------------------------------------------------
93
93
  export function upgradeIdentifiers(pkg) {
94
94
  const metadata = getMetadataElement(pkg);
95
95
  if (!metadata)
96
96
  return;
97
- for (const ident of findAllByName("dc:identifier", metadata.metadata)) {
98
- const attrs = ident[":@"] ?? {};
99
- let val = textContentOf(ident);
100
- let scheme = attrs["@_opf:scheme"];
97
+ for (const ident of findAllByNameNS(NS.dc, "identifier", metadata)) {
98
+ let val = ident.textContent ?? "";
99
+ let scheme = ident.getAttributeNS(NS.opf, "scheme");
101
100
  if (val.toLowerCase().startsWith("urn:")) {
102
101
  const rest = val.slice(4);
103
102
  const colonIdx = rest.indexOf(":");
@@ -109,21 +108,20 @@ export function upgradeIdentifiers(pkg) {
109
108
  if (scheme && val && !scheme.toLowerCase().startsWith("uri")) {
110
109
  val = `${scheme}:${val}`;
111
110
  }
112
- const id = attrs["@_id"];
113
- ident[":@"] = id ? { "@_id": id } : {};
114
- Epub.replaceXmlChildren(ident, [Epub.createXmlTextNode(val)]);
111
+ const id = ident.getAttribute("id");
112
+ replace(metadata, _jsx("dc:identifier", { id: id, children: val }), ident);
115
113
  }
116
114
  }
117
115
  export function upgradeTitle(pkg) {
118
116
  const metadata = getMetadataElement(pkg);
119
117
  if (!metadata)
120
118
  return;
121
- const titles = findAllByName("dc:title", metadata.metadata);
119
+ const titles = findAllByNameNS(NS.dc, "title", metadata);
122
120
  let firstTitle = null;
123
121
  for (const title of titles) {
124
- const text = textContentOf(title);
122
+ const text = title.textContent?.trim();
125
123
  if (!text) {
126
- removeFromArray(metadata.metadata, title);
124
+ metadata.removeChild(title);
127
125
  continue;
128
126
  }
129
127
  firstTitle ??= title;
@@ -131,43 +129,38 @@ export function upgradeTitle(pkg) {
131
129
  if (!firstTitle)
132
130
  return;
133
131
  const titleId = ensureId(firstTitle);
134
- metadata.metadata.push(Epub.createXmlElement("meta", { refines: `#${titleId}`, property: "title-type" }, [Epub.createXmlTextNode("main")]));
132
+ insert(metadata, _jsx("meta", { refines: `#${titleId}`, property: "title-type", children: "main" }));
135
133
  }
136
134
  export function upgradeLanguages(pkg) {
137
135
  const metadata = getMetadataElement(pkg);
138
136
  if (!metadata)
139
137
  return;
140
- const langs = findAllByName("dc:language", metadata.metadata);
138
+ const langs = findAllByNameNS(NS.dc, "language", metadata);
141
139
  if (langs.length > 0) {
142
140
  for (const lang of langs) {
143
- const id = lang[":@"]?.["@_id"];
144
- lang[":@"] = id ? { "@_id": id } : {};
141
+ const id = lang.getAttribute("id");
142
+ replace(metadata, _jsx("dc:language", { id: id, children: Array.from(lang.childNodes) }), lang);
145
143
  }
146
144
  return;
147
145
  }
148
- metadata.metadata.push(Epub.createXmlElement("dc:language", {}, [Epub.createXmlTextNode("und")]));
146
+ insert(metadata, _jsx("dc:language", { children: "und" }));
149
147
  }
150
148
  export function upgradeAuthors(pkg) {
151
149
  const metadata = getMetadataElement(pkg);
152
150
  if (!metadata)
153
151
  return;
154
- for (const which of ["dc:creator", "dc:contributor"]) {
155
- for (const elem of findAllByName(which, metadata.metadata)) {
156
- const attrs = elem[":@"] ?? {};
157
- const role = attrs["@_opf:role"];
158
- const sort = attrs["@_opf:file-as"];
159
- const elemId = role || sort ? ensureId(elem) : attrs["@_id"];
152
+ for (const which of ["creator", "contributor"]) {
153
+ for (const elem of findAllByNameNS(NS.dc, which, metadata)) {
154
+ const role = elem.getAttributeNS(NS.opf, "role");
155
+ const sort = elem.getAttributeNS(NS.opf, "file-as");
156
+ const elemId = role || sort ? ensureId(elem) : elem.getAttribute("id");
160
157
  // strip all non-id attributes
161
- elem[":@"] = elemId ? { "@_id": elemId } : {};
158
+ replace(metadata, x(`dc:${which}`, elemId === null ? null : { id: elemId }, ...elem.childNodes), elem);
162
159
  if (role) {
163
- metadata.metadata.push(Epub.createXmlElement("meta", {
164
- refines: `#${elemId}`,
165
- property: "role",
166
- scheme: "marc:relators",
167
- }, [Epub.createXmlTextNode(role)]));
160
+ insert(metadata, _jsx("meta", { refines: `#${elemId}`, property: "role", scheme: "marc:relators", children: role }));
168
161
  }
169
162
  if (sort) {
170
- metadata.metadata.push(Epub.createXmlElement("meta", { refines: `#${elemId}`, property: "file-as" }, [Epub.createXmlTextNode(sort)]));
163
+ insert(metadata, _jsx("meta", { refines: `#${elemId}`, property: "file-as", children: sort }));
171
164
  }
172
165
  }
173
166
  }
@@ -176,27 +169,26 @@ export function upgradeDate(pkg) {
176
169
  const metadata = getMetadataElement(pkg);
177
170
  if (!metadata)
178
171
  return;
179
- const dates = findAllByName("dc:date", metadata.metadata);
172
+ const dates = findAllByNameNS(NS.dc, "date", metadata);
180
173
  let kept = false;
181
174
  for (const date of dates) {
182
- const text = textContentOf(date);
175
+ const text = date.textContent;
183
176
  if (!text || kept) {
184
- removeFromArray(metadata.metadata, date);
177
+ metadata.removeChild(date);
185
178
  continue;
186
179
  }
187
180
  kept = true;
188
- const id = date[":@"]?.["@_id"];
189
- date[":@"] = id ? { "@_id": id } : {};
181
+ const id = date.getAttribute("id");
182
+ replace(metadata, _jsx("dc:date", { id: id, children: Array.from(date.childNodes) }), date);
190
183
  }
191
184
  }
192
185
  export function upgradeMeta(pkg) {
193
186
  const metadata = getMetadataElement(pkg);
194
187
  if (!metadata)
195
188
  return;
196
- for (const meta of findAllByName("meta", metadata.metadata)) {
197
- const attrs = meta[":@"] ?? {};
198
- const name = attrs["@_name"] ?? "";
199
- const content = attrs["@_content"] ?? "";
189
+ for (const meta of findAllByName("meta", metadata)) {
190
+ const name = meta.getAttribute("name") ?? "";
191
+ const content = meta.getAttribute("content") ?? "";
200
192
  let prop = null;
201
193
  let value = content;
202
194
  const cleanName = name.startsWith("rendition:")
@@ -219,10 +211,13 @@ export function upgradeMeta(pkg) {
219
211
  }
220
212
  if (!prop)
221
213
  continue;
222
- delete attrs["@_name"];
223
- delete attrs["@_content"];
224
- attrs["@_property"] = prop;
225
- Epub.replaceXmlChildren(meta, [Epub.createXmlTextNode(value)]);
214
+ meta.removeAttribute("name");
215
+ meta.removeAttribute("content");
216
+ meta.setAttribute("property", prop);
217
+ while (meta.firstChild) {
218
+ meta.removeChild(meta.firstChild);
219
+ }
220
+ insert(meta, text(value));
226
221
  }
227
222
  }
228
223
  export function upgradeCover(pkg) {
@@ -230,27 +225,26 @@ export function upgradeCover(pkg) {
230
225
  const manifest = getManifestElement(pkg);
231
226
  if (!metadata || !manifest)
232
227
  return;
233
- for (const meta of findAllByName("meta", metadata.metadata)) {
234
- const attrs = meta[":@"];
235
- const isCoverMeta = attrs?.["@_name"] === "cover" && attrs["@_content"];
228
+ for (const meta of findAllByName("meta", metadata)) {
229
+ const content = meta.getAttribute("content");
230
+ const isCoverMeta = meta.getAttribute("name") === "cover" && content;
236
231
  if (!isCoverMeta)
237
232
  continue;
238
- const itemId = attrs["@_content"];
239
- for (const item of findAllByName("item", manifest.manifest)) {
240
- const itemAttrs = item[":@"];
241
- if (!itemAttrs || itemAttrs["@_id"] !== itemId)
233
+ const itemId = content;
234
+ for (const item of findAllByName("item", manifest)) {
235
+ if (item.getAttribute("id") !== itemId)
242
236
  continue;
243
- const mediaType = (itemAttrs["@_media-type"] ?? "").toLowerCase();
237
+ const mediaType = (item.getAttribute("media-type") ?? "").toLowerCase();
244
238
  // only mark actual images, not html wrappers
245
239
  const isImage = mediaType && !mediaType.includes("xml") && !mediaType.includes("html");
246
240
  if (!isImage)
247
241
  continue;
248
- const existing = (itemAttrs["@_properties"] ?? "")
242
+ const existing = (item.getAttribute("properties") ?? "")
249
243
  .split(" ")
250
244
  .filter(Boolean);
251
245
  const props = new Set(existing);
252
246
  props.add("cover-image");
253
- itemAttrs["@_properties"] = [...props].sort().join(" ");
247
+ item.setAttribute("properties", [...props].sort().join(" "));
254
248
  }
255
249
  }
256
250
  }
@@ -258,35 +252,27 @@ export function removeInvalidDcAttrs(pkg) {
258
252
  const metadata = getMetadataElement(pkg);
259
253
  if (!metadata)
260
254
  return;
261
- for (const node of metadata.metadata) {
262
- if (Epub.isXmlTextNode(node))
263
- continue;
264
- const name = Epub.getXmlElementName(node);
265
- if (!name.startsWith("dc:"))
255
+ for (const node of Array.from(metadata.children)) {
256
+ if (node.namespaceURI !== NS.dc)
266
257
  continue;
267
- const attrs = node[":@"];
268
- if (!attrs)
269
- continue;
270
- const id = attrs["@_id"];
271
- node[":@"] = id ? { "@_id": id } : {};
258
+ for (const attr of Array.from(node.attributes)) {
259
+ if (attr.name === "id" || attr.name.startsWith("xmlns:"))
260
+ continue;
261
+ node.removeAttributeNode(attr);
262
+ }
272
263
  }
273
264
  }
274
265
  export function setLastModified(pkg) {
275
266
  const metadata = getMetadataElement(pkg);
276
267
  if (!metadata)
277
268
  return;
278
- for (let i = metadata.metadata.length - 1; i >= 0; i--) {
279
- const node = metadata.metadata[i];
280
- if (!node || Epub.isXmlTextNode(node))
281
- continue;
282
- if (node[":@"]?.["@_property"] === "dcterms:modified") {
283
- metadata.metadata.splice(i, 1);
269
+ for (const node of Array.from(metadata.children)) {
270
+ if (node.getAttribute("property") === "dcterms:modified") {
271
+ metadata.removeChild(node);
284
272
  }
285
273
  }
286
274
  const now = new Date().toISOString().replace(/\.\d+/, "");
287
- metadata.metadata.push(Epub.createXmlElement("meta", { property: "dcterms:modified" }, [
288
- Epub.createXmlTextNode(now),
289
- ]));
275
+ insert(metadata, _jsx("meta", { property: "dcterms:modified", children: now }));
290
276
  }
291
277
  export function upgradePackageMetadata(pkg) {
292
278
  upgradeIdentifiers(pkg);
@@ -300,19 +286,16 @@ export function upgradePackageMetadata(pkg) {
300
286
  setLastModified(pkg);
301
287
  }
302
288
  export function extractGuideLandmarks(pkg) {
303
- const guide = Epub.findXmlChildByName("guide", Epub.getXmlChildren(pkg));
289
+ const guide = findXmlChildByNameNS(NS.opf, "guide", pkg);
304
290
  if (!guide)
305
291
  return [];
306
292
  const landmarks = [];
307
- for (const node of Epub.getXmlChildren(guide)) {
308
- if (Epub.isXmlTextNode(node))
309
- continue;
293
+ for (const node of Array.from(guide.children)) {
310
294
  if (!("reference" in node))
311
295
  continue;
312
- const attrs = node[":@"] ?? {};
313
- const href = attrs["@_href"] ?? "";
314
- const title = attrs["@_title"] ?? "";
315
- const guideType = (attrs["@_type"] ?? "").toLowerCase();
296
+ const href = node.getAttribute("href") ?? "";
297
+ const title = node.getAttribute("title") ?? "";
298
+ const guideType = (node.getAttribute("type") ?? "").toLowerCase();
316
299
  const epubType = GUIDE_TO_EPUBTYPE[guideType];
317
300
  if (epubType === undefined || !href)
318
301
  continue;
@@ -321,17 +304,14 @@ export function extractGuideLandmarks(pkg) {
321
304
  return landmarks;
322
305
  }
323
306
  export function removeGuide(pkg) {
324
- const children = Epub.getXmlChildren(pkg);
325
- const guide = Epub.findXmlChildByName("guide", children);
307
+ const guide = findXmlChildByNameNS(NS.opf, "guide", pkg);
326
308
  if (guide) {
327
- removeFromArray(children, guide);
309
+ pkg.removeChild(guide);
328
310
  }
329
311
  }
330
312
  export function removeSpineTocRef(pkg) {
331
313
  const spine = getSpineElement(pkg);
332
- if (!spine?.[":@"])
333
- return;
334
- delete spine[":@"]["@_toc"];
314
+ spine?.removeAttribute("toc");
335
315
  }
336
316
  export async function collectManifestProperties(epub) {
337
317
  const manifest = await epub.getManifest();
@@ -339,22 +319,25 @@ export async function collectManifestProperties(epub) {
339
319
  const mediaType = MediaType.fromMime(item.mediaType ?? "");
340
320
  if (!mediaType || !XHTML_MEDIA_TYPES.has(mediaType))
341
321
  continue;
342
- let xml;
322
+ let documentElement;
343
323
  try {
344
- xml = await epub.readXhtmlItemContents(item.id);
324
+ documentElement = (await epub.readXhtmlItemContents(item.id))
325
+ .documentElement;
345
326
  }
346
327
  catch {
347
328
  continue;
348
329
  }
330
+ if (!documentElement)
331
+ continue;
349
332
  const props = new Set(item.properties ?? []);
350
333
  const before = props.size;
351
- if (hasElementDeep(xml, "svg"))
334
+ if (findXmlDescendantByName("svg", documentElement))
352
335
  props.add("svg");
353
- if (hasElementDeep(xml, "script"))
336
+ if (findXmlDescendantByName("script", documentElement))
354
337
  props.add("scripted");
355
- if (hasElementDeep(xml, "math"))
338
+ if (findXmlDescendantByName("math", documentElement))
356
339
  props.add("mathml");
357
- if (hasElementDeep(xml, "epub:switch"))
340
+ if (findXmlDescendantByNameNS(NS.epub, "switch", documentElement))
358
341
  props.add("switch");
359
342
  if (props.size === before)
360
343
  continue;
@@ -368,15 +351,14 @@ export function fixFontMimeTypes(pkg) {
368
351
  const manifest = getManifestElement(pkg);
369
352
  if (!manifest)
370
353
  return;
371
- for (const item of findAllByName("item", manifest.manifest)) {
372
- const declared = item[":@"]?.["@_media-type"] ?? "";
354
+ for (const item of findAllByName("item", manifest)) {
355
+ const declared = item.getAttribute("media-type") ?? "";
373
356
  if (!MediaType.fromMime(declared)?.is("font"))
374
357
  continue;
375
- const corrected = MediaType.fromPath(item[":@"]?.["@_href"] ?? "");
358
+ const corrected = MediaType.fromPath(item.getAttribute("href") ?? "");
376
359
  if (!corrected?.is("font") || corrected.mime === declared)
377
360
  continue;
378
- item[":@"] ??= {};
379
- item[":@"]["@_media-type"] = corrected.mime;
361
+ item.setAttribute("media-type", corrected.mime);
380
362
  }
381
363
  }
382
364
  export function buildTocOl(entries) {
@@ -384,19 +366,17 @@ export function buildTocOl(entries) {
384
366
  const label = entry.title.replace(/\s+/g, " ").trim();
385
367
  const liChildren = [];
386
368
  if (entry.href) {
387
- liChildren.push(Epub.createXmlElement("a", { href: entry.href }, [
388
- Epub.createXmlTextNode(label),
389
- ]));
369
+ liChildren.push(_jsx("a", { href: entry.href, children: label }));
390
370
  }
391
371
  else {
392
- liChildren.push(Epub.createXmlElement("span", {}, [Epub.createXmlTextNode(label)]));
372
+ liChildren.push(_jsx("span", { children: label }));
393
373
  }
394
374
  if (entry.children && entry.children.length > 0) {
395
375
  liChildren.push(buildTocOl(entry.children));
396
376
  }
397
- return Epub.createXmlElement("li", {}, liChildren);
377
+ return _jsx("li", { children: liChildren });
398
378
  });
399
- return Epub.createXmlElement("ol", {}, children);
379
+ return _jsx("ol", { children: children });
400
380
  }
401
381
  export async function buildNavDocument(epub, tocEntries, landmarks) {
402
382
  let tocOl;
@@ -407,41 +387,21 @@ export async function buildNavDocument(epub, tocEntries, landmarks) {
407
387
  // epub 3 requires a non-empty <ol>, fall back to first spine item
408
388
  const spineItems = await epub.getSpineItems();
409
389
  const firstHref = spineItems[0]?.href ?? "#";
410
- tocOl = Epub.createXmlElement("ol", {}, [
411
- Epub.createXmlElement("li", {}, [
412
- Epub.createXmlElement("a", { href: firstHref }, [
413
- Epub.createXmlTextNode("Start"),
414
- ]),
415
- ]),
416
- ]);
390
+ tocOl = (_jsx("ol", { children: _jsx("li", { children: _jsx("a", { href: firstHref, children: "Start" }) }) }));
417
391
  }
418
- const tocNav = Epub.createXmlElement("nav", { "epub:type": "toc" }, [
419
- Epub.createXmlElement("h1", {}, [
420
- Epub.createXmlTextNode("Table of Contents"),
421
- ]),
422
- tocOl,
423
- ]);
424
- const body = [tocNav];
392
+ const tocNav = (_jsxs("nav", { "epub:type": "toc", children: [_jsx("h1", { children: "Table of Contents" }), tocOl] }));
393
+ const bodyChildren = [tocNav];
425
394
  const validLandmarks = landmarks.filter((lm) => lm.type);
426
395
  if (validLandmarks.length > 0) {
427
- const lmOl = Epub.createXmlElement("ol", {}, validLandmarks.map((lm) => Epub.createXmlElement("li", {}, [
428
- Epub.createXmlElement("a", { "epub:type": lm.type, href: lm.href }, [
429
- Epub.createXmlTextNode(lm.title || lm.type),
430
- ]),
431
- ])));
432
- body.push(Epub.createXmlElement("nav", { "epub:type": "landmarks", hidden: "" }, [
433
- lmOl,
434
- ]));
396
+ const lmOl = (_jsx("ol", { children: validLandmarks.map((lm) => (_jsx("li", { children: _jsx("a", { "epub:type": lm.type, href: lm.href, children: lm.title || lm.type }) }))) }));
397
+ bodyChildren.push(_jsx("nav", { "epub:type": "landmarks", hidden: "", children: lmOl }));
435
398
  }
436
- const head = [
437
- Epub.createXmlElement("title", {}, [Epub.createXmlTextNode("Navigation")]),
438
- ];
439
- const navDoc = await epub.createXhtmlDocument(body, head);
440
- return Epub.xhtmlBuilder.build(navDoc);
399
+ const head = [_jsx("title", { children: "Navigation" })];
400
+ const navDoc = await epub.createXhtmlDocument(bodyChildren, head);
401
+ return serialize(navDoc);
441
402
  }
442
403
  export function setPackageVersion(pkg, version) {
443
- pkg[":@"] ??= {};
444
- pkg[":@"]["@_version"] = version;
404
+ pkg.setAttribute("version", version);
445
405
  }
446
406
  export async function chooseNavHref(epub) {
447
407
  const manifest = await epub.getManifest();
package/dist/xml.d.ts ADDED
@@ -0,0 +1,103 @@
1
+ import xmldom, { type Document, type CharacterData, type Element, type Node, type Text, type DocumentType } from "@xmldom/xmldom";
2
+ export declare const XMLSerializer: typeof xmldom.XMLSerializer;
3
+ export declare function isElement(n: Node): n is Element;
4
+ export declare function isText(n: Node): n is CharacterData;
5
+ export declare function isCdata(n: Node): n is CharacterData;
6
+ export declare function isComment(n: Node): n is CharacterData;
7
+ /**
8
+ * Give every element in `node`'s subtree the namespace its position implies,
9
+ * without overwriting any element that already has one.
10
+ *
11
+ * @privateremarks
12
+ *
13
+ * `x()` builds elements in no namespace. That serializes correctly, because a
14
+ * child in no namespace inherits the enclosing default namespace on the way
15
+ * out, but in memory the elements report `namespaceURI === null` and so are
16
+ * invisible to namespace-aware queries such as `getElementsByTagNameNS`.
17
+ * Calling this after inserting a node makes the tree report the namespace its
18
+ * position implies.
19
+ */
20
+ export declare function adoptNamespaces(node: Element): void;
21
+ /**
22
+ * Declare `uri` as the default namespace of `element` and bring the elements
23
+ * already under it in line with that declaration.
24
+ *
25
+ * xmldom records a namespace declaration without applying it to the tree, so a
26
+ * document read without `xmlns` stays in no namespace in memory even once the
27
+ * declaration is added. Namespace-aware lookups cannot see any of it until the
28
+ * subtree is fixed up, and the document element has no element parent for
29
+ * `adoptNamespaces` to inherit from, so it has to be bound explicitly.
30
+ */
31
+ export declare function declareDefaultNamespace(element: Element, uri: string): void;
32
+ /**
33
+ * Insert `child` into `parent`, then adopt its namespace.
34
+ *
35
+ * If `reference` is omitted, appends child to parent.
36
+ */
37
+ export declare function insert<T extends Node>(parent: Element, child: T, reference?: Node | null): T;
38
+ /**
39
+ * Replace `oldChild` with `newChild` in `parent`, then adopt the new child's
40
+ * namespace.
41
+ */
42
+ export declare function replace<T extends Node>(parent: Element, newChild: T, oldChild: Node): T;
43
+ /**
44
+ * Given an XHTML document, return the body element.
45
+ */
46
+ export declare function getXhtmlBody(document: Document): Element;
47
+ /**
48
+ * Given an Element, find the first child matching
49
+ * the provided namespace, name and optional filter.
50
+ */
51
+ export declare function findXmlChildByNameNS(namespaceUri: string | null, name: string, element: Element, filter?: (node: Element) => boolean): Element | undefined;
52
+ /**
53
+ * Given an Element, find the first child matching
54
+ * the provided name and optional filter.
55
+ */
56
+ export declare function findXmlChildByName(name: string, element: Element, filter?: (node: Element) => boolean): Element | undefined;
57
+ /**
58
+ * Given an Element, find the first descendant matching
59
+ * the provided name and optional filter.
60
+ *
61
+ * Will perform a breadth first search for the element, returning
62
+ * the highest element in the tree matching the name and filter.
63
+ */
64
+ export declare function findXmlDescendantByNameNS(namespaceUri: string | null, name: string, element: Element, filter?: (node: Element) => boolean): Element | undefined;
65
+ /**
66
+ * Given an Element, find the first descendant matching
67
+ * the provided name and optional filter.
68
+ *
69
+ * Will perform a breadth first search for the element, returning
70
+ * the highest element in the tree matching the name and filter.
71
+ */
72
+ export declare function findXmlDescendantByName(name: string, element: Element, filter?: (node: Element) => boolean): Element | undefined;
73
+ /**
74
+ * Given an XHTML document, add a `link` element to the
75
+ * `head` of the document.
76
+ */
77
+ export declare function addLinkToXhtmlHead(document: Document, link: {
78
+ rel: string;
79
+ href: string;
80
+ type: string;
81
+ }): void;
82
+ export type XmlDiagnostic = {
83
+ level: "warning" | "error" | "fatalError";
84
+ message: string;
85
+ line?: number | undefined;
86
+ column?: number | undefined;
87
+ };
88
+ export declare function parse(xml: string, mediaType: string, onError?: (diagnostic: XmlDiagnostic) => void): Document;
89
+ export declare function serialize(doc: Node, options?: {
90
+ validate?: boolean | undefined;
91
+ }): string;
92
+ export declare const NS: {
93
+ xmlns: string;
94
+ xml: string;
95
+ xhtml: string;
96
+ opf: string;
97
+ dc: string;
98
+ epub: string;
99
+ };
100
+ export declare function x(name: string, attributes?: Record<string, string | undefined> | null, ...children: Array<Node | string | null | undefined>): Element;
101
+ export declare function text(data: string): Text;
102
+ export declare function createDocument(namespaceUri: string | null, qualifiedName: string, docType?: DocumentType | null): Document;
103
+ export declare function createDocumentType(qualifiedName: string, publicId?: string, systemId?: string): DocumentType;