@pretextbook/pretext-html 0.4.0 → 0.5.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.
Files changed (42) hide show
  1. package/README.md +86 -1
  2. package/assets/preview-revealjs.xsl +85 -0
  3. package/assets/upstream.json +1 -1
  4. package/assets/xsl/LICENSE-pretext +16 -0
  5. package/assets/xsl/entities.ent +7 -0
  6. package/assets/xsl/extract-biblio-csl.xsl +5 -3
  7. package/assets/xsl/extract-dynamic.xsl +14 -12
  8. package/assets/xsl/extract-interactive.xsl +2 -2
  9. package/assets/xsl/extract-pg.xsl +12 -9
  10. package/assets/xsl/extract-qrcode.xsl +1 -1
  11. package/assets/xsl/extract-trace.xsl +3 -3
  12. package/assets/xsl/extract-youtube.xsl +1 -1
  13. package/assets/xsl/latex/pretext-latex-texstyle.xsl +3 -3
  14. package/assets/xsl/pretext-assembly.xsl +362 -357
  15. package/assets/xsl/pretext-braille-preprint.xsl +1 -1
  16. package/assets/xsl/pretext-common.xsl +101 -90
  17. package/assets/xsl/pretext-epub.xsl +17 -6
  18. package/assets/xsl/pretext-fo.xsl +10 -10
  19. package/assets/xsl/pretext-html.xsl +83 -60
  20. package/assets/xsl/pretext-latex-common.xsl +8 -8
  21. package/assets/xsl/pretext-numbers.xsl +35 -35
  22. package/assets/xsl/pretext-revealjs.xsl +1 -1
  23. package/assets/xsl/pretext-runestone-fitb.xsl +14 -13
  24. package/assets/xsl/pretext-runestone-static.xsl +17 -17
  25. package/assets/xsl/pretext-runestone.xsl +33 -31
  26. package/assets/xsl/pretext-view-source.xsl +5 -3
  27. package/assets/xsl/pretext-ww-problem-sets.xsl +6 -1
  28. package/assets/xsl/publisher-variables.xsl +23 -47
  29. package/assets/xsl-bundle.json +1 -1
  30. package/dist/cli-DZsyZERX.js +694 -0
  31. package/dist/cli.js +1 -1
  32. package/dist/index.browser.d.ts +5 -0
  33. package/dist/index.browser.js +402 -256
  34. package/dist/index.d.ts +5 -0
  35. package/dist/index.js +23 -18
  36. package/dist/publication.d.ts +25 -19
  37. package/dist/renderer.d.ts +48 -0
  38. package/dist/reveal.d.ts +77 -0
  39. package/dist/reveal.js +110 -0
  40. package/dist/target.d.ts +34 -0
  41. package/package.json +12 -1
  42. package/dist/cli-Cp6C0sif.js +0 -654
package/dist/index.d.ts CHANGED
@@ -3,6 +3,11 @@ export type { RenderOptions, RenderResult } from './renderer.js';
3
3
  export { findSourceMapEntry } from './sourcemap.js';
4
4
  export type { PtxSourceMap, SourceMapEntry } from './sourcemap.js';
5
5
  export { forcePortablePublication } from './publication.js';
6
+ export type { PublicationOverrides } from './publication.js';
7
+ export { detectRenderTarget } from './target.js';
8
+ export type { RenderTarget } from './target.js';
9
+ export { injectRevealBridge, isRevealView, revealBridgeScript, } from './reveal.js';
10
+ export type { RevealView, RevealViewOptions } from './reveal.js';
6
11
  export { rewriteAssetUrls } from './assets.js';
7
12
  export type { AssetKind, AssetUrlResolver } from './assets.js';
8
13
  export { describeAsset, missingAssetPlaceholder, missingAssetSvg, } from './missing-asset.js';
