Commit 2f14a20767
Verified · cmc
Layout: unified · split
.github/workflows/ci.yml +2 −2
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | name: CI |
| 2 | 2 | |
| 3 | # Build, test and lint on both platforms org-ssg is used from, plus a compiler-floor job. | |
| 3 | # Build, test and lint on both platforms orgo is used from, plus a compiler-floor job. | |
| 4 | 4 | # |
| 5 | 5 | # WHAT THIS CATCHES THAT LOCAL WORK DOES NOT: |
| 6 | 6 | # |
| @@ -96,7 +96,7 @@ jobs: | ||
| 96 | 96 | # Build only. The tests pull in dev-dependencies whose own floors move |
| 97 | 97 | # independently, and chasing those would make this job about someone else's MSRV. |
| 98 | 98 | # |
| 99 | # The floor is set by dependencies rather than by org-ssg — its own code compiles on | |
| 99 | # The floor is set by dependencies rather than by orgo — its own code compiles on | |
| 100 | 100 | # 1.82 — which is precisely why it is checked here instead of reasoned about: a |
| 101 | 101 | # dependency raising its floor is invisible until someone on an older compiler |
| 102 | 102 | # tries to build. |
.github/workflows/release.yml +3 −3
| @@ -2,7 +2,7 @@ name: Release | ||
| 2 | 2 | |
| 3 | 3 | # Build binaries for a tag and attach them to a GitHub release. |
| 4 | 4 | # |
| 5 | # WHY BINARIES AT ALL, given `cargo install org-ssg` exists: installing from source needs | |
| 5 | # WHY BINARIES AT ALL, given `cargo install orgo` exists: installing from source needs | |
| 6 | 6 | # a Rust toolchain and about a minute of compiling syntect. Someone evaluating a site |
| 7 | 7 | # generator should be able to download one file and point it at their notes. |
| 8 | 8 | # |
| @@ -74,9 +74,9 @@ jobs: | ||
| 74 | 74 | - name: Package |
| 75 | 75 | shell: bash |
| 76 | 76 | run: | |
| 77 | staging="org-ssg-${{ github.event.inputs.tag || github.ref_name }}-${{ matrix.target }}" | |
| 77 | staging="orgo-${{ github.event.inputs.tag || github.ref_name }}-${{ matrix.target }}" | |
| 78 | 78 | mkdir "$staging" |
| 79 | cp "target/${{ matrix.target }}/release/org-ssg" "$staging/" | |
| 79 | cp "target/${{ matrix.target }}/release/orgo" "$staging/" | |
| 80 | 80 | cp README.md LICENSE CHANGELOG.md "$staging/" |
| 81 | 81 | tar czf "$staging.tar.gz" "$staging" |
| 82 | 82 | shasum -a 256 "$staging.tar.gz" > "$staging.tar.gz.sha256" |
CHANGELOG.md +4 −4
| @@ -7,7 +7,7 @@ Two conventions worth knowing before reading: | ||
| 7 | 7 | |
| 8 | 8 | - **A cache-format bump is not a change you need to act on.** The incremental cache is |
| 9 | 9 | versioned and discards itself; a bump means the next build re-renders everything once. |
| 10 | - **Output changes are called out.** org-ssg aims at what Emacs exports from the same | |
| 10 | - **Output changes are called out.** orgo aims at what Emacs exports from the same | |
| 11 | 11 | file, so an entry that says "now renders X" means your pages will change. That is the |
| 12 | 12 | product, not a regression — but it belongs in a changelog rather than a diff you find |
| 13 | 13 | later. |
| @@ -47,7 +47,7 @@ Release engineering, so that a version number is worth reading. | ||
| 47 | 47 | built with `--strict`. Emacs is installed on both, so the oracle suite runs for real |
| 48 | 48 | instead of skipping. |
| 49 | 49 | - **A checked MSRV**, 1.88 — which is how it came to be 1.88 rather than the 1.82 |
| 50 | org-ssg's own code needs. The floor comes from dependencies, and nobody finds that out | |
| 50 | orgo's own code needs. The floor comes from dependencies, and nobody finds that out | |
| 51 | 51 | by reasoning about it. |
| 52 | 52 | - **Release binaries** for macOS (arm64, x86_64) and Linux (gnu, musl), built on tag into |
| 53 | 53 | a draft release. The tag is checked against `Cargo.toml` before anything is built. |
| @@ -117,7 +117,7 @@ publishes from the same sources. **All of these change output.** | ||
| 117 | 117 | ## 0.12.0 |
| 118 | 118 | |
| 119 | 119 | - `serve`: a development server with live reload, bound to loopback. |
| 120 | - A documentation site under `docs/`, built by org-ssg itself. | |
| 120 | - A documentation site under `docs/`, built by orgo itself. | |
| 121 | 121 | |
| 122 | 122 | ## 0.11.0 |
| 123 | 123 | |
| @@ -153,7 +153,7 @@ publishes from the same sources. **All of these change output.** | ||
| 153 | 153 | ## 0.5.0 |
| 154 | 154 | |
| 155 | 155 | - Parse diagnostics carry `file:line`, and pages render in parallel. |
| 156 | - The corpus audit (`org-ssg audit`) and the `emacs --batch` oracle. | |
| 156 | - The corpus audit (`orgo audit`) and the `emacs --batch` oracle. | |
| 157 | 157 | - `#+SLUG:` decides a page's output filename — found by auditing a real corpus, where it |
| 158 | 158 | affected 169 of 182 URLs. |
| 159 | 159 | |
Cargo.lock +1 −1
| @@ -674,7 +674,7 @@ dependencies = [ | ||
| 674 | 674 | ] |
| 675 | 675 | |
| 676 | 676 | [[package]] |
| 677 | name = "org-ssg" | |
| 677 | name = "orgo" | |
| 678 | 678 | version = "0.19.1" |
| 679 | 679 | dependencies = [ |
| 680 | 680 | "anyhow", |
Cargo.toml +7 −8
| @@ -1,17 +1,16 @@ | ||
| 1 | 1 | [package] |
| 2 | name = "org-ssg" | |
| 2 | name = "orgo" | |
| 3 | 3 | version = "0.19.1" |
| 4 | 4 | edition = "2021" |
| 5 | 5 | description = "Org-mode static site generator that renders the org element tree straight to HTML" |
| 6 | license = "MIT" | |
| 6 | license = "0BSD" | |
| 7 | 7 | readme = "README.md" |
| 8 | 8 | keywords = ["org-mode", "static-site-generator", "emacs", "html", "blog"] |
| 9 | 9 | categories = ["command-line-utilities", "text-processing"] |
| 10 | # Set before the first `cargo publish`: crates.io shows it on the crate page, and a | |
| 11 | # missing link is the first thing anyone evaluating a generator looks for. | |
| 12 | # repository = "https://git.krz.sh/cmc/org-ssg.git/" | |
| 10 | repository = "https://github.com/ccleberg/orgo" | |
| 11 | homepage = "https://ccleberg.github.io/orgo/" | |
| 13 | 12 | |
| 14 | # The compiler floor, checked in CI rather than assumed. org-ssg's own code needs 1.82 | |
| 13 | # The compiler floor, checked in CI rather than assumed. orgo's own code needs 1.82 | |
| 15 | 14 | # (`Option::is_none_or`); the floor is 1.88 because dependencies in Cargo.lock declare it |
| 16 | 15 | # — `plist` and `time`, both by way of syntect. Raising this is a minor-version change, |
| 17 | 16 | # never a patch. |
| @@ -23,11 +22,11 @@ rust-version = "1.88" | ||
| 23 | 22 | exclude = ["docs/", "target/", "/.github/"] |
| 24 | 23 | |
| 25 | 24 | [lib] |
| 26 | name = "org_ssg" | |
| 25 | name = "orgo" | |
| 27 | 26 | path = "src/lib.rs" |
| 28 | 27 | |
| 29 | 28 | [[bin]] |
| 30 | name = "org-ssg" | |
| 29 | name = "orgo" | |
| 31 | 30 | path = "src/main.rs" |
| 32 | 31 | |
| 33 | 32 | [dependencies] |
README.md +18 −18
| @@ -1,4 +1,4 @@ | ||
| 1 | # org-ssg | |
| 1 | # orgo | |
| 2 | 2 | |
| 3 | 3 | An org-mode static site generator, in Rust. Org is treated as the *source language*, |
| 4 | 4 | not an inconvenient input to be normalized into markdown. The org element tree — |
| @@ -14,7 +14,7 @@ it imposes on the data model — pure, hashable, dependency-tracked units — is | ||
| 14 | 14 | deliverable, even while the corpus is small enough that a full rebuild is instant. |
| 15 | 15 | |
| 16 | 16 | **Full documentation is in [`docs/`](docs/)** — a site written in org and built by |
| 17 | org-ssg itself. Build and read it with: | |
| 17 | orgo itself. Build and read it with: | |
| 18 | 18 | |
| 19 | 19 | ```bash |
| 20 | 20 | cargo run -- serve docs -o docs/_site |
| @@ -33,23 +33,23 @@ Or skip the scaffolding entirely — point it at any directory of `.org` files: | ||
| 33 | 33 | cargo run -- build ~/notes -o _site |
| 34 | 34 | ``` |
| 35 | 35 | |
| 36 | **Zero configuration is a supported path, not a demo.** With no `org-ssg.toml`, no | |
| 37 | templates and no org-ssg-specific markup in your files, you get a complete site: pages, | |
| 36 | **Zero configuration is a supported path, not a demo.** With no `orgo.toml`, no | |
| 37 | templates and no orgo-specific markup in your files, you get a complete site: pages, | |
| 38 | 38 | navigation, syntax-highlighted code and the stylesheet to colour it. Configuration |
| 39 | 39 | changes what you get; it is never what makes it work. |
| 40 | 40 | |
| 41 | 41 | Discovery skips what should not be published — dot-directories such as `.git`, the config |
| 42 | 42 | file, the templates directory, and the output directory when it sits inside the source, so |
| 43 | `org-ssg build . -o _site` does the obvious thing. | |
| 43 | `orgo build . -o _site` does the obvious thing. | |
| 44 | 44 | |
| 45 | 45 | ## Configuration |
| 46 | 46 | |
| 47 | Everything is optional. `org-ssg init` writes a fully commented `org-ssg.toml`; every | |
| 47 | Everything is optional. `orgo init` writes a fully commented `orgo.toml`; every | |
| 48 | 48 | value below is the default. |
| 49 | 49 | |
| 50 | 50 | ```toml |
| 51 | 51 | [site] |
| 52 | title = "org-ssg site" | |
| 52 | title = "orgo site" | |
| 53 | 53 | base_url = "" # absolute URL, no trailing slash; needed for feeds/canonical links |
| 54 | 54 | description = "" |
| 55 | 55 | language = "en" |
| @@ -224,7 +224,7 @@ group. | ||
| 224 | 224 | |
| 225 | 225 | **A feed is a listing page with an XML template**, not a separate feature — templates are |
| 226 | 226 | loaded by full filename and any extension, so `output = "feed.xml"` with |
| 227 | `template = "feed.xml"` is all it takes. `org-ssg init` writes a working RSS template. | |
| 227 | `template = "feed.xml"` is all it takes. `orgo init` writes a working RSS template. | |
| 228 | 228 | |
| 229 | 229 | A feed is read away from the site that served it, so relative links in one are simply |
| 230 | 230 | broken. Set `site.base_url` and use the `absolute` filter: |
| @@ -319,7 +319,7 @@ is the only inherently global stage — it is where the link dependency graph is | ||
| 319 | 319 | |
| 320 | 320 | | Stage | Module | Notes | |
| 321 | 321 | |---|---|---| |
| 322 | | config | `src/config.rs` | `org-ssg.toml`: site metadata, nav mode, templates, theme. A hash input. | | |
| 322 | | config | `src/config.rs` | `orgo.toml`: site metadata, nav mode, templates, theme. A hash input. | | |
| 323 | 323 | | PARSE | `src/parser.rs` | Hand-written recursive descent: line lexer → element builder → inline tokenizer. | |
| 324 | 324 | | audit | `src/audit.rs` | Phase 0 corpus audit: construct frequencies against the IN/OUT line. | |
| 325 | 325 | | model | `src/model.rs` | The org element tree — Elements (block) vs Objects (inline). | |
| @@ -424,7 +424,7 @@ feature):** | ||
| 424 | 424 | The config component folds in a **site-structure hash** (every page's `(path, title)`), |
| 425 | 425 | because the shared nav bar is global chrome — a title change or a page add/remove alters |
| 426 | 426 | the nav on every page and so must re-render them all (otherwise byte-equivalence breaks). |
| 427 | - **Persisted cache manifest** (`<out>/.org-ssg-cache.json`, JSON), carrying per-page | |
| 427 | - **Persisted cache manifest** (`<out>/.orgo-cache.json`, JSON), carrying per-page | |
| 428 | 428 | records, the config/template hashes, and the serialized dependency graph, tagged with |
| 429 | 429 | `CACHE_FORMAT_VERSION`. A version mismatch, a missing file, or a corrupt file all fall |
| 430 | 430 | back to a clean full rebuild — the cache is an optimization, never a correctness |
| @@ -523,7 +523,7 @@ it over the original and touches the directory, which is one edit and several ev | ||
| 523 | 523 | Two rules decide what counts as a change, and they are not the same rules the build uses |
| 524 | 524 | to find content: |
| 525 | 525 | |
| 526 | - **A build input is a change.** Editing `org-ssg.toml` or a template rebuilds, even | |
| 526 | - **A build input is a change.** Editing `orgo.toml` or a template rebuilds, even | |
| 527 | 527 | though discovery skips both as non-content. The question is "would this change the |
| 528 | 528 | site?", not "is this a page?". |
| 529 | 529 | - **Our own output is not.** `watch . -o _site` puts the output inside the source, so a |
| @@ -544,7 +544,7 @@ Emacs does. | ||
| 544 | 544 | The audit runs against any corpus — point it at your own notes before trusting this tool |
| 545 | 545 | with them. The numbers below come from a 179-file site published today by weblorg, a |
| 546 | 546 | wrapper around org's own HTML exporter, which makes it both a realistic workload and a |
| 547 | directly comparable incumbent. With collections configured, org-ssg now reproduces | |
| 547 | directly comparable incumbent. With collections configured, orgo now reproduces | |
| 548 | 548 | **all 182 of that site's URLs**. |
| 549 | 549 | |
| 550 | 550 | ``` |
| @@ -560,7 +560,7 @@ whole out-of-scope tail is 8 uses: four `#+TBLFM:` in a post *about* org-mode, t | ||
| 560 | 560 | |
| 561 | 561 | **`#+SLUG:` was a hole big enough to sink the project.** 178 of 179 files set it, and the |
| 562 | 562 | published URL comes from it, not from the filename: `2018-11-28-aes-encryption.org` is |
| 563 | served at `blog/aes-encryption.html`. org-ssg derived output paths from source filenames, | |
| 563 | served at `blog/aes-encryption.html`. orgo derived output paths from source filenames, | |
| 564 | 564 | so **169 of 179 pages would have been published at the wrong URL** — every inbound link and |
| 565 | 565 | every search result, broken, by a tool that reported a clean build. Output paths now come |
| 566 | 566 | from `#+SLUG:` when present ([`util::output_path`](src/util.rs)); slugs are sanitized so an |
| @@ -588,10 +588,10 @@ report gets reviewed and shows up as a diff, where a permanently red test gets i | ||
| 588 | 588 | Three invariants are asserted outright, and all three hold — heading structure, list |
| 589 | 589 | nesting, and source-block text match Emacs exactly. |
| 590 | 590 | |
| 591 | **No bugs in org-ssg.** Every remaining divergence is a deliberate choice to emit better | |
| 591 | **No bugs in orgo.** Every remaining divergence is a deliberate choice to emit better | |
| 592 | 592 | HTML than org does: |
| 593 | 593 | |
| 594 | | | org-ssg | Emacs | why | | |
| 594 | | | orgo | Emacs | why | | |
| 595 | 595 | |---|---|---|---| |
| 596 | 596 | | emphasis | `<em>`/`<strong>` | `<i>`/`<b>` | semantic, not presentational | |
| 597 | 597 | | captioned image | `<figure>`/`<figcaption>` | `<p>` + `"Figure 1: …"` | real figure semantics | |
| @@ -695,7 +695,7 @@ a major version — is what you actually build a site against: | ||
| 695 | 695 | |
| 696 | 696 | | Stable | Detail | |
| 697 | 697 | |---|---| |
| 698 | | `org-ssg.toml` keys | Names, types and meaning. New keys are minor releases; removing one is major. | | |
| 698 | | `orgo.toml` keys | Names, types and meaning. New keys are minor releases; removing one is major. | | |
| 699 | 699 | | Template context | `page`, `site`, `nav`, `root`, `pages`, `group`, `groups`, `paginator`, `stylesheet`, and the `absolute` / `rfc822` / `truncate` filters. | |
| 700 | 700 | | CLI | Command names, flags, and exit codes. | |
| 701 | 701 | | URLs | How a source path becomes an output path, including `#+SLUG:`. A generator that moves your URLs breaks every link anyone has to you. | |
| @@ -704,14 +704,14 @@ Explicitly **not stable**, so that the above can be: | ||
| 704 | 704 | |
| 705 | 705 | - **The incremental cache.** Versioned, discarded on mismatch, never a correctness |
| 706 | 706 | dependency. It changes whenever it needs to, in any release. |
| 707 | - **Rendered HTML details.** org-ssg tracks what Emacs exports from the same file, and | |
| 707 | - **Rendered HTML details.** orgo tracks what Emacs exports from the same file, and | |
| 708 | 708 | closing a gap changes markup. Changes that affect output are called out in |
| 709 | 709 | [CHANGELOG.md](CHANGELOG.md) — the class names the documentation names (`post-list`, |
| 710 | 710 | `figure-number`, `section-number-N`, `footnote-ref`) are the ones to write CSS against. |
| 711 | 711 | - **The Rust API.** The crate is published so the binary can be installed with |
| 712 | 712 | `cargo install`; the library exists to serve it, and its types move as the tool does. |
| 713 | 713 | |
| 714 | The **MSRV is 1.88**, checked in CI on every change. org-ssg's own code compiles on | |
| 714 | The **MSRV is 1.88**, checked in CI on every change. orgo's own code compiles on | |
| 715 | 715 | 1.82; the floor comes from dependencies. Raising it is a minor version, never a patch. |
| 716 | 716 | |
| 717 | 717 | ## Dependencies |
RELEASING.md +4 −3
| @@ -7,14 +7,15 @@ reports `0.17.0` is the kind of mistake nobody notices for months. | ||
| 7 | 7 | |
| 8 | 8 | ## Before the first publish |
| 9 | 9 | |
| 10 | `repository` in `Cargo.toml` is commented out, because a wrong URL on a crates.io page is | |
| 11 | worse than none. Set it, then: | |
| 12 | ||
| 13 | 10 | ```bash |
| 14 | 11 | cargo login # a crates.io token, once per machine |
| 15 | 12 | cargo publish --dry-run |
| 16 | 13 | ``` |
| 17 | 14 | |
| 15 | `repository` and `homepage` in `Cargo.toml` point at GitHub and at the documentation site | |
| 16 | on Pages. If git.krz.sh becomes the primary remote, `repository` should follow it — | |
| 17 | crates.io shows that link on the crate page, and it should lead somewhere you read. | |
| 18 | ||
| 18 | 19 | ## Every release |
| 19 | 20 | |
| 20 | 21 | 1. **Write the changelog entry first.** [CHANGELOG.md](CHANGELOG.md) names behaviour, not |
docs/guide/01-cli.org +11 −11
| @@ -7,7 +7,7 @@ | ||
| 7 | 7 | =build=, =serve= and =watch= all take the same pair: |
| 8 | 8 | |
| 9 | 9 | #+BEGIN_SRC sh |
| 10 | org-ssg <command> <SOURCE> -o <OUTPUT> | |
| 10 | orgo <command> <SOURCE> -o <OUTPUT> | |
| 11 | 11 | #+END_SRC |
| 12 | 12 | |
| 13 | 13 | *SOURCE is the URL root*, not "the project". =SOURCE/blog/post.org= is published at |
| @@ -22,7 +22,7 @@ consumes its own output. | ||
| 22 | 22 | * build |
| 23 | 23 | |
| 24 | 24 | #+BEGIN_SRC sh |
| 25 | org-ssg build <INPUT> -o <OUTPUT> [--no-cache] [--strict] [--drafts] [--config FILE] | |
| 25 | orgo build <INPUT> -o <OUTPUT> [--no-cache] [--strict] [--drafts] [--config FILE] | |
| 26 | 26 | #+END_SRC |
| 27 | 27 | |
| 28 | 28 | If =INPUT= is a directory, it is walked and built into a linked site at =OUTPUT=. If it |
| @@ -35,7 +35,7 @@ though with no other documents to resolve against, internal links keep a best-ef | ||
| 35 | 35 | | =--no-cache= | Ignore the incremental cache and re-render every page. | |
| 36 | 36 | | =--strict= | Broken internal links and parse diagnostics become a non-zero exit. | |
| 37 | 37 | | =--drafts= | Include pages marked =#+DRAFT:=. | |
| 38 | | =--config FILE= | Use this config instead of =org-ssg.toml= in the source directory. | | |
| 38 | | =--config FILE= | Use this config instead of =orgo.toml= in the source directory. | | |
| 39 | 39 | |
| 40 | 40 | The summary line reports what happened: |
| 41 | 41 | |
| @@ -52,13 +52,13 @@ Without it, a broken link is a warning and the build succeeds. With it, the buil | ||
| 52 | 52 | and names every problem. Use it wherever a bad build should not ship: |
| 53 | 53 | |
| 54 | 54 | #+BEGIN_SRC sh |
| 55 | org-ssg build content -o _site --strict | |
| 55 | orgo build content -o _site --strict | |
| 56 | 56 | #+END_SRC |
| 57 | 57 | |
| 58 | 58 | * serve |
| 59 | 59 | |
| 60 | 60 | #+BEGIN_SRC sh |
| 61 | org-ssg serve <INPUT> -o <OUTPUT> [-p PORT] [--host HOST] [--drafts] [--config FILE] | |
| 61 | orgo serve <INPUT> -o <OUTPUT> [-p PORT] [--host HOST] [--drafts] [--config FILE] | |
| 62 | 62 | #+END_SRC |
| 63 | 63 | |
| 64 | 64 | Builds, watches, serves, and reloads the browser when a rebuild lands. This is the |
| @@ -74,7 +74,7 @@ command to use while writing. | ||
| 74 | 74 | laptop, so reaching the local network is something you ask for: |
| 75 | 75 | |
| 76 | 76 | #+BEGIN_SRC sh |
| 77 | org-ssg serve content -o _site --host 0.0.0.0 | |
| 77 | orgo serve content -o _site --host 0.0.0.0 | |
| 78 | 78 | #+END_SRC |
| 79 | 79 | |
| 80 | 80 | The live-reload script is injected into responses and never written to disk, so what you |
| @@ -83,7 +83,7 @@ deploy stays clean. Details in [[file:../guide/08-workflow.org][Watching and ser | ||
| 83 | 83 | * watch |
| 84 | 84 | |
| 85 | 85 | #+BEGIN_SRC sh |
| 86 | org-ssg watch <INPUT> -o <OUTPUT> [--no-cache] [--strict] [--drafts] [--config FILE] | |
| 86 | orgo watch <INPUT> -o <OUTPUT> [--no-cache] [--strict] [--drafts] [--config FILE] | |
| 87 | 87 | #+END_SRC |
| 88 | 88 | |
| 89 | 89 | Rebuilds on filesystem events with no server — for when something else is already serving |
| @@ -92,10 +92,10 @@ the output, or you just want the build to keep up as you write. | ||
| 92 | 92 | * audit |
| 93 | 93 | |
| 94 | 94 | #+BEGIN_SRC sh |
| 95 | org-ssg audit <INPUT> | |
| 95 | orgo audit <INPUT> | |
| 96 | 96 | #+END_SRC |
| 97 | 97 | |
| 98 | Reports which org constructs a corpus uses and how they land against what org-ssg | |
| 98 | Reports which org constructs a corpus uses and how they land against what orgo | |
| 99 | 99 | supports, plus a census of every keyword, block type, drawer and link scheme seen. Point |
| 100 | 100 | it at your notes before trusting a tool with them. See [[file:../guide/09-auditing.org][Auditing a corpus]]. |
| 101 | 101 | |
| @@ -105,7 +105,7 @@ private notes is safe to share. | ||
| 105 | 105 | * init |
| 106 | 106 | |
| 107 | 107 | #+BEGIN_SRC sh |
| 108 | org-ssg init [DIRECTORY] | |
| 108 | orgo init [DIRECTORY] | |
| 109 | 109 | #+END_SRC |
| 110 | 110 | |
| 111 | 111 | Scaffolds a working site: a fully commented config, an editable copy of the built-in |
| @@ -118,7 +118,7 @@ that already has content — it fills in what is missing and leaves the rest alo | ||
| 118 | 118 | * clean |
| 119 | 119 | |
| 120 | 120 | #+BEGIN_SRC sh |
| 121 | org-ssg clean <OUTPUT> | |
| 121 | orgo clean <OUTPUT> | |
| 122 | 122 | #+END_SRC |
| 123 | 123 | |
| 124 | 124 | Removes the output directory, including the incremental cache manifest inside it. You |
docs/guide/02-configuration.org +6 −6
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | #+TITLE: Configuration |
| 2 | #+DESCRIPTION: Every setting in org-ssg.toml, what it changes, and what it costs. | |
| 2 | #+DESCRIPTION: Every setting in orgo.toml, what it changes, and what it costs. | |
| 3 | 3 | #+LEDE: All of it optional. A missing config is a valid config. |
| 4 | 4 | |
| 5 | org-ssg looks for =org-ssg.toml= in the source directory. Pass a different path with | |
| 5 | orgo looks for =orgo.toml= in the source directory. Pass a different path with | |
| 6 | 6 | =--config=. Every field has a default, so a directory of org files with no config still |
| 7 | 7 | builds a complete site. |
| 8 | 8 | |
| @@ -14,7 +14,7 @@ lose an afternoon. | ||
| 14 | 14 | |
| 15 | 15 | #+BEGIN_SRC toml |
| 16 | 16 | [site] |
| 17 | title = "org-ssg site" | |
| 17 | title = "orgo site" | |
| 18 | 18 | base_url = "" |
| 19 | 19 | description = "" |
| 20 | 20 | language = "en" |
| @@ -47,7 +47,7 @@ Plus any number of =[[collections]]= blocks, documented in [[file:03-collections | ||
| 47 | 47 | |
| 48 | 48 | | Key | Default | Meaning | |
| 49 | 49 | |-----+---------+---------| |
| 50 | | =title= | ="org-ssg site"= | Site name. Available as ={{ site.title }}=. | | |
| 50 | | =title= | ="orgo site"= | Site name. Available as ={{ site.title }}=. | | |
| 51 | 51 | | =base_url= | ="" | Absolute origin, *no trailing slash*. | |
| 52 | 52 | | =description= | ="" | Available as ={{ site.description }}=. | |
| 53 | 53 | | =language= | ="en"= | Goes in =<html lang>= in the built-in layout. | |
| @@ -181,7 +181,7 @@ An unknown name is an error listing the valid ones. | ||
| 181 | 181 | Highlighting emits *CSS classes*, never inline styles, so themes live in a stylesheet. |
| 182 | 182 | Each build writes =syntax.css= into the output and every page links it. |
| 183 | 183 | |
| 184 | org-ssg bundles TOML and Org on top of syntect's built-in languages. Anything else | |
| 184 | orgo bundles TOML and Org on top of syntect's built-in languages. Anything else | |
| 185 | 185 | missing is a file away: put a =.sublime-syntax= definition in =syntaxes_dir= and it is |
| 186 | 186 | loaded. A definition that fails to parse is reported and skipped, because one bad file |
| 187 | 187 | should not stop a site from building. |
| @@ -258,6 +258,6 @@ Off is spelled =nil=, =false=, =no=, =0= or =off=; anything else is on. | ||
| 258 | 258 | |
| 259 | 259 | * Configuration is a cache input |
| 260 | 260 | |
| 261 | The resolved config is hashed into every page's render key, so editing =org-ssg.toml= | |
| 261 | The resolved config is hashed into every page's render key, so editing =orgo.toml= | |
| 262 | 262 | re-renders exactly the pages it affects — which for most settings is all of them. You |
| 263 | 263 | never need =--no-cache= after a config change. |
docs/guide/03-collections.org +1 −1
| @@ -230,7 +230,7 @@ title = "Feed" | ||
| 230 | 230 | #+END_SRC |
| 231 | 231 | |
| 232 | 232 | This needs =site.base_url=, because a feed with relative links is invalid everywhere it |
| 233 | is read. =org-ssg init= writes this template and leaves the collection commented out | |
| 233 | is read. =orgo init= writes this template and leaves the collection commented out | |
| 234 | 234 | until there is a base URL to make absolute links from. |
| 235 | 235 | |
| 236 | 236 | * Every setting |
docs/guide/04-templates.org +1 −1
| @@ -122,7 +122,7 @@ Empty on generated pages, which build their content from =pages= or =groups= ins | ||
| 122 | 122 | | =keywords= | *Every* =#+KEYWORD:=, by lowercased name. | |
| 123 | 123 | |
| 124 | 124 | =page.keywords= is the escape hatch: =#+CUSTOM_THING: x= is |
| 125 | ={{ page.keywords.custom_thing }}=, so your own metadata works without org-ssg knowing it | |
| 125 | ={{ page.keywords.custom_thing }}=, so your own metadata works without orgo knowing it | |
| 126 | 126 | exists. |
| 127 | 127 | |
| 128 | 128 | ** site |
docs/guide/05-org-support.org +8 −8
| @@ -2,7 +2,7 @@ | ||
| 2 | 2 | #+DESCRIPTION: Exactly which org syntax is handled, which is not, and how the rest degrades. |
| 3 | 3 | #+LEDE: A deliberate subset, with the boundary enforced by tests rather than by hope. |
| 4 | 4 | |
| 5 | org-ssg parses a defined slice of org. The boundary is not aspirational: every supported | |
| 5 | orgo parses a defined slice of org. The boundary is not aspirational: every supported | |
| 6 | 6 | construct has a golden-file test, and every excluded one has a test asserting how it |
| 7 | 7 | degrades. That is what stops the parser drifting toward all-of-org. |
| 8 | 8 | |
| @@ -37,7 +37,7 @@ machine-readable =datetime=. | ||
| 37 | 37 | |
| 38 | 38 | *** Text conversions |
| 39 | 39 | |
| 40 | Org rewrites some prose on export, and so does org-ssg: | |
| 40 | Org rewrites some prose on export, and so does orgo: | |
| 41 | 41 | |
| 42 | 42 | | Written | Published | |
| 43 | 43 | |---------+-----------| |
| @@ -94,7 +94,7 @@ An =html= export block passes through verbatim; every other backend is dropped, | ||
| 94 | 94 | emitting LaTeX into an HTML page is worse than emitting nothing. |
| 95 | 95 | |
| 96 | 96 | *Any other name is a special block*: =#+BEGIN_NOTE= becomes =<div class="note">= holding |
| 97 | *parsed org*, which is what makes the convention usable without org-ssg knowing the word | |
| 97 | *parsed org*, which is what makes the convention usable without orgo knowing the word | |
| 98 | 98 | "note". A =COMMENT= block is not published. |
| 99 | 99 | |
| 100 | 100 | *** Which languages highlight |
| @@ -108,7 +108,7 @@ Recognised, among others: =bash= / =sh=, =c=, =c++=, =css=, =clojure=, =diff=, = | ||
| 108 | 108 | =makefile=, =markdown=, =matlab=, =objective-c=, =ocaml=, =perl=, =php=, =python=, =r=, |
| 109 | 109 | =ruby=, =rust=, =scala=, =sql=, =tcl=, =xml=, =yaml=. |
| 110 | 110 | |
| 111 | org-ssg adds two syntect does not ship: *TOML* and *Org*. Both are what this project's | |
| 111 | orgo adds two syntect does not ship: *TOML* and *Org*. Both are what this project's | |
| 112 | 112 | own documentation needed on its first page — every config example is TOML, and a tool for |
| 113 | 113 | org users gets written about in org — so they are compiled into the binary and work with |
| 114 | 114 | no setup. |
| @@ -121,7 +121,7 @@ to parse is reported and skipped rather than failing the build. | ||
| 121 | 121 | *** The comma escape |
| 122 | 122 | |
| 123 | 123 | A line inside a block that would otherwise look like document structure is written with a |
| 124 | leading comma — =,* heading=, =,#+KEYWORD:= — and org-ssg removes exactly one comma on | |
| 124 | leading comma — =,* heading=, =,#+KEYWORD:= — and orgo removes exactly one comma on | |
| 125 | 125 | output, as Emacs does. Every org example in this documentation relies on it. |
| 126 | 126 | |
| 127 | 127 | The escape is not optional politeness: an unescaped =*= at column zero *ends the block*, |
| @@ -169,7 +169,7 @@ broken. | ||
| 169 | 169 | | =#+CAPTION:=, =#+ATTR_HTML:= | Attach to the image *directly* below them — a blank line in between attaches to nothing, as in org. A captioned image is numbered =Figure N:=. | |
| 170 | 170 | |
| 171 | 171 | Every other =#+KEYWORD:= is available to templates as |
| 172 | ={{ page.keywords.that_keyword }}=, so metadata org-ssg has never heard of still reaches | |
| 172 | ={{ page.keywords.that_keyword }}=, so metadata orgo has never heard of still reaches | |
| 173 | 173 | your layout. |
| 174 | 174 | |
| 175 | 175 | * Not supported, and what happens instead |
| @@ -219,7 +219,7 @@ source-block text are asserted to match exactly. | ||
| 219 | 219 | |
| 220 | 220 | The rest differs deliberately: |
| 221 | 221 | |
| 222 | | | org-ssg | Emacs | | |
| 222 | | | orgo | Emacs | | |
| 223 | 223 | |-+---------+-------| |
| 224 | 224 | | emphasis | =<em>= / =<strong>= | =<i>= / =<b>= | |
| 225 | 225 | | captioned image | =<figure>= / =<figcaption>= | =<p>= + "Figure 1: …" | |
| @@ -229,6 +229,6 @@ The rest differs deliberately: | ||
| 229 | 229 | | code | =<pre><code>= | =<pre>= | |
| 230 | 230 | |
| 231 | 231 | One genuine semantic difference: org treats a single blank line between a =1.= list and a |
| 232 | following =-= list as *one* list, keeping the first item's bullet type. org-ssg starts a | |
| 232 | following =-= list as *one* list, keeping the first item's bullet type. orgo starts a | |
| 233 | 233 | second list. That was kept on measurement — the pattern occurred zero times across a |
| 234 | 234 | 179-file reference corpus — rather than on taste. |
docs/guide/06-authoring.org +4 −4
| @@ -39,7 +39,7 @@ That is deliberate: it is what that link would be on the published site, and bet | ||
| 39 | 39 | now than by a reader. |
| 40 | 40 | |
| 41 | 41 | #+BEGIN_SRC sh |
| 42 | org-ssg serve content -o _site --drafts | |
| 42 | orgo serve content -o _site --drafts | |
| 43 | 43 | #+END_SRC |
| 44 | 44 | |
| 45 | 45 | The keyword is read forgivingly. =t=, =yes=, =1= and a bare =#+DRAFT:= all mean draft, |
| @@ -125,7 +125,7 @@ Every =#+KEYWORD:= reaches templates under its lowercased name: | ||
| 125 | 125 | {% if page.keywords.subtitle %}<p class="subtitle">{{ page.keywords.subtitle }}</p>{% endif %} |
| 126 | 126 | #+END_SRC |
| 127 | 127 | |
| 128 | Nothing needs to be registered, and org-ssg needs no release to support a keyword you | |
| 128 | Nothing needs to be registered, and orgo needs no release to support a keyword you | |
| 129 | 129 | invented. |
| 130 | 130 | |
| 131 | 131 | * Assets |
| @@ -138,9 +138,9 @@ Four things are *never* published: | ||
| 138 | 138 | |
| 139 | 139 | - Dot-entries such as =.git= and =.env=. A source directory is often a repository, and |
| 140 | 140 | publishing its history next to the homepage is a real way to leak a project. |
| 141 | - =org-ssg.toml=, which is a build input. | |
| 141 | - =orgo.toml=, which is a build input. | |
| 142 | 142 | - The templates directory, likewise. |
| 143 | - The output directory, when it lives inside the source — so =org-ssg build . -o _site= | |
| 143 | - The output directory, when it lives inside the source — so =orgo build . -o _site= | |
| 144 | 144 | does the obvious thing rather than copying its own output back into itself. |
| 145 | 145 | |
| 146 | 146 | Note that excluding dot-entries also means =.well-known/= cannot be published. |
docs/guide/07-incremental.org +7 −7
| @@ -2,7 +2,7 @@ | ||
| 2 | 2 | #+DESCRIPTION: How the cache decides what to re-render, and why that shape is the architecture. |
| 3 | 3 | #+LEDE: Editing one post rebuilds four pages, whatever the size of the site. |
| 4 | 4 | |
| 5 | Incremental rebuilding is not an optimisation bolted on to org-ssg; it is the constraint | |
| 5 | Incremental rebuilding is not an optimisation bolted on to orgo; it is the constraint | |
| 6 | 6 | the data model was built around. Parsing is a pure function of one file's bytes, link |
| 7 | 7 | resolution reports the edges it used, and rendering is a pure function of a resolved |
| 8 | 8 | document. Those properties are what make caching sound — and they are also what make the |
| @@ -14,13 +14,13 @@ invalidates what explains the behaviour you will see. | ||
| 14 | 14 | * What you observe |
| 15 | 15 | |
| 16 | 16 | #+BEGIN_EXAMPLE |
| 17 | $ org-ssg build content -o _site | |
| 17 | $ orgo build content -o _site | |
| 18 | 18 | built 182 page(s) (182 rendered, 0 cached) ... |
| 19 | 19 | |
| 20 | $ org-ssg build content -o _site | |
| 20 | $ orgo build content -o _site | |
| 21 | 21 | built 182 page(s) (0 rendered, 182 cached) ... |
| 22 | 22 | |
| 23 | $ vim content/blog/post.org && org-ssg build content -o _site | |
| 23 | $ vim content/blog/post.org && orgo build content -o _site | |
| 24 | 24 | built 182 page(s) (4 rendered, 178 cached) ... |
| 25 | 25 | #+END_EXAMPLE |
| 26 | 26 | |
| @@ -35,7 +35,7 @@ Every page has a key composed from four hashes: | ||
| 35 | 35 | |-----------+--------------| |
| 36 | 36 | | content | The source file's bytes change. | |
| 37 | 37 | | resolved links | A link's target moves, is renamed, or disappears. | |
| 38 | | config | =org-ssg.toml= changes, or the shared chrome does. | | |
| 38 | | config | =orgo.toml= changes, or the shared chrome does. | | |
| 39 | 39 | | templates | *This page's* layout changes, or something that layout extends or includes. | |
| 40 | 40 | |
| 41 | 41 | If a page's key matches the cached one and its output file still exists, the file on disk |
| @@ -54,7 +54,7 @@ A template whose include is computed at render time — ={% include chooser %}= | ||
| 54 | 54 | followed, so it is treated as depending on every template. Over-invalidating costs time; |
| 55 | 55 | under-invalidating publishes a stale page. |
| 56 | 56 | |
| 57 | The cache lives in =<output>/.org-ssg-cache.json= and is tagged with a format version. A | |
| 57 | The cache lives in =<output>/.orgo-cache.json= and is tagged with a format version. A | |
| 58 | 58 | version mismatch, a missing file or a corrupt file all fall back to a full rebuild — the |
| 59 | 59 | cache is an optimisation, never a correctness dependency. There is a test for each of |
| 60 | 60 | those three fallbacks. |
| @@ -124,5 +124,5 @@ correctly on their own. It exists to answer "is the cache lying to me?" — and | ||
| 124 | 124 | is, that is a bug worth reporting, with the two builds' output to compare. |
| 125 | 125 | |
| 126 | 126 | #+BEGIN_SRC sh |
| 127 | org-ssg build content -o _site --no-cache | |
| 127 | orgo build content -o _site --no-cache | |
| 128 | 128 | #+END_SRC |
docs/guide/08-workflow.org +6 −6
| @@ -5,7 +5,7 @@ | ||
| 5 | 5 | * serve |
| 6 | 6 | |
| 7 | 7 | #+BEGIN_SRC sh |
| 8 | org-ssg serve content -o _site | |
| 8 | orgo serve content -o _site | |
| 9 | 9 | #+END_SRC |
| 10 | 10 | |
| 11 | 11 | Builds, watches, serves at [[http://127.0.0.1:3000][127.0.0.1:3000]], and reloads the |
| @@ -43,7 +43,7 @@ the build just failed tells you nothing — the error is already on your termina | ||
| 43 | 43 | * watch |
| 44 | 44 | |
| 45 | 45 | #+BEGIN_SRC sh |
| 46 | org-ssg watch content -o _site | |
| 46 | orgo watch content -o _site | |
| 47 | 47 | #+END_SRC |
| 48 | 48 | |
| 49 | 49 | The same rebuilding without the server, for when something else already serves the output. |
| @@ -54,7 +54,7 @@ Rebuilds are driven by OS filesystem events, so nothing happens while nothing ha | ||
| 54 | 54 | The rule for what triggers one is deliberately *not* the rule the build uses to find |
| 55 | 55 | content — the question is "would this change the site?", not "is this a page?". |
| 56 | 56 | |
| 57 | *Triggers a rebuild:* any =.org= file, any asset, =org-ssg.toml=, and anything in the | |
| 57 | *Triggers a rebuild:* any =.org= file, any asset, =orgo.toml=, and anything in the | |
| 58 | 58 | templates directory. The last two are skipped by the build when looking for content, but |
| 59 | 59 | both change the output. |
| 60 | 60 | |
| @@ -86,7 +86,7 @@ blog/post.org changed: 2 rendered, 180 cached | ||
| 86 | 86 | |
| 87 | 87 | * Where native watching is unavailable |
| 88 | 88 | |
| 89 | Some network and container filesystems have no event API. org-ssg falls back to polling | |
| 89 | Some network and container filesystems have no event API. orgo falls back to polling | |
| 90 | 90 | every two seconds and says so, rather than failing: |
| 91 | 91 | |
| 92 | 92 | #+BEGIN_EXAMPLE |
| @@ -106,10 +106,10 @@ note: native file watching unavailable (...); polling every 2s | ||
| 106 | 106 | |
| 107 | 107 | #+BEGIN_SRC sh |
| 108 | 108 | # One terminal, left running. |
| 109 | org-ssg serve content -o _site --drafts | |
| 109 | orgo serve content -o _site --drafts | |
| 110 | 110 | |
| 111 | 111 | # Write. The browser keeps up. |
| 112 | 112 | |
| 113 | 113 | # Before publishing, check what a real build says. |
| 114 | org-ssg build content -o _site --strict | |
| 114 | orgo build content -o _site --strict | |
| 115 | 115 | #+END_SRC |
docs/guide/09-auditing.org +6 −6
| @@ -3,7 +3,7 @@ | ||
| 3 | 3 | #+LEDE: Construct frequencies, unknown-name census, and no document text in the output. |
| 4 | 4 | |
| 5 | 5 | #+BEGIN_SRC sh |
| 6 | org-ssg audit ~/notes | |
| 6 | orgo audit ~/notes | |
| 7 | 7 | #+END_SRC |
| 8 | 8 | |
| 9 | 9 | The audit answers two questions about a body of org files: |
| @@ -11,7 +11,7 @@ The audit answers two questions about a body of org files: | ||
| 11 | 11 | 1. *Coverage.* Of the constructs this corpus uses, which are supported? A construct that |
| 12 | 12 | is common here and unsupported is a problem with the tool's scope, not with your |
| 13 | 13 | writing. |
| 14 | 2. *Blind spots.* Which names appear that org-ssg has no opinion about at all? These are | |
| 14 | 2. *Blind spots.* Which names appear that orgo has no opinion about at all? These are | |
| 15 | 15 | the dangerous ones — not "known unsupported" but unknown. |
| 16 | 16 | |
| 17 | 17 | * Reading the output |
| @@ -40,7 +40,7 @@ KEYWORDS | ||
| 40 | 40 | - =IN= is supported; =OUT= is excluded by design and degrades as described in |
| 41 | 41 | [[file:05-org-support.org][Org support]]. |
| 42 | 42 | - The *coverage* line is the number to look at first. |
| 43 | - =???= marks a name org-ssg does not recognise at all — in this example =#+SLUG:=, from | |
| 43 | - =???= marks a name orgo does not recognise at all — in this example =#+SLUG:=, from | |
| 44 | 44 | a run made before it was supported. |
| 45 | 45 | |
| 46 | 46 | Four censuses follow the construct table: every distinct =#+KEYWORD:=, block type, |
| @@ -80,13 +80,13 @@ green run; it simply measures one thing less. | ||
| 80 | 80 | |
| 81 | 81 | #+BEGIN_SRC sh |
| 82 | 82 | # What is in there? |
| 83 | org-ssg audit ~/notes | |
| 83 | orgo audit ~/notes | |
| 84 | 84 | |
| 85 | 85 | # Build it and see what the builder itself complains about. |
| 86 | org-ssg build ~/notes -o /tmp/preview --strict | |
| 86 | orgo build ~/notes -o /tmp/preview --strict | |
| 87 | 87 | |
| 88 | 88 | # Look at the result. |
| 89 | org-ssg serve ~/notes -o /tmp/preview | |
| 89 | orgo serve ~/notes -o /tmp/preview | |
| 90 | 90 | #+END_SRC |
| 91 | 91 | |
| 92 | 92 | =--strict= surfaces broken internal links and malformed constructs as failures rather |
docs/guide/10-deploying.org +10 −10
| @@ -5,7 +5,7 @@ | ||
| 5 | 5 | * The production build |
| 6 | 6 | |
| 7 | 7 | #+BEGIN_SRC sh |
| 8 | org-ssg build content -o _site --strict | |
| 8 | orgo build content -o _site --strict | |
| 9 | 9 | #+END_SRC |
| 10 | 10 | |
| 11 | 11 | Two differences from the build you run while writing: |
| @@ -34,12 +34,12 @@ No trailing slash. | ||
| 34 | 34 | |
| 35 | 35 | * One thing to exclude |
| 36 | 36 | |
| 37 | The build writes =.org-ssg-cache.json= into the output directory. It is a dot-file, so | |
| 37 | The build writes =.orgo-cache.json= into the output directory. It is a dot-file, so | |
| 38 | 38 | most static hosts ignore it, but it is not part of the site — exclude it if your host |
| 39 | 39 | uploads everything: |
| 40 | 40 | |
| 41 | 41 | #+BEGIN_SRC sh |
| 42 | rsync -a --delete --exclude '.org-ssg-cache.json' _site/ user@host:/var/www/site/ | |
| 42 | rsync -a --delete --exclude '.orgo-cache.json' _site/ user@host:/var/www/site/ | |
| 43 | 43 | #+END_SRC |
| 44 | 44 | |
| 45 | 45 | Keeping the cache *between* deploys, where the CI runner can see it, is what makes CI |
| @@ -57,7 +57,7 @@ jobs: | ||
| 57 | 57 | - uses: actions/checkout@v4 |
| 58 | 58 | - uses: dtolnay/rust-toolchain@stable |
| 59 | 59 | - run: cargo install --path . |
| 60 | - run: org-ssg build content -o _site --strict | |
| 60 | - run: orgo build content -o _site --strict | |
| 61 | 61 | - uses: actions/upload-artifact@v4 |
| 62 | 62 | with: |
| 63 | 63 | name: site |
| @@ -69,7 +69,7 @@ failed build. | ||
| 69 | 69 | |
| 70 | 70 | ** Caching between runs |
| 71 | 71 | |
| 72 | Cache =_site/.org-ssg-cache.json= *and* =_site= together, or not at all. The manifest | |
| 72 | Cache =_site/.orgo-cache.json= *and* =_site= together, or not at all. The manifest | |
| 73 | 73 | describes files it expects to find; a cache without its outputs simply triggers a full |
| 74 | 74 | rebuild, which is correct but pointless. |
| 75 | 75 | |
| @@ -78,16 +78,16 @@ builds is rarely worth the configuration. | ||
| 78 | 78 | |
| 79 | 79 | * Static hosts |
| 80 | 80 | |
| 81 | Nothing here is org-ssg-specific; a built site is ordinary static files. | |
| 81 | Nothing here is orgo-specific; a built site is ordinary static files. | |
| 82 | 82 | |
| 83 | 83 | - *Netlify, Vercel, Cloudflare Pages*: publish directory =_site=, build command |
| 84 | =cargo install --path . && org-ssg build content -o _site --strict=. | |
| 84 | =cargo install --path . && orgo build content -o _site --strict=. | |
| 85 | 85 | - *GitHub Pages*: upload =_site= as the Pages artifact. |
| 86 | 86 | - *Any web server*: copy =_site= to the document root. |
| 87 | 87 | |
| 88 | 88 | ** URLs end in .html |
| 89 | 89 | |
| 90 | org-ssg writes =blog/post.html= and links to it that way, so the site works with no | |
| 90 | orgo writes =blog/post.html= and links to it that way, so the site works with no | |
| 91 | 91 | server configuration at all — including opening it from a filesystem path. |
| 92 | 92 | |
| 93 | 93 | If you prefer extensionless URLs, that is a server-side rewrite, and you should also set |
| @@ -96,8 +96,8 @@ If you prefer extensionless URLs, that is a server-side rewrite, and you should | ||
| 96 | 96 | * Checking a build before shipping |
| 97 | 97 | |
| 98 | 98 | #+BEGIN_SRC sh |
| 99 | org-ssg build content -o _site --strict | |
| 100 | org-ssg serve content -o _site | |
| 99 | orgo build content -o _site --strict | |
| 100 | orgo serve content -o _site | |
| 101 | 101 | #+END_SRC |
| 102 | 102 | |
| 103 | 103 | Serving the production build locally is the last check worth doing: it catches a missing |
docs/guide/11-versioning.org +6 −6
| @@ -11,7 +11,7 @@ Changing any of this incompatibly requires a major version. | ||
| 11 | 11 | |
| 12 | 12 | | Stable | What that covers | |
| 13 | 13 | |--------+------------------| |
| 14 | | =org-ssg.toml= keys | Their names, types and meaning. | | |
| 14 | | =orgo.toml= keys | Their names, types and meaning. | | |
| 15 | 15 | | Template context | =page=, =site=, =nav=, =root=, =pages=, =group=, =groups=, =paginator=, =stylesheet=, and the =absolute=, =rfc822= and =truncate= filters. | |
| 16 | 16 | | The CLI | Command names, flags and exit codes. | |
| 17 | 17 | | URLs | How a source path becomes an output path, =#+SLUG:= included. | |
| @@ -28,13 +28,13 @@ Three things move freely, so the list above can hold still. | ||
| 28 | 28 | |
| 29 | 29 | ** The incremental cache |
| 30 | 30 | |
| 31 | =<output>/.org-ssg-cache.json= is versioned and discards itself on a mismatch. A cache | |
| 31 | =<output>/.orgo-cache.json= is versioned and discards itself on a mismatch. A cache | |
| 32 | 32 | format bump means one full rebuild, and nothing else. It is never a correctness |
| 33 | 33 | dependency: a missing, stale or corrupt cache produces exactly the same site, more slowly. |
| 34 | 34 | |
| 35 | 35 | ** Rendered HTML details |
| 36 | 36 | |
| 37 | org-ssg aims at what Emacs exports from the same file, and closing a gap changes markup. | |
| 37 | orgo aims at what Emacs exports from the same file, and closing a gap changes markup. | |
| 38 | 38 | That is the product working rather than a regression — but it is called out in the |
| 39 | 39 | changelog every time, because your stylesheet is downstream of it. |
| 40 | 40 | |
| @@ -50,7 +50,7 @@ exists to serve the binary, and its types move as the tool does. | ||
| 50 | 50 | * The compiler floor |
| 51 | 51 | |
| 52 | 52 | The MSRV is *1.88*, checked in CI on every change rather than assumed — which is how it |
| 53 | came to be 1.88 rather than the 1.82 org-ssg's own code needs. The floor is set by | |
| 53 | came to be 1.88 rather than the 1.82 orgo's own code needs. The floor is set by | |
| 54 | 54 | dependencies, and a dependency raising its own is invisible until someone on an older |
| 55 | 55 | compiler tries to build. |
| 56 | 56 | |
| @@ -59,8 +59,8 @@ Raising it is a minor version, never a patch. | ||
| 59 | 59 | * Upgrading |
| 60 | 60 | |
| 61 | 61 | #+BEGIN_SRC sh |
| 62 | cargo install org-ssg # or download a release binary | |
| 63 | org-ssg build content -o _site --no-cache --strict | |
| 62 | cargo install orgo # or download a release binary | |
| 63 | orgo build content -o _site --no-cache --strict | |
| 64 | 64 | #+END_SRC |
| 65 | 65 | |
| 66 | 66 | =--no-cache= makes the first build after an upgrade a full one, so you are comparing the |
docs/index.org +6 −6
| @@ -1,9 +1,9 @@ | ||
| 1 | #+TITLE: org-ssg | |
| 1 | #+TITLE: orgo | |
| 2 | 2 | #+DESCRIPTION: An org-mode static site generator in Rust, where the org element tree is the document model. |
| 3 | 3 | #+LEDE: Org is the source language, not an inconvenient input to be normalised into markdown. |
| 4 | 4 | #+OPTIONS: toc:nil |
| 5 | 5 | |
| 6 | org-ssg turns a directory of =.org= files into a static website. It treats org as the | |
| 6 | orgo turns a directory of =.org= files into a static website. It treats org as the | |
| 7 | 7 | *source language*: the org element tree — headings, drawers, blocks, links with their |
| 8 | 8 | org-specific semantics — /is/ the document model, and that tree is rendered straight to |
| 9 | 9 | HTML. There is no markdown-shaped intermediate representation, because the point is to |
| @@ -26,10 +26,10 @@ Open [[http://127.0.0.1:3000][127.0.0.1:3000]], edit any =.org= file, and the br | ||
| 26 | 26 | |
| 27 | 27 | Point it at a directory of org files and you get a complete site: pages, navigation, |
| 28 | 28 | syntax-highlighted code, and the stylesheet that colours it. Nothing about your files has |
| 29 | to change, and no =org-ssg.toml= is required. | |
| 29 | to change, and no =orgo.toml= is required. | |
| 30 | 30 | |
| 31 | 31 | #+BEGIN_SRC sh |
| 32 | org-ssg build ~/notes -o _site | |
| 32 | orgo build ~/notes -o _site | |
| 33 | 33 | #+END_SRC |
| 34 | 34 | |
| 35 | 35 | Configuration changes what you get. It is never what makes it work. |
| @@ -54,7 +54,7 @@ listed in [[file:guide/05-org-support.org][Org support]]. | ||
| 54 | 54 | ** It tells you what your corpus actually uses |
| 55 | 55 | |
| 56 | 56 | #+BEGIN_SRC sh |
| 57 | org-ssg audit ~/notes | |
| 57 | orgo audit ~/notes | |
| 58 | 58 | #+END_SRC |
| 59 | 59 | |
| 60 | 60 | The audit reports which org constructs appear in a corpus, how often, and whether each is |
| @@ -75,6 +75,6 @@ notes stays safe to paste into an issue. | ||
| 75 | 75 | |
| 76 | 76 | * Status |
| 77 | 77 | |
| 78 | This documentation site is itself an org-ssg site — the sources are in =docs/= and it is | |
| 78 | This documentation site is itself an orgo site — the sources are in =docs/= and it is | |
| 79 | 79 | built with the command in [[file:quickstart.org][Quick start]]. If a feature is described here, it is being used |
| 80 | 80 | to render the page describing it. |
docs/install.org +13 −13
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | #+TITLE: Install |
| 2 | #+DESCRIPTION: Build org-ssg from source, put it on your PATH, and check that it works. | |
| 2 | #+DESCRIPTION: Build orgo from source, put it on your PATH, and check that it works. | |
| 3 | 3 | #+LEDE: One Rust toolchain, one command, no runtime dependencies. |
| 4 | 4 | |
| 5 | 5 | * Requirements |
| @@ -13,15 +13,15 @@ | ||
| 13 | 13 | * From source |
| 14 | 14 | |
| 15 | 15 | #+BEGIN_SRC sh |
| 16 | git clone <repository-url> org-ssg | |
| 17 | cd org-ssg | |
| 16 | git clone <repository-url> orgo | |
| 17 | cd orgo | |
| 18 | 18 | cargo build --release |
| 19 | 19 | #+END_SRC |
| 20 | 20 | |
| 21 | The binary lands at =target/release/org-ssg=. Copy it somewhere on your =PATH=: | |
| 21 | The binary lands at =target/release/orgo=. Copy it somewhere on your =PATH=: | |
| 22 | 22 | |
| 23 | 23 | #+BEGIN_SRC sh |
| 24 | cp target/release/org-ssg ~/.local/bin/ | |
| 24 | cp target/release/orgo ~/.local/bin/ | |
| 25 | 25 | #+END_SRC |
| 26 | 26 | |
| 27 | 27 | Or let cargo do it, which puts it in =~/.cargo/bin=: |
| @@ -33,7 +33,7 @@ cargo install --path . | ||
| 33 | 33 | * Running without installing |
| 34 | 34 | |
| 35 | 35 | Every command in this documentation works through cargo if you would rather not install |
| 36 | anything. Replace =org-ssg= with =cargo run --= and add =--release= for a fast build: | |
| 36 | anything. Replace =orgo= with =cargo run --= and add =--release= for a fast build: | |
| 37 | 37 | |
| 38 | 38 | #+BEGIN_SRC sh |
| 39 | 39 | cargo run --release -- build my-site -o _site |
| @@ -45,9 +45,9 @@ syntax highlighting dominates and is not optimised in a debug profile. | ||
| 45 | 45 | * Check that it works |
| 46 | 46 | |
| 47 | 47 | #+BEGIN_SRC sh |
| 48 | org-ssg --version | |
| 49 | org-ssg init /tmp/org-ssg-check | |
| 50 | org-ssg build /tmp/org-ssg-check -o /tmp/org-ssg-check/_site | |
| 48 | orgo --version | |
| 49 | orgo init /tmp/orgo-check | |
| 50 | orgo build /tmp/orgo-check -o /tmp/orgo-check/_site | |
| 51 | 51 | #+END_SRC |
| 52 | 52 | |
| 53 | 53 | You should see a line reporting the pages built: |
| @@ -56,10 +56,10 @@ You should see a line reporting the pages built: | ||
| 56 | 56 | built 5 page(s) (5 rendered, 0 cached), copied 0 asset(s) ... (0 unresolved link(s), 0 diagnostic(s)) |
| 57 | 57 | #+END_EXAMPLE |
| 58 | 58 | |
| 59 | Open =/tmp/org-ssg-check/_site/index.html= in a browser, or serve it properly: | |
| 59 | Open =/tmp/orgo-check/_site/index.html= in a browser, or serve it properly: | |
| 60 | 60 | |
| 61 | 61 | #+BEGIN_SRC sh |
| 62 | org-ssg serve /tmp/org-ssg-check -o /tmp/org-ssg-check/_site | |
| 62 | orgo serve /tmp/orgo-check -o /tmp/orgo-check/_site | |
| 63 | 63 | #+END_SRC |
| 64 | 64 | |
| 65 | 65 | * Running the test suite |
| @@ -82,13 +82,13 @@ still gets a green test run — it simply measures one thing less. | ||
| 82 | 82 | |
| 83 | 83 | * Upgrading |
| 84 | 84 | |
| 85 | org-ssg stores an incremental cache in =<output>/.org-ssg-cache.json=, tagged with a | |
| 85 | orgo stores an incremental cache in =<output>/.orgo-cache.json=, tagged with a | |
| 86 | 86 | format version. A newer binary that changes how output is produced bumps that version, |
| 87 | 87 | and a version it does not recognise is discarded in favour of a full rebuild. You never |
| 88 | 88 | need to clear the cache by hand after an upgrade — but if you want to: |
| 89 | 89 | |
| 90 | 90 | #+BEGIN_SRC sh |
| 91 | org-ssg clean _site | |
| 91 | orgo clean _site | |
| 92 | 92 | #+END_SRC |
| 93 | 93 | |
| 94 | 94 | * Next |
docs/org-ssg.toml → docs/orgo.toml renamed +3 −3
| @@ -1,10 +1,10 @@ | ||
| 1 | # Configuration for the org-ssg documentation site. | |
| 1 | # Configuration for the orgo documentation site. | |
| 2 | 2 | # |
| 3 | # This site is built by org-ssg itself, so this file doubles as a worked example: every | |
| 3 | # This site is built by orgo itself, so this file doubles as a worked example: every | |
| 4 | 4 | # setting here is one the docs describe, used the way the docs recommend. |
| 5 | 5 | |
| 6 | 6 | [site] |
| 7 | title = "org-ssg" | |
| 7 | title = "orgo" | |
| 8 | 8 | description = "An org-mode static site generator, in Rust." |
| 9 | 9 | language = "en" |
| 10 | 10 | # Left empty so the docs build with relative URLs and open from the filesystem. Set it to |
docs/quickstart.org +19 −19
| @@ -5,8 +5,8 @@ | ||
| 5 | 5 | * Two commands |
| 6 | 6 | |
| 7 | 7 | #+BEGIN_SRC sh |
| 8 | org-ssg init my-site | |
| 9 | org-ssg serve my-site -o _site | |
| 8 | orgo init my-site | |
| 9 | orgo serve my-site -o _site | |
| 10 | 10 | #+END_SRC |
| 11 | 11 | |
| 12 | 12 | Open [[http://127.0.0.1:3000][127.0.0.1:3000]]. Edit =my-site/index.org= in your editor, save, and the page reloads |
| @@ -19,7 +19,7 @@ already has content is safe and additive. | ||
| 19 | 19 | |
| 20 | 20 | #+BEGIN_EXAMPLE |
| 21 | 21 | my-site/ |
| 22 | org-ssg.toml every setting, at its default, commented | |
| 22 | orgo.toml every setting, at its default, commented | |
| 23 | 23 | index.org the home page |
| 24 | 24 | blog/first-post.org a post, to show the collection working |
| 25 | 25 | templates/ |
| @@ -35,7 +35,7 @@ You do not need =init=, a config file, or templates. Every command takes the sam | ||
| 35 | 35 | paths: |
| 36 | 36 | |
| 37 | 37 | #+BEGIN_SRC sh |
| 38 | org-ssg serve <SOURCE> -o <OUTPUT> | |
| 38 | orgo serve <SOURCE> -o <OUTPUT> | |
| 39 | 39 | #+END_SRC |
| 40 | 40 | |
| 41 | 41 | ** SOURCE is the URL root |
| @@ -49,14 +49,14 @@ repository around it: | ||
| 49 | 49 | |
| 50 | 50 | #+BEGIN_SRC sh |
| 51 | 51 | cd ~/my-site |
| 52 | org-ssg serve content -o _site # → /blog/post.html | |
| 52 | orgo serve content -o _site # → /blog/post.html | |
| 53 | 53 | #+END_SRC |
| 54 | 54 | |
| 55 | 55 | Pointing one level too high still builds, which is what makes it worth saying out loud. |
| 56 | 56 | It just builds the wrong site: |
| 57 | 57 | |
| 58 | 58 | #+BEGIN_SRC sh |
| 59 | org-ssg serve . -o _site # → /content/blog/post.html | |
| 59 | orgo serve . -o _site # → /content/blog/post.html | |
| 60 | 60 | #+END_SRC |
| 61 | 61 | |
| 62 | 62 | Every URL gains a =/content/= prefix, and every non-org file in the repository — |
| @@ -67,24 +67,24 @@ If you see either symptom, you picked the directory above the one you meant. | ||
| 67 | 67 | |
| 68 | 68 | | Your files | Command | |
| 69 | 69 | |------------+---------| |
| 70 | | =~/notes/*.org= | =org-ssg serve ~/notes -o /tmp/notes-site= | | |
| 71 | | =my-site/content/**/*.org= | =cd my-site && org-ssg serve content -o _site= | | |
| 72 | | =my-site/*.org= at the top level | =cd my-site && org-ssg serve . -o _site= | | |
| 70 | | =~/notes/*.org= | =orgo serve ~/notes -o /tmp/notes-site= | | |
| 71 | | =my-site/content/**/*.org= | =cd my-site && orgo serve content -o _site= | | |
| 72 | | =my-site/*.org= at the top level | =cd my-site && orgo serve . -o _site= | | |
| 73 | 73 | | Org files scattered in a code repo | Do not. Copy or symlink the ones you publish into one directory. | |
| 74 | 74 | |
| 75 | 75 | ** OUTPUT can live inside the source |
| 76 | 76 | |
| 77 | =org-ssg serve . -o _site= is fine: the output directory is recognised and skipped, so | |
| 77 | =orgo serve . -o _site= is fine: the output directory is recognised and skipped, so | |
| 78 | 78 | the build never copies its own output back into itself. Nothing dot-prefixed is published |
| 79 | 79 | either, so =.git= stays out of a site built from a repository root. |
| 80 | 80 | |
| 81 | 81 | ** Where config and templates go |
| 82 | 82 | |
| 83 | Both live in the *source* directory — =SOURCE/org-ssg.toml= and =SOURCE/templates/= — and | |
| 83 | Both live in the *source* directory — =SOURCE/orgo.toml= and =SOURCE/templates/= — and | |
| 84 | 84 | neither is published. If you would rather keep the config elsewhere, name it: |
| 85 | 85 | |
| 86 | 86 | #+BEGIN_SRC sh |
| 87 | org-ssg serve content -o _site --config config/org-ssg.toml | |
| 87 | orgo serve content -o _site --config config/orgo.toml | |
| 88 | 88 | #+END_SRC |
| 89 | 89 | |
| 90 | 90 | ** A worked example |
| @@ -95,13 +95,13 @@ A repository laid out as =content/= (org files), =theme/= (unrelated), =build.py | ||
| 95 | 95 | cd ~/my-site |
| 96 | 96 | |
| 97 | 97 | # What is actually in there, before trusting anything with it. |
| 98 | org-ssg audit content | |
| 98 | orgo audit content | |
| 99 | 99 | |
| 100 | 100 | # Build it somewhere disposable and look. |
| 101 | org-ssg serve content -o /tmp/preview | |
| 101 | orgo serve content -o /tmp/preview | |
| 102 | 102 | |
| 103 | 103 | # Happy with it? Build for real, failing on broken links. |
| 104 | org-ssg build content -o _site --strict | |
| 104 | orgo build content -o _site --strict | |
| 105 | 105 | #+END_SRC |
| 106 | 106 | |
| 107 | 107 | The audit reports which org constructs appear, how often, and whether each is supported — |
| @@ -159,7 +159,7 @@ The page is not written, and does not appear in listings or navigation. Preview | ||
| 159 | 159 | you work with =--drafts=: |
| 160 | 160 | |
| 161 | 161 | #+BEGIN_SRC sh |
| 162 | org-ssg serve my-site -o _site --drafts | |
| 162 | orgo serve my-site -o _site --drafts | |
| 163 | 163 | #+END_SRC |
| 164 | 164 | |
| 165 | 165 | * Change the design |
| @@ -193,7 +193,7 @@ still looking at it. The full list of variables is in [[file:guide/04-templates. | ||
| 193 | 193 | |
| 194 | 194 | * Add a blog index |
| 195 | 195 | |
| 196 | Listing pages have no source file; they are declared in =org-ssg.toml=: | |
| 196 | Listing pages have no source file; they are declared in =orgo.toml=: | |
| 197 | 197 | |
| 198 | 198 | #+BEGIN_SRC toml |
| 199 | 199 | [[collections]] |
| @@ -212,7 +212,7 @@ settings. See [[file:guide/03-collections.org][Collections]]. | ||
| 212 | 212 | * Build for real |
| 213 | 213 | |
| 214 | 214 | #+BEGIN_SRC sh |
| 215 | org-ssg build my-site -o _site --strict | |
| 215 | orgo build my-site -o _site --strict | |
| 216 | 216 | #+END_SRC |
| 217 | 217 | |
| 218 | 218 | =--strict= turns broken internal links and parse diagnostics into a non-zero exit, which |
| @@ -222,5 +222,5 @@ is what you want in CI. Deployment is just copying =_site= somewhere; see | ||
| 222 | 222 | * Next |
| 223 | 223 | |
| 224 | 224 | - [[file:guide/01-cli.org][Command reference]] — every command and flag. |
| 225 | - [[file:guide/02-configuration.org][Configuration]] — every setting in =org-ssg.toml=. | |
| 225 | - [[file:guide/02-configuration.org][Configuration]] — every setting in =orgo.toml=. | |
| 226 | 226 | - [[file:guide/05-org-support.org][Org support]] — exactly which org syntax is handled. |
docs/style.css +1 −1
| @@ -74,7 +74,7 @@ code { | ||
| 74 | 74 | } |
| 75 | 75 | |
| 76 | 76 | /* Code blocks are dark in both colour schemes, matching the syntect theme named in |
| 77 | org-ssg.toml. One generated stylesheet cannot follow prefers-color-scheme, so the page | |
| 77 | orgo.toml. One generated stylesheet cannot follow prefers-color-scheme, so the page | |
| 78 | 78 | commits to the theme's palette instead of leaving highlighted code unreadable in one |
| 79 | 79 | of the two. */ |
| 80 | 80 | pre { |
docs/templates/base.html +1 −1
| @@ -45,7 +45,7 @@ | ||
| 45 | 45 | {%- endif %} |
| 46 | 46 | {% block content %}{{ body | safe }}{% endblock %}</main> |
| 47 | 47 | <footer class="site"> |
| 48 | Built with org-ssg — these docs are an org-ssg site. | |
| 48 | Built with orgo — these docs are an orgo site. | |
| 49 | 49 | </footer> |
| 50 | 50 | </body> |
| 51 | 51 | </html> |
fixtures/site/about.org +1 −1
| @@ -1,3 +1,3 @@ | ||
| 1 | 1 | #+TITLE: About |
| 2 | 2 | |
| 3 | This site is built by org-ssg. Back to [[file:index.org][home]]. | |
| 3 | This site is built by orgo. Back to [[file:index.org][home]]. | |
src/config.rs +10 −10
| @@ -1,4 +1,4 @@ | ||
| 1 | //! User-facing build configuration (`org-ssg.toml`). | |
| 1 | //! User-facing build configuration (`orgo.toml`). | |
| 2 | 2 | //! |
| 3 | 3 | //! Everything here was once a constant in the source: the page layout, the nav rule, the |
| 4 | 4 | //! highlighting theme. That made the generator produce exactly one kind of site — a |
| @@ -7,10 +7,10 @@ | ||
| 7 | 7 | //! Two properties matter beyond the settings themselves: |
| 8 | 8 | //! |
| 9 | 9 | //! 1. **Absent config is a valid config.** Every field has a default, so a directory of |
| 10 | //! `.org` files with no `org-ssg.toml` still builds. Configuration is how you change | |
| 10 | //! `.org` files with no `orgo.toml` still builds. Configuration is how you change | |
| 11 | 11 | //! the output, never how you make it work at all. |
| 12 | 12 | //! 2. **Config is a hash input** (spec §4.1). [`Config`] serializes deterministically and |
| 13 | //! its hash is folded into every page's render key, so editing `org-ssg.toml` re-renders | |
| 13 | //! its hash is folded into every page's render key, so editing `orgo.toml` re-renders | |
| 14 | 14 | //! exactly the pages it affects — which for most settings is all of them. |
| 15 | 15 | |
| 16 | 16 | use anyhow::{Context, Result}; |
| @@ -18,7 +18,7 @@ use camino::{Utf8Path, Utf8PathBuf}; | ||
| 18 | 18 | use serde::{Deserialize, Serialize}; |
| 19 | 19 | |
| 20 | 20 | /// The config file's name, looked for in the source directory. |
| 21 | pub const CONFIG_FILE: &str = "org-ssg.toml"; | |
| 21 | pub const CONFIG_FILE: &str = "orgo.toml"; | |
| 22 | 22 | |
| 23 | 23 | /// Resolved build configuration. Serialized into the config hash, so field order and |
| 24 | 24 | /// defaults are part of the cache contract. |
| @@ -319,7 +319,7 @@ pub struct Site { | ||
| 319 | 319 | impl Default for Site { |
| 320 | 320 | fn default() -> Self { |
| 321 | 321 | Site { |
| 322 | title: "org-ssg site".to_string(), | |
| 322 | title: "orgo site".to_string(), | |
| 323 | 323 | base_url: String::new(), |
| 324 | 324 | description: String::new(), |
| 325 | 325 | language: "en".to_string(), |
| @@ -405,7 +405,7 @@ impl Default for Highlight { | ||
| 405 | 405 | } |
| 406 | 406 | |
| 407 | 407 | impl Config { |
| 408 | /// Load `org-ssg.toml` from `dir`, or return defaults if there is none. | |
| 408 | /// Load `orgo.toml` from `dir`, or return defaults if there is none. | |
| 409 | 409 | /// |
| 410 | 410 | /// A *missing* config is normal and silent. A *malformed* one is an error: someone |
| 411 | 411 | /// who wrote a config meant it, and silently building the default site would hide |
| @@ -533,13 +533,13 @@ impl Config { | ||
| 533 | 533 | } |
| 534 | 534 | } |
| 535 | 535 | |
| 536 | /// The starter config written by `org-ssg init`, and the documentation of record for | |
| 536 | /// The starter config written by `orgo init`, and the documentation of record for | |
| 537 | 537 | /// what is configurable. Every value shown is the default, so deleting any line is safe. |
| 538 | pub const STARTER_CONFIG: &str = r#"# org-ssg configuration. Every setting here is optional and shown at its default, | |
| 538 | pub const STARTER_CONFIG: &str = r#"# orgo configuration. Every setting here is optional and shown at its default, | |
| 539 | 539 | # so you can delete any line you do not need — or the whole file. |
| 540 | 540 | |
| 541 | 541 | [site] |
| 542 | title = "org-ssg site" | |
| 542 | title = "orgo site" | |
| 543 | 543 | # Absolute base URL, no trailing slash. Needed for feeds and canonical links, which |
| 544 | 544 | # cannot be relative — set it and uncomment the [[collections]] feed block below. |
| 545 | 545 | base_url = "" |
| @@ -574,7 +574,7 @@ expose_page_list = false | ||
| 574 | 574 | # A syntect theme name: InspiredGitHub, Solarized (dark), base16-ocean.dark, |
| 575 | 575 | # base16-eighties.dark, base16-mocha.dark, base16-ocean.light. |
| 576 | 576 | theme = "InspiredGitHub" |
| 577 | # Extra .sublime-syntax files for languages neither syntect nor org-ssg bundles. | |
| 577 | # Extra .sublime-syntax files for languages neither syntect nor orgo bundles. | |
| 578 | 578 | syntaxes_dir = "syntaxes" |
| 579 | 579 | |
| 580 | 580 | [build] |
src/incremental.rs +2 −2
| @@ -35,7 +35,7 @@ pub const CACHE_FORMAT_VERSION: u32 = 7; | ||
| 35 | 35 | pub type Hash = ContentHash; |
| 36 | 36 | |
| 37 | 37 | /// The resolved global build config is [`crate::config::Config`]; its hash is a |
| 38 | /// component of every page's render key (spec §4.1), so editing `org-ssg.toml` | |
| 38 | /// component of every page's render key (spec §4.1), so editing `orgo.toml` | |
| 39 | 39 | /// invalidates the pages it affects. |
| 40 | 40 | pub use crate::config::Config as BuildConfig; |
| 41 | 41 | |
| @@ -199,7 +199,7 @@ pub struct Manifest { | ||
| 199 | 199 | /// The cache-manifest file lives inside the output directory (spec §4.5: an on-disk |
| 200 | 200 | /// cache dir). `clean` removes the output directory, taking the cache with it. |
| 201 | 201 | pub fn manifest_path(out: &Utf8Path) -> Utf8PathBuf { |
| 202 | out.join(".org-ssg-cache.json") | |
| 202 | out.join(".orgo-cache.json") | |
| 203 | 203 | } |
| 204 | 204 | |
| 205 | 205 | /// Load the manifest, returning `None` on ANY of: missing file, read/parse error, or a |
src/lib.rs +1 −1
| @@ -1,4 +1,4 @@ | ||
| 1 | //! org-ssg — an org-mode static site generator. | |
| 1 | //! orgo — an org-mode static site generator. | |
| 2 | 2 | //! |
| 3 | 3 | //! Org is the source language, not an input to be normalized into markdown. The org |
| 4 | 4 | //! element tree ([`model`]) *is* the document model; we render it straight to HTML. |
src/main.rs +19 −19
| @@ -6,15 +6,15 @@ use anyhow::{Context, Result}; | ||
| 6 | 6 | use camino::{Utf8Path, Utf8PathBuf}; |
| 7 | 7 | use clap::{Parser, Subcommand}; |
| 8 | 8 | |
| 9 | use org_ssg::parser::parse; | |
| 10 | use org_ssg::config::{self, Config}; | |
| 11 | use org_ssg::render::{self, render, Html, SyntectHighlighter}; | |
| 12 | use org_ssg::resolve::ResolvedDoc; | |
| 13 | use org_ssg::site::{build_site, BuildOptions, SYNTAX_STYLESHEET}; | |
| 14 | use org_ssg::template::{PageContext, RenderContext, SiteContext, Templater}; | |
| 9 | use orgo::parser::parse; | |
| 10 | use orgo::config::{self, Config}; | |
| 11 | use orgo::render::{self, render, Html, SyntectHighlighter}; | |
| 12 | use orgo::resolve::ResolvedDoc; | |
| 13 | use orgo::site::{build_site, BuildOptions, SYNTAX_STYLESHEET}; | |
| 14 | use orgo::template::{PageContext, RenderContext, SiteContext, Templater}; | |
| 15 | 15 | |
| 16 | 16 | #[derive(Parser)] |
| 17 | #[command(name = "org-ssg", version, about = "Org-mode static site generator")] | |
| 17 | #[command(name = "orgo", version, about = "Org-mode static site generator")] | |
| 18 | 18 | struct Cli { |
| 19 | 19 | #[command(subcommand)] |
| 20 | 20 | command: Command, |
| @@ -36,7 +36,7 @@ enum Command { | ||
| 36 | 36 | /// Treat broken links and parse diagnostics as errors (spec §4.3.4). |
| 37 | 37 | #[arg(long)] |
| 38 | 38 | strict: bool, |
| 39 | /// Config file to use, overriding `org-ssg.toml` in the source directory. | |
| 39 | /// Config file to use, overriding `orgo.toml` in the source directory. | |
| 40 | 40 | #[arg(long, value_name = "FILE")] |
| 41 | 41 | config: Option<Utf8PathBuf>, |
| 42 | 42 | /// Include pages marked `#+DRAFT:`. |
| @@ -57,7 +57,7 @@ enum Command { | ||
| 57 | 57 | /// Treat broken links and parse diagnostics as errors. |
| 58 | 58 | #[arg(long)] |
| 59 | 59 | strict: bool, |
| 60 | /// Config file to use, overriding `org-ssg.toml` in the source directory. | |
| 60 | /// Config file to use, overriding `orgo.toml` in the source directory. | |
| 61 | 61 | #[arg(long, value_name = "FILE")] |
| 62 | 62 | config: Option<Utf8PathBuf>, |
| 63 | 63 | /// Include pages marked `#+DRAFT:`. Handy while writing one. |
| @@ -81,7 +81,7 @@ enum Command { | ||
| 81 | 81 | /// Include pages marked `#+DRAFT:`. |
| 82 | 82 | #[arg(long)] |
| 83 | 83 | drafts: bool, |
| 84 | /// Config file to use, overriding `org-ssg.toml` in the source directory. | |
| 84 | /// Config file to use, overriding `orgo.toml` in the source directory. | |
| 85 | 85 | #[arg(long, value_name = "FILE")] |
| 86 | 86 | config: Option<Utf8PathBuf>, |
| 87 | 87 | }, |
| @@ -151,7 +151,7 @@ fn main() -> Result<()> { | ||
| 151 | 151 | strict, |
| 152 | 152 | config, |
| 153 | 153 | drafts, |
| 154 | } => org_ssg::watch::run( | |
| 154 | } => orgo::watch::run( | |
| 155 | 155 | &input, |
| 156 | 156 | &output, |
| 157 | 157 | &BuildOptions { |
| @@ -162,8 +162,8 @@ fn main() -> Result<()> { | ||
| 162 | 162 | }, |
| 163 | 163 | ), |
| 164 | 164 | Command::Audit { input } => { |
| 165 | let result = org_ssg::audit::audit(&input)?; | |
| 166 | print!("{}", org_ssg::audit::report(&result)); | |
| 165 | let result = orgo::audit::audit(&input)?; | |
| 166 | print!("{}", orgo::audit::report(&result)); | |
| 167 | 167 | Ok(()) |
| 168 | 168 | } |
| 169 | 169 | Command::Serve { |
| @@ -173,7 +173,7 @@ fn main() -> Result<()> { | ||
| 173 | 173 | host, |
| 174 | 174 | drafts, |
| 175 | 175 | config, |
| 176 | } => org_ssg::serve::run( | |
| 176 | } => orgo::serve::run( | |
| 177 | 177 | &input, |
| 178 | 178 | &output, |
| 179 | 179 | &BuildOptions { |
| @@ -201,8 +201,8 @@ fn main() -> Result<()> { | ||
| 201 | 201 | /// Scaffold a working site. Writes only files that do not already exist, so running it |
| 202 | 202 | /// in a directory that has content is safe and additive rather than destructive. |
| 203 | 203 | fn init(dir: &Utf8Path) -> Result<()> { |
| 204 | use org_ssg::config::{CONFIG_FILE, STARTER_CONFIG}; | |
| 205 | use org_ssg::template::{ | |
| 204 | use orgo::config::{CONFIG_FILE, STARTER_CONFIG}; | |
| 205 | use orgo::template::{ | |
| 206 | 206 | starter_template, STARTER_FEED_TEMPLATE, STARTER_LIST_TEMPLATE, STARTER_TAGS_TEMPLATE, |
| 207 | 207 | }; |
| 208 | 208 | |
| @@ -234,7 +234,7 @@ fn init(dir: &Utf8Path) -> Result<()> { | ||
| 234 | 234 | "#+FILETAGS: :example:\n", |
| 235 | 235 | "\n", |
| 236 | 236 | "Posts in this directory are collected into /blog/ by the [[collections]] block\n", |
| 237 | "in org-ssg.toml, newest first.\n", | |
| 237 | "in orgo.toml, newest first.\n", | |
| 238 | 238 | ); |
| 239 | 239 | |
| 240 | 240 | let files: [(Utf8PathBuf, &str); 7] = [ |
| @@ -260,7 +260,7 @@ fn init(dir: &Utf8Path) -> Result<()> { | ||
| 260 | 260 | for path in &created { |
| 261 | 261 | println!("created {path}"); |
| 262 | 262 | } |
| 263 | println!("\nNext: org-ssg build {dir} -o _site"); | |
| 263 | println!("\nNext: orgo build {dir} -o _site"); | |
| 264 | 264 | Ok(()) |
| 265 | 265 | } |
| 266 | 266 | |
| @@ -321,7 +321,7 @@ fn build_file(input: &Utf8Path, output: &Utf8Path) -> Result<()> { | ||
| 321 | 321 | word_count: 0, |
| 322 | 322 | reading_time: 0, |
| 323 | 323 | keywords: Default::default(), |
| 324 | toc: org_ssg::util::table_of_contents(&resolved.document.root), | |
| 324 | toc: orgo::util::table_of_contents(&resolved.document.root), | |
| 325 | 325 | }; |
| 326 | 326 | let mut ctx = RenderContext::new(&site, &page_ctx, &[], SYNTAX_STYLESHEET, ""); |
| 327 | 327 | ctx.body = &fragment; |
src/render.rs +2 −2
| @@ -45,7 +45,7 @@ const CLASS_STYLE: ClassStyle = ClassStyle::Spaced; | ||
| 45 | 45 | |
| 46 | 46 | /// Syntax definitions syntect does not bundle, compiled into the binary. |
| 47 | 47 | /// |
| 48 | /// Both are gaps this project hits on its own first page: every `org-ssg.toml` example is | |
| 48 | /// Both are gaps this project hits on its own first page: every `orgo.toml` example is | |
| 49 | 49 | /// TOML, and a tool for org users is going to be written about in org. Embedding them |
| 50 | 50 | /// rather than shipping files means they work with no setup, which is the same promise |
| 51 | 51 | /// the rest of the zero-config path makes. |
| @@ -928,7 +928,7 @@ fn strip_special_column(table: &crate::model::Table) -> crate::model::Table { | ||
| 928 | 928 | |
| 929 | 929 | /// Split text into alternating prose and LaTeX spans, `(text, is_latex)`. |
| 930 | 930 | /// |
| 931 | /// org-ssg does not typeset LaTeX — it passes it through for MathJax or a reader's eyes — | |
| 931 | /// orgo does not typeset LaTeX — it passes it through for MathJax or a reader's eyes — | |
| 932 | 932 | /// but it must know where a fragment *is*, because the export-time text conversions would |
| 933 | 933 | /// otherwise rewrite the mathematics: `x^2` inside `$…$` is not a superscript to be |
| 934 | 934 | /// marked up, and `--` inside one is a minus sign twice. |
src/serve.rs +1 −1
| @@ -30,7 +30,7 @@ use crate::site::{build_site, BuildOptions}; | ||
| 30 | 30 | |
| 31 | 31 | /// Where the browser subscribes for reload events. Namespaced so it cannot collide with |
| 32 | 32 | /// a real page. |
| 33 | pub const RELOAD_PATH: &str = "/__org-ssg/reload"; | |
| 33 | pub const RELOAD_PATH: &str = "/__orgo/reload"; | |
| 34 | 34 | |
| 35 | 35 | /// How long a poll waits before answering "nothing yet". Long enough that an idle tab is |
| 36 | 36 | /// nearly silent, short enough to stay under any proxy or browser idle timeout. |
src/site.rs +2 −2
| @@ -62,7 +62,7 @@ pub struct BuildOptions { | ||
| 62 | 62 | pub no_cache: bool, |
| 63 | 63 | /// Treat broken internal links as a build error rather than a warning (spec §4.3.4). |
| 64 | 64 | pub strict: bool, |
| 65 | /// Explicit config file, overriding `org-ssg.toml` in the source directory. | |
| 65 | /// Explicit config file, overriding `orgo.toml` in the source directory. | |
| 66 | 66 | pub config_path: Option<Utf8PathBuf>, |
| 67 | 67 | /// Include pages marked `#+DRAFT:`, overriding `build.drafts` when set. |
| 68 | 68 | pub drafts: bool, |
| @@ -1368,7 +1368,7 @@ fn collect_assets( | ||
| 1368 | 1368 | /// directory (build input, not content) and the output directory when it lives inside |
| 1369 | 1369 | /// the source. |
| 1370 | 1370 | /// |
| 1371 | /// The output case is not a corner case — `org-ssg build . -o _site` is the obvious | |
| 1371 | /// The output case is not a corner case — `orgo build . -o _site` is the obvious | |
| 1372 | 1372 | /// thing to type, and without this the build copies its own output back into itself, |
| 1373 | 1373 | /// growing `_site/_site/_site/…` on every run. |
| 1374 | 1374 | fn excluded_dirs(src: &Utf8Path, config: &Config, out: Option<&Utf8Path>) -> Vec<Utf8PathBuf> { |
src/template.rs +5 −5
| @@ -375,7 +375,7 @@ impl<'a> RenderContext<'a> { | ||
| 375 | 375 | } |
| 376 | 376 | } |
| 377 | 377 | |
| 378 | /// The starter tag-index template written by `org-ssg init`: shows how `groups` is | |
| 378 | /// The starter tag-index template written by `orgo init`: shows how `groups` is | |
| 379 | 379 | /// iterated, and how a group page is linked. |
| 380 | 380 | pub const STARTER_TAGS_TEMPLATE: &str = r#"<!DOCTYPE html> |
| 381 | 381 | <html lang="{{ site.language }}"> |
| @@ -410,7 +410,7 @@ pub const STARTER_TAGS_TEMPLATE: &str = r#"<!DOCTYPE html> | ||
| 410 | 410 | </html> |
| 411 | 411 | "#; |
| 412 | 412 | |
| 413 | /// The starter listing template written by `org-ssg init`: a blog index, showing how a | |
| 413 | /// The starter listing template written by `orgo init`: a blog index, showing how a | |
| 414 | 414 | /// collection's `pages` are iterated. |
| 415 | 415 | pub const STARTER_LIST_TEMPLATE: &str = r#"<!DOCTYPE html> |
| 416 | 416 | <html lang="{{ site.language }}"> |
| @@ -486,7 +486,7 @@ fn add_filters(env: &mut Environment<'static>, base_url: &str) { | ||
| 486 | 486 | return Err(minijinja::Error::new( |
| 487 | 487 | minijinja::ErrorKind::InvalidOperation, |
| 488 | 488 | "the `absolute` filter needs site.base_url, which is empty; \ |
| 489 | set it in org-ssg.toml (e.g. base_url = \"https://example.com\")", | |
| 489 | set it in orgo.toml (e.g. base_url = \"https://example.com\")", | |
| 490 | 490 | )); |
| 491 | 491 | } |
| 492 | 492 | if path.starts_with("http://") || path.starts_with("https://") { |
| @@ -538,7 +538,7 @@ fn add_filters(env: &mut Environment<'static>, base_url: &str) { | ||
| 538 | 538 | }); |
| 539 | 539 | } |
| 540 | 540 | |
| 541 | /// The starter RSS feed written by `org-ssg init`. A listing page with an XML template: | |
| 541 | /// The starter RSS feed written by `orgo init`. A listing page with an XML template: | |
| 542 | 542 | /// no feed-specific machinery, just `absolute` and `rfc822` doing what syndication needs. |
| 543 | 543 | /// |
| 544 | 544 | /// Emitted commented-out guidance rather than a broken feed when `site.base_url` is |
| @@ -667,7 +667,7 @@ fn render_error_detail(error: minijinja::Error) -> String { | ||
| 667 | 667 | out |
| 668 | 668 | } |
| 669 | 669 | |
| 670 | /// The starter layout written by `org-ssg init`: the built-in template, on disk, ready | |
| 670 | /// The starter layout written by `orgo init`: the built-in template, on disk, ready | |
| 671 | 671 | /// to edit. |
| 672 | 672 | pub fn starter_template() -> &'static str { |
| 673 | 673 | BASE_TEMPLATE |
src/watch.rs +1 −1
| @@ -6,7 +6,7 @@ | ||
| 6 | 6 | //! |
| 7 | 7 | //! Two things matter more than the watching itself: |
| 8 | 8 | //! |
| 9 | //! 1. **Not watching our own output.** `org-ssg watch . -o _site` puts the output inside | |
| 9 | //! 1. **Not watching our own output.** `orgo watch . -o _site` puts the output inside | |
| 10 | 10 | //! the source. Rebuilding writes files, writing files raises events, and events |
| 11 | 11 | //! trigger a rebuild — a loop that never stops and never idles. [`ChangeFilter`] is |
| 12 | 12 | //! what prevents it, and it is a pure function precisely so it can be tested without |
syntaxes/Org.sublime-syntax +1 −1
| @@ -5,7 +5,7 @@ | ||
| 5 | 5 | # needs it. |
| 6 | 6 | # |
| 7 | 7 | # This highlights org as *source text you are reading about*, which is a different job |
| 8 | # from parsing it: org-ssg's own parser (src/parser.rs) is what turns org into a | |
| 8 | # from parsing it: orgo's own parser (src/parser.rs) is what turns org into a | |
| 9 | 9 | # document. Where the two could disagree, this one stays conservative — a highlighter |
| 10 | 10 | # that colours something wrongly is a cosmetic bug, and one that swallows a line is not. |
| 11 | 11 | name: Org |
syntaxes/TOML.sublime-syntax +1 −1
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | %YAML 1.2 |
| 2 | 2 | --- |
| 3 | 3 | # TOML, for syntect. Not one of the definitions syntect bundles, and the first thing a |
| 4 | # config-heavy site needs — every org-ssg.toml example in the documentation is one. | |
| 4 | # config-heavy site needs — every orgo.toml example in the documentation is one. | |
| 5 | 5 | # |
| 6 | 6 | # Scope names are the standard TextMate ones, so any syntect theme colours this without |
| 7 | 7 | # knowing it exists. |
tests/config.rs +51 −51
| @@ -2,7 +2,7 @@ | ||
| 2 | 2 | //! generator for one site or for anyone's. |
| 3 | 3 | //! |
| 4 | 4 | //! The theme running through these tests is that **the zero-config path has to work**. |
| 5 | //! A directory of `.org` files with no `org-ssg.toml`, no templates and no knowledge of | |
| 5 | //! A directory of `.org` files with no `orgo.toml`, no templates and no knowledge of | |
| 6 | 6 | //! this tool must build into a real site; configuration is how you change the output, |
| 7 | 7 | //! never how you make it work at all. |
| 8 | 8 | |
| @@ -10,15 +10,15 @@ use std::sync::atomic::{AtomicU32, Ordering}; | ||
| 10 | 10 | |
| 11 | 11 | use camino::Utf8PathBuf; |
| 12 | 12 | |
| 13 | use org_ssg::config::{Config, NavMode}; | |
| 14 | use org_ssg::site::{build_site, BuildOptions}; | |
| 13 | use orgo::config::{Config, NavMode}; | |
| 14 | use orgo::site::{build_site, BuildOptions}; | |
| 15 | 15 | |
| 16 | 16 | fn tmpdir(tag: &str) -> Utf8PathBuf { |
| 17 | 17 | static N: AtomicU32 = AtomicU32::new(0); |
| 18 | 18 | let n = N.fetch_add(1, Ordering::Relaxed); |
| 19 | 19 | let base = Utf8PathBuf::from_path_buf(std::env::temp_dir()) |
| 20 | 20 | .expect("utf-8 temp dir") |
| 21 | .join(format!("org-ssg-cfg-{}-{tag}-{n}", std::process::id())); | |
| 21 | .join(format!("orgo-cfg-{}-{tag}-{n}", std::process::id())); | |
| 22 | 22 | let _ = std::fs::remove_dir_all(&base); |
| 23 | 23 | std::fs::create_dir_all(&base).unwrap(); |
| 24 | 24 | base |
| @@ -36,7 +36,7 @@ fn write_site(src: &Utf8PathBuf) { | ||
| 36 | 36 | .unwrap(); |
| 37 | 37 | } |
| 38 | 38 | |
| 39 | fn build(src: &Utf8PathBuf, out: &Utf8PathBuf) -> org_ssg::site::SiteReport { | |
| 39 | fn build(src: &Utf8PathBuf, out: &Utf8PathBuf) -> orgo::site::SiteReport { | |
| 40 | 40 | build_site(src, out, &BuildOptions::default()).expect("build") |
| 41 | 41 | } |
| 42 | 42 | |
| @@ -81,9 +81,9 @@ fn a_malformed_config_is_an_error_but_a_missing_one_is_not() { | ||
| 81 | 81 | |
| 82 | 82 | assert_eq!(Config::load(&src).unwrap(), Config::default()); |
| 83 | 83 | |
| 84 | std::fs::write(src.join("org-ssg.toml"), "[site\ntitle = broken").unwrap(); | |
| 84 | std::fs::write(src.join("orgo.toml"), "[site\ntitle = broken").unwrap(); | |
| 85 | 85 | let err = Config::load(&src).expect_err("malformed config must fail"); |
| 86 | assert!(format!("{err:#}").contains("org-ssg.toml"), "names the file: {err:#}"); | |
| 86 | assert!(format!("{err:#}").contains("orgo.toml"), "names the file: {err:#}"); | |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 89 | /// A misspelled key is a silent no-op in most config formats, which is exactly how |
| @@ -93,7 +93,7 @@ fn an_unknown_config_key_is_rejected() { | ||
| 93 | 93 | let root = tmpdir("unknownkey"); |
| 94 | 94 | let src = root.join("src"); |
| 95 | 95 | std::fs::create_dir_all(&src).unwrap(); |
| 96 | std::fs::write(src.join("org-ssg.toml"), "[site]\ntittle = \"typo\"\n").unwrap(); | |
| 96 | std::fs::write(src.join("orgo.toml"), "[site]\ntittle = \"typo\"\n").unwrap(); | |
| 97 | 97 | |
| 98 | 98 | let err = Config::load(&src).expect_err("unknown key must fail"); |
| 99 | 99 | assert!( |
| @@ -126,7 +126,7 @@ fn nav_modes_select_different_pages() { | ||
| 126 | 126 | std::fs::create_dir_all(&src).unwrap(); |
| 127 | 127 | write_site(&src); |
| 128 | 128 | std::fs::write( |
| 129 | src.join("org-ssg.toml"), | |
| 129 | src.join("orgo.toml"), | |
| 130 | 130 | format!("[nav]\nmode = \"{mode}\"\n"), |
| 131 | 131 | ) |
| 132 | 132 | .unwrap(); |
| @@ -155,7 +155,7 @@ fn explicit_nav_uses_the_configured_order() { | ||
| 155 | 155 | std::fs::create_dir_all(&src).unwrap(); |
| 156 | 156 | write_site(&src); |
| 157 | 157 | std::fs::write( |
| 158 | src.join("org-ssg.toml"), | |
| 158 | src.join("orgo.toml"), | |
| 159 | 159 | "[nav]\nmode = \"explicit\"\npages = [\"blog/post.org\", \"index.org\"]\n", |
| 160 | 160 | ) |
| 161 | 161 | .unwrap(); |
| @@ -178,7 +178,7 @@ fn explicit_nav_rejects_a_page_that_does_not_exist() { | ||
| 178 | 178 | std::fs::create_dir_all(&src).unwrap(); |
| 179 | 179 | write_site(&src); |
| 180 | 180 | std::fs::write( |
| 181 | src.join("org-ssg.toml"), | |
| 181 | src.join("orgo.toml"), | |
| 182 | 182 | "[nav]\nmode = \"explicit\"\npages = [\"nope.org\"]\n", |
| 183 | 183 | ) |
| 184 | 184 | .unwrap(); |
| @@ -304,7 +304,7 @@ fn the_page_list_is_opt_in_and_widens_invalidation() { | ||
| 304 | 304 | std::fs::create_dir_all(src.join("templates")).unwrap(); |
| 305 | 305 | write_site(&src); |
| 306 | 306 | std::fs::write( |
| 307 | src.join("org-ssg.toml"), | |
| 307 | src.join("orgo.toml"), | |
| 308 | 308 | "[templates]\nexpose_page_list = true\n", |
| 309 | 309 | ) |
| 310 | 310 | .unwrap(); |
| @@ -352,7 +352,7 @@ fn heading_offset_shifts_content_headings_below_the_page_title() { | ||
| 352 | 352 | "a level-1 org heading renders as <h2> by default" |
| 353 | 353 | ); |
| 354 | 354 | |
| 355 | std::fs::write(src.join("org-ssg.toml"), "[html]\nheading_offset = 0\n").unwrap(); | |
| 355 | std::fs::write(src.join("orgo.toml"), "[html]\nheading_offset = 0\n").unwrap(); | |
| 356 | 356 | let out2 = root.join("out2"); |
| 357 | 357 | build(&src, &out2); |
| 358 | 358 | assert!( |
| @@ -369,7 +369,7 @@ fn an_unknown_highlight_theme_is_rejected_with_the_available_ones() { | ||
| 369 | 369 | let src = root.join("src"); |
| 370 | 370 | std::fs::create_dir_all(&src).unwrap(); |
| 371 | 371 | write_site(&src); |
| 372 | std::fs::write(src.join("org-ssg.toml"), "[highlight]\ntheme = \"nope\"\n").unwrap(); | |
| 372 | std::fs::write(src.join("orgo.toml"), "[highlight]\ntheme = \"nope\"\n").unwrap(); | |
| 373 | 373 | |
| 374 | 374 | let err = build_site(&src, &root.join("out"), &BuildOptions::default()) |
| 375 | 375 | .expect_err("unknown theme must fail"); |
| @@ -385,7 +385,7 @@ fn an_unknown_highlight_theme_is_rejected_with_the_available_ones() { | ||
| 385 | 385 | // Discovery |
| 386 | 386 | // --------------------------------------------------------------------------- |
| 387 | 387 | |
| 388 | /// `org-ssg build . -o _site` is the obvious thing to type. Without excluding the output | |
| 388 | /// `orgo build . -o _site` is the obvious thing to type. Without excluding the output | |
| 389 | 389 | /// directory, the build copies its own output back into itself, growing `_site/_site/…` |
| 390 | 390 | /// on every run. |
| 391 | 391 | #[test] |
| @@ -421,7 +421,7 @@ fn dot_directories_and_build_inputs_are_never_published() { | ||
| 421 | 421 | write_site(&src); |
| 422 | 422 | std::fs::write(src.join(".git/config"), "[remote]\nurl = private\n").unwrap(); |
| 423 | 423 | std::fs::write(src.join(".env"), "SECRET=hunter2\n").unwrap(); |
| 424 | std::fs::write(src.join("org-ssg.toml"), "[site]\ntitle = \"T\"\n").unwrap(); | |
| 424 | std::fs::write(src.join("orgo.toml"), "[site]\ntitle = \"T\"\n").unwrap(); | |
| 425 | 425 | std::fs::write(src.join("templates/base.html"), "<html>{{ body | safe }}</html>").unwrap(); |
| 426 | 426 | std::fs::write(src.join("style.css"), "body{}\n").unwrap(); |
| 427 | 427 | let out = root.join("out"); |
| @@ -430,7 +430,7 @@ fn dot_directories_and_build_inputs_are_never_published() { | ||
| 430 | 430 | assert!(!out.join(".git").exists(), ".git must never be published"); |
| 431 | 431 | assert!(!out.join(".env").exists(), "dotfiles must never be published"); |
| 432 | 432 | assert!( |
| 433 | !out.join("org-ssg.toml").exists(), | |
| 433 | !out.join("orgo.toml").exists(), | |
| 434 | 434 | "the config is a build input, not content" |
| 435 | 435 | ); |
| 436 | 436 | assert!( |
| @@ -472,7 +472,7 @@ fn write_blog(src: &Utf8PathBuf, extra_config: &str) { | ||
| 472 | 472 | ) |
| 473 | 473 | .unwrap(); |
| 474 | 474 | std::fs::write( |
| 475 | src.join("org-ssg.toml"), | |
| 475 | src.join("orgo.toml"), | |
| 476 | 476 | format!( |
| 477 | 477 | "[[collections]]\nsource = \"blog\"\noutput = \"blog/index.html\"\n\ |
| 478 | 478 | template = \"list.html\"\ntitle = \"Blog\"\n{extra_config}" |
| @@ -758,12 +758,12 @@ fn a_feed_is_just_a_listing_page_with_an_xml_template() { | ||
| 758 | 758 | <pubDate>{{ p.date_iso }}</pubDate></item>{% endfor %}</channel></rss>", |
| 759 | 759 | ) |
| 760 | 760 | .unwrap(); |
| 761 | let mut config = std::fs::read_to_string(src.join("org-ssg.toml")).unwrap(); | |
| 761 | let mut config = std::fs::read_to_string(src.join("orgo.toml")).unwrap(); | |
| 762 | 762 | config.push_str( |
| 763 | 763 | "\n[[collections]]\nsource = \"blog\"\noutput = \"feed.xml\"\n\ |
| 764 | 764 | template = \"feed.xml\"\ntitle = \"Feed\"\n", |
| 765 | 765 | ); |
| 766 | std::fs::write(src.join("org-ssg.toml"), config).unwrap(); | |
| 766 | std::fs::write(src.join("orgo.toml"), config).unwrap(); | |
| 767 | 767 | let out = root.join("out"); |
| 768 | 768 | build(&src, &out); |
| 769 | 769 | |
| @@ -833,16 +833,16 @@ fn colliding_collection_outputs_are_rejected() { | ||
| 833 | 833 | std::fs::create_dir_all(&src).unwrap(); |
| 834 | 834 | write_blog(&src, ""); |
| 835 | 835 | |
| 836 | let mut config = std::fs::read_to_string(src.join("org-ssg.toml")).unwrap(); | |
| 836 | let mut config = std::fs::read_to_string(src.join("orgo.toml")).unwrap(); | |
| 837 | 837 | config.push_str("\n[[collections]]\nsource = \"\"\noutput = \"blog/index.html\"\n"); |
| 838 | std::fs::write(src.join("org-ssg.toml"), &config).unwrap(); | |
| 838 | std::fs::write(src.join("orgo.toml"), &config).unwrap(); | |
| 839 | 839 | let err = build_site(&src, &root.join("out"), &BuildOptions::default()) |
| 840 | 840 | .expect_err("two collections writing one file must fail"); |
| 841 | 841 | assert!(format!("{err:#}").contains("blog/index.html"), "{err:#}"); |
| 842 | 842 | |
| 843 | 843 | // And a listing that would overwrite a real page. |
| 844 | 844 | std::fs::write( |
| 845 | src.join("org-ssg.toml"), | |
| 845 | src.join("orgo.toml"), | |
| 846 | 846 | "[[collections]]\nsource = \"blog\"\noutput = \"index.html\"\ntemplate = \"list.html\"\n", |
| 847 | 847 | ) |
| 848 | 848 | .unwrap(); |
| @@ -859,7 +859,7 @@ fn a_missing_collection_template_names_the_ones_that_exist() { | ||
| 859 | 859 | std::fs::create_dir_all(&src).unwrap(); |
| 860 | 860 | write_blog(&src, ""); |
| 861 | 861 | std::fs::write( |
| 862 | src.join("org-ssg.toml"), | |
| 862 | src.join("orgo.toml"), | |
| 863 | 863 | "[[collections]]\nsource = \"blog\"\noutput = \"blog/index.html\"\ntemplate = \"nope.html\"\n", |
| 864 | 864 | ) |
| 865 | 865 | .unwrap(); |
| @@ -912,7 +912,7 @@ fn write_tagged_blog(src: &Utf8PathBuf, extra: &str) { | ||
| 912 | 912 | ) |
| 913 | 913 | .unwrap(); |
| 914 | 914 | std::fs::write( |
| 915 | src.join("org-ssg.toml"), | |
| 915 | src.join("orgo.toml"), | |
| 916 | 916 | format!( |
| 917 | 917 | "[[collections]]\nsource = \"blog\"\ngroup_by = \"tags\"\n\ |
| 918 | 918 | output = \"tags/{{tag}}.html\"\ntemplate = \"tag.html\"\ntitle = \"Tagged: {{tag}}\"\n\ |
| @@ -1044,7 +1044,7 @@ fn a_collection_can_group_by_any_keyword() { | ||
| 1044 | 1044 | ) |
| 1045 | 1045 | .unwrap(); |
| 1046 | 1046 | std::fs::write( |
| 1047 | src.join("org-ssg.toml"), | |
| 1047 | src.join("orgo.toml"), | |
| 1048 | 1048 | "[[collections]]\nsource = \"blog\"\ngroup_by = \"category\"\n\ |
| 1049 | 1049 | output = \"cat/{tag}.html\"\ntemplate = \"tag.html\"\ntitle = \"{tag}\"\n", |
| 1050 | 1050 | ) |
| @@ -1081,7 +1081,7 @@ fn grouping_without_a_placeholder_is_rejected() { | ||
| 1081 | 1081 | std::fs::create_dir_all(&src).unwrap(); |
| 1082 | 1082 | write_tagged_blog(&src, ""); |
| 1083 | 1083 | std::fs::write( |
| 1084 | src.join("org-ssg.toml"), | |
| 1084 | src.join("orgo.toml"), | |
| 1085 | 1085 | "[[collections]]\nsource = \"blog\"\ngroup_by = \"tags\"\n\ |
| 1086 | 1086 | output = \"tags/all.html\"\ntemplate = \"tag.html\"\n", |
| 1087 | 1087 | ) |
| @@ -1149,7 +1149,7 @@ fn write_paginated_blog(src: &Utf8PathBuf, count: usize, extra: &str) { | ||
| 1149 | 1149 | ) |
| 1150 | 1150 | .unwrap(); |
| 1151 | 1151 | std::fs::write( |
| 1152 | src.join("org-ssg.toml"), | |
| 1152 | src.join("orgo.toml"), | |
| 1153 | 1153 | format!( |
| 1154 | 1154 | "[[collections]]\nsource = \"blog\"\noutput = \"blog/index.html\"\n\ |
| 1155 | 1155 | template = \"list.html\"\ntitle = \"Blog\"\n{extra}" |
| @@ -1275,7 +1275,7 @@ fn groups_paginate_independently() { | ||
| 1275 | 1275 | .unwrap(); |
| 1276 | 1276 | } |
| 1277 | 1277 | std::fs::write( |
| 1278 | src.join("org-ssg.toml"), | |
| 1278 | src.join("orgo.toml"), | |
| 1279 | 1279 | "[[collections]]\nsource = \"blog\"\ngroup_by = \"tags\"\n\ |
| 1280 | 1280 | output = \"tags/{tag}.html\"\ntemplate = \"list.html\"\ntitle = \"{tag}\"\n\ |
| 1281 | 1281 | paginate = 2\npaginate_output = \"tags/{tag}/page/{n}.html\"\n", |
| @@ -1317,7 +1317,7 @@ fn pagination_placeholders_are_validated() { | ||
| 1317 | 1317 | |
| 1318 | 1318 | // Grouped without {tag} in the page pattern. |
| 1319 | 1319 | std::fs::write( |
| 1320 | src.join("org-ssg.toml"), | |
| 1320 | src.join("orgo.toml"), | |
| 1321 | 1321 | "[[collections]]\nsource = \"blog\"\ngroup_by = \"tags\"\n\ |
| 1322 | 1322 | output = \"tags/{tag}.html\"\ntemplate = \"list.html\"\n\ |
| 1323 | 1323 | paginate = 2\npaginate_output = \"tags/page/{n}.html\"\n", |
| @@ -1380,7 +1380,7 @@ fn write_feed_site(src: &Utf8PathBuf, base_url: &str) { | ||
| 1380 | 1380 | ) |
| 1381 | 1381 | .unwrap(); |
| 1382 | 1382 | std::fs::write( |
| 1383 | src.join("org-ssg.toml"), | |
| 1383 | src.join("orgo.toml"), | |
| 1384 | 1384 | format!( |
| 1385 | 1385 | "[site]\nbase_url = \"{base_url}\"\n\n\ |
| 1386 | 1386 | [[collections]]\nsource = \"blog\"\noutput = \"feed.xml\"\n\ |
| @@ -1442,7 +1442,7 @@ fn absolute_without_a_base_url_is_an_error_that_says_what_to_set() { | ||
| 1442 | 1442 | .expect_err("absolute with no base_url must fail"); |
| 1443 | 1443 | let message = format!("{err:#}"); |
| 1444 | 1444 | assert!(message.contains("base_url"), "names the setting: {message}"); |
| 1445 | assert!(message.contains("org-ssg.toml"), "names where to set it: {message}"); | |
| 1445 | assert!(message.contains("orgo.toml"), "names where to set it: {message}"); | |
| 1446 | 1446 | assert!(message.contains("feed.xml"), "names the template: {message}"); |
| 1447 | 1447 | } |
| 1448 | 1448 | |
| @@ -1484,7 +1484,7 @@ fn the_default_layout_emits_a_canonical_link_only_with_a_base_url() { | ||
| 1484 | 1484 | std::fs::create_dir_all(&src).unwrap(); |
| 1485 | 1485 | write_site(&src); |
| 1486 | 1486 | std::fs::write( |
| 1487 | src.join("org-ssg.toml"), | |
| 1487 | src.join("orgo.toml"), | |
| 1488 | 1488 | format!("[site]\nbase_url = \"{base}\"\n"), |
| 1489 | 1489 | ) |
| 1490 | 1490 | .unwrap(); |
| @@ -1509,7 +1509,7 @@ fn changing_base_url_re_renders_the_site() { | ||
| 1509 | 1509 | std::fs::create_dir_all(&src).unwrap(); |
| 1510 | 1510 | write_site(&src); |
| 1511 | 1511 | std::fs::write( |
| 1512 | src.join("org-ssg.toml"), | |
| 1512 | src.join("orgo.toml"), | |
| 1513 | 1513 | "[site]\nbase_url = \"https://example.com\"\n", |
| 1514 | 1514 | ) |
| 1515 | 1515 | .unwrap(); |
| @@ -1518,7 +1518,7 @@ fn changing_base_url_re_renders_the_site() { | ||
| 1518 | 1518 | assert!(build(&src, &out).rendered.is_empty(), "unchanged rebuild renders nothing"); |
| 1519 | 1519 | |
| 1520 | 1520 | std::fs::write( |
| 1521 | src.join("org-ssg.toml"), | |
| 1521 | src.join("orgo.toml"), | |
| 1522 | 1522 | "[site]\nbase_url = \"https://moved.example\"\n", |
| 1523 | 1523 | ) |
| 1524 | 1524 | .unwrap(); |
| @@ -1556,7 +1556,7 @@ fn write_excerpt_site(src: &Utf8PathBuf, extra_config: &str) { | ||
| 1556 | 1556 | ) |
| 1557 | 1557 | .unwrap(); |
| 1558 | 1558 | std::fs::write( |
| 1559 | src.join("org-ssg.toml"), | |
| 1559 | src.join("orgo.toml"), | |
| 1560 | 1560 | format!( |
| 1561 | 1561 | "[[collections]]\nsource = \"blog\"\noutput = \"blog/index.html\"\n\ |
| 1562 | 1562 | template = \"list.html\"\ntitle = \"Blog\"\n{extra_config}" |
| @@ -1731,9 +1731,9 @@ fn a_link_to_a_draft_is_reported_as_broken() { | ||
| 1731 | 1731 | /// "no" has to mean no. |
| 1732 | 1732 | #[test] |
| 1733 | 1733 | fn draft_truthiness_is_forgiving_but_respects_an_explicit_negative() { |
| 1734 | use org_ssg::model::Keywords; | |
| 1734 | use orgo::model::Keywords; | |
| 1735 | 1735 | let draft = |value: &str| { |
| 1736 | org_ssg::util::is_draft(&Keywords { | |
| 1736 | orgo::util::is_draft(&Keywords { | |
| 1737 | 1737 | entries: vec![("DRAFT".to_string(), value.to_string())], |
| 1738 | 1738 | }) |
| 1739 | 1739 | }; |
| @@ -1744,7 +1744,7 @@ fn draft_truthiness_is_forgiving_but_respects_an_explicit_negative() { | ||
| 1744 | 1744 | assert!(!draft(no), "{no:?} should mean published"); |
| 1745 | 1745 | } |
| 1746 | 1746 | assert!( |
| 1747 | !org_ssg::util::is_draft(&Keywords::default()), | |
| 1747 | !orgo::util::is_draft(&Keywords::default()), | |
| 1748 | 1748 | "no keyword at all means published" |
| 1749 | 1749 | ); |
| 1750 | 1750 | } |
| @@ -1773,7 +1773,7 @@ fn write_toc_site(src: &Utf8PathBuf, options: &str, config: &str) { | ||
| 1773 | 1773 | <nav>{{ walk(page.toc) }}</nav>{{ body | safe }}</body></html>", |
| 1774 | 1774 | ) |
| 1775 | 1775 | .unwrap(); |
| 1776 | std::fs::write(src.join("org-ssg.toml"), config).unwrap(); | |
| 1776 | std::fs::write(src.join("orgo.toml"), config).unwrap(); | |
| 1777 | 1777 | } |
| 1778 | 1778 | |
| 1779 | 1779 | /// A table of contents is a tree, and reconstructing one from a flat list of levels |
| @@ -1889,7 +1889,7 @@ fn section_numbering_resets_at_each_level() { | ||
| 1889 | 1889 | * One\n** A\n** B\n* Two\n** C\n*** Deep\n* Three\n", |
| 1890 | 1890 | ) |
| 1891 | 1891 | .unwrap(); |
| 1892 | std::fs::write(src.join("org-ssg.toml"), "").unwrap(); | |
| 1892 | std::fs::write(src.join("orgo.toml"), "").unwrap(); | |
| 1893 | 1893 | let out = root.join("out"); |
| 1894 | 1894 | build(&src, &out); |
| 1895 | 1895 | |
| @@ -1914,8 +1914,8 @@ fn section_numbering_resets_at_each_level() { | ||
| 1914 | 1914 | /// `#+OPTIONS:` is a space-separated list of switches, and org spells "off" several ways. |
| 1915 | 1915 | #[test] |
| 1916 | 1916 | fn export_options_parse_as_org_writes_them() { |
| 1917 | use org_ssg::model::Keywords; | |
| 1918 | use org_ssg::util::option_enabled; | |
| 1917 | use orgo::model::Keywords; | |
| 1918 | use orgo::util::option_enabled; | |
| 1919 | 1919 | let keywords = |v: &str| Keywords { |
| 1920 | 1920 | entries: vec![("OPTIONS".to_string(), v.to_string())], |
| 1921 | 1921 | }; |
| @@ -1955,7 +1955,7 @@ fn write_two_layouts(src: &Utf8PathBuf, config: &str) { | ||
| 1955 | 1955 | <p>Reply by email</p></body></html>", |
| 1956 | 1956 | ) |
| 1957 | 1957 | .unwrap(); |
| 1958 | std::fs::write(src.join("org-ssg.toml"), config).unwrap(); | |
| 1958 | std::fs::write(src.join("orgo.toml"), config).unwrap(); | |
| 1959 | 1959 | } |
| 1960 | 1960 | |
| 1961 | 1961 | /// A section's layout is a property of the section: one rule covers every page under it, |
| @@ -2124,7 +2124,7 @@ fn adding_a_pages_rule_rerenders_the_pages_it_covers() { | ||
| 2124 | 2124 | build(&src, &out); |
| 2125 | 2125 | |
| 2126 | 2126 | std::fs::write( |
| 2127 | src.join("org-ssg.toml"), | |
| 2127 | src.join("orgo.toml"), | |
| 2128 | 2128 | "[[pages]]\nmatch = \"blog\"\ntemplate = \"post.html\"\n", |
| 2129 | 2129 | ) |
| 2130 | 2130 | .unwrap(); |
| @@ -2142,7 +2142,7 @@ fn adding_a_pages_rule_rerenders_the_pages_it_covers() { | ||
| 2142 | 2142 | #[test] |
| 2143 | 2143 | fn a_pages_rule_without_a_template_is_rejected() { |
| 2144 | 2144 | let mut config = Config::default(); |
| 2145 | config.pages.push(org_ssg::config::PageRule { | |
| 2145 | config.pages.push(orgo::config::PageRule { | |
| 2146 | 2146 | pattern: Utf8PathBuf::from("blog"), |
| 2147 | 2147 | template: String::new(), |
| 2148 | 2148 | }); |
| @@ -2211,7 +2211,7 @@ fn same_day_entries_sort_by_time_of_day() { | ||
| 2211 | 2211 | ) |
| 2212 | 2212 | .unwrap(); |
| 2213 | 2213 | std::fs::write( |
| 2214 | src.join("org-ssg.toml"), | |
| 2214 | src.join("orgo.toml"), | |
| 2215 | 2215 | "[[collections]]\nsource = \"blog\"\noutput = \"blog/index.html\"\n\ |
| 2216 | 2216 | template = \"list.html\"\ntitle = \"Blog\"\nsort = \"date\"\norder = \"desc\"\n", |
| 2217 | 2217 | ) |
| @@ -2249,7 +2249,7 @@ fn an_asset_root_publishes_to_the_site_root() { | ||
| 2249 | 2249 | std::fs::write(root.join("theme/static/robots.txt"), "User-agent: *\n").unwrap(); |
| 2250 | 2250 | std::fs::write(root.join("theme/static/img/logo.svg"), "<svg/>").unwrap(); |
| 2251 | 2251 | std::fs::write( |
| 2252 | src.join("org-ssg.toml"), | |
| 2252 | src.join("orgo.toml"), | |
| 2253 | 2253 | "[build]\nassets = [\"../theme/static\"]\n", |
| 2254 | 2254 | ) |
| 2255 | 2255 | .unwrap(); |
| @@ -2280,7 +2280,7 @@ fn two_assets_claiming_one_url_is_an_error() { | ||
| 2280 | 2280 | std::fs::create_dir_all(root.join("static")).unwrap(); |
| 2281 | 2281 | std::fs::write(root.join("static/style.css"), "body{color:red}").unwrap(); |
| 2282 | 2282 | std::fs::write( |
| 2283 | src.join("org-ssg.toml"), | |
| 2283 | src.join("orgo.toml"), | |
| 2284 | 2284 | "[build]\nassets = [\"../static\"]\n", |
| 2285 | 2285 | ) |
| 2286 | 2286 | .unwrap(); |
| @@ -2301,7 +2301,7 @@ fn a_missing_asset_root_is_an_error() { | ||
| 2301 | 2301 | std::fs::create_dir_all(&src).unwrap(); |
| 2302 | 2302 | write_site(&src); |
| 2303 | 2303 | std::fs::write( |
| 2304 | src.join("org-ssg.toml"), | |
| 2304 | src.join("orgo.toml"), | |
| 2305 | 2305 | "[build]\nassets = [\"../nope\"]\n", |
| 2306 | 2306 | ) |
| 2307 | 2307 | .unwrap(); |
| @@ -2330,7 +2330,7 @@ fn a_collection_can_carry_its_entries_rendered_bodies() { | ||
| 2330 | 2330 | ) |
| 2331 | 2331 | .unwrap(); |
| 2332 | 2332 | std::fs::write( |
| 2333 | src.join("org-ssg.toml"), | |
| 2333 | src.join("orgo.toml"), | |
| 2334 | 2334 | "[[collections]]\nsource = \"blog\"\noutput = \"feed.xml\"\n\ |
| 2335 | 2335 | template = \"feed.xml\"\ntitle = \"Feed\"\ninclude_content = true\n", |
| 2336 | 2336 | ) |
tests/constructs.rs +6 −6
| @@ -13,10 +13,10 @@ | ||
| 13 | 13 | |
| 14 | 14 | use camino::Utf8PathBuf; |
| 15 | 15 | |
| 16 | use org_ssg::model::Document; | |
| 17 | use org_ssg::parser::parse; | |
| 18 | use org_ssg::render::{render, Html, SyntectHighlighter}; | |
| 19 | use org_ssg::resolve::ResolvedDoc; | |
| 16 | use orgo::model::Document; | |
| 17 | use orgo::parser::parse; | |
| 18 | use orgo::render::{render, Html, SyntectHighlighter}; | |
| 19 | use orgo::resolve::ResolvedDoc; | |
| 20 | 20 | |
| 21 | 21 | fn parse_fixture(name: &str) -> Document { |
| 22 | 22 | let path = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")) |
| @@ -160,7 +160,7 @@ fn highlighting_emits_classes_not_inline_styles() { | ||
| 160 | 160 | "highlighting must not emit inline styles:\n{html}" |
| 161 | 161 | ); |
| 162 | 162 | assert!( |
| 163 | org_ssg::render::syntax_css("InspiredGitHub") | |
| 163 | orgo::render::syntax_css("InspiredGitHub") | |
| 164 | 164 | .expect("a built-in theme") |
| 165 | 165 | .contains(".storage"), |
| 166 | 166 | "the generated stylesheet must define the emitted classes" |
| @@ -559,7 +559,7 @@ fn special_strings_leave_code_alone() { | ||
| 559 | 559 | ); |
| 560 | 560 | } |
| 561 | 561 | |
| 562 | /// `#+OPTIONS: -:nil` is how a document opts out, and org-ssg honours org's own switch | |
| 562 | /// `#+OPTIONS: -:nil` is how a document opts out, and orgo honours org's own switch | |
| 563 | 563 | /// rather than inventing one. |
| 564 | 564 | #[test] |
| 565 | 565 | fn a_document_can_turn_special_strings_off() { |
tests/harness.rs +2 −2
| @@ -5,8 +5,8 @@ | ||
| 5 | 5 | //! - an `insta` JSON snapshot of a hand-built element-tree value, standing in for the |
| 6 | 6 | //! element-tree snapshots the parser will produce from Phase 1 onward. |
| 7 | 7 | |
| 8 | use org_ssg::model::{Link, LinkTarget, Object}; | |
| 9 | use org_ssg::parser::content_hash; | |
| 8 | use orgo::model::{Link, LinkTarget, Object}; | |
| 9 | use orgo::parser::content_hash; | |
| 10 | 10 | |
| 11 | 11 | #[test] |
| 12 | 12 | fn content_hash_is_deterministic_blake3() { |
tests/incremental.rs +5 −5
| @@ -16,8 +16,8 @@ use std::sync::atomic::{AtomicU32, Ordering}; | ||
| 16 | 16 | |
| 17 | 17 | use camino::Utf8PathBuf; |
| 18 | 18 | |
| 19 | use org_ssg::incremental::{manifest_path, Manifest, CACHE_FORMAT_VERSION}; | |
| 20 | use org_ssg::site::{build_site, BuildOptions}; | |
| 19 | use orgo::incremental::{manifest_path, Manifest, CACHE_FORMAT_VERSION}; | |
| 20 | use orgo::site::{build_site, BuildOptions}; | |
| 21 | 21 | |
| 22 | 22 | /// A fresh, empty temp directory unique to this process + call. |
| 23 | 23 | fn tmpdir(tag: &str) -> Utf8PathBuf { |
| @@ -25,7 +25,7 @@ fn tmpdir(tag: &str) -> Utf8PathBuf { | ||
| 25 | 25 | let n = N.fetch_add(1, Ordering::Relaxed); |
| 26 | 26 | let base = Utf8PathBuf::from_path_buf(std::env::temp_dir()) |
| 27 | 27 | .expect("utf-8 temp dir") |
| 28 | .join(format!("org-ssg-it-{}-{tag}-{n}", std::process::id())); | |
| 28 | .join(format!("orgo-it-{}-{tag}-{n}", std::process::id())); | |
| 29 | 29 | if base.exists() { |
| 30 | 30 | std::fs::remove_dir_all(&base).unwrap(); |
| 31 | 31 | } |
| @@ -47,7 +47,7 @@ fn output_files(out: &Utf8PathBuf) -> BTreeMap<String, Vec<u8>> { | ||
| 47 | 47 | continue; |
| 48 | 48 | } |
| 49 | 49 | let path = Utf8PathBuf::from_path_buf(entry.path().to_owned()).unwrap(); |
| 50 | if path.file_name() == Some(".org-ssg-cache.json") { | |
| 50 | if path.file_name() == Some(".orgo-cache.json") { | |
| 51 | 51 | continue; |
| 52 | 52 | } |
| 53 | 53 | let rel = path.strip_prefix(out).unwrap().to_string(); |
| @@ -476,7 +476,7 @@ fn editing_one_template_rebuilds_only_the_pages_that_use_it() { | ||
| 476 | 476 | ) |
| 477 | 477 | .unwrap(); |
| 478 | 478 | std::fs::write( |
| 479 | src.join("org-ssg.toml"), | |
| 479 | src.join("orgo.toml"), | |
| 480 | 480 | "[[pages]]\nmatch = \"blog\"\ntemplate = \"post.html\"\n", |
| 481 | 481 | ) |
| 482 | 482 | .unwrap(); |
tests/oracle.el +3 −3
| @@ -1,4 +1,4 @@ | ||
| 1 | ;;; oracle.el --- ground-truth HTML export for the org-ssg differential tests -*- lexical-binding: t -*- | |
| 1 | ;;; oracle.el --- ground-truth HTML export for the orgo differential tests -*- lexical-binding: t -*- | |
| 2 | 2 | |
| 3 | 3 | ;; Exports the org file named by $ORG_ORACLE_INPUT to HTML on stdout, using org's own |
| 4 | 4 | ;; exporter — the same one weblorg wraps to publish the corpus this project targets. |
| @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ;; learn what stock org does — normalizing that away would be marking our own homework. |
| 17 | 17 | (setq org-export-with-toc nil ; we emit no table of contents |
| 18 | 18 | org-export-with-section-numbers nil ; we do not number headings |
| 19 | ;; org-html-toplevel-hlevel is left at its default of 2. org-ssg's own default | |
| 19 | ;; org-html-toplevel-hlevel is left at its default of 2. orgo's own default | |
| 20 | 20 | ;; heading_offset is 1, which produces the same <h2>, so both sides now agree |
| 21 | 21 | ;; without the oracle being told to. |
| 22 | 22 | org-html-htmlize-output-type nil ; plain <pre>, not htmlize spans: we highlight |
| @@ -24,7 +24,7 @@ | ||
| 24 | 24 | ; the meaningful part |
| 25 | 25 | org-html-head-include-default-style nil |
| 26 | 26 | org-html-head-include-scripts nil |
| 27 | ;; Fixtures link to ids that live in org-ssg's own symbol table, not in an | |
| 27 | ;; Fixtures link to ids that live in orgo's own symbol table, not in an | |
| 28 | 28 | ;; `org-id' database. Without this, org aborts the whole export on the first one. |
| 29 | 29 | org-export-with-broken-links t |
| 30 | 30 | make-backup-files nil) |
tests/oracle.rs +12 −12
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | //! The `emacs --batch` ground-truth oracle (spec §5, Phase 0). |
| 2 | 2 | //! |
| 3 | //! Every other test in this suite checks org-ssg against org-ssg: a snapshot says our | |
| 3 | //! Every other test in this suite checks orgo against orgo: a snapshot says our | |
| 4 | 4 | //! output has not *changed*, never that it is *right*. Those two questions are different, |
| 5 | 5 | //! and only one of them matters to someone whose site is currently published by Emacs. |
| 6 | 6 | //! This file answers the second by exporting the same fixture with org's own HTML |
| @@ -26,9 +26,9 @@ use std::process::Command; | ||
| 26 | 26 | |
| 27 | 27 | use camino::Utf8PathBuf; |
| 28 | 28 | |
| 29 | use org_ssg::parser::parse; | |
| 30 | use org_ssg::render::{render, Html, SyntectHighlighter}; | |
| 31 | use org_ssg::resolve::ResolvedDoc; | |
| 29 | use orgo::parser::parse; | |
| 30 | use orgo::render::{render, Html, SyntectHighlighter}; | |
| 31 | use orgo::resolve::ResolvedDoc; | |
| 32 | 32 | |
| 33 | 33 | fn manifest_dir() -> Utf8PathBuf { |
| 34 | 34 | Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")) |
| @@ -62,7 +62,7 @@ fn org_export(fixture: &str) -> String { | ||
| 62 | 62 | String::from_utf8(output.stdout).expect("emacs emits UTF-8") |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | /// Render a fixture with org-ssg. | |
| 65 | /// Render a fixture with orgo. | |
| 66 | 66 | fn our_export(fixture: &str) -> String { |
| 67 | 67 | let path = manifest_dir().join("fixtures").join(fixture); |
| 68 | 68 | let source = std::fs::read_to_string(&path).expect("read fixture"); |
| @@ -252,12 +252,12 @@ fn decode_entities(s: &str) -> String { | ||
| 252 | 252 | // Divergence report |
| 253 | 253 | // --------------------------------------------------------------------------- |
| 254 | 254 | |
| 255 | /// One divergence org-ssg makes on purpose, so the report can separate "we chose this" | |
| 255 | /// One divergence orgo makes on purpose, so the report can separate "we chose this" | |
| 256 | 256 | /// from "we got this wrong". |
| 257 | 257 | /// |
| 258 | 258 | /// Without this split the agreement percentage is noise: the timestamps fixture sat at |
| 259 | 259 | /// 40% while being entirely correct, because org writes `<2024-01-15 Mon>` as text and |
| 260 | /// org-ssg writes a `<time datetime>` element. A number that cannot fall when a real | |
| 260 | /// orgo writes a `<time datetime>` element. A number that cannot fall when a real | |
| 261 | 261 | /// defect appears is not measuring anything. |
| 262 | 262 | struct Deliberate { |
| 263 | 263 | name: &'static str, |
| @@ -335,7 +335,7 @@ const DELIBERATE: &[Deliberate] = &[ | ||
| 335 | 335 | }, |
| 336 | 336 | in_notes_only: false, |
| 337 | 337 | }, |
| 338 | // Org emits a `<colgroup>` of empty `<col>`s to carry column alignment; org-ssg | |
| 338 | // Org emits a `<colgroup>` of empty `<col>`s to carry column alignment; orgo | |
| 339 | 339 | // leaves alignment to the stylesheet. |
| 340 | 340 | Deliberate { |
| 341 | 341 | name: "no-colgroup", |
| @@ -511,7 +511,7 @@ fn align(ours: &[String], theirs: &[String]) -> Vec<Op> { | ||
| 511 | 511 | } |
| 512 | 512 | |
| 513 | 513 | /// A unified diff of the two skeletons, with hunks that are deliberate collapsed to a |
| 514 | /// named line. `-` is org-ssg, `+` is Emacs. | |
| 514 | /// named line. `-` is orgo, `+` is Emacs. | |
| 515 | 515 | /// |
| 516 | 516 | /// The number that matters is the last one: *unexplained* lines. Agreement can be low |
| 517 | 517 | /// while unexplained is zero, and that is a passing state. |
| @@ -566,7 +566,7 @@ fn divergence(ours: &[String], theirs: &[String]) -> String { | ||
| 566 | 566 | "agreement: {agreed}/{total} skeleton lines ({pct:.1}%)\n\ |
| 567 | 567 | deliberate: {deliberate} line(s){}\n\ |
| 568 | 568 | unexplained: {unexplained} line(s)\n\ |
| 569 | (- org-ssg, + emacs, ~ a difference we mean to have)\n\n{body}", | |
| 569 | (- orgo, + emacs, ~ a difference we mean to have)\n\n{body}", | |
| 570 | 570 | if rules.is_empty() { |
| 571 | 571 | String::new() |
| 572 | 572 | } else { |
| @@ -575,7 +575,7 @@ fn divergence(ours: &[String], theirs: &[String]) -> String { | ||
| 575 | 575 | ) |
| 576 | 576 | } |
| 577 | 577 | |
| 578 | /// Snapshot the divergence between org-ssg and Emacs for one fixture. | |
| 578 | /// Snapshot the divergence between orgo and Emacs for one fixture. | |
| 579 | 579 | fn compare(fixture: &str) -> Option<String> { |
| 580 | 580 | if !emacs_available() { |
| 581 | 581 | eprintln!("skipping oracle comparison for {fixture}: no emacs on PATH"); |
| @@ -612,7 +612,7 @@ oracle_test!(oracle_elements, "elements.org"); | ||
| 612 | 612 | /// |
| 613 | 613 | /// The percentages above are context, not a target — the timestamps fixture agrees on |
| 614 | 614 | /// 40% of its lines and is entirely correct, because org writes a date as text where |
| 615 | /// org-ssg writes `<time datetime>`. What must hold is that nothing diverges for a | |
| 615 | /// orgo writes `<time datetime>`. What must hold is that nothing diverges for a | |
| 616 | 616 | /// reason nobody has written down. A new unexplained line means either a defect to fix |
| 617 | 617 | /// or a decision to record in `DELIBERATE`. |
| 618 | 618 | #[test] |
tests/pipeline.rs +4 −4
| @@ -6,10 +6,10 @@ | ||
| 6 | 6 | |
| 7 | 7 | use camino::Utf8PathBuf; |
| 8 | 8 | |
| 9 | use org_ssg::model::Document; | |
| 10 | use org_ssg::parser::parse; | |
| 11 | use org_ssg::render::{render, Html, SyntectHighlighter}; | |
| 12 | use org_ssg::resolve::ResolvedDoc; | |
| 9 | use orgo::model::Document; | |
| 10 | use orgo::parser::parse; | |
| 11 | use orgo::render::{render, Html, SyntectHighlighter}; | |
| 12 | use orgo::resolve::ResolvedDoc; | |
| 13 | 13 | |
| 14 | 14 | fn parse_fixture(name: &str) -> Document { |
| 15 | 15 | let path = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")) |
tests/serve.rs +11 −11
| @@ -12,15 +12,15 @@ use std::time::{Duration, Instant}; | ||
| 12 | 12 | |
| 13 | 13 | use camino::{Utf8Path, Utf8PathBuf}; |
| 14 | 14 | |
| 15 | use org_ssg::serve::{inject_reload_script, resolve, since_parameter}; | |
| 16 | use org_ssg::site::BuildOptions; | |
| 15 | use orgo::serve::{inject_reload_script, resolve, since_parameter}; | |
| 16 | use orgo::site::BuildOptions; | |
| 17 | 17 | |
| 18 | 18 | fn tmpdir(tag: &str) -> Utf8PathBuf { |
| 19 | 19 | static N: AtomicU32 = AtomicU32::new(0); |
| 20 | 20 | let n = N.fetch_add(1, Ordering::Relaxed); |
| 21 | 21 | let base = Utf8PathBuf::from_path_buf(std::env::temp_dir()) |
| 22 | 22 | .expect("utf-8 temp dir") |
| 23 | .join(format!("org-ssg-serve-{}-{tag}-{n}", std::process::id())); | |
| 23 | .join(format!("orgo-serve-{}-{tag}-{n}", std::process::id())); | |
| 24 | 24 | let _ = std::fs::remove_dir_all(&base); |
| 25 | 25 | std::fs::create_dir_all(&base).unwrap(); |
| 26 | 26 | base |
| @@ -107,10 +107,10 @@ fn plus_is_not_decoded_as_a_space() { | ||
| 107 | 107 | |
| 108 | 108 | #[test] |
| 109 | 109 | fn the_poll_parameter_is_read_from_the_query() { |
| 110 | assert_eq!(since_parameter("/__org-ssg/reload?since=7"), 7); | |
| 111 | assert_eq!(since_parameter("/__org-ssg/reload?x=1&since=42"), 42); | |
| 112 | assert_eq!(since_parameter("/__org-ssg/reload"), 0, "absent means start from zero"); | |
| 113 | assert_eq!(since_parameter("/__org-ssg/reload?since=nope"), 0, "unparseable means zero"); | |
| 110 | assert_eq!(since_parameter("/__orgo/reload?since=7"), 7); | |
| 111 | assert_eq!(since_parameter("/__orgo/reload?x=1&since=42"), 42); | |
| 112 | assert_eq!(since_parameter("/__orgo/reload"), 0, "absent means start from zero"); | |
| 113 | assert_eq!(since_parameter("/__orgo/reload?since=nope"), 0, "unparseable means zero"); | |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | 116 | // --------------------------------------------------------------------------- |
| @@ -167,7 +167,7 @@ fn start_server(src: &Utf8Path, out: &Utf8Path) -> u16 { | ||
| 167 | 167 | let port = 20000 + ((std::process::id() % 10000) as u16) + NEXT.fetch_add(1, Ordering::Relaxed) as u16; |
| 168 | 168 | let (s, o) = (src.to_owned(), out.to_owned()); |
| 169 | 169 | std::thread::spawn(move || { |
| 170 | let _ = org_ssg::serve::run(&s, &o, &BuildOptions::default(), "127.0.0.1", port); | |
| 170 | let _ = orgo::serve::run(&s, &o, &BuildOptions::default(), "127.0.0.1", port); | |
| 171 | 171 | }); |
| 172 | 172 | let deadline = Instant::now() + Duration::from_secs(20); |
| 173 | 173 | while Instant::now() < deadline { |
| @@ -194,16 +194,16 @@ fn serving_a_site_reloads_the_browser_when_a_source_changes() { | ||
| 194 | 194 | let home = get(port, "/", Duration::from_secs(5)).expect("a response"); |
| 195 | 195 | assert!(home.contains("200 OK"), "{home}"); |
| 196 | 196 | assert!(home.contains("First version."), "the page is served: {home}"); |
| 197 | assert!(home.contains("__org-ssg/reload"), "with the reload script: {home}"); | |
| 197 | assert!(home.contains("__orgo/reload"), "with the reload script: {home}"); | |
| 198 | 198 | assert!( |
| 199 | !std::fs::read_to_string(out.join("index.html")).unwrap().contains("__org-ssg"), | |
| 199 | !std::fs::read_to_string(out.join("index.html")).unwrap().contains("__orgo"), | |
| 200 | 200 | "but the file on disk stays clean" |
| 201 | 201 | ); |
| 202 | 202 | |
| 203 | 203 | // A poll for a generation we already have must block, not answer immediately. |
| 204 | 204 | let poller = std::thread::spawn(move || { |
| 205 | 205 | let started = Instant::now(); |
| 206 | let body = get(port, "/__org-ssg/reload?since=0", Duration::from_secs(30)); | |
| 206 | let body = get(port, "/__orgo/reload?since=0", Duration::from_secs(30)); | |
| 207 | 207 | (started.elapsed(), body) |
| 208 | 208 | }); |
| 209 | 209 | std::thread::sleep(Duration::from_millis(400)); |
tests/site.rs +8 −8
| @@ -6,11 +6,11 @@ | ||
| 6 | 6 | |
| 7 | 7 | use camino::Utf8PathBuf; |
| 8 | 8 | |
| 9 | use org_ssg::index::{SymbolTable, TargetId}; | |
| 10 | use org_ssg::parser::parse; | |
| 11 | use org_ssg::render::{render, Html, SyntectHighlighter}; | |
| 12 | use org_ssg::resolve::{resolve, ResolvedDoc}; | |
| 13 | use org_ssg::site::{render_site, BuiltPage}; | |
| 9 | use orgo::index::{SymbolTable, TargetId}; | |
| 10 | use orgo::parser::parse; | |
| 11 | use orgo::render::{render, Html, SyntectHighlighter}; | |
| 12 | use orgo::resolve::{resolve, ResolvedDoc}; | |
| 13 | use orgo::site::{render_site, BuiltPage}; | |
| 14 | 14 | |
| 15 | 15 | fn fixtures() -> Utf8PathBuf { |
| 16 | 16 | Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures") |
| @@ -169,13 +169,13 @@ fn links_resolve_through_the_slug() { | ||
| 169 | 169 | /// be impossible by construction rather than by convention. |
| 170 | 170 | #[test] |
| 171 | 171 | fn slugs_cannot_escape_the_output_directory() { |
| 172 | use org_ssg::model::Keywords; | |
| 172 | use orgo::model::Keywords; | |
| 173 | 173 | let source = Utf8PathBuf::from("blog/post.org"); |
| 174 | 174 | let slugged = |value: &str| { |
| 175 | 175 | let keywords = Keywords { |
| 176 | 176 | entries: vec![("SLUG".to_string(), value.to_string())], |
| 177 | 177 | }; |
| 178 | org_ssg::util::output_path(&source, &keywords).to_string() | |
| 178 | orgo::util::output_path(&source, &keywords).to_string() | |
| 179 | 179 | }; |
| 180 | 180 | assert_eq!(slugged("../../etc/passwd"), "blog/etc-passwd.html"); |
| 181 | 181 | assert_eq!(slugged("/absolute"), "blog/absolute.html"); |
| @@ -190,7 +190,7 @@ fn slugs_cannot_escape_the_output_directory() { | ||
| 190 | 190 | /// invisible in the source filenames, so the build refuses rather than picking a winner. |
| 191 | 191 | #[test] |
| 192 | 192 | fn colliding_slugs_are_a_build_error() { |
| 193 | let dir = std::env::temp_dir().join(format!("org-ssg-slug-{}", std::process::id())); | |
| 193 | let dir = std::env::temp_dir().join(format!("orgo-slug-{}", std::process::id())); | |
| 194 | 194 | let dir = Utf8PathBuf::from_path_buf(dir).expect("utf-8 temp dir"); |
| 195 | 195 | let _ = std::fs::remove_dir_all(&dir); |
| 196 | 196 | std::fs::create_dir_all(&dir).unwrap(); |
tests/snapshots/oracle__oracle_blocks.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 67/77 skeleton lines (87.0%) |
| 6 | 6 | deliberate: 11 line(s) — pre-code ×6, semantic-emphasis ×4, verse-trailing-break ×1 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <h2> |
| 11 | 11 | "Quote" |
tests/snapshots/oracle__oracle_core.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 51/58 skeleton lines (87.9%) |
| 6 | 6 | deliberate: 7 line(s) — list-per-bullet-type ×2, pre-code ×2, semantic-emphasis ×2 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <p> |
| 11 | 11 | "Intro paragraph with a bare URL" |
tests/snapshots/oracle__oracle_elements.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 64/82 skeleton lines (78.0%) |
| 6 | 6 | deliberate: 26 line(s) — footnote-anchor-naming ×1, footnote-section-shape ×2, id-link-resolution ×1, no-colgroup ×1, pre-code ×2 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <h2> |
| 11 | 11 | "Code and tables" |
tests/snapshots/oracle__oracle_footnote.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 30/53 skeleton lines (56.6%) |
| 6 | 6 | deliberate: 29 line(s) — footnote-anchor-naming ×3, footnote-section-shape ×6, semantic-emphasis ×2 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <p> |
| 11 | 11 | "Text with a reference." |
tests/snapshots/oracle__oracle_headings.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 28/30 skeleton lines (93.3%) |
| 6 | 6 | deliberate: 2 line(s) — semantic-time ×2 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <h2> |
| 11 | 11 | ~ semantic-time (1 line(s)) |
tests/snapshots/oracle__oracle_images.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 30/42 skeleton lines (71.4%) |
| 6 | 6 | deliberate: 14 line(s) — figure-element ×8, semantic-emphasis ×2 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <h2> |
| 11 | 11 | "Bare image" |
tests/snapshots/oracle__oracle_lists.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 109/111 skeleton lines (98.2%) |
| 6 | 6 | deliberate: 2 line(s) — semantic-emphasis ×2 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <h2> |
| 11 | 11 | "Nesting" |
tests/snapshots/oracle__oracle_minimal.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 38/42 skeleton lines (90.5%) |
| 6 | 6 | deliberate: 4 line(s) — semantic-emphasis ×4 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <p> |
| 11 | 11 | "A single paragraph of preamble text before any heading." |
tests/snapshots/oracle__oracle_table.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 30/34 skeleton lines (88.2%) |
| 6 | 6 | deliberate: 4 line(s) — no-colgroup ×1 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <table> |
| 11 | 11 | ~ no-colgroup (4 line(s)) |
tests/snapshots/oracle__oracle_timestamps.snap +1 −1
| @@ -5,7 +5,7 @@ expression: report | ||
| 5 | 5 | agreement: 25/62 skeleton lines (40.3%) |
| 6 | 6 | deliberate: 37 line(s) — semantic-time ×5 |
| 7 | 7 | unexplained: 0 line(s) |
| 8 | (- org-ssg, + emacs, ~ a difference we mean to have) | |
| 8 | (- orgo, + emacs, ~ a difference we mean to have) | |
| 9 | 9 | |
| 10 | 10 | <h2> |
| 11 | 11 | "Single" |
tests/snapshots/site__site_guide_html.snap +2 −2
| @@ -7,12 +7,12 @@ expression: "page(&pages, \"guide.org\").html" | ||
| 7 | 7 | <head> |
| 8 | 8 | <meta charset="utf-8"> |
| 9 | 9 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 10 | <title>Guide · org-ssg site</title> | |
| 10 | <title>Guide · orgo site</title> | |
| 11 | 11 | <link rel="stylesheet" href="syntax.css"> |
| 12 | 12 | </head> |
| 13 | 13 | <body> |
| 14 | 14 | <header> |
| 15 | <a class="site-title" href="index.html">org-ssg site</a> | |
| 15 | <a class="site-title" href="index.html">orgo site</a> | |
| 16 | 16 | <nav> |
| 17 | 17 | <a href="about.html">About</a> |
| 18 | 18 | <a href="#">Guide</a> |
tests/snapshots/site__site_index_html.snap +2 −2
| @@ -7,12 +7,12 @@ expression: "page(&pages, \"index.org\").html" | ||
| 7 | 7 | <head> |
| 8 | 8 | <meta charset="utf-8"> |
| 9 | 9 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 10 | <title>Home · org-ssg site</title> | |
| 10 | <title>Home · orgo site</title> | |
| 11 | 11 | <link rel="stylesheet" href="syntax.css"> |
| 12 | 12 | </head> |
| 13 | 13 | <body> |
| 14 | 14 | <header> |
| 15 | <a class="site-title" href="index.html">org-ssg site</a> | |
| 15 | <a class="site-title" href="index.html">orgo site</a> | |
| 16 | 16 | <nav> |
| 17 | 17 | <a href="about.html">About</a> |
| 18 | 18 | <a href="guide.html">Guide</a> |
tests/watch.rs +7 −7
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | //! `watch`: the change filter, and one end-to-end run against real filesystem events. |
| 2 | 2 | //! |
| 3 | //! The filter carries the weight here. `org-ssg watch . -o _site` puts the output inside | |
| 3 | //! The filter carries the weight here. `orgo watch . -o _site` puts the output inside | |
| 4 | 4 | //! the source, so a rebuild writes files, writing files raises events, and events trigger |
| 5 | 5 | //! a rebuild — a loop that never stops. That it is a pure function is what makes the |
| 6 | 6 | //! guarantee testable without waiting on a filesystem. |
| @@ -10,15 +10,15 @@ use std::time::{Duration, Instant}; | ||
| 10 | 10 | |
| 11 | 11 | use camino::{Utf8Path, Utf8PathBuf}; |
| 12 | 12 | |
| 13 | use org_ssg::site::{build_site, BuildOptions}; | |
| 14 | use org_ssg::watch::ChangeFilter; | |
| 13 | use orgo::site::{build_site, BuildOptions}; | |
| 14 | use orgo::watch::ChangeFilter; | |
| 15 | 15 | |
| 16 | 16 | fn tmpdir(tag: &str) -> Utf8PathBuf { |
| 17 | 17 | static N: AtomicU32 = AtomicU32::new(0); |
| 18 | 18 | let n = N.fetch_add(1, Ordering::Relaxed); |
| 19 | 19 | let base = Utf8PathBuf::from_path_buf(std::env::temp_dir()) |
| 20 | 20 | .expect("utf-8 temp dir") |
| 21 | .join(format!("org-ssg-watch-{}-{tag}-{n}", std::process::id())); | |
| 21 | .join(format!("orgo-watch-{}-{tag}-{n}", std::process::id())); | |
| 22 | 22 | let _ = std::fs::remove_dir_all(&base); |
| 23 | 23 | std::fs::create_dir_all(&base).unwrap(); |
| 24 | 24 | base |
| @@ -39,7 +39,7 @@ fn changes_under_the_output_directory_are_ignored() { | ||
| 39 | 39 | let filter = ChangeFilter::new(&src, &out); |
| 40 | 40 | assert!(!filter.is_relevant(Utf8Path::new("_site/index.html"))); |
| 41 | 41 | assert!(!filter.is_relevant(Utf8Path::new("_site/blog/post.html"))); |
| 42 | assert!(!filter.is_relevant(Utf8Path::new("_site/.org-ssg-cache.json"))); | |
| 42 | assert!(!filter.is_relevant(Utf8Path::new("_site/.orgo-cache.json"))); | |
| 43 | 43 | assert!(filter.is_relevant(Utf8Path::new("index.org")), "real sources still count"); |
| 44 | 44 | } |
| 45 | 45 | |
| @@ -68,7 +68,7 @@ fn build_inputs_trigger_a_rebuild_even_though_discovery_skips_them() { | ||
| 68 | 68 | std::fs::create_dir_all(&src).unwrap(); |
| 69 | 69 | let filter = ChangeFilter::new(&src, &root.join("out")); |
| 70 | 70 | |
| 71 | assert!(filter.is_relevant(Utf8Path::new("org-ssg.toml"))); | |
| 71 | assert!(filter.is_relevant(Utf8Path::new("orgo.toml"))); | |
| 72 | 72 | assert!(filter.is_relevant(Utf8Path::new("templates/base.html"))); |
| 73 | 73 | assert!(filter.is_relevant(Utf8Path::new("templates/feed.xml"))); |
| 74 | 74 | assert!(filter.is_relevant(Utf8Path::new("style.css")), "assets are copied through"); |
| @@ -149,7 +149,7 @@ fn watching_rebuilds_the_site_when_a_source_file_changes() { | ||
| 149 | 149 | |
| 150 | 150 | let (src_t, out_t) = (src.clone(), out.clone()); |
| 151 | 151 | let handle = std::thread::spawn(move || { |
| 152 | let _ = org_ssg::watch::run(&src_t, &out_t, &BuildOptions::default()); | |
| 152 | let _ = orgo::watch::run(&src_t, &out_t, &BuildOptions::default()); | |
| 153 | 153 | }); |
| 154 | 154 | |
| 155 | 155 | // Give the watcher a moment to register before making the change it should see. |