@pretextbook/pretext-html 0.2.0 → 0.3.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/README.md +20 -0
- package/dist/assets.d.ts +43 -0
- package/dist/assets.js +76 -0
- package/dist/{cli-B2LiKunq.js → cli-CzoinNle.js} +264 -213
- package/dist/cli.js +1 -1
- package/dist/index.browser.js +343 -292
- package/dist/index.d.ts +5 -0
- package/dist/index.js +16 -11
- package/dist/missing-asset.d.ts +31 -0
- package/dist/publication.d.ts +27 -0
- package/dist/renderer.d.ts +29 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -219,11 +219,31 @@ the message helper.
|
|
|
219
219
|
| JSPI required (flagged on Node) | Loader fetches resources mid-transform via JSPI | Feature-detect with `isJspiAvailable()`; engines without it need a fallback |
|
|
220
220
|
| No generated images (latex-image, sageplot, …) | Produced by the Python toolchain, not XSLT | Out of scope; run `pretext generate` and the preview will pick the files up |
|
|
221
221
|
| `xi:include` with `xpointer` unsupported | JS resolver stands in for libxml2's | Rebuild adding `xmlXIncludeProcessFlags` to the JSPI export list |
|
|
222
|
+
| Renders run one at a time, never in parallel | Shared libxslt state; the transform suspends mid-run | Inherent; `renderHtml` queues concurrent calls for you |
|
|
222
223
|
|
|
223
224
|
The whole-book stack overflow ("memory access out of bounds") that the stock
|
|
224
225
|
`libxslt-wasm` build hit on large documents is **fixed** in the
|
|
225
226
|
`@pretextbook/libxslt-wasm` fork, which links with a larger WASM stack.
|
|
226
227
|
|
|
228
|
+
### Concurrency
|
|
229
|
+
|
|
230
|
+
A render is not reentrant. It drives a single cached compiled stylesheet
|
|
231
|
+
through a patched `globalThis.fetch` and shared mount tables, and it *suspends*
|
|
232
|
+
mid-transform to fetch stylesheets — which is precisely the window in which
|
|
233
|
+
another render would get to run. Overlapping renders interleave inside libxslt
|
|
234
|
+
and corrupt it.
|
|
235
|
+
|
|
236
|
+
`renderHtml` therefore **queues**: concurrent calls are safe, and each resolves
|
|
237
|
+
with its own result, but they execute one at a time rather than in parallel. A
|
|
238
|
+
render that rejects does not stall the queue.
|
|
239
|
+
|
|
240
|
+
This matters mainly if you drive the internals yourself. If you do, serialize
|
|
241
|
+
your own calls — the corruption is not self-describing: it surfaces as an
|
|
242
|
+
out-of-bounds memory fault, and afterwards the WASM instance aborts on *every*
|
|
243
|
+
later call for the lifetime of the process. Once that has happened, only a
|
|
244
|
+
restart (or a page reload) recovers; `renderHtml` detects the state and says so
|
|
245
|
+
rather than repeating the underlying assertion.
|
|
246
|
+
|
|
227
247
|
Missing generated assets degrade gracefully: the transform serves a stub for
|
|
228
248
|
missing files, PreTeXt emits its usual `PTX:ERROR` advice, and the preview
|
|
229
249
|
renders without the image.
|
package/dist/assets.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retargeting the asset URLs PreTeXt emits.
|
|
3
|
+
*
|
|
4
|
+
* A portable build inlines `latex-image` and `prefigure` SVGs directly into
|
|
5
|
+
* the page (pretext-html.xsl, mode="svg-embedded"), so those need nothing from
|
|
6
|
+
* us — they are read at transform time through the fetch shim. Everything else
|
|
7
|
+
* — author-supplied images and media, sageplot, asymptote — is emitted as a
|
|
8
|
+
* *runtime* URL under one of two fixed prefixes:
|
|
9
|
+
*
|
|
10
|
+
* <img src="external/kitten.png">
|
|
11
|
+
* <img src="generated/sageplot/plot.svg">
|
|
12
|
+
*
|
|
13
|
+
* Those prefixes (`$external-directory` / `$generated-directory` in
|
|
14
|
+
* publisher-variables.xsl) name directories a real build populates by copying
|
|
15
|
+
* files next to the output HTML. The preview has no output directory, so the
|
|
16
|
+
* URLs dangle and the assets silently fail to load.
|
|
17
|
+
*
|
|
18
|
+
* The prefixes are hardcoded rather than derived from the publication file,
|
|
19
|
+
* which is what makes retargeting reliable: find them, and hand each path to a
|
|
20
|
+
* host that knows how to serve it — `Webview.asWebviewUri` under VS Code, a
|
|
21
|
+
* blob or hosted URL in a browser. Mapping the prefix back to a real directory
|
|
22
|
+
* needs the publication file's `@external`/`@generated`, which the renderer
|
|
23
|
+
* reports as {@link RenderResult.assetDirs}.
|
|
24
|
+
*/
|
|
25
|
+
/** Which of PreTeXt's two asset directories a URL points into. */
|
|
26
|
+
export type AssetKind = "external" | "generated";
|
|
27
|
+
export { describeAsset, missingAssetPlaceholder, missingAssetSvg, } from './missing-asset.js';
|
|
28
|
+
export type { MissingAsset } from './missing-asset.js';
|
|
29
|
+
/**
|
|
30
|
+
* Maps one asset path to a URL the host can actually load.
|
|
31
|
+
*
|
|
32
|
+
* `relPath` is percent-decoded and relative to the directory `kind` names
|
|
33
|
+
* (e.g. `"kitten.png"`, `"sageplot/plot.svg"`). Return `undefined` to leave
|
|
34
|
+
* the URL untouched — appropriate when the file does not exist, so the page
|
|
35
|
+
* keeps PreTeXt's own broken-image markup rather than pointing at a URL that
|
|
36
|
+
* will 404 anyway.
|
|
37
|
+
*/
|
|
38
|
+
export type AssetUrlResolver = (kind: AssetKind, relPath: string) => string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Rewrite every `external/`- or `generated/`-prefixed asset URL in `html`
|
|
41
|
+
* through `resolve`. Paths the resolver declines are left untouched.
|
|
42
|
+
*/
|
|
43
|
+
export declare function rewriteAssetUrls(html: string, resolve: AssetUrlResolver): string;
|
package/dist/assets.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const l = {
|
|
2
|
+
png: "image",
|
|
3
|
+
jpg: "image",
|
|
4
|
+
jpeg: "image",
|
|
5
|
+
gif: "image",
|
|
6
|
+
svg: "image",
|
|
7
|
+
webp: "image",
|
|
8
|
+
avif: "image",
|
|
9
|
+
mp4: "video",
|
|
10
|
+
webm: "video",
|
|
11
|
+
ogv: "video",
|
|
12
|
+
mov: "video",
|
|
13
|
+
mp3: "audio",
|
|
14
|
+
wav: "audio",
|
|
15
|
+
ogg: "audio",
|
|
16
|
+
pdf: "PDF"
|
|
17
|
+
};
|
|
18
|
+
function p(e, t) {
|
|
19
|
+
const n = t.split("/").filter(Boolean), i = n[n.length - 1] ?? t;
|
|
20
|
+
if (e === "generated")
|
|
21
|
+
return {
|
|
22
|
+
kind: e,
|
|
23
|
+
assetType: n.length > 1 ? n[0] : "generated",
|
|
24
|
+
fileName: i
|
|
25
|
+
};
|
|
26
|
+
const r = i.split(".").pop()?.toLowerCase() ?? "";
|
|
27
|
+
return { kind: e, assetType: l[r] ?? "file", fileName: i };
|
|
28
|
+
}
|
|
29
|
+
function o(e) {
|
|
30
|
+
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
31
|
+
}
|
|
32
|
+
function g(e, t = 38) {
|
|
33
|
+
return e.length <= t ? e : `${e.slice(0, t - 1)}…`;
|
|
34
|
+
}
|
|
35
|
+
function f(e) {
|
|
36
|
+
return e.kind === "generated" ? [
|
|
37
|
+
"Asset not generated yet",
|
|
38
|
+
`${e.assetType} · ${g(e.fileName)}`,
|
|
39
|
+
`Run: pretext generate ${e.assetType}`
|
|
40
|
+
] : [
|
|
41
|
+
"External asset not found",
|
|
42
|
+
`${e.assetType} · ${g(e.fileName)}`,
|
|
43
|
+
"Check @source and the external directory"
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
const d = "#fffbeb", u = "#f59e0b", m = "#92400e", s = "#b45309", a = "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif";
|
|
47
|
+
function $(e) {
|
|
48
|
+
const [t, n, i] = f(e), r = `${t}: ${e.fileName}`;
|
|
49
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 130" role="img" aria-label="${o(r)}" data-ptx-missing="${o(`${e.kind}:${e.assetType}`)}"><rect x="1" y="1" width="418" height="128" rx="8" fill="${d}" stroke="${u}" stroke-width="2" stroke-dasharray="6 4"/><path d="M40 44 L60 80 L20 80 Z" fill="none" stroke="${s}" stroke-width="3" stroke-linejoin="round"/><rect x="38.5" y="56" width="3" height="12" fill="${s}"/><rect x="38.5" y="71" width="3" height="3" fill="${s}"/><text x="78" y="54" font-family="${a}" font-size="15" font-weight="600" fill="${m}">${o(t)}</text><text x="78" y="77" font-family="${a}" font-size="13" fill="${s}">${o(n)}</text><text x="78" y="99" font-family="${a}" font-size="12" fill="${s}" opacity="0.85">${o(i)}</text></svg>`;
|
|
50
|
+
}
|
|
51
|
+
function v(e, t) {
|
|
52
|
+
return `data:image/svg+xml,${$(p(e, t)).replace(/%/g, "%25").replace(/#/g, "%23").replace(/&/g, "%26").replace(/"/g, "%22").replace(/'/g, "%27").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ")}`;
|
|
53
|
+
}
|
|
54
|
+
const h = /\bsrc="(external|generated)\/([^"]*)"/g;
|
|
55
|
+
function x(e) {
|
|
56
|
+
return e.replace(/&/g, "&").replace(/"/g, """);
|
|
57
|
+
}
|
|
58
|
+
function y(e) {
|
|
59
|
+
try {
|
|
60
|
+
return decodeURIComponent(e);
|
|
61
|
+
} catch {
|
|
62
|
+
return e;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function w(e, t) {
|
|
66
|
+
return e.replace(h, (n, i, r) => {
|
|
67
|
+
const c = t(i, y(r));
|
|
68
|
+
return c === void 0 ? n : `src="${x(c)}"`;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
p as describeAsset,
|
|
73
|
+
v as missingAssetPlaceholder,
|
|
74
|
+
$ as missingAssetSvg,
|
|
75
|
+
w as rewriteAssetUrls
|
|
76
|
+
};
|