package/dist/index.js CHANGED
@@ -1,23 +1,28 @@
1
- import { d as r, f as i, a as t, i as a, r as l, m as o, s as m, b as n, x as d } from "./cli-Cp6C0sif.js";
2
- import { describeAsset as c, missingAssetPlaceholder as f, missingAssetSvg as g, rewriteAssetUrls as A } from "./assets.js";
3
- import { PREVIEW_THEME_MESSAGE as h, injectThemeBridge as S, isPreviewTheme as b, previewThemeMessage as u, themeBridgeScript as x } from "./theme.js";
1
+ import { d as r, a as i, f as t, b as a, i as l, r as o, m as d, s as m, c as n, x as c } from "./cli-DZsyZERX.js";
2
+ import { injectRevealBridge as p, isRevealView as f, revealBridgeScript as v } from "./reveal.js";
3
+ import { describeAsset as E, missingAssetPlaceholder as S, missingAssetSvg as h, rewriteAssetUrls as x } from "./assets.js";
4
+ import { PREVIEW_THEME_MESSAGE as u, injectThemeBridge as B, isPreviewTheme as M, previewThemeMessage as P, themeBridgeScript as R } from "./theme.js";
4
5
  export {
5
- h as PREVIEW_THEME_MESSAGE,
6
+ u as PREVIEW_THEME_MESSAGE,
6
7
  r as defaultXslDir,
7
- c as describeAsset,
8
- i as findSourceMapEntry,
9
- t as forcePortablePublication,
10
- S as injectThemeBridge,
11
- a as isJspiAvailable,
12
- b as isPreviewTheme,
13
- f as missingAssetPlaceholder,
14
- g as missingAssetSvg,
15
- u as previewThemeMessage,
16
- l as renderHtml,
17
- A as rewriteAssetUrls,
18
- o as runCli,
8
+ E as describeAsset,
9
+ i as detectRenderTarget,
10
+ t as findSourceMapEntry,
11
+ a as forcePortablePublication,
12
+ p as injectRevealBridge,
13
+ B as injectThemeBridge,
14
+ l as isJspiAvailable,
15
+ M as isPreviewTheme,
16
+ f as isRevealView,
17
+ S as missingAssetPlaceholder,
18
+ h as missingAssetSvg,
19
+ P as previewThemeMessage,
20
+ o as renderHtml,
21
+ v as revealBridgeScript,
22
+ x as rewriteAssetUrls,
23
+ d as runCli,
19
24
  m as setAssetsBase,
20
25
  n as setMountReader,
21
- x as themeBridgeScript,
22
- d as xpathStringLiteral
26
+ R as themeBridgeScript,
27
+ c as xpathStringLiteral
23
28
  };
@@ -1,27 +1,33 @@
1
- /**
2
- * Publication-file handling for single-page preview builds.
3
- *
4
- * The preview relies on PreTeXt's "portable HTML" mode
5
- * (`<html><platform portable="yes"/></html>` in the publication file), which
6
- * forces chunk level 0 (the whole document on one page), serves css/js from
7
- * the jsDelivr CDN, and suppresses most auxiliary file output. Publisher
8
- * variables can only come from a publication file — there is no stringparam
9
- * for `portable` — so we rewrite the user's publication file (or synthesize a
10
- * minimal one) and serve it through the fetch shim as a virtual file.
11
- *
12
- * That rewrite is also how an embedder's default HTML theme gets in
13
- * (`<html><css theme="..."/></html>`): same reason, no stringparam for it
14
- * either, short of `debug.html.theme-name`.
15
- */
1
+ import { RenderTarget } from './target.js';
2
+ /** Optional adjustments layered onto the publication file for a render. */
3
+ export interface PublicationOverrides {
4
+ /**
5
+ * Fallback PreTeXt HTML theme for projects that name none; see
6
+ * `RenderOptions.cssTheme`. A blank value is ignored.
7
+ */
8
+ cssTheme?: string;
9
+ /**
10
+ * Fallback reveal.js theme for slideshows that name none; see
11
+ * `RenderOptions.revealTheme`. Only consulted when `target` is "slides".
12
+ */
13
+ revealTheme?: string;
14
+ /**
15
+ * Which conversion this publication file is being prepared for. "slides"
16
+ * additionally forces the reveal.js resource host; see
17
+ * {@link forceRevealResources}. Defaults to "html".
18
+ */
19
+ target?: RenderTarget;
20
+ }
16
21
  /**
17
22
  * Return publication-file XML with `<html><platform portable="yes"/></html>`
18
23
  * forced, preserving everything else from `publicationXml` when given.
19
24
  *
20
- * `cssTheme`, when given, supplies `<html><css theme="..."/></html>` for
21
- * projects that do not choose an HTML theme themselves; see
22
- * `RenderOptions.cssTheme`. A blank value is ignored.
25
+ * Portable mode is forced for slideshows too, even though the reveal.js
26
+ * conversion never chunks: it is what sets `$cdn-prefix`, and so what puts the
27
+ * PreTeXt-side css and js (sagecell, syntax highlighting, the slide styling)
28
+ * on the CDN rather than at a relative path that does not exist.
23
29
  */
