@uniweb/unipress 0.2.3 → 0.2.5
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/CHANGELOG.md +88 -0
- package/README.md +10 -2
- package/docs/templates/book.md +2 -3
- package/docs/templates/data-report.md +2 -2
- package/docs/templates/directory.md +3 -3
- package/docs/templates/monograph.md +169 -18
- package/docs/templates/report.md +2 -3
- package/package.json +6 -6
- package/src/content-loader.js +105 -1
- package/src/foundations-data.js +2 -2
- package/src/orchestrator.js +34 -0
- package/src/templates-data.js +34 -35
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,93 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Release binaries are now signed.** macOS binaries are signed with
|
|
8
|
+
Apple Developer ID Application + Apple-notarized; Windows binaries
|
|
9
|
+
are signed via Azure Trusted Signing. macOS users no longer see a
|
|
10
|
+
Gatekeeper warning on first run. The 0.2.3 release assets were
|
|
11
|
+
re-signed in place — re-download from the release page if you
|
|
12
|
+
grabbed an unsigned copy before this change landed.
|
|
13
|
+
|
|
14
|
+
### Removed
|
|
15
|
+
|
|
16
|
+
- `.github/workflows/release.yml`. Binaries are no longer built in
|
|
17
|
+
this public repo — they're built and signed in a private Proximify
|
|
18
|
+
pipeline. Pushing a `unipress@<version>` tag no longer triggers a
|
|
19
|
+
binary build; the maintainer dispatches the signed pipeline manually
|
|
20
|
+
after each tag, and the resulting binaries are uploaded to the same
|
|
21
|
+
GitHub Release on this repo. Source code, docs, npm publishing, and
|
|
22
|
+
the foundation-deploy workflow are unchanged.
|
|
23
|
+
|
|
24
|
+
## 0.2.3
|
|
25
|
+
|
|
26
|
+
The first complete-out-of-the-box release. `npm i -g @uniweb/unipress`
|
|
27
|
+
+ `unipress create my-book --template book` + `unipress compile my-book`
|
|
28
|
+
produces a real PDF on the first try, no setup, no auth, no local
|
|
29
|
+
servers, no `--foundation` overrides.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **GitHub Pages distribution for bundled foundations.**
|
|
34
|
+
`https://uniweb.github.io/unipress/foundations/<name>/<version>/foundation.js`
|
|
35
|
+
serves every published foundation. A new workflow
|
|
36
|
+
(`.github/workflows/deploy-foundations.yml`) builds each
|
|
37
|
+
`foundations/<name>/` on every push to main and accumulates versions
|
|
38
|
+
on the `gh-pages` branch — older versions stay reachable indefinitely.
|
|
39
|
+
No registry namespace claim, no Cloudflare worker, no platform
|
|
40
|
+
authorization needed for the open-source foundations.
|
|
41
|
+
- **`scripts/ci-flip-workspace-deps.js`** — pre-install rewrite that
|
|
42
|
+
translates `workspace:*` deps to npm version ranges in CI. Both
|
|
43
|
+
workflows (foundation deploy, binary release) call it as the first
|
|
44
|
+
step. Closes the standalone-install gap that the workspace:*
|
|
45
|
+
resolution introduced.
|
|
46
|
+
- **`prepublishOnly` regenerates `templates-data.js`.** `pnpm publish`
|
|
47
|
+
now refreshes the bundled template data from `documents/<name>/`
|
|
48
|
+
before packing, so a stale generated file can't ship even if a
|
|
49
|
+
developer forgot to regenerate before commit.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **EPUB cover image no longer skipped.** The foundation's
|
|
54
|
+
`buildEpubOptions` was using a `fetch`-based URL resolver that
|
|
55
|
+
silently 404'd in unipress's Node compile context. Replaced with
|
|
56
|
+
the same `loadAsset` abstraction the typst path uses. Cover image
|
|
57
|
+
now embeds at `OEBPS/images/<hash>.jpg` alongside a synthesized
|
|
58
|
+
`cover.xhtml` page.
|
|
59
|
+
- **DOCX inline-image path threaded through `loadAsset`.**
|
|
60
|
+
`framework/press`'s docx adapter walker now plumbs the host-supplied
|
|
61
|
+
byte loader from `buildDocument` → `convertChildren` →
|
|
62
|
+
`irToImageParagraph` → `fetchImageData` → `fetchAsset`. Future
|
|
63
|
+
docx-emitting templates with inline images compile cleanly in
|
|
64
|
+
unipress.
|
|
65
|
+
- **Cover round-trip in scaffolded projects** — front + back JPGs
|
|
66
|
+
ship via the binary-asset support added to the template generator.
|
|
67
|
+
EPUB readers + most PDF viewers now show the book's thumbnail
|
|
68
|
+
without the user adding any artwork.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- Catalog URLs in `src/foundations-data.js` flipped from local-registry
|
|
73
|
+
(`http://localhost:4001/...`) to GH Pages
|
|
74
|
+
(`https://uniweb.github.io/unipress/foundations/...`). The 0.2.0
|
|
75
|
+
caveat in the changelog is resolved.
|
|
76
|
+
- `foundation-loader.js`'s `DEFAULT_REGISTRY_BASE` flipped to
|
|
77
|
+
`https://uniweb.github.io/unipress`. `UNIWEB_REGISTRY_URL` env var
|
|
78
|
+
remains the override for local dev / private alternatives.
|
|
79
|
+
- `@uniweb/*` deps in `package.json` are `workspace:*` again (dev
|
|
80
|
+
resolves local siblings); `pnpm publish` translates them to real
|
|
81
|
+
version specs in the published tarball.
|
|
82
|
+
|
|
83
|
+
### Notes
|
|
84
|
+
|
|
85
|
+
- Binary downloads (darwin-arm64, linux-x64, windows-x64) ship via
|
|
86
|
+
the existing `unipress@<version>` tag-driven release workflow. The
|
|
87
|
+
npm path is now equivalent for users with Node 20+.
|
|
88
|
+
- See `RELEASING.md` for the framework-publish + workspace:* flip-back
|
|
89
|
+
dance the next maintainer release follows.
|
|
90
|
+
|
|
3
91
|
## 0.2.0
|
|
4
92
|
|
|
5
93
|
The credibility-shifting release. Two production-shaped foundations
|
package/README.md
CHANGED
|
@@ -34,9 +34,17 @@ Foundations consume [@uniweb/press](https://github.com/uniweb/press) for the byt
|
|
|
34
34
|
|
|
35
35
|
## Install
|
|
36
36
|
|
|
37
|
-
**
|
|
37
|
+
**Homebrew (macOS and Linux — recommended).** No Node, no manual download:
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
```bash
|
|
40
|
+
brew install uniweb/unipress/unipress
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Updates land via `brew upgrade unipress`. The macOS binary is signed with the Proximify Inc. Developer ID and Apple-notarized — no Gatekeeper warnings.
|
|
44
|
+
|
|
45
|
+
**Manual download (Windows, or any platform without Homebrew).** Grab the asset for your platform from [the releases page](https://github.com/uniweb/unipress/releases), make it executable (Unix), and put it somewhere on your `PATH`. On macOS and Linux, `/usr/local/bin/unipress` is a common location.
|
|
46
|
+
|
|
47
|
+
**npm.** If you already have Node ≥ 20.19:
|
|
40
48
|
|
|
41
49
|
```bash
|
|
42
50
|
npm i -g @uniweb/unipress
|
package/docs/templates/book.md
CHANGED
|
@@ -15,8 +15,7 @@ unipress compile . --format pdf --out my-book.pdf
|
|
|
15
15
|
```
|
|
16
16
|
my-book/
|
|
17
17
|
├── document.yml pinned to @uniweb/book@<version>
|
|
18
|
-
├──
|
|
19
|
-
│ ├── folder.yml activates folder mode
|
|
18
|
+
├── content/
|
|
20
19
|
│ ├── 01-preface.md type: BackMatter — unnumbered front matter
|
|
21
20
|
│ ├── 02-chapter-one.md
|
|
22
21
|
│ └── 03-chapter-two.md
|
|
@@ -42,7 +41,7 @@ my-book/
|
|
|
42
41
|
|
|
43
42
|
## Add chapters
|
|
44
43
|
|
|
45
|
-
Drop a markdown file under `
|
|
44
|
+
Drop a markdown file under `content/` and add its base name (without `.md`) to `document.yml`'s `content:` list in the order you want it to appear. Chapters need no frontmatter — the foundation's `Chapter` section handles them by default. For non-chapter pages (acknowledgments, colophon, appendices), use `type: BackMatter` in frontmatter to skip chapter numbering.
|
|
46
45
|
|
|
47
46
|
## Common customizations
|
|
48
47
|
|
|
@@ -20,7 +20,7 @@ q3-metrics/
|
|
|
20
20
|
├── collections/
|
|
21
21
|
│ ├── members/ three sample records (19th-century naturalists)
|
|
22
22
|
│ └── queries/ saved query examples for the Population dropdown
|
|
23
|
-
└──
|
|
23
|
+
└── content/
|
|
24
24
|
└── report/ cover, members, publications-by-{type,journal,year},
|
|
25
25
|
publications-list, funding, supervisions
|
|
26
26
|
```
|
|
@@ -33,7 +33,7 @@ The starter ships three sample members so the first compile produces a non-empty
|
|
|
33
33
|
|----------------------------|--------------------------------------------------------|
|
|
34
34
|
| `name`, `author`, `year` | Workbook metadata (shown in the file's properties). |
|
|
35
35
|
| `format` | `xlsx` (default) or `docx`. Override on the CLI. |
|
|
36
|
-
| `index` | Routes `/` → `
|
|
36
|
+
| `index` | Routes `/` → `content/<index>` (here: `report`). |
|
|
37
37
|
| `collections.members.path` | Where the records live (default `collections/members`).|
|
|
38
38
|
| `collections.members.queryable` | Filterable surface — drives the FilterPanel UI. |
|
|
39
39
|
| `collections.queries.path` | Saved-view dropdown (Population selector). |
|
|
@@ -17,7 +17,7 @@ faculty/
|
|
|
17
17
|
├── document.yml pinned to @uniweb/data@<version>; one queryable collection
|
|
18
18
|
├── collections/
|
|
19
19
|
│ └── members/ three sample records
|
|
20
|
-
└──
|
|
20
|
+
└── content/
|
|
21
21
|
└── directory/ cover (Loom-rendered count) + listing
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -27,7 +27,7 @@ faculty/
|
|
|
27
27
|
|-----------------------------|--------------------------------------------------------|
|
|
28
28
|
| `name` | Workbook title. |
|
|
29
29
|
| `format` | `xlsx` (default) or `docx`. |
|
|
30
|
-
| `index` | Routes `/` → `
|
|
30
|
+
| `index` | Routes `/` → `content/directory`. |
|
|
31
31
|
| `collections.members.path` | Where the records live (`collections/members`). |
|
|
32
32
|
| `collections.members.queryable` | Filterable fields — drives the FilterPanel UI. |
|
|
33
33
|
|
|
@@ -58,7 +58,7 @@ Add a `joined_year:` range field, for example, if filtering by start year matter
|
|
|
58
58
|
|
|
59
59
|
## Hand-pick the displayed columns
|
|
60
60
|
|
|
61
|
-
The Members section reads every key from each record by default. To restrict the columns shown in the web preview and the xlsx export, set `columns:` on the section frontmatter in `
|
|
61
|
+
The Members section reads every key from each record by default. To restrict the columns shown in the web preview and the xlsx export, set `columns:` on the section frontmatter in `content/directory/members.md`.
|
|
62
62
|
|
|
63
63
|
## When to pick `directory` over `data-report`
|
|
64
64
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `monograph`
|
|
2
2
|
|
|
3
|
-
A scholarly monograph — sustained, single-author argument on a focused topic. Royal-octavo trim, classical typography (EB Garamond by default with Garamond and Georgia fall-backs), three-deep TOC, roman-numeralled front matter. Same `@uniweb/book` foundation as the `book` and `report` templates, configured for academic press conventions.
|
|
3
|
+
A scholarly monograph — sustained, single-author argument on a focused topic with a working citation system. Royal-octavo trim, classical typography (EB Garamond by default with Garamond and Georgia fall-backs), three-deep TOC, roman-numeralled front matter, inline `[@key]` cites, and a back-matter bibliography formatted by [citestyle](https://github.com/uniweb/csl) in any of nine styles. Same `@uniweb/book` foundation as the `book` and `report` templates, configured for academic press conventions.
|
|
4
4
|
|
|
5
5
|
## Scaffold
|
|
6
6
|
|
|
@@ -10,41 +10,192 @@ cd my-monograph
|
|
|
10
10
|
unipress compile . --format pdf --out my-monograph.pdf
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
The starter ships a small Victorian-naturalist bibliography (nine entries — Darwin, Wallace, Lyell, Mendel, Huxley, Hooker, Spencer) and a chapter that exercises every inline-cite shape. Compile out of the box and read the result alongside the markdown source to see what each shape produces.
|
|
14
|
+
|
|
13
15
|
## What you get
|
|
14
16
|
|
|
15
17
|
```
|
|
16
18
|
my-monograph/
|
|
17
|
-
├── document.yml
|
|
18
|
-
├──
|
|
19
|
-
│
|
|
20
|
-
│
|
|
21
|
-
|
|
22
|
-
│
|
|
19
|
+
├── document.yml pinned to @uniweb/book@<version>; royal-octavo, EB Garamond
|
|
20
|
+
├── collections/
|
|
21
|
+
│ └── bibliography/
|
|
22
|
+
│ └── refs.bib BibTeX file — every @entry is one record
|
|
23
|
+
├── content/
|
|
24
|
+
│ ├── 01-preface.md type: BackMatter
|
|
25
|
+
│ ├── 02-introduction.md type: Chapter
|
|
26
|
+
│ ├── 03-chapter-one.md type: Chapter (worked cite example)
|
|
27
|
+
│ └── 99-bibliography.md type: Bibliography (back-matter list)
|
|
23
28
|
└── README.md
|
|
24
29
|
```
|
|
25
30
|
|
|
26
31
|
## When to pick `monograph` over `book`
|
|
27
32
|
|
|
33
|
+
- You need cited bibliographic references in your prose.
|
|
28
34
|
- You want section numbering deeper than two levels (default `tocDepth: 3`).
|
|
29
35
|
- You want classical book typography (EB Garamond) rather than the foundation's default sans/serif fall-back.
|
|
30
36
|
- You want a slightly larger page (royal-octavo, 6.14×9.21in) common in academic hardcovers.
|
|
31
37
|
|
|
32
38
|
For trade-paperback fiction or non-fiction prose, use `book` instead. For a technical report with tables and code, use `report`.
|
|
33
39
|
|
|
40
|
+
## Citations
|
|
41
|
+
|
|
42
|
+
The citation system has three pieces: a **style** declared in `document.yml`, a **collection** of bibliography records, and **inline cites** in the prose that reference those records by key.
|
|
43
|
+
|
|
44
|
+
### Pick a style
|
|
45
|
+
|
|
46
|
+
Set `book.citationStyle:` in `document.yml`. Nine styles ship statically wired — switch the document's whole bibliographic apparatus by changing one value:
|
|
47
|
+
|
|
48
|
+
| Style | Inline shape | Use case |
|
|
49
|
+
|------------------------|--------------------|-------------------------------------------------|
|
|
50
|
+
| `chicago-author-date` | (Darwin 1859, 42) | Humanities, history, social sciences (default) |
|
|
51
|
+
| `apa` | (Darwin, 1859) | Psychology, education, social sciences |
|
|
52
|
+
| `mla` | (Darwin 42) | Literature, modern languages |
|
|
53
|
+
| `harvard` | (Darwin 1859: 42) | UK humanities, business |
|
|
54
|
+
| `ieee` | [1, p. 42] | Engineering, computer science |
|
|
55
|
+
| `vancouver` | (1) | Medicine, biomedicine |
|
|
56
|
+
| `ama` | ¹ | Medical journals |
|
|
57
|
+
| `nature` | ¹ | Nature journals |
|
|
58
|
+
| `science` | (1) | Science journals |
|
|
59
|
+
|
|
60
|
+
Numbered styles (IEEE, Vancouver, Nature, Science, AMA) reuse the same numbering across inline cites and the back-matter list — `[1]` inline matches `[1]` in the back-matter.
|
|
61
|
+
|
|
62
|
+
Optional sort: `book.bibliography.sortBy:` — `author` (default), `year`, or `collection-order`.
|
|
63
|
+
|
|
64
|
+
### Author bibliography entries
|
|
65
|
+
|
|
66
|
+
Drop a `.bib` file into `collections/bibliography/`. Every `@entry{key, ...}` becomes one record; the BibTeX cite key is the entry id you reference from prose with `[@key]`. Standard BibTeX entry types work (`@article`, `@book`, `@incollection`, `@inproceedings`, `@phdthesis`, `@techreport`, `@misc`, etc.); LaTeX accents (`\"u`, `\'e`, `\v{c}`) are converted to Unicode automatically.
|
|
67
|
+
|
|
68
|
+
```bibtex
|
|
69
|
+
% collections/bibliography/refs.bib
|
|
70
|
+
|
|
71
|
+
@book{darwin1859,
|
|
72
|
+
author = {Darwin, Charles},
|
|
73
|
+
title = {On the Origin of Species},
|
|
74
|
+
publisher = {John Murray},
|
|
75
|
+
address = {London},
|
|
76
|
+
year = {1859}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@article{wallace1858,
|
|
80
|
+
author = {Wallace, Alfred Russel},
|
|
81
|
+
title = {On the Tendency of Varieties to Depart Indefinitely from the Original Type},
|
|
82
|
+
journal = {Journal of the Proceedings of the Linnean Society of London. Zoology},
|
|
83
|
+
volume = {3},
|
|
84
|
+
number = {9},
|
|
85
|
+
pages = {53--62},
|
|
86
|
+
year = {1858}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
If a record needs a CSL field BibTeX can't carry — multi-script titles, fine-grained date parts, fielded notes — drop a hand-written YAML file (CSL-JSON shape) into the same folder. The loader merges every `.bib`, `.yml`, and `.json` it finds, so authors can mix the format their reference manager exports with one-off hand-edited entries.
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
# collections/bibliography/wallace1858.yml — full CSL-JSON, overrides if a duplicate
|
|
94
|
+
# cite key exists in any .bib file in the same folder.
|
|
95
|
+
id: wallace1858
|
|
96
|
+
type: article-journal
|
|
97
|
+
author:
|
|
98
|
+
- family: Wallace
|
|
99
|
+
given: Alfred Russel
|
|
100
|
+
title: "On the Tendency of Varieties to Depart Indefinitely from the Original Type"
|
|
101
|
+
container-title: "Journal of the Proceedings of the Linnean Society of London. Zoology"
|
|
102
|
+
volume: 3
|
|
103
|
+
issue: 9
|
|
104
|
+
page: 53-62
|
|
105
|
+
issued:
|
|
106
|
+
date-parts: [[1858]]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Common entry types
|
|
110
|
+
|
|
111
|
+
| `type:` value | What it is |
|
|
112
|
+
|-------------------------|-----------------------------------------------------------------------|
|
|
113
|
+
| `book` | Single-volume monograph (Darwin's *Origin*). |
|
|
114
|
+
| `article-journal` | Journal article (Mendel's *Versuche*). |
|
|
115
|
+
| `article-magazine` | Magazine article. |
|
|
116
|
+
| `article-newspaper` | Newspaper article. |
|
|
117
|
+
| `chapter` | Chapter in an edited volume — set `editor:` and `container-title:`. |
|
|
118
|
+
| `paper-conference` | Conference proceedings. |
|
|
119
|
+
| `thesis` | Dissertation or thesis — set `genre: PhD` / `Master's` if relevant. |
|
|
120
|
+
| `report` | Technical or institutional report. |
|
|
121
|
+
| `webpage` | Web page, blog post, or any URL-only source. |
|
|
122
|
+
| `personal_communication`| Letter, email, interview. |
|
|
123
|
+
|
|
124
|
+
The full CSL 1.0.2 type list is at [docs.citationstyles.org](https://docs.citationstyles.org/en/stable/specification.html#appendix-iii-types). The foundation passes `type:` through to citestyle unchanged; any valid CSL type works.
|
|
125
|
+
|
|
126
|
+
#### Common fields
|
|
127
|
+
|
|
128
|
+
| Field | Purpose |
|
|
129
|
+
|------------------------------------------------------|----------------------------------------------------------------------|
|
|
130
|
+
| `id` | Cite key (defaults to the filename stem). |
|
|
131
|
+
| `type` | One of the CSL types above. |
|
|
132
|
+
| `author` | String (`"Last, First"`) or array of name objects (`[{family, given}]`). |
|
|
133
|
+
| `editor`, `translator`, `container-author` | Same shape as `author`. |
|
|
134
|
+
| `title` | The work's title. |
|
|
135
|
+
| `container-title` | Journal name, edited-volume title, magazine. |
|
|
136
|
+
| `publisher`, `publisher-place` | Publisher and city. |
|
|
137
|
+
| `volume`, `issue`, `page` | Journal locators (string or number). |
|
|
138
|
+
| `year` (shorthand) or `issued: { date-parts: [[YYYY]] }` | Publication date. |
|
|
139
|
+
| `DOI`, `URL`, `ISBN`, `ISSN`, `PMID` | Identifiers — citestyle auto-links DOIs. |
|
|
140
|
+
| `edition`, `genre`, `note`, `language` | Misc fields most styles consume. |
|
|
141
|
+
|
|
142
|
+
### Cite in prose
|
|
143
|
+
|
|
144
|
+
Inline citations use a Pandoc-style sugar that compiles to an inline inset. Five shapes cover the full range:
|
|
145
|
+
|
|
146
|
+
| Markdown | Renders as (chicago-author-date) |
|
|
147
|
+
|-----------------------------------|-----------------------------------------------------------|
|
|
148
|
+
| `[@darwin1859]` | (Darwin 1859) |
|
|
149
|
+
| `[@darwin1859]{page=42}` | (Darwin 1859, 42) |
|
|
150
|
+
| `[@darwin1859; @wallace1858]` | (Darwin 1859; Wallace 1858) |
|
|
151
|
+
| `[@darwin1859]{suppress-author}` | (1859) — when "Darwin" already appears in the prose |
|
|
152
|
+
| `[@nope]` | [?] — visible placeholder, no compile failure |
|
|
153
|
+
|
|
154
|
+
The locator can be `page=42`, `chapter=3`, `section=2.1`, `paragraph=12`, etc. Set `label=<kind>` to label it explicitly: `[@darwin1859]{locator=2 label=chapter}` renders as `(Darwin 1859, chap. 2)` in styles that abbreviate the label.
|
|
155
|
+
|
|
156
|
+
Multi-cite clusters collapse per the active style's rules. In chicago-author-date, two cites by the same author share the author and join their years with a comma: `[@darwin1859; @darwin1871]` renders as `(Darwin 1859, 1871)`.
|
|
157
|
+
|
|
158
|
+
The `[@key]` shorthand desugars to `[@key](@Cite){k=v}` — the framework's textual inline-inset form. You can also call other inline insets directly with the same `[text](@Component){k=v}` syntax (see the foundation README).
|
|
159
|
+
|
|
160
|
+
### The back-matter bibliography
|
|
161
|
+
|
|
162
|
+
`content/99-bibliography.md` is a one-line back-matter section that lists every record from the collection in style-correct order:
|
|
163
|
+
|
|
164
|
+
```markdown
|
|
165
|
+
---
|
|
166
|
+
type: Bibliography
|
|
167
|
+
title: References
|
|
168
|
+
data: bibliography
|
|
169
|
+
---
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The `data: bibliography` shorthand fetches from the collection of that name declared in `document.yml`. For numbered styles the entries appear in citation-encounter order; for author-date styles they're alphabetical by author then year.
|
|
173
|
+
|
|
174
|
+
For collections of references that don't all need to ride together — say, a list of "further reading" separate from the main bibliography — declare a second collection in `document.yml` and reference it from a second `Bibliography` section with `data: <other-name>` and an optional `style: <override>`.
|
|
175
|
+
|
|
34
176
|
## `document.yml` fields
|
|
35
177
|
|
|
36
|
-
Same shape as `book` (see [book.md](./book.md)). The
|
|
178
|
+
Same shape as `book` (see [book.md](./book.md)). The defaults differ:
|
|
179
|
+
|
|
180
|
+
| Field | `monograph` default |
|
|
181
|
+
|--------------------------------------|------------------------------------------------------|
|
|
182
|
+
| `book.trim` | `royal-octavo` |
|
|
183
|
+
| `book.typography.bodyFont` | `["EB Garamond", "Garamond", "Georgia"]` |
|
|
184
|
+
| `book.typography.headingFont` | `["EB Garamond", "Garamond", "Georgia"]` |
|
|
185
|
+
| `book.typography.bodySize` | `10.5pt` |
|
|
186
|
+
| `book.typography.leading` | `0.68em` |
|
|
187
|
+
| `book.structure.tocDepth` | `3` |
|
|
188
|
+
| `book.structure.frontMatterNumbering`| `roman` |
|
|
189
|
+
| `book.citationStyle` | `chicago-author-date` |
|
|
190
|
+
| `book.bibliography.sortBy` | `author` |
|
|
191
|
+
| `collections.bibliography.path` | `collections/bibliography` |
|
|
192
|
+
|
|
193
|
+
## Common customizations
|
|
37
194
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
| `book.typography.bodyFont` | `["EB Garamond", "Garamond", "Georgia"]` |
|
|
42
|
-
| `book.typography.headingFont`| `["EB Garamond", "Garamond", "Georgia"]` |
|
|
43
|
-
| `book.typography.bodySize` | `10.5pt` |
|
|
44
|
-
| `book.typography.leading` | `0.68em` |
|
|
45
|
-
| `book.structure.tocDepth` | `3` |
|
|
46
|
-
| `book.structure.frontMatterNumbering` | `roman` |
|
|
195
|
+
- **Switch citation style** by changing one line: `book.citationStyle: ieee` (or any of the nine styles above). Re-compile; every cite and the back-matter list re-formats to match.
|
|
196
|
+
- **Add a "further reading" section** by declaring a second collection in `document.yml` (`collections.further-reading.path: collections/further-reading`) and a second `Bibliography` content file with `data: further-reading`.
|
|
197
|
+
- **Drop the citations entirely** by deleting `99-bibliography.md`, the `collections/` directory, and the `book.citationStyle:` block. The remaining template behaves like a citation-free `book` with monograph typography.
|
|
47
198
|
|
|
48
199
|
## Foundation reference
|
|
49
200
|
|
|
50
|
-
`@uniweb/book` — see the foundation
|
|
201
|
+
`@uniweb/book` — see [foundations/book/README.md](https://github.com/uniweb/unipress/blob/main/foundations/book/README.md) for the full list of foundation knobs and `foundations/book/CHANGELOG.md` for the version history.
|
package/docs/templates/report.md
CHANGED
|
@@ -15,8 +15,7 @@ unipress compile . --format pdf --out q3-report.pdf
|
|
|
15
15
|
```
|
|
16
16
|
q3-report/
|
|
17
17
|
├── document.yml pinned to @uniweb/book@<version>; trade-7x10, block paragraphs
|
|
18
|
-
├──
|
|
19
|
-
│ ├── folder.yml
|
|
18
|
+
├── content/
|
|
20
19
|
│ ├── 01-summary.md executive summary + recommendations table
|
|
21
20
|
│ ├── 02-findings.md body, with code listing and pull-quote
|
|
22
21
|
│ └── 03-methodology.md
|
|
@@ -46,7 +45,7 @@ Same shape as `book` (see [book.md](./book.md)). The differences are defaults:
|
|
|
46
45
|
|
|
47
46
|
## Common customizations
|
|
48
47
|
|
|
49
|
-
- **Add an appendix**: a new markdown file with `type: BackMatter` in frontmatter, listed in `
|
|
48
|
+
- **Add an appendix**: a new markdown file with `type: BackMatter` in frontmatter, listed in `content:`.
|
|
50
49
|
- **Wider code blocks**: bump `book.typography.codeMarginRelief` to `0.5in` (code extends 0.5in past each side of the body column).
|
|
51
50
|
- **No TOC** for short reports: set `book.structure.toc: false`.
|
|
52
51
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/unipress",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Compile a content directory into a document (PDF, EPUB, Paged.js HTML, Typst source bundle, DOCX, XLSX) using a Uniweb foundation. Five built-in templates: book, monograph, report, data-report, directory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"prompts": "^2.4.2",
|
|
50
50
|
"react": "^18.0.0 || ^19.0.0",
|
|
51
51
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
52
|
-
"@uniweb/build": "0.11.
|
|
53
|
-
"@uniweb/runtime": "0.8.
|
|
54
|
-
"@uniweb/
|
|
55
|
-
"@uniweb/
|
|
56
|
-
"@uniweb/
|
|
52
|
+
"@uniweb/build": "0.11.4",
|
|
53
|
+
"@uniweb/runtime": "0.8.6",
|
|
54
|
+
"@uniweb/semantic-parser": "1.1.13",
|
|
55
|
+
"@uniweb/core": "0.7.5",
|
|
56
|
+
"@uniweb/content-reader": "1.1.7"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"test": "echo \"no tests yet\" && exit 0",
|
package/src/content-loader.js
CHANGED
|
@@ -10,10 +10,108 @@
|
|
|
10
10
|
|
|
11
11
|
import { existsSync } from 'node:fs'
|
|
12
12
|
import { resolve, join } from 'node:path'
|
|
13
|
-
import { collectSiteContent } from '@uniweb/build/content'
|
|
13
|
+
import { collectSiteContent, processCollections } from '@uniweb/build/content'
|
|
14
14
|
import { detectConfigFile, CONFIG_FILE_NAMES } from './document-yml.js'
|
|
15
15
|
import { ContentDirectoryError, DocumentYmlError } from './errors.js'
|
|
16
16
|
|
|
17
|
+
// Match a parsed collection-backed fetch path. parseFetchConfig (in
|
|
18
|
+
// @uniweb/build) normalises `{ collection: <name> }` into
|
|
19
|
+
// `{ path: '/data/<name>.json', schema: <name>, ... }` — so this regex is
|
|
20
|
+
// the inverse: pull the collection name back out of the resolved path.
|
|
21
|
+
const COLLECTION_PATH_RE = /^\/data\/(.+)\.json$/
|
|
22
|
+
|
|
23
|
+
function attachData(section, schema, data) {
|
|
24
|
+
if (!section || !schema) return
|
|
25
|
+
if (!section.parsedContent) section.parsedContent = {}
|
|
26
|
+
if (!section.parsedContent.data) section.parsedContent.data = {}
|
|
27
|
+
// Don't clobber a section-level value with a cascaded page-level one.
|
|
28
|
+
// The Block constructor will spread parsedContent.data through, so the
|
|
29
|
+
// first writer wins for a given schema.
|
|
30
|
+
if (section.parsedContent.data[schema] === undefined) {
|
|
31
|
+
section.parsedContent.data[schema] = data
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findCollectionRecords(fetchConfig, resolved) {
|
|
36
|
+
if (!fetchConfig?.path || !fetchConfig?.schema) return null
|
|
37
|
+
const m = COLLECTION_PATH_RE.exec(fetchConfig.path)
|
|
38
|
+
if (!m) return null
|
|
39
|
+
const records = resolved[m[1]]
|
|
40
|
+
if (!Array.isArray(records)) return null
|
|
41
|
+
return records
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function attachSectionFetches(sections, resolved) {
|
|
45
|
+
if (!Array.isArray(sections)) return
|
|
46
|
+
for (const section of sections) {
|
|
47
|
+
const records = findCollectionRecords(section.fetch, resolved)
|
|
48
|
+
if (records) attachData(section, section.fetch.schema, records)
|
|
49
|
+
if (Array.isArray(section.subsections) && section.subsections.length) {
|
|
50
|
+
attachSectionFetches(section.subsections, resolved)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Materialize file-based collections into each section's
|
|
57
|
+
* `parsedContent.data` so the SSR render pipeline reads populated data
|
|
58
|
+
* synchronously (no `useFetched` round-trip; no `public/` directory).
|
|
59
|
+
*
|
|
60
|
+
* In a regular Uniweb site build, the Vite plugin runs `processCollections`
|
|
61
|
+
* + `writeCollectionFiles` and the runtime resolves `fetch:` declarations
|
|
62
|
+
* over HTTP at render time. Under `unipress compile` neither of those
|
|
63
|
+
* happens — there's no public dir, and SSR skips effects. We close the
|
|
64
|
+
* gap by resolving collections in-memory and attaching the records
|
|
65
|
+
* directly to each block's `parsedContent.data.<schema>`. The Block
|
|
66
|
+
* constructor (framework/core/src/block.js) preserves that field, and
|
|
67
|
+
* `prepareProps` then surfaces it as `content.data.<schema>` to the
|
|
68
|
+
* component — same shape the runtime would produce.
|
|
69
|
+
*
|
|
70
|
+
* Page-level fetch cascades to every section on the page; section-level
|
|
71
|
+
* fetch overrides on a per-section basis. Only collection-backed fetches
|
|
72
|
+
* (parsed `path: '/data/<name>.json'`) are resolved here — remote URL
|
|
73
|
+
* fetches, refine configs, and array-form `fetch: [...]` declarations
|
|
74
|
+
* are left untouched (those have their own gaps; out of scope here).
|
|
75
|
+
*/
|
|
76
|
+
async function resolveLocalCollections(siteContent, sitePath) {
|
|
77
|
+
const collectionsConfig = siteContent?.config?.collections
|
|
78
|
+
if (!collectionsConfig || typeof collectionsConfig !== 'object') return
|
|
79
|
+
if (Object.keys(collectionsConfig).length === 0) return
|
|
80
|
+
|
|
81
|
+
const resolved = await processCollections(
|
|
82
|
+
sitePath,
|
|
83
|
+
collectionsConfig,
|
|
84
|
+
sitePath,
|
|
85
|
+
'/',
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
for (const page of siteContent.pages || []) {
|
|
89
|
+
const pageRecords = findCollectionRecords(page.fetch, resolved)
|
|
90
|
+
if (pageRecords) {
|
|
91
|
+
for (const section of page.sections || []) {
|
|
92
|
+
attachData(section, page.fetch.schema, pageRecords)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
attachSectionFetches(page.sections, resolved)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Stash the resolved arrays on the website config too, so any section
|
|
99
|
+
// (regardless of its own page's fetch declaration) can self-bootstrap
|
|
100
|
+
// — e.g., a Cite inset rendering inside a Chapter on page A needs the
|
|
101
|
+
// bibliography records that the Bibliography section declared on page
|
|
102
|
+
// B. Foundations read this via `block.website.config.collections.<name>.records`
|
|
103
|
+
// as a synchronous fallback.
|
|
104
|
+
if (!siteContent.config) siteContent.config = {}
|
|
105
|
+
if (!siteContent.config.collections) siteContent.config.collections = {}
|
|
106
|
+
for (const name of Object.keys(resolved)) {
|
|
107
|
+
const existing = siteContent.config.collections[name]
|
|
108
|
+
siteContent.config.collections[name] = {
|
|
109
|
+
...(existing && typeof existing === 'object' ? existing : {}),
|
|
110
|
+
records: resolved[name],
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
17
115
|
export async function loadContent(dir, options = {}) {
|
|
18
116
|
const sitePath = resolve(dir)
|
|
19
117
|
|
|
@@ -52,5 +150,11 @@ export async function loadContent(dir, options = {}) {
|
|
|
52
150
|
throw err
|
|
53
151
|
}
|
|
54
152
|
|
|
153
|
+
await resolveLocalCollections(content, sitePath)
|
|
154
|
+
|
|
155
|
+
// Cross-reference registry is built AFTER the foundation loads, in
|
|
156
|
+
// orchestrator.loadAndInit, so foundation-declared `xref.kinds`
|
|
157
|
+
// contribute to the kind inference at id-collection time.
|
|
158
|
+
|
|
55
159
|
return { content, configFile, sitePath }
|
|
56
160
|
}
|
package/src/foundations-data.js
CHANGED
|
@@ -72,8 +72,8 @@ function publicUrl(name, version) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
const BOOK_FOUNDATION = {
|
|
75
|
-
ref: '@uniweb/book@0.
|
|
76
|
-
source: { url: publicUrl('book', '0.
|
|
75
|
+
ref: '@uniweb/book@0.2.0',
|
|
76
|
+
source: { url: publicUrl('book', '0.2.0') },
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const DATA_FOUNDATION = {
|
package/src/orchestrator.js
CHANGED
|
@@ -30,6 +30,26 @@ import { readFile } from 'node:fs/promises'
|
|
|
30
30
|
import { initPrerender } from '@uniweb/runtime/ssr'
|
|
31
31
|
import { FoundationResolutionError, CompileError } from './errors.js'
|
|
32
32
|
|
|
33
|
+
// `buildXrefRegistry` is a recent addition to @uniweb/build/content
|
|
34
|
+
// (cross-references milestone). Older published versions don't expose
|
|
35
|
+
// it; the dynamic resolver below falls back to a no-op registry so the
|
|
36
|
+
// pipeline keeps working against any installed version. Once the build
|
|
37
|
+
// package republishes with the new export, the real registry kicks in
|
|
38
|
+
// automatically — no code change required.
|
|
39
|
+
let _buildXrefRegistry = null
|
|
40
|
+
async function getBuildXrefRegistry() {
|
|
41
|
+
if (_buildXrefRegistry !== null) return _buildXrefRegistry
|
|
42
|
+
try {
|
|
43
|
+
const mod = await import('@uniweb/build/content')
|
|
44
|
+
_buildXrefRegistry = typeof mod.buildXrefRegistry === 'function'
|
|
45
|
+
? mod.buildXrefRegistry
|
|
46
|
+
: () => ({ entries: {} })
|
|
47
|
+
} catch {
|
|
48
|
+
_buildXrefRegistry = () => ({ entries: {} })
|
|
49
|
+
}
|
|
50
|
+
return _buildXrefRegistry
|
|
51
|
+
}
|
|
52
|
+
|
|
33
53
|
export async function importFoundation(resolvedPath) {
|
|
34
54
|
try {
|
|
35
55
|
return await import(pathToFileURL(resolvedPath).href)
|
|
@@ -49,8 +69,22 @@ export function initOrchestrator({ content, foundation, extensions = [], onProgr
|
|
|
49
69
|
// Convenience: import + init in one step. Returns the uniweb instance,
|
|
50
70
|
// or throws (the caller decides whether to surface as fatal or attached
|
|
51
71
|
// to the inspect dump).
|
|
72
|
+
//
|
|
73
|
+
// Cross-reference registry is built here, after the foundation imports
|
|
74
|
+
// so any foundation-declared `xref.kinds` participate in id-collection.
|
|
75
|
+
// Built-in kinds (figure / equation / section / table) work without
|
|
76
|
+
// foundation cooperation; foundation extensions land alongside.
|
|
52
77
|
export async function loadAndInit({ content, resolvedPath, extensions = [], onProgress } = {}) {
|
|
53
78
|
const foundation = await importFoundation(resolvedPath)
|
|
79
|
+
|
|
80
|
+
const foundationKinds =
|
|
81
|
+
foundation?.default?.capabilities?.xref?.kinds ||
|
|
82
|
+
foundation?.default?.xref?.kinds ||
|
|
83
|
+
foundation?.xref?.kinds ||
|
|
84
|
+
{}
|
|
85
|
+
const buildXrefRegistry = await getBuildXrefRegistry()
|
|
86
|
+
content.xref = buildXrefRegistry(content, { foundationKinds })
|
|
87
|
+
|
|
54
88
|
const uniweb = initOrchestrator({ content, foundation, extensions, onProgress })
|
|
55
89
|
return { foundation, uniweb }
|
|
56
90
|
}
|