@voidberg/quarto 0.1.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/LICENSE +21 -0
- package/README.md +157 -0
- package/assets/quarto.png +0 -0
- package/dist/css.d.ts +6 -0
- package/dist/css.js +109 -0
- package/dist/css.js.map +1 -0
- package/dist/epub.d.ts +8 -0
- package/dist/epub.js +232 -0
- package/dist/epub.js.map +1 -0
- package/dist/html.d.ts +54 -0
- package/dist/html.js +430 -0
- package/dist/html.js.map +1 -0
- package/dist/images.d.ts +24 -0
- package/dist/images.js +51 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/kepub.d.ts +10 -0
- package/dist/kepub.js +32 -0
- package/dist/kepub.js.map +1 -0
- package/dist/opf.d.ts +65 -0
- package/dist/opf.js +172 -0
- package/dist/opf.js.map +1 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +25 -0
- package/dist/util.js +157 -0
- package/dist/util.js.map +1 -0
- package/dist/zip.d.ts +9 -0
- package/dist/zip.js +22 -0
- package/dist/zip.js.map +1 -0
- package/package.json +61 -0
package/dist/opf.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { escapeXml } from "./util.js";
|
|
2
|
+
export const CONTAINER_XML = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3
|
+
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
|
4
|
+
<rootfiles>
|
|
5
|
+
<rootfile full-path="EPUB/content.opf" media-type="application/oebps-package+xml"/>
|
|
6
|
+
</rootfiles>
|
|
7
|
+
</container>
|
|
8
|
+
`;
|
|
9
|
+
/** A fixed timestamp keeps builds reproducible when no date is supplied. */
|
|
10
|
+
const EPOCH = "1970-01-01T00:00:00Z";
|
|
11
|
+
/**
|
|
12
|
+
* EPUB3 requires `dcterms:modified` to be exactly `CCYY-MM-DDThh:mm:ssZ` — UTC,
|
|
13
|
+
* no fractional seconds. Normalize whatever the caller passed (commonly a full
|
|
14
|
+
* `Date.toISOString()` with milliseconds) into that form, falling back to
|
|
15
|
+
* {@link EPOCH} when it isn't a parseable date.
|
|
16
|
+
*/
|
|
17
|
+
function normalizeModified(date) {
|
|
18
|
+
if (!date)
|
|
19
|
+
return EPOCH;
|
|
20
|
+
const t = Date.parse(date);
|
|
21
|
+
if (Number.isNaN(t))
|
|
22
|
+
return EPOCH;
|
|
23
|
+
return `${new Date(t).toISOString().slice(0, 19)}Z`;
|
|
24
|
+
}
|
|
25
|
+
export function buildOpf(meta, manifest, spine, options = {}) {
|
|
26
|
+
const modified = normalizeModified(meta.date);
|
|
27
|
+
const metaTags = [
|
|
28
|
+
`<dc:identifier id="pub-id">${escapeXml(meta.id)}</dc:identifier>`,
|
|
29
|
+
`<dc:title>${escapeXml(meta.title)}</dc:title>`,
|
|
30
|
+
`<dc:language>${escapeXml(meta.language)}</dc:language>`,
|
|
31
|
+
`<meta property="dcterms:modified">${modified}</meta>`,
|
|
32
|
+
];
|
|
33
|
+
for (const author of meta.authors) {
|
|
34
|
+
metaTags.push(`<dc:creator>${escapeXml(author)}</dc:creator>`);
|
|
35
|
+
}
|
|
36
|
+
if (meta.publisher)
|
|
37
|
+
metaTags.push(`<dc:publisher>${escapeXml(meta.publisher)}</dc:publisher>`);
|
|
38
|
+
if (meta.description)
|
|
39
|
+
metaTags.push(`<dc:description>${escapeXml(meta.description)}</dc:description>`);
|
|
40
|
+
if (meta.date)
|
|
41
|
+
metaTags.push(`<dc:date>${escapeXml(meta.date)}</dc:date>`);
|
|
42
|
+
if (meta.coverImageId)
|
|
43
|
+
metaTags.push(`<meta name="cover" content="${escapeXml(meta.coverImageId)}"/>`);
|
|
44
|
+
const manifestTags = manifest.map((item) => {
|
|
45
|
+
const props = item.properties ? ` properties="${item.properties}"` : "";
|
|
46
|
+
return `<item id="${escapeXml(item.id)}" href="${escapeXml(item.href)}" media-type="${item.mediaType}"${props}/>`;
|
|
47
|
+
});
|
|
48
|
+
const spineTags = spine.map((idref) => `<itemref idref="${escapeXml(idref)}"/>`);
|
|
49
|
+
const spineToc = options.ncxId ? ` toc="${escapeXml(options.ncxId)}"` : "";
|
|
50
|
+
// EPUB2 guide — deprecated in EPUB3 but still used by Kindle/older readers to
|
|
51
|
+
// locate the cover and reading start.
|
|
52
|
+
const guideBlock = options.guide?.length
|
|
53
|
+
? `\n <guide>\n ${options.guide
|
|
54
|
+
.map((r) => `<reference type="${escapeXml(r.type)}" title="${escapeXml(r.title)}" href="${escapeXml(r.href)}"/>`)
|
|
55
|
+
.join("\n ")}\n </guide>`
|
|
56
|
+
: "";
|
|
57
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
58
|
+
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" unique-identifier="pub-id" xml:lang="${escapeXml(meta.language)}">
|
|
59
|
+
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
60
|
+
${metaTags.join("\n ")}
|
|
61
|
+
</metadata>
|
|
62
|
+
<manifest>
|
|
63
|
+
${manifestTags.join("\n ")}
|
|
64
|
+
</manifest>
|
|
65
|
+
<spine${spineToc}>
|
|
66
|
+
${spineTags.join("\n ")}
|
|
67
|
+
</spine>${guideBlock}
|
|
68
|
+
</package>
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* EPUB3 navigation document. EPUB3 mandates exactly one `nav` document, so even
|
|
73
|
+
* when a book opts out of a table of contents we still emit one — but mark it
|
|
74
|
+
* `hidden` and keep it out of the spine so no visible TOC page appears.
|
|
75
|
+
*/
|
|
76
|
+
export function buildNav(navTitle, language, entries, hidden = false, landmarks = []) {
|
|
77
|
+
const items = entries
|
|
78
|
+
.map((e) => ` <li><a href="${escapeXml(e.href)}">${escapeXml(e.title)}</a></li>`)
|
|
79
|
+
.join("\n");
|
|
80
|
+
const hiddenAttr = hidden ? ` hidden=""` : "";
|
|
81
|
+
const heading = hidden ? "" : `\n <h1>${escapeXml(navTitle)}</h1>`;
|
|
82
|
+
// EPUB3 landmarks nav — lets readers jump to the cover / reading start. Hidden,
|
|
83
|
+
// it's a navigation aid, not a visible page.
|
|
84
|
+
const landmarksNav = landmarks.length
|
|
85
|
+
? `\n <nav epub:type="landmarks" hidden="">\n <ol>\n${landmarks
|
|
86
|
+
.map((l) => ` <li><a epub:type="${escapeXml(l.type)}" href="${escapeXml(l.href)}">${escapeXml(l.title)}</a></li>`)
|
|
87
|
+
.join("\n")}\n </ol>\n </nav>`
|
|
88
|
+
: "";
|
|
89
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
90
|
+
<!DOCTYPE html>
|
|
91
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="${escapeXml(language)}" xml:lang="${escapeXml(language)}">
|
|
92
|
+
<head>
|
|
93
|
+
<meta charset="utf-8"/>
|
|
94
|
+
<title>${escapeXml(navTitle)}</title>
|
|
95
|
+
</head>
|
|
96
|
+
<body>
|
|
97
|
+
<nav epub:type="toc" id="toc"${hiddenAttr}>${heading}
|
|
98
|
+
<ol>
|
|
99
|
+
${items}
|
|
100
|
+
</ol>
|
|
101
|
+
</nav>${landmarksNav}
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
/** EPUB2 NCX, kept for older e-reader compatibility. */
|
|
107
|
+
export function buildNcx(id, title, entries) {
|
|
108
|
+
const points = entries
|
|
109
|
+
.map((e, i) => ` <navPoint id="nav-${i + 1}" playOrder="${i + 1}">
|
|
110
|
+
<navLabel><text>${escapeXml(e.title)}</text></navLabel>
|
|
111
|
+
<content src="${escapeXml(e.href)}"/>
|
|
112
|
+
</navPoint>`)
|
|
113
|
+
.join("\n");
|
|
114
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
115
|
+
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
|
|
116
|
+
<head>
|
|
117
|
+
<meta name="dtb:uid" content="${escapeXml(id)}"/>
|
|
118
|
+
<meta name="dtb:depth" content="1"/>
|
|
119
|
+
<meta name="dtb:totalPageCount" content="0"/>
|
|
120
|
+
<meta name="dtb:maxPageNumber" content="0"/>
|
|
121
|
+
</head>
|
|
122
|
+
<docTitle><text>${escapeXml(title)}</text></docTitle>
|
|
123
|
+
<navMap>
|
|
124
|
+
${points}
|
|
125
|
+
</navMap>
|
|
126
|
+
</ncx>
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* A cover page that wraps the image in an SVG scaled to fit the viewport
|
|
131
|
+
* (`preserveAspectRatio="xMidYMid meet"`). This is the portable way to make a
|
|
132
|
+
* cover fill and center consistently across readers — inline `<img>` layout
|
|
133
|
+
* anchors awkwardly on e-ink devices like Kobo.
|
|
134
|
+
*/
|
|
135
|
+
export function buildSvgCover(opts) {
|
|
136
|
+
const bg = opts.background ? `;background:${opts.background}` : "";
|
|
137
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
138
|
+
<!DOCTYPE html>
|
|
139
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="${escapeXml(opts.language)}" xml:lang="${escapeXml(opts.language)}">
|
|
140
|
+
<head>
|
|
141
|
+
<meta charset="utf-8"/>
|
|
142
|
+
<title>${escapeXml(opts.title)}</title>
|
|
143
|
+
<style>@page{margin:0}html,body{margin:0;padding:0;height:100%;overflow:hidden${bg}}svg{display:block;width:100%;height:100vh${bg}}</style>
|
|
144
|
+
</head>
|
|
145
|
+
<body epub:type="cover">
|
|
146
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100%" height="100vh" viewBox="0 0 ${opts.width} ${opts.height}" preserveAspectRatio="xMidYMid meet">
|
|
147
|
+
<image width="${opts.width}" height="${opts.height}" xlink:href="${escapeXml(opts.imageHref)}"/>
|
|
148
|
+
</svg>
|
|
149
|
+
</body>
|
|
150
|
+
</html>
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
/** Wrap body HTML in a complete XHTML content document. */
|
|
154
|
+
export function buildXhtml(opts) {
|
|
155
|
+
const css = opts.cssHref
|
|
156
|
+
? `\n <link rel="stylesheet" type="text/css" href="${escapeXml(opts.cssHref)}"/>`
|
|
157
|
+
: "";
|
|
158
|
+
const bodyClass = opts.bodyClass ? ` class="${escapeXml(opts.bodyClass)}"` : "";
|
|
159
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
160
|
+
<!DOCTYPE html>
|
|
161
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="${escapeXml(opts.language)}" xml:lang="${escapeXml(opts.language)}">
|
|
162
|
+
<head>
|
|
163
|
+
<meta charset="utf-8"/>
|
|
164
|
+
<title>${escapeXml(opts.title)}</title>${css}
|
|
165
|
+
</head>
|
|
166
|
+
<body${bodyClass}>
|
|
167
|
+
${opts.bodyHtml}
|
|
168
|
+
</body>
|
|
169
|
+
</html>
|
|
170
|
+
`;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=opf.js.map
|
package/dist/opf.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opf.js","sourceRoot":"","sources":["../src/opf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAiCtC,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;CAM5B,CAAC;AAEF,4EAA4E;AAC5E,MAAM,KAAK,GAAG,sBAAsB,CAAC;AAErC;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,IAAa;IACtC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAClC,OAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,IAAiB,EACjB,QAAwB,EACxB,KAAe,EACf,UAAmD,EAAE;IAErD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAa;QACzB,8BAA8B,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,kBAAkB;QAClE,aAAa,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa;QAC/C,gBAAgB,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB;QACxD,qCAAqC,QAAQ,SAAS;KACvD,CAAC;IAEF,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,eAAe,SAAS,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,IAAI,CAAC,SAAS;QAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC/F,IAAI,IAAI,CAAC,WAAW;QAClB,QAAQ,CAAC,IAAI,CAAC,mBAAmB,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;IACnF,IAAI,IAAI,CAAC,IAAI;QAAE,QAAQ,CAAC,IAAI,CAAC,YAAY,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC3E,IAAI,IAAI,CAAC,YAAY;QACnB,QAAQ,CAAC,IAAI,CAAC,+BAA+B,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAElF,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACzC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO,aAAa,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,SAAS,IAAI,KAAK,IAAI,CAAC;IACpH,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACjF,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3E,8EAA8E;IAC9E,sCAAsC;IACtC,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM;QACtC,CAAC,CAAC,oBAAoB,OAAO,CAAC,KAAK;aAC9B,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,oBAAoB,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CACvG;aACA,IAAI,CAAC,QAAQ,CAAC,cAAc;QACjC,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;mGAC0F,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;;MAErH,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;;;MAGvB,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;;UAEvB,QAAQ;MACZ,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;YAClB,UAAU;;CAErB,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,QAAgB,EAChB,QAAgB,EAChB,OAAmB,EACnB,MAAM,GAAG,KAAK,EACd,YAAyB,EAAE;IAE3B,MAAM,KAAK,GAAG,OAAO;SAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,wBAAwB,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC;SACvF,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC;IAExE,gFAAgF;IAChF,6CAA6C;IAC7C,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM;QACnC,CAAC,CAAC,4DAA4D,SAAS;aAClE,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,6BAA6B,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAC/G;aACA,IAAI,CAAC,IAAI,CAAC,2BAA2B;QAC1C,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;6FAEoF,SAAS,CAAC,QAAQ,CAAC,eAAe,SAAS,CAAC,QAAQ,CAAC;;;aAGrI,SAAS,CAAC,QAAQ,CAAC;;;mCAGG,UAAU,IAAI,OAAO;;EAEtD,KAAK;;YAEK,YAAY;;;CAGvB,CAAC;AACF,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,QAAQ,CAAC,EAAU,EAAE,KAAa,EAAE,OAAmB;IACrE,MAAM,MAAM,GAAG,OAAO;SACnB,GAAG,CACF,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,yBAAyB,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC;wBAC3C,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;sBACpB,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;gBACvB,CACX;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;;;oCAG2B,SAAS,CAAC,EAAE,CAAC;;;;;oBAK7B,SAAS,CAAC,KAAK,CAAC;;EAElC,MAAM;;;CAGP,CAAC;AACF,CAAC;AAYD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAqB;IACjD,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,OAAO;;6FAEoF,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;;;aAG/I,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;oFACkD,EAAE,6CAA6C,EAAE;;;gJAGW,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM;sBACnJ,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,MAAM,iBAAiB,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;;;;CAIjG,CAAC;AACF,CAAC;AAUD,2DAA2D;AAC3D,MAAM,UAAU,UAAU,CAAC,IAAsB;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO;QACtB,CAAC,CAAC,sDAAsD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK;QACpF,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAEhF,OAAO;;6FAEoF,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;;;aAG/I,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG;;SAEvC,SAAS;EAChB,IAAI,CAAC,QAAQ;;;CAGd,CAAC;AACF,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/** A single chapter (content document) in the EPUB. */
|
|
2
|
+
export interface Chapter {
|
|
3
|
+
/** Chapter title. Used in the navigation/TOC and as the page heading. */
|
|
4
|
+
title: string;
|
|
5
|
+
/**
|
|
6
|
+
* Chapter body as an HTML fragment. It does not need to be well-formed XML —
|
|
7
|
+
* it is parsed and re-serialized as valid XHTML for you.
|
|
8
|
+
*/
|
|
9
|
+
html: string;
|
|
10
|
+
/**
|
|
11
|
+
* When `true`, the chapter is included in the reading order (spine) but kept
|
|
12
|
+
* out of the navigation document and NCX. Useful for single-article books
|
|
13
|
+
* where a TOC entry would be redundant.
|
|
14
|
+
*/
|
|
15
|
+
excludeFromToc?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Optional author for this specific chapter, shown under the title.
|
|
18
|
+
*/
|
|
19
|
+
author?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Set to `false` to omit the auto-generated `<h1>` title heading and render
|
|
22
|
+
* only your HTML. Defaults to `true`.
|
|
23
|
+
*/
|
|
24
|
+
insertTitle?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** An image to embed, either fetched from a URL or provided as raw bytes. */
|
|
27
|
+
export type ImageSource = string | Uint8Array;
|
|
28
|
+
/** Raw image bytes plus MIME type, as seen by an {@link ImageTransform}. */
|
|
29
|
+
export interface RawImage {
|
|
30
|
+
data: Uint8Array;
|
|
31
|
+
mime: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Transform a fetched image before it is embedded. Return replacement
|
|
35
|
+
* bytes + MIME, or `null` to drop the image. May be async.
|
|
36
|
+
*/
|
|
37
|
+
export type ImageTransform = (image: RawImage) => RawImage | null | Promise<RawImage | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Build the final cover. Receives the resolved cover image (downloaded and
|
|
40
|
+
* passed through {@link ImageTransform}, or `null` when the book has no cover
|
|
41
|
+
* source) plus the book's metadata, and returns the bytes to embed — e.g. to
|
|
42
|
+
* compose a designed cover with the title. Return `null` for no cover. May be async.
|
|
43
|
+
*/
|
|
44
|
+
export type CoverTransform = (cover: RawImage | null, meta: {
|
|
45
|
+
title: string;
|
|
46
|
+
author?: string;
|
|
47
|
+
}) => RawImage | null | Promise<RawImage | null>;
|
|
48
|
+
export interface EpubOptions {
|
|
49
|
+
/** Book title. */
|
|
50
|
+
title: string;
|
|
51
|
+
/** Book author(s). A single string or a list. */
|
|
52
|
+
author?: string | string[];
|
|
53
|
+
/** Publisher metadata. */
|
|
54
|
+
publisher?: string;
|
|
55
|
+
/** Free-text description / synopsis. */
|
|
56
|
+
description?: string;
|
|
57
|
+
/** BCP-47 language tag. Defaults to `"en"`. */
|
|
58
|
+
language?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Unique identifier for the book. If omitted, a deterministic `urn:uuid:` is
|
|
61
|
+
* derived from the title + author so repeated builds are stable.
|
|
62
|
+
*/
|
|
63
|
+
id?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Cover image. A URL (downloaded) or raw bytes. When provided, a dedicated
|
|
66
|
+
* cover page is generated.
|
|
67
|
+
*/
|
|
68
|
+
cover?: ImageSource;
|
|
69
|
+
/**
|
|
70
|
+
* When no `cover` is given, use the first chapter's *leading* image (one that
|
|
71
|
+
* appears before any text) as the cover, removing it from the body so it isn't
|
|
72
|
+
* shown twice. Images that follow text are left untouched. Defaults to `false`.
|
|
73
|
+
*/
|
|
74
|
+
coverFromLeadImage?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Whether to emit a navigation document / TOC. When `false`, no nav page is
|
|
77
|
+
* generated and chapters are not listed — the differentiator this library
|
|
78
|
+
* exists for. Defaults to `true`.
|
|
79
|
+
*/
|
|
80
|
+
includeToc?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Title shown on the table-of-contents page. Defaults to `"Table of Contents"`.
|
|
83
|
+
*/
|
|
84
|
+
tocTitle?: string;
|
|
85
|
+
/**
|
|
86
|
+
* CSS colour used to fill the letterbox bands around the cover on the cover
|
|
87
|
+
* page (when the cover image's aspect doesn't match the device page). Set it to
|
|
88
|
+
* the cover's edge/background colour so the bands blend in. Defaults to the
|
|
89
|
+
* reader's page background.
|
|
90
|
+
*/
|
|
91
|
+
coverBackground?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Override the bundled stylesheet. Pass a CSS string, or `false` to ship no
|
|
94
|
+
* stylesheet at all.
|
|
95
|
+
*/
|
|
96
|
+
css?: string | false;
|
|
97
|
+
/**
|
|
98
|
+
* Fetch and embed `<img>` sources (and the cover) into the package so the EPUB
|
|
99
|
+
* is self-contained. Defaults to `true`.
|
|
100
|
+
*
|
|
101
|
+
* When `false`, nothing is fetched and any non-`data:` `<img>` (remote URLs and
|
|
102
|
+
* relative paths) is dropped — only inline `data:` images survive, keeping the
|
|
103
|
+
* output valid. To embed already-fetched bytes without hitting the network,
|
|
104
|
+
* keep this `true` and pass a custom {@link EpubOptions.fetch}.
|
|
105
|
+
*/
|
|
106
|
+
downloadImages?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Publication date as an ISO-8601 string (e.g. `"2026-06-25T00:00:00Z"`).
|
|
109
|
+
* Injected for you when running on a normal runtime; pass explicitly for
|
|
110
|
+
* reproducible builds.
|
|
111
|
+
*/
|
|
112
|
+
date?: string;
|
|
113
|
+
/** Custom fetch implementation (defaults to the global `fetch`). */
|
|
114
|
+
fetch?: typeof fetch;
|
|
115
|
+
/**
|
|
116
|
+
* Hook to transform each fetched image (and the cover) before it is embedded —
|
|
117
|
+
* e.g. to transcode a format the target device can't render, or to resize.
|
|
118
|
+
* Runs after download and before the core-media-type check, so it can also
|
|
119
|
+
* rescue an otherwise-unsupported type by converting it. Return replacement
|
|
120
|
+
* bytes + MIME, or `null` to drop the image. Defaults to leaving images as-is.
|
|
121
|
+
*/
|
|
122
|
+
transformImage?: ImageTransform;
|
|
123
|
+
/**
|
|
124
|
+
* Build the final cover before embedding (after the source image is downloaded
|
|
125
|
+
* and transcoded). Called even when there is no cover source, so it can compose
|
|
126
|
+
* a cover from metadata alone. See {@link CoverTransform}.
|
|
127
|
+
*/
|
|
128
|
+
transformCover?: CoverTransform;
|
|
129
|
+
}
|
|
130
|
+
/** Input passed to {@link generateEpub}. */
|
|
131
|
+
export interface EpubInput extends EpubOptions {
|
|
132
|
+
chapters: Chapter[];
|
|
133
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Escape a string for use in XML text / attribute content. */
|
|
2
|
+
export declare function escapeXml(value: string): string;
|
|
3
|
+
export declare function encodeUtf8(value: string): Uint8Array;
|
|
4
|
+
export declare function decodeUtf8(bytes: Uint8Array): string;
|
|
5
|
+
/**
|
|
6
|
+
* Derive a stable `urn:uuid:` from arbitrary seed text using a FNV-1a based
|
|
7
|
+
* hash. Deterministic by design — repeated builds of the same book produce the
|
|
8
|
+
* same identifier, and we avoid `Math.random()`/`Date` so the library stays
|
|
9
|
+
* runtime-agnostic and reproducible.
|
|
10
|
+
*/
|
|
11
|
+
export declare function deterministicUuid(seed: string): string;
|
|
12
|
+
export declare function mimeFromUrl(url: string): string | undefined;
|
|
13
|
+
export declare function extFromMime(mime: string): string;
|
|
14
|
+
export declare function isCoreImageType(mime: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Read an image's pixel dimensions from its header, without decoding it.
|
|
17
|
+
* Supports PNG, GIF and JPEG (the types quarto embeds as covers); returns
|
|
18
|
+
* `undefined` for anything else or malformed data.
|
|
19
|
+
*/
|
|
20
|
+
export declare function imageSize(bytes: Uint8Array, mime: string): {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
} | undefined;
|
|
24
|
+
/** Detect an image MIME type from its leading magic bytes. */
|
|
25
|
+
export declare function sniffImageMime(bytes: Uint8Array): string | undefined;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/** Escape a string for use in XML text / attribute content. */
|
|
2
|
+
export function escapeXml(value) {
|
|
3
|
+
return value
|
|
4
|
+
.replace(/&/g, "&")
|
|
5
|
+
.replace(/</g, "<")
|
|
6
|
+
.replace(/>/g, ">")
|
|
7
|
+
.replace(/"/g, """)
|
|
8
|
+
.replace(/'/g, "'");
|
|
9
|
+
}
|
|
10
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
11
|
+
export function encodeUtf8(value) {
|
|
12
|
+
return TEXT_ENCODER.encode(value);
|
|
13
|
+
}
|
|
14
|
+
const TEXT_DECODER = new TextDecoder();
|
|
15
|
+
export function decodeUtf8(bytes) {
|
|
16
|
+
return TEXT_DECODER.decode(bytes);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Derive a stable `urn:uuid:` from arbitrary seed text using a FNV-1a based
|
|
20
|
+
* hash. Deterministic by design — repeated builds of the same book produce the
|
|
21
|
+
* same identifier, and we avoid `Math.random()`/`Date` so the library stays
|
|
22
|
+
* runtime-agnostic and reproducible.
|
|
23
|
+
*/
|
|
24
|
+
export function deterministicUuid(seed) {
|
|
25
|
+
// Produce 16 bytes by hashing the seed with several different offsets.
|
|
26
|
+
const bytes = new Uint8Array(16);
|
|
27
|
+
for (let i = 0; i < 16; i++) {
|
|
28
|
+
let hash = 0x811c9dc5 ^ (i * 0x01000193);
|
|
29
|
+
for (let j = 0; j < seed.length; j++) {
|
|
30
|
+
hash ^= seed.charCodeAt(j) + i;
|
|
31
|
+
hash = Math.imul(hash, 0x01000193);
|
|
32
|
+
}
|
|
33
|
+
bytes[i] = (hash >>> 16) & 0xff;
|
|
34
|
+
}
|
|
35
|
+
// Set RFC-4122 version (4) and variant bits so it is a well-formed UUID.
|
|
36
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
37
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
38
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
39
|
+
return `urn:uuid:${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex
|
|
40
|
+
.slice(6, 8)
|
|
41
|
+
.join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
42
|
+
}
|
|
43
|
+
const MIME_BY_EXT = {
|
|
44
|
+
jpg: "image/jpeg",
|
|
45
|
+
jpeg: "image/jpeg",
|
|
46
|
+
png: "image/png",
|
|
47
|
+
gif: "image/gif",
|
|
48
|
+
svg: "image/svg+xml",
|
|
49
|
+
webp: "image/webp",
|
|
50
|
+
};
|
|
51
|
+
const EXT_BY_MIME = {
|
|
52
|
+
"image/jpeg": "jpg",
|
|
53
|
+
"image/png": "png",
|
|
54
|
+
"image/gif": "gif",
|
|
55
|
+
"image/svg+xml": "svg",
|
|
56
|
+
"image/webp": "webp",
|
|
57
|
+
};
|
|
58
|
+
export function mimeFromUrl(url) {
|
|
59
|
+
const clean = url.split("?")[0]?.split("#")[0] ?? "";
|
|
60
|
+
const ext = clean.split(".").pop()?.toLowerCase();
|
|
61
|
+
return ext ? MIME_BY_EXT[ext] : undefined;
|
|
62
|
+
}
|
|
63
|
+
export function extFromMime(mime) {
|
|
64
|
+
return EXT_BY_MIME[mime.split(";")[0].trim().toLowerCase()] ?? "img";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* EPUB 3.3 "core media types" for images. Anything outside this set needs a
|
|
68
|
+
* manifest fallback to be valid, so quarto only embeds these and drops the rest
|
|
69
|
+
* (it does no image transcoding).
|
|
70
|
+
*/
|
|
71
|
+
const CORE_IMAGE_TYPES = new Set([
|
|
72
|
+
"image/gif",
|
|
73
|
+
"image/jpeg",
|
|
74
|
+
"image/png",
|
|
75
|
+
"image/svg+xml",
|
|
76
|
+
"image/webp",
|
|
77
|
+
]);
|
|
78
|
+
export function isCoreImageType(mime) {
|
|
79
|
+
return CORE_IMAGE_TYPES.has(mime.split(";")[0].trim().toLowerCase());
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Read an image's pixel dimensions from its header, without decoding it.
|
|
83
|
+
* Supports PNG, GIF and JPEG (the types quarto embeds as covers); returns
|
|
84
|
+
* `undefined` for anything else or malformed data.
|
|
85
|
+
*/
|
|
86
|
+
export function imageSize(bytes, mime) {
|
|
87
|
+
switch (mime.split(";")[0].trim().toLowerCase()) {
|
|
88
|
+
case "image/png":
|
|
89
|
+
return pngSize(bytes);
|
|
90
|
+
case "image/gif":
|
|
91
|
+
return gifSize(bytes);
|
|
92
|
+
case "image/jpeg":
|
|
93
|
+
return jpegSize(bytes);
|
|
94
|
+
default:
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function u16be(b, o) {
|
|
99
|
+
return (b[o] << 8) | b[o + 1];
|
|
100
|
+
}
|
|
101
|
+
function u32be(b, o) {
|
|
102
|
+
return ((b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]) >>> 0;
|
|
103
|
+
}
|
|
104
|
+
function pngSize(b) {
|
|
105
|
+
// 8-byte signature + IHDR length(4) + "IHDR"(4); width@16, height@20 (big-endian).
|
|
106
|
+
if (b.length < 24)
|
|
107
|
+
return undefined;
|
|
108
|
+
return { width: u32be(b, 16), height: u32be(b, 20) };
|
|
109
|
+
}
|
|
110
|
+
function gifSize(b) {
|
|
111
|
+
// Logical screen descriptor: width@6, height@8 (little-endian).
|
|
112
|
+
if (b.length < 10)
|
|
113
|
+
return undefined;
|
|
114
|
+
return { width: b[6] | (b[7] << 8), height: b[8] | (b[9] << 8) };
|
|
115
|
+
}
|
|
116
|
+
function jpegSize(b) {
|
|
117
|
+
let o = 2; // skip SOI (0xFFD8)
|
|
118
|
+
while (o + 9 < b.length) {
|
|
119
|
+
if (b[o] !== 0xff) {
|
|
120
|
+
o++;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const marker = b[o + 1];
|
|
124
|
+
// Start-of-frame markers carry the dimensions (excluding DHT/JPG/DAC).
|
|
125
|
+
if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
|
|
126
|
+
return { height: u16be(b, o + 5), width: u16be(b, o + 7) };
|
|
127
|
+
}
|
|
128
|
+
const len = u16be(b, o + 2);
|
|
129
|
+
if (len < 2)
|
|
130
|
+
return undefined;
|
|
131
|
+
o += 2 + len;
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
/** Detect an image MIME type from its leading magic bytes. */
|
|
136
|
+
export function sniffImageMime(bytes) {
|
|
137
|
+
if (bytes.length < 4)
|
|
138
|
+
return undefined;
|
|
139
|
+
if (bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
|
|
140
|
+
return "image/jpeg";
|
|
141
|
+
if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47)
|
|
142
|
+
return "image/png";
|
|
143
|
+
if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46)
|
|
144
|
+
return "image/gif";
|
|
145
|
+
if (bytes.length >= 12 &&
|
|
146
|
+
bytes[0] === 0x52 &&
|
|
147
|
+
bytes[1] === 0x49 &&
|
|
148
|
+
bytes[2] === 0x46 &&
|
|
149
|
+
bytes[3] === 0x46 &&
|
|
150
|
+
bytes[8] === 0x57 &&
|
|
151
|
+
bytes[9] === 0x45 &&
|
|
152
|
+
bytes[10] === 0x42 &&
|
|
153
|
+
bytes[11] === 0x50)
|
|
154
|
+
return "image/webp";
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/dist/zip.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Unzipped } from "fflate";
|
|
2
|
+
/**
|
|
3
|
+
* Build an EPUB zip. The OCF spec requires the first entry to be an uncompressed
|
|
4
|
+
* `mimetype` file containing exactly `application/epub+zip`; everything else is
|
|
5
|
+
* deflated normally.
|
|
6
|
+
*/
|
|
7
|
+
export declare function zipEpub(files: Record<string, Uint8Array>): Uint8Array;
|
|
8
|
+
/** Unzip an EPUB into a path → bytes map. */
|
|
9
|
+
export declare function unzipEpub(epub: Uint8Array): Unzipped;
|
package/dist/zip.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { unzipSync, zipSync } from "fflate";
|
|
2
|
+
import { encodeUtf8 } from "./util.js";
|
|
3
|
+
/**
|
|
4
|
+
* Build an EPUB zip. The OCF spec requires the first entry to be an uncompressed
|
|
5
|
+
* `mimetype` file containing exactly `application/epub+zip`; everything else is
|
|
6
|
+
* deflated normally.
|
|
7
|
+
*/
|
|
8
|
+
export function zipEpub(files) {
|
|
9
|
+
const zippable = {
|
|
10
|
+
// Stored (level 0), and first by virtue of insertion order.
|
|
11
|
+
mimetype: [encodeUtf8("application/epub+zip"), { level: 0 }],
|
|
12
|
+
};
|
|
13
|
+
for (const [path, data] of Object.entries(files)) {
|
|
14
|
+
zippable[path] = data;
|
|
15
|
+
}
|
|
16
|
+
return zipSync(zippable, { level: 6 });
|
|
17
|
+
}
|
|
18
|
+
/** Unzip an EPUB into a path → bytes map. */
|
|
19
|
+
export function unzipEpub(epub) {
|
|
20
|
+
return unzipSync(epub);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=zip.js.map
|
package/dist/zip.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zip.js","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,SAAS,EAAiB,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAiC;IACvD,MAAM,QAAQ,GAAa;QACzB,4DAA4D;QAC5D,QAAQ,EAAE,CAAC,UAAU,CAAC,sBAAsB,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;KAC7D,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxB,CAAC;IAED,OAAO,OAAO,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,SAAS,CAAC,IAAgB;IACxC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voidberg/quarto",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generate EPUB3 and Kobo kepub files from HTML — runtime-agnostic, in-memory, with optional table of contents.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"epub",
|
|
7
|
+
"epub3",
|
|
8
|
+
"kepub",
|
|
9
|
+
"kobo",
|
|
10
|
+
"ebook",
|
|
11
|
+
"html-to-epub"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Alexandru Badiu <alexandru@badiu.me>",
|
|
15
|
+
"homepage": "https://github.com/voidberg/quarto",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/voidberg/quarto.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/voidberg/quarto/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.js",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"assets"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"clean": "rm -rf dist",
|
|
41
|
+
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"validate": "node scripts/validate-epubcheck.mjs",
|
|
46
|
+
"lint": "biome check .",
|
|
47
|
+
"format": "biome format --write .",
|
|
48
|
+
"fallow": "npx -y fallow",
|
|
49
|
+
"prepublishOnly": "npm run build"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"fflate": "^0.8.2",
|
|
53
|
+
"parse5": "^7.2.1"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@biomejs/biome": "^2.5.1",
|
|
57
|
+
"@types/node": "^24.13.2",
|
|
58
|
+
"typescript": "^5.9.3",
|
|
59
|
+
"vitest": "^4.1.9"
|
|
60
|
+
}
|
|
61
|
+
}
|