24
- export declare function forcePortablePublication(publicationXml?: string, cssTheme?: string): string;
30
+ export declare function forcePortablePublication(publicationXml?: string, overrides?: PublicationOverrides): string;
25
31
  /**
26
32
  * Where a project's images live, as declared by
27
33
  * `<source><directories external generated/></source>`.
@@ -1,4 +1,6 @@
1
+ import { RevealView } from './reveal.js';
1
2
  import { PtxSourceMap } from './sourcemap.js';
3
+ import { RenderTarget } from './target.js';
2
4
  import { PreviewTheme } from './theme.js';
3
5
  export interface RenderOptions {
4
6
  /** Path to the root PreTeXt source file (typically source/main.ptx). */
@@ -55,6 +57,46 @@ export interface RenderOptions {
55
57
  * *within* whichever css theme is in force.
56
58
  */
57
59
  cssTheme?: string;
60
+ /**
61
+ * Which conversion to run. Defaults to detecting it from the document: a
62
+ * `<slideshow>` renders as a reveal.js deck, anything else as ordinary HTML.
63
+ * Set it explicitly to override the detection — most usefully in fragment
64
+ * mode, where a lone `<section>` of slides is ambiguous.
65
+ */
66
+ target?: RenderTarget;
67
+ /**
68
+ * How a slideshow is presented: "scroll" for the whole deck as one
69
+ * continuous page (the useful default for authoring), "slides" for reveal's
70
+ * ordinary one-slide-at-a-time presentation. Ignored for non-slideshows.
71
+ *
72
+ * Switching later needs no re-render: `injectRevealBridge` applies the other
73
+ * value to the HTML already returned. Defaults to "scroll".
74
+ */
75
+ revealView?: RevealView;
76
+ /**
77
+ * How large a deck's content is drawn in the scroll view, as a fraction of
78
+ * its presented size (0.25 to 1, default 1).
79
+ *
80
+ * Zooming out shrinks the *text*, not the slide: the slide stays the same
81
+ * size on screen and more of its content fits inside it. That is what makes
82
+ * content overflowing a slide readable at all — reveal clips it, so at full
83
+ * size there is no way to see what was cut off. Ignored when presenting.
84
+ */
85
+ revealZoom?: number;
86
+ /**
87
+ * reveal.js theme to fall back on for slideshows whose publication file
88
+ * names none, inserted as `<revealjs><appearance theme="..."/></revealjs>`.
89
+ * A default, not an override, exactly like {@link RenderOptions.cssTheme}.
90
+ *
91
+ * These are reveal's own themes, not PreTeXt's — "simple" (reveal's default
92
+ * here), "white", "black", "league", "night", "moon", and so on — and they
93
+ * are loaded from the CDN by name, so an unrecognised one renders unstyled.
94
+ *
95
+ * Note that {@link RenderOptions.theme} does nothing for a slideshow: a deck
96
+ * loads none of the PreTeXt javascript that implements light/dark mode. A
97
+ * dark reveal theme is how an embedder gets a dark deck.
98
+ */
99
+ revealTheme?: string;
58
100
  /** Additional XSLT string parameters, passed as strings (quoted for you). */
59
101
  stringParams?: Record<string, string>;
60
102
  /** Directory of PreTeXt XSL stylesheets. Defaults to the vendored copy. */
@@ -108,6 +150,12 @@ export interface RenderOptions {
108
150
  export interface RenderResult {
109
151
  /** Complete standalone HTML page (CDN-hosted css/js/MathJax). */
110
152
  html: string;
153
+ /**
154
+ * The conversion that actually ran — as asked for, or as detected. Lets an
155
+ * embedder tell a deck from a document without re-inspecting the source,
156
+ * which is what decides whether slideshow-only UI (the view toggle) applies.
157
+ */
158
+ target: RenderTarget;
111
159
  /** Present when RenderOptions.sourceMap was set. */
112
160
  sourceMap?: PtxSourceMap;
113
161
  /**
@@ -0,0 +1,77 @@
1
+ /**
2
+ * View control for rendered reveal.js slideshows.
3
+ *
4
+ * A built deck opens as a presentation: one slide at a time, arrow keys to
5
+ * advance. That is what an audience should see and a poor way to *write* a
6
+ * deck — an author wants the whole thing at once, scrolling like every other
7
+ * preview in the editor. reveal.js has exactly that built in (its "scroll
8
+ * view"), and this module turns it on.
9
+ *
10
+ * ## Why a script, and why this one
11
+ *
12
+ * Reveal reads `view: 'scroll'` from the config passed to `Reveal.initialize`,
13
+ * and only there: internally the activation runs once, from the routine that
14
+ * fires after plugins load, so a later `Reveal.configure({view: 'scroll'})`
15
+ * sets the value but activates nothing. The URL form (`?view=scroll`) is no
16
+ * help either — the extension assigns `webview.html` directly, so there is no
17
+ * query string of ours for reveal to parse.
18
+ *
19
+ * That leaves the config object, which PreTeXt writes into the page as a
20
+ * literal at the end of the document. Rather than rewrite that literal — which
21
+ * would bind us to its exact emitted text — {@link revealBridgeScript} wraps
22
+ * `Reveal.initialize` itself and merges the overrides over whatever the deck
23
+ * passes. The script is injected immediately after reveal.js's own `<script
24
+ * src>`: classic scripts run in order, so it is guaranteed to see `Reveal`
25
+ * defined and to run before the deck's initialize call.
26
+ *
27
+ * ## Switching views
28
+ *
29
+ * By re-rendering the page, not by talking to a live deck. The scroll view
30
+ * restructures the DOM extensively and `Reveal.destroy()` does not fully undo
31
+ * it, so a destroy/re-initialize toggle leaves debris. Since the embedder
32
+ * already holds the rendered HTML, switching views is just re-injecting with
33
+ * the other value and re-assigning it — no re-render, no reveal internals.
34
+ */
35
+ /**
36
+ * How a rendered deck is presented.
37
+ *
38
+ * - "slides" - reveal.js as built: one slide at a time. What an audience sees.
39
+ * - "scroll" - the whole deck as one continuous scroll. Better for authoring.
40
+ */
41
+ export type RevealView = "slides" | "scroll";
42
+ /** True when `value` is one of the {@link RevealView} strings. */
43
+ export declare function isRevealView(value: unknown): value is RevealView;
44
+ /** Tuning for how a deck is laid out in the preview. */
45
+ export interface RevealViewOptions {
46
+ /**
47
+ * How large a deck's content is drawn, as a fraction of its presented size,
48
+ * between 0.25 and 1. Zooming out shrinks the text rather than the slide —
49
+ * the slide stays the same size on screen and more of its content fits
50
+ * inside, which is the only way to read content that overflows a slide (see
51
+ * {@link zoomedSlideSize}). Scroll view only. Defaults to
52
+ * {@link DEFAULT_REVEAL_ZOOM}.
53
+ */
54
+ zoom?: number;
55
+ }
56
+ /**
57
+ * Zoom when the caller states none: the deck exactly as it will be presented.
58
+ */
59
+ export declare const DEFAULT_REVEAL_ZOOM = 1;
60
+ /**
61
+ * The inline `<script>` (as a string) that applies `view` to a rendered deck.
62
+ *
63
+ * Written in ES5-ish, self-contained JS: it is embedded verbatim in the output
64
+ * page, so no bundler runs over it, and it re-executes on every in-place
65
+ * `document.write` rebuild the extension performs.
66
+ */
67
+ export declare function revealBridgeScript(view: RevealView, options?: RevealViewOptions): string;
68
+ /**
69
+ * Inject the view bridge into a rendered slideshow, immediately after
70
+ * reveal.js's own `<script src>`.
71
+ *
72
+ * Returns the page unchanged when that tag is not found — which means either
73
+ * the page is not a deck, or upstream has changed how reveal.js is loaded. In
74
+ * both cases the deck still works, just in its built-in view, and a silently
75
+ * ordinary slideshow is a far better outcome than a thrown error in a preview.
76
+ */
77
+ export declare function injectRevealBridge(html: string, view: RevealView, options?: RevealViewOptions): string;
package/dist/reveal.js ADDED
@@ -0,0 +1,110 @@
1
+ function h(e) {
2
+ return e === "slides" || e === "scroll";
3
+ }
4
+ const o = 960, a = 700, v = 1, d = 0.04;
5
+ function f(e) {
6
+ const r = typeof e == "number" && isFinite(e) ? e : v, i = Math.min(1, Math.max(0.25, r));
7
+ return {
8
+ width: Math.round(o / i),
9
+ height: Math.round(a / i)
10
+ };
11
+ }
12
+ function u(e) {
13
+ return {
14
+ view: "scroll",
15
+ scrollLayout: "compact",
16
+ scrollSnap: !1,
17
+ scrollProgress: !0,
18
+ fragments: !1,
19
+ controls: !1,
20
+ controlsTutorial: !1,
21
+ hash: !1,
22
+ ...f(e.zoom),
23
+ margin: d
24
+ };
25
+ }
26
+ function p() {
27
+ return {
28
+ // Explicitly null rather than absent: the same page may have been
29
+ // delivered in scroll view a moment ago, and an embedder that re-injects
30
+ // over already-injected HTML would otherwise leave the old value standing.
31
+ view: null,
32
+ // See scrollViewConfig; the webview objection to hash applies either way.
33
+ hash: !1,
34
+ // The nominal size matters just as much here: a deck previewed at pane
35
+ // size with a percentage width renders its text far larger, relative to
36
+ // the slide, than the same deck will on a projector. No zoom margin
37
+ // though — presenting means filling whatever it is shown in.
38
+ width: o,
39
+ height: a
40
+ };
41
+ }
42
+ const E = [
43
+ "<style>",
44
+ ".reveal-viewport.reveal-scroll .scroll-page section {",
45
+ " box-shadow: inset 0 0 0 calc(1px / var(--slide-scale, 1))",
46
+ " rgba(128, 128, 128, 0.5);",
47
+ "}",
48
+ "</style>"
49
+ ].join(`
50
+ `), g = [
51
+ " var paused = document.querySelectorAll('.reveal .fragment');",
52
+ " for (var i = 0; i < paused.length; i++) {",
53
+ " paused[i].classList.remove('fragment');",
54
+ " }"
55
+ ];
56
+ function R(e, r = {}) {
57
+ const i = e === "scroll" ? u(r) : p();
58
+ return [
59
+ "<script>",
60
+ "(function () {",
61
+ " var OVERRIDES = " + JSON.stringify(i) + ";",
62
+ // Nothing to wrap if reveal.js failed to load (offline, CDN blocked). The
63
+ // deck is broken either way, but throwing here would replace the browser's
64
+ // useful network error with a confusing one from an injected script.
65
+ " if (typeof Reveal === 'undefined' || !Reveal.initialize) { return; }",
66
+ // Guard against double-wrapping: an embedder may inject into HTML that was
67
+ // already injected into, and wrapping a wrapper would still work but grows
68
+ // a chain on every redelivery.
69
+ " if (Reveal.__ptxViewWrapped) {",
70
+ " Reveal.__ptxViewOverrides = OVERRIDES;",
71
+ " return;",
72
+ " }",
73
+ " Reveal.__ptxViewWrapped = true;",
74
+ " Reveal.__ptxViewOverrides = OVERRIDES;",
75
+ " var initialize = Reveal.initialize.bind(Reveal);",
76
+ " Reveal.initialize = function (config) {",
77
+ " var merged = {};",
78
+ " var key;",
79
+ " for (key in config || {}) { merged[key] = config[key]; }",
80
+ " var o = Reveal.__ptxViewOverrides;",
81
+ " for (key in o) { merged[key] = o[key]; }",
82
+ // Inside the wrapper rather than at top level: this script sits in <head>,
83
+ // where the slides have not been parsed yet, but the deck calls initialize
84
+ // from the end of the document, by which time they have.
85
+ ...e === "scroll" ? g : [],
86
+ " return initialize(merged);",
87
+ " };",
88
+ "})();",
89
+ "<\/script>"
90
+ ].join(`
91
+ `);
92
+ }
93
+ const _ = /<script\b[^>]*\bsrc=["'][^"']*\breveal\.js["'][^>]*>\s*<\/script>/i, S = "<!--ptx-reveal-view-->", x = "<!--/ptx-reveal-view-->", w = /\n?<!--ptx-reveal-view-->[\s\S]*?<!--\/ptx-reveal-view-->/;
94
+ function I(e, r, i = {}) {
95
+ const n = w.exec(e), t = n ? e.slice(0, n.index) + e.slice(n.index + n[0].length) : e, s = _.exec(t);
96
+ if (!s)
97
+ return t;
98
+ const l = s.index + s[0].length, c = `
99
+ ${S}
100
+ ${E}
101
+ ${R(r, i)}
102
+ ${x}`;
103
+ return t.slice(0, l) + c + t.slice(l);
104
+ }
105
+ export {
106
+ v as DEFAULT_REVEAL_ZOOM,
107
+ I as injectRevealBridge,
108
+ h as isRevealView,
109
+ R as revealBridgeScript
110
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Which PreTeXt conversion a render drives.
3
+ *
4
+ * The two share almost everything — the same assembly passes, the same
5
+ * publication file, the same portable/CDN resource handling — and differ in
6
+ * the stylesheet they enter through:
7
+ *
8
+ * "html" - assets/preview-html.xsl, wrapping pretext-html.xsl.
9
+ * "slides" - assets/preview-revealjs.xsl, wrapping pretext-revealjs.xsl,
10
+ * for a `<slideshow>` document rendered as a reveal.js deck.
11
+ *
12
+ * A document is one or the other by its type, not by the author's choosing, so
13
+ * the renderer detects it (see {@link detectRenderTarget}) unless a caller says
14
+ * otherwise.
15
+ */
16
+ export type RenderTarget = "html" | "slides";
17
+ /**
18
+ * Which conversion `xml` calls for: "slides" if it is a `<slideshow>` — or a
19
+ * piece of one, so that previewing a single xi:included file of slides still
20
+ * builds a deck — and "html" otherwise.
21
+ *
22
+ * Deliberately a scan rather than a parse: this runs on every render, on
23
+ * xinclude-merged source that can be megabytes, and parsing the whole tree to
24
+ * look at element names costs far more than the question is worth. The scan is
25
+ * sound because neither tag is ambiguous in PreTeXt: `<slideshow>` is a
26
+ * document type, appearing only as a child of `<pretext>`, and `<slide>`
27
+ * appears only within one. Neither has a homonym elsewhere in the vocabulary.
28
+ *
29
+ * Comments and CDATA are stripped first. A commented-out `<slideshow>`, or a
30
+ * document about PreTeXt quoting the markup, is unlikely — but it would
31
+ * misroute the *entire* render, which is far too large a failure to leave to
32
+ * chance for the cost of two replaces.
33
+ */
34
+ export declare function detectRenderTarget(xml: string): RenderTarget;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pretextbook/pretext-html",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Convert PreTeXt documents to HTML in pure JavaScript using the official PreTeXt XSLT stylesheets via libxslt-wasm. No Python or PreTeXt installation required.",
5
5
  "author": "Oscar Levin",
6
6
  "license": "MIT",
@@ -23,6 +23,9 @@
23
23
  ],
24
24
  "assets": [
25
25
  "./dist/assets.d.ts"
26
+ ],
27
+ "reveal": [
28
+ "./dist/reveal.d.ts"
26
29
  ]
27
30
  }
28
31
  },
@@ -49,6 +52,11 @@
49
52
  "import": "./dist/assets.js",
50
53
  "default": "./dist/assets.js"
51
54
  },
55
+ "./reveal": {
56
+ "types": "./dist/reveal.d.ts",
57
+ "import": "./dist/reveal.js",
58
+ "default": "./dist/reveal.js"
59
+ },
52
60
  "./cli": "./dist/cli.js"
53
61
  },
54
62
  "sideEffects": false,
@@ -69,6 +77,9 @@
69
77
  "xast-util-from-xml": "^4.0.0",
70
78
  "xast-util-to-xml": "^4.0.0"
71
79
  },
80
+ "devDependencies": {
81
+ "jszip": "^3.10.1"
82
+ },
72
83
  "engines": {
73
84
  "node": ">=24"
74
85
  },