@uniweb/press 0.4.7 → 0.4.8
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/package.json +3 -3
- package/src/adapters/latex.js +13 -3
- package/src/adapters/typst.js +13 -3
- package/src/assets/inline-images.js +84 -0
- package/src/compileRegistrations.js +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/press",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "Press your React components into downloadable files — Word, Excel, PDF. Registration-based pattern with lazy-loaded format adapters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"parse5": "^7.2.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"react": "^
|
|
50
|
-
"react-dom": "^
|
|
49
|
+
"react": "^19.0.0",
|
|
50
|
+
"react-dom": "^19.0.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@testing-library/react": "^16.0.0",
|
package/src/adapters/latex.js
CHANGED
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
import JSZip from 'jszip'
|
|
50
|
+
import { bundleInlineImages } from '../assets/inline-images.js'
|
|
50
51
|
import { RAW_BEGIN, RAW_END, markRawLatex } from '../latex/raw.js'
|
|
51
52
|
|
|
52
53
|
// Re-export so consumers reaching the adapter via the dynamic-import
|
|
@@ -84,9 +85,18 @@ export { markRawLatex }
|
|
|
84
85
|
* @returns {Promise<Blob>}
|
|
85
86
|
*/
|
|
86
87
|
export async function compileLatex(input, options = {}) {
|
|
87
|
-
const { mode = 'sources', ...rest } = options
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
const { mode = 'sources', loadAsset, ...rest } = options
|
|
89
|
+
|
|
90
|
+
// Fetch inline body images and rewrite their IR src to a bundle-local
|
|
91
|
+
// assets/<hash>.<ext> path, then merge the bytes into the bundle so
|
|
92
|
+
// latexmk/pdflatex resolves \includegraphics{...} against the extracted
|
|
93
|
+
// sources. Cover art arrives pre-bundled in rest.assets via the
|
|
94
|
+
// foundation's getOptions; this handles document-body images.
|
|
95
|
+
const inlineImageAssets = await bundleInlineImages(input, loadAsset)
|
|
96
|
+
const bundle = buildBundle(input, {
|
|
97
|
+
...rest,
|
|
98
|
+
assets: { ...(rest.assets || {}), ...inlineImageAssets },
|
|
99
|
+
})
|
|
90
100
|
|
|
91
101
|
if (mode === 'sources') {
|
|
92
102
|
return zipBundle(bundle)
|
package/src/adapters/typst.js
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import JSZip from 'jszip'
|
|
31
|
+
import { bundleInlineImages } from '../assets/inline-images.js'
|
|
31
32
|
|
|
32
33
|
// ============================================================================
|
|
33
34
|
// Public API
|
|
@@ -53,9 +54,18 @@ import JSZip from 'jszip'
|
|
|
53
54
|
* @returns {Promise<Blob>}
|
|
54
55
|
*/
|
|
55
56
|
export async function compileTypst(input, options = {}) {
|
|
56
|
-
const { mode = 'sources', ...rest } = options
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const { mode = 'sources', loadAsset, ...rest } = options
|
|
58
|
+
|
|
59
|
+
// Fetch inline body images (``) and rewrite their IR src to a
|
|
60
|
+
// bundle-local `assets/<hash>.<ext>` path, then merge the bytes into the
|
|
61
|
+
// bundle so `typst compile` resolves them. Cover images arrive pre-bundled
|
|
62
|
+
// in rest.assets via the foundation's getOptions; this handles the images
|
|
63
|
+
// an author drops into the document body.
|
|
64
|
+
const inlineImageAssets = await bundleInlineImages(input, loadAsset)
|
|
65
|
+
const bundle = buildBundle(input, {
|
|
66
|
+
...rest,
|
|
67
|
+
assets: { ...(rest.assets || {}), ...inlineImageAssets },
|
|
68
|
+
})
|
|
59
69
|
|
|
60
70
|
if (mode === 'sources') {
|
|
61
71
|
return zipBundle(bundle)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline image bundling for source-bundle adapters (typst, latex).
|
|
3
|
+
*
|
|
4
|
+
* A source-bundle adapter emits an image reference (`#image(src)`,
|
|
5
|
+
* `\includegraphics{src}`) into a text file and ships a directory of assets
|
|
6
|
+
* alongside it. The IR carries only the author's `src` string, so the bytes
|
|
7
|
+
* have to be fetched and written into the bundle — and the src rewritten to the
|
|
8
|
+
* bundle-local path — before `typst` / `latex` can resolve them. Cover images
|
|
9
|
+
* arrive pre-bundled via the foundation's getOptions; this handles the images
|
|
10
|
+
* an author drops into the document body.
|
|
11
|
+
*
|
|
12
|
+
* Extracted from the typst adapter when latex became the second bundle adapter
|
|
13
|
+
* needing it — principle 6 in docs/architecture/principles.md, the same reason
|
|
14
|
+
* src/assets/fetch.js exists. EPUB does its own image embedding (it needs OPF
|
|
15
|
+
* manifest entries and a different on-disk layout), so it doesn't share this.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { fetchAssets } from './fetch.js'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Fetch every inline image referenced in the IR and rewrite its node `src` to a
|
|
22
|
+
* bundle-local `assets/<hash>.<ext>` path, returning the map of bundle files to
|
|
23
|
+
* merge into the source bundle.
|
|
24
|
+
*
|
|
25
|
+
* The format walker emits the (rewritten) src verbatim, so the compiler
|
|
26
|
+
* resolves it against the extracted bundle directory. Byte-loading goes through
|
|
27
|
+
* the shared fetchAssets helper: the host-supplied loadAsset (node:fs in
|
|
28
|
+
* unipress, fetch in the browser) turns a resolved src into bytes. Images that
|
|
29
|
+
* fail to load are left untouched so one missing asset doesn't abort the whole
|
|
30
|
+
* document.
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} input - compileOutputs output ({ sections, header, footer }).
|
|
33
|
+
* @param {(src: string) => Promise<Uint8Array|null>} [loadAsset]
|
|
34
|
+
* @returns {Promise<Record<string, Uint8Array>>} bundlePath → bytes
|
|
35
|
+
*/
|
|
36
|
+
export async function bundleInlineImages(input, loadAsset) {
|
|
37
|
+
const imageNodes = collectImageNodes(input)
|
|
38
|
+
if (!imageNodes.length) return {}
|
|
39
|
+
|
|
40
|
+
const srcs = [...new Set(imageNodes.map((n) => n.src).filter(Boolean))]
|
|
41
|
+
if (!srcs.length) return {}
|
|
42
|
+
|
|
43
|
+
const fetched = await fetchAssets(srcs, { loadAsset })
|
|
44
|
+
|
|
45
|
+
const assets = {}
|
|
46
|
+
const rewrite = new Map()
|
|
47
|
+
for (const [src, result] of fetched) {
|
|
48
|
+
if (!result || result.error || !result.bytes) continue
|
|
49
|
+
const bundlePath = `assets/${result.hash}.${result.ext}`
|
|
50
|
+
assets[bundlePath] = result.bytes
|
|
51
|
+
rewrite.set(src, bundlePath)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Rewrite the IR in place so the walker emits the bundle path. The IR is
|
|
55
|
+
// freshly built by compileOutputs for this compile, so mutation is safe and
|
|
56
|
+
// avoids threading a rewrite map through the whole walker.
|
|
57
|
+
for (const node of imageNodes) {
|
|
58
|
+
const bundlePath = rewrite.get(node.src)
|
|
59
|
+
if (bundlePath) node.src = bundlePath
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return assets
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Collect every image IR node reachable from the compiled input, descending
|
|
67
|
+
* through `children` (paragraphs, lists, tables, figures, …) across sections,
|
|
68
|
+
* header, and footer.
|
|
69
|
+
*/
|
|
70
|
+
export function collectImageNodes(input) {
|
|
71
|
+
const found = []
|
|
72
|
+
const visit = (nodes) => {
|
|
73
|
+
if (!Array.isArray(nodes)) return
|
|
74
|
+
for (const node of nodes) {
|
|
75
|
+
if (!node || typeof node !== 'object') continue
|
|
76
|
+
if (node.type === 'image') found.push(node)
|
|
77
|
+
if (Array.isArray(node.children)) visit(node.children)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const section of input?.sections || []) visit(section)
|
|
81
|
+
visit(input?.header || [])
|
|
82
|
+
visit(input?.footer || [])
|
|
83
|
+
return found
|
|
84
|
+
}
|
|
@@ -276,6 +276,11 @@ export async function compileDocument(treeOrWebsite, options = {}) {
|
|
|
276
276
|
: {}
|
|
277
277
|
|
|
278
278
|
const mergedAdapterOptions = {
|
|
279
|
+
// Thread the host loadAsset through to the adapter so it can embed
|
|
280
|
+
// inline body images (the typst/latex bundlers fetch bytes here, the
|
|
281
|
+
// same capability the docx adapter uses). A foundation's getOptions or
|
|
282
|
+
// the caller can still override it via adapterOptions.
|
|
283
|
+
loadAsset,
|
|
279
284
|
...foundationAdapterOptions.adapterOptions,
|
|
280
285
|
...overrideAdapterOptions,
|
|
281
286
|
}
|