forked from github/quartz
Compare commits
2 Commits
86eaaae945
...
ec1426241c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec1426241c | ||
|
|
692f23bc36 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -9,3 +9,7 @@ tsconfig.tsbuildinfo
|
|||||||
private/
|
private/
|
||||||
.replit
|
.replit
|
||||||
replit.nix
|
replit.nix
|
||||||
|
erl_crash.dump
|
||||||
|
# content/ is generated by the export script; only keep the placeholder
|
||||||
|
content/*
|
||||||
|
!content/.gitkeep
|
||||||
|
|||||||
40
AGENTS.md
40
AGENTS.md
@@ -17,6 +17,16 @@ Built with TypeScript, Preact, and unified/remark/rehype for markdown processing
|
|||||||
| Build Tool | esbuild |
|
| Build Tool | esbuild |
|
||||||
| Styling | SCSS via esbuild-sass-plugin |
|
| Styling | SCSS via esbuild-sass-plugin |
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
This is a Nix project. Use the provided `flake.nix` to enter a dev shell with Node.js 22 and npm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix develop
|
||||||
|
```
|
||||||
|
|
||||||
|
All `npm` commands below must be run inside the dev shell.
|
||||||
|
|
||||||
## Build, Lint, and Test Commands
|
## Build, Lint, and Test Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -234,6 +244,36 @@ git branch -d feature/my-feature
|
|||||||
|
|
||||||
**Merge direction:** `upstream → main → org-roam → feature/*`
|
**Merge direction:** `upstream → main → org-roam → feature/*`
|
||||||
|
|
||||||
|
## Org-Roam Workflow
|
||||||
|
|
||||||
|
Notes live in a **separate directory** outside this repo. The export script
|
||||||
|
converts them to Markdown via ox-hugo, then Quartz builds the site.
|
||||||
|
|
||||||
|
### Tooling
|
||||||
|
|
||||||
|
The dev shell (`nix develop`) provides:
|
||||||
|
|
||||||
|
- `nodejs_22` — Quartz build
|
||||||
|
- `elixir` — runs the export script
|
||||||
|
- `emacs` + `ox-hugo` — performs the org → markdown conversion
|
||||||
|
|
||||||
|
### Export and build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Export only (wipes content/, exports all .org files)
|
||||||
|
NOTES_DIR=/path/to/notes npm run export
|
||||||
|
|
||||||
|
# Export then build the site
|
||||||
|
NOTES_DIR=/path/to/notes npm run build:notes
|
||||||
|
|
||||||
|
# Positional arg also works
|
||||||
|
elixir scripts/export.exs /path/to/notes
|
||||||
|
```
|
||||||
|
|
||||||
|
The export script (`scripts/export.exs`) runs Emacs in batch mode, calling
|
||||||
|
`org-hugo-export-to-md` per file (per-file mode, not per-subtree). It uses
|
||||||
|
YAML frontmatter (ox-hugo default). `content/` is wiped before each export.
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
- **Client-side scripts**: Use `.inline.ts` suffix, bundled via esbuild
|
- **Client-side scripts**: Use `.inline.ts` suffix, bundled via esbuild
|
||||||
|
|||||||
64
README.md
64
README.md
@@ -1,13 +1,71 @@
|
|||||||
# Quartz v4
|
# Quartz v4 — org-roam edition
|
||||||
|
|
||||||
> “[One] who works with the door open gets all kinds of interruptions, but [they] also occasionally gets clues as to what the world is and what might be important.” — Richard Hamming
|
> "[One] who works with the door open gets all kinds of interruptions, but [they] also occasionally gets clues as to what the world is and what might be important." — Richard Hamming
|
||||||
|
|
||||||
Quartz is a set of tools that helps you publish your [digital garden](https://jzhao.xyz/posts/networked-thought) and notes as a website for free.
|
Quartz is a set of tools that helps you publish your [digital garden](https://jzhao.xyz/posts/networked-thought) and notes as a website for free.
|
||||||
|
|
||||||
🔗 Read the documentation and get started: https://quartz.jzhao.xyz/
|
This fork adds first-class support for [org-roam](https://www.orgroam.com/) notes via [ox-hugo](https://ox-hugo.scripter.co/).
|
||||||
|
|
||||||
|
🔗 Upstream documentation: https://quartz.jzhao.xyz/
|
||||||
|
|
||||||
[Join the Discord Community](https://discord.gg/cRFFHYye7t)
|
[Join the Discord Community](https://discord.gg/cRFFHYye7t)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
This project uses Nix. Enter the development shell, which provides Node.js 22, Elixir, and Emacs with ox-hugo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix develop
|
||||||
|
```
|
||||||
|
|
||||||
|
All commands below must be run inside this shell.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building from org-roam notes
|
||||||
|
|
||||||
|
Your org-roam notes live in a separate directory. Point `NOTES_DIR` at it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Export notes to content/ and build the site
|
||||||
|
NOTES_DIR=/path/to/notes npm run build:notes
|
||||||
|
|
||||||
|
# Export, build, and serve with hot reload
|
||||||
|
NOTES_DIR=/path/to/notes npm run serve:notes
|
||||||
|
|
||||||
|
# Export only (wipes content/ and re-exports all .org files)
|
||||||
|
NOTES_DIR=/path/to/notes npm run export
|
||||||
|
```
|
||||||
|
|
||||||
|
The export script mirrors the subdirectory structure of your notes into `content/`
|
||||||
|
and generates a fallback `index.md` if your notes don't include one.
|
||||||
|
|
||||||
|
### Building without org-roam notes
|
||||||
|
|
||||||
|
If you manage `content/` directly with Markdown files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the site
|
||||||
|
npx quartz build
|
||||||
|
|
||||||
|
# Build and serve with hot reload
|
||||||
|
npx quartz build --serve
|
||||||
|
```
|
||||||
|
|
||||||
|
The site is generated in `public/`. When serving, visit http://localhost:8080.
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run check # type check + format check
|
||||||
|
npm run format # auto-format with Prettier
|
||||||
|
npm run test # run tests
|
||||||
|
```
|
||||||
|
|
||||||
## Sponsors
|
## Sponsors
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1771008912,
|
||||||
|
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
30
flake.nix
Normal file
30
flake.nix
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
description = "Quartz org-roam dev shell";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.nodejs_22
|
||||||
|
pkgs.elixir
|
||||||
|
((pkgs.emacsPackagesFor pkgs.emacs-nox).emacsWithPackages (epkgs: [ epkgs.ox-hugo ]))
|
||||||
|
pkgs.mcp-nixos
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "Node $(node --version) / npm $(npm --version)"
|
||||||
|
elixir --version 2>/dev/null | head -1 || true
|
||||||
|
echo "Emacs $(emacs --version | head -1)"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
16
notes/bus/emt-madrid.org
Normal file
16
notes/bus/emt-madrid.org
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: emt-madrid
|
||||||
|
:END:
|
||||||
|
#+title: EMT Madrid (urban bus)
|
||||||
|
|
||||||
|
Empresa Municipal de Transportes (EMT) operates the urban bus network
|
||||||
|
within the municipality of Madrid — around 200 lines.
|
||||||
|
|
||||||
|
* Notable lines
|
||||||
|
- *Line 27* — connects Embajadores with Barrio de la Concepción, one of the
|
||||||
|
oldest routes in the network.
|
||||||
|
- *Line 34* — Argüelles to Carabanchel, crossing the city centre via Gran Vía.
|
||||||
|
- *Búho (owl) lines* — night buses running from Cibeles from midnight to 6 am.
|
||||||
|
|
||||||
|
* See also
|
||||||
|
- [[id:madrid-transport][Madrid Public Transport]]
|
||||||
17
notes/madrid-transport.org
Normal file
17
notes/madrid-transport.org
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: madrid-transport
|
||||||
|
:END:
|
||||||
|
#+title: Madrid Public Transport
|
||||||
|
|
||||||
|
Madrid has one of the most extensive public transport networks in Europe,
|
||||||
|
operated primarily by [[id:crtm][Consorcio Regional de Transportes de Madrid]] (CRTM).
|
||||||
|
|
||||||
|
* Modes
|
||||||
|
- [[id:metro-madrid][Metro de Madrid]] — 13 lines, ~300 km of track
|
||||||
|
- [[id:emt-madrid][EMT Bus]] — urban buses within the city
|
||||||
|
- Cercanías — suburban rail run by Renfe
|
||||||
|
- Interurbano — regional buses to the wider Community of Madrid
|
||||||
|
|
||||||
|
* Ticketing
|
||||||
|
A single [[https://www.crtm.es][tarjeta transporte]] (transport card) works across all modes.
|
||||||
|
The Multi card covers zones A–C and is topped up at any metro station.
|
||||||
18
notes/metro/metro-madrid.org
Normal file
18
notes/metro/metro-madrid.org
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: metro-madrid
|
||||||
|
:END:
|
||||||
|
#+title: Metro de Madrid
|
||||||
|
|
||||||
|
The Madrid Metro is the main rapid transit network in the city, opened in 1919.
|
||||||
|
It is the second oldest metro in the Iberian Peninsula after Barcelona.
|
||||||
|
|
||||||
|
* Key Lines
|
||||||
|
| Line | Name | Colour | Terminals |
|
||||||
|
|------+-----------------+--------+------------------------------|
|
||||||
|
| L1 | Pinar de Chamartín–Valdecarros | Blue | Pinar de Chamartín / Valdecarros |
|
||||||
|
| L6 | Circular | Grey | Circular (loop) |
|
||||||
|
| L10 | — | Dark blue | Hospital Infanta Sofía / Tres Olivos |
|
||||||
|
|
||||||
|
* See also
|
||||||
|
- [[id:madrid-transport][Madrid Public Transport]]
|
||||||
|
- [[id:sol-interchange][Sol interchange]]
|
||||||
12
notes/metro/sol-interchange.org
Normal file
12
notes/metro/sol-interchange.org
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: sol-interchange
|
||||||
|
:END:
|
||||||
|
#+title: Sol (interchange)
|
||||||
|
|
||||||
|
Sol is the busiest interchange station in the Madrid Metro, sitting beneath
|
||||||
|
Puerta del Sol in the city centre.
|
||||||
|
|
||||||
|
Lines serving Sol: [[id:metro-madrid][L1]], L2, L3.
|
||||||
|
|
||||||
|
It also connects to the Cercanías hub underneath, making it the de-facto
|
||||||
|
zero point of Madrid's public transport.
|
||||||
22
notes/roads/crtm.org
Normal file
22
notes/roads/crtm.org
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: crtm
|
||||||
|
:END:
|
||||||
|
#+title: CRTM — Consorcio Regional de Transportes de Madrid
|
||||||
|
|
||||||
|
The CRTM is the regional authority that coordinates public transport across
|
||||||
|
the Community of Madrid. It does not operate services directly but sets
|
||||||
|
fares, zones, and integration policy.
|
||||||
|
|
||||||
|
* Fare zones
|
||||||
|
| Zone | Coverage |
|
||||||
|
|-------+-----------------------------|
|
||||||
|
| A | Municipality of Madrid |
|
||||||
|
| B1 | Inner ring municipalities |
|
||||||
|
| B2 | Outer ring municipalities |
|
||||||
|
| B3 | Further suburban area |
|
||||||
|
| C1–C2 | Commuter belt |
|
||||||
|
|
||||||
|
* Related
|
||||||
|
- [[id:madrid-transport][Madrid Public Transport]]
|
||||||
|
- [[id:metro-madrid][Metro de Madrid]]
|
||||||
|
- [[id:emt-madrid][EMT Madrid]]
|
||||||
19
notes/roads/m30.org
Normal file
19
notes/roads/m30.org
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: m30
|
||||||
|
:END:
|
||||||
|
#+title: M-30
|
||||||
|
|
||||||
|
The M-30 is Madrid's innermost ring road, circling the city centre at a
|
||||||
|
radius of roughly 3–5 km from Puerta del Sol.
|
||||||
|
|
||||||
|
It runs mostly underground through the Madrid Río tunnel section along the
|
||||||
|
Manzanares river, built during the 2004–2007 renovation that reclaimed the
|
||||||
|
riverbank as a public park.
|
||||||
|
|
||||||
|
* Key junctions
|
||||||
|
- Nudo Norte — connects to A-1 (Burgos) and A-6 (La Coruña)
|
||||||
|
- Nudo Sur — connects to A-4 (Cádiz) and A-42 (Toledo)
|
||||||
|
|
||||||
|
* See also
|
||||||
|
- [[id:crtm][CRTM]]
|
||||||
|
- [[id:madrid-transport][Madrid Public Transport]]
|
||||||
10
opencode.json
Normal file
10
opencode.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"mcp": {
|
||||||
|
"nixos": {
|
||||||
|
"type": "local",
|
||||||
|
"command": ["mcp-nixos"],
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,10 @@
|
|||||||
"check": "tsc --noEmit && npx prettier . --check",
|
"check": "tsc --noEmit && npx prettier . --check",
|
||||||
"format": "npx prettier . --write",
|
"format": "npx prettier . --write",
|
||||||
"test": "tsx --test",
|
"test": "tsx --test",
|
||||||
"profile": "0x -D prof ./quartz/bootstrap-cli.mjs build --concurrency=1"
|
"profile": "0x -D prof ./quartz/bootstrap-cli.mjs build --concurrency=1",
|
||||||
|
"export": "elixir scripts/export.exs",
|
||||||
|
"build:notes": "elixir scripts/export.exs && npx quartz build",
|
||||||
|
"serve:notes": "elixir scripts/export.exs && npx quartz build --serve"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"npm": ">=10.9.2",
|
"npm": ">=10.9.2",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const config: QuartzConfig = {
|
|||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
transformers: [
|
transformers: [
|
||||||
Plugin.FrontMatter(),
|
Plugin.FrontMatter({ delimiters: "+++", language: "toml" }),
|
||||||
Plugin.CreatedModifiedDate({
|
Plugin.CreatedModifiedDate({
|
||||||
priority: ["frontmatter", "git", "filesystem"],
|
priority: ["frontmatter", "git", "filesystem"],
|
||||||
}),
|
}),
|
||||||
@@ -66,7 +66,11 @@ const config: QuartzConfig = {
|
|||||||
},
|
},
|
||||||
keepBackground: false,
|
keepBackground: false,
|
||||||
}),
|
}),
|
||||||
Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }),
|
// OxHugoFlavouredMarkdown must come before GitHubFlavoredMarkdown.
|
||||||
|
// Note: not compatible with ObsidianFlavoredMarkdown — use one or the other.
|
||||||
|
// If ox-hugo exports TOML frontmatter, change FrontMatter to:
|
||||||
|
// Plugin.FrontMatter({ delims: "+++", language: "toml" })
|
||||||
|
Plugin.OxHugoFlavouredMarkdown(),
|
||||||
Plugin.GitHubFlavoredMarkdown(),
|
Plugin.GitHubFlavoredMarkdown(),
|
||||||
Plugin.TableOfContents(),
|
Plugin.TableOfContents(),
|
||||||
Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
|
Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
|
||||||
|
|||||||
136
scripts/export.exs
Normal file
136
scripts/export.exs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#!/usr/bin/env elixir
|
||||||
|
# Export org-roam notes (per-file) to content/ via ox-hugo.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# NOTES_DIR=~/notes elixir scripts/export.exs
|
||||||
|
# elixir scripts/export.exs /path/to/notes
|
||||||
|
#
|
||||||
|
# The positional argument takes precedence over the NOTES_DIR env var.
|
||||||
|
|
||||||
|
notes_dir =
|
||||||
|
case System.argv() do
|
||||||
|
[dir | _] -> dir
|
||||||
|
[] ->
|
||||||
|
System.get_env("NOTES_DIR") ||
|
||||||
|
(IO.puts(:stderr, "Usage: NOTES_DIR=/path/to/notes elixir scripts/export.exs"); System.halt(1))
|
||||||
|
end
|
||||||
|
|
||||||
|
notes_dir = Path.expand(notes_dir)
|
||||||
|
repo_root = __DIR__ |> Path.join("..") |> Path.expand()
|
||||||
|
content_dir = Path.join(repo_root, "content")
|
||||||
|
|
||||||
|
unless File.dir?(notes_dir) do
|
||||||
|
IO.puts(:stderr, "Error: notes directory does not exist: #{notes_dir}")
|
||||||
|
System.halt(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wipe content/, preserving .gitkeep
|
||||||
|
IO.puts("==> Wiping #{content_dir}")
|
||||||
|
|
||||||
|
content_dir
|
||||||
|
|> File.ls!()
|
||||||
|
|> Enum.reject(&(&1 == ".gitkeep"))
|
||||||
|
|> Enum.each(fn entry ->
|
||||||
|
Path.join(content_dir, entry) |> File.rm_rf!()
|
||||||
|
end)
|
||||||
|
|
||||||
|
# Collect all .org files
|
||||||
|
IO.puts("==> Exporting org files from #{notes_dir}")
|
||||||
|
|
||||||
|
org_files =
|
||||||
|
Path.join(notes_dir, "**/*.org")
|
||||||
|
|> Path.wildcard()
|
||||||
|
|
||||||
|
if org_files == [] do
|
||||||
|
IO.puts("No .org files found in #{notes_dir}")
|
||||||
|
System.halt(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Export each file via emacs --batch
|
||||||
|
results =
|
||||||
|
Enum.map(org_files, fn orgfile ->
|
||||||
|
IO.puts(" exporting: #{orgfile}")
|
||||||
|
|
||||||
|
# Mirror the notes subdirectory structure under content/
|
||||||
|
section =
|
||||||
|
orgfile
|
||||||
|
|> Path.dirname()
|
||||||
|
|> Path.relative_to(notes_dir)
|
||||||
|
|
||||||
|
{output, exit_code} =
|
||||||
|
System.cmd(
|
||||||
|
"emacs",
|
||||||
|
[
|
||||||
|
"--batch",
|
||||||
|
"--eval", "(require 'ox-hugo)",
|
||||||
|
"--eval", ~s[(setq org-hugo-base-dir "#{repo_root}")],
|
||||||
|
"--eval", ~s[(setq org-hugo-default-section-directory "#{section}")],
|
||||||
|
"--visit", orgfile,
|
||||||
|
"--funcall", "org-hugo-export-to-md"
|
||||||
|
],
|
||||||
|
stderr_to_stdout: true
|
||||||
|
)
|
||||||
|
|
||||||
|
# Filter noisy emacs startup lines, same as the shell script
|
||||||
|
filtered =
|
||||||
|
output
|
||||||
|
|> String.split("\n")
|
||||||
|
|> Enum.reject(&String.match?(&1, ~r/^Loading|^ad-handle|^For information/))
|
||||||
|
|> Enum.join("\n")
|
||||||
|
|
||||||
|
if filtered != "", do: IO.puts(filtered)
|
||||||
|
|
||||||
|
{orgfile, exit_code}
|
||||||
|
end)
|
||||||
|
|
||||||
|
failures = Enum.filter(results, fn {_, code} -> code != 0 end)
|
||||||
|
|
||||||
|
if failures != [] do
|
||||||
|
IO.puts(:stderr, "\nFailed to export #{length(failures)} file(s):")
|
||||||
|
Enum.each(failures, fn {f, code} -> IO.puts(:stderr, " [exit #{code}] #{f}") end)
|
||||||
|
System.halt(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
md_count =
|
||||||
|
Path.join(content_dir, "**/*.md")
|
||||||
|
|> Path.wildcard()
|
||||||
|
|> length()
|
||||||
|
|
||||||
|
# Generate a default index.md if none was exported
|
||||||
|
index_path = Path.join(content_dir, "index.md")
|
||||||
|
|
||||||
|
unless File.exists?(index_path) do
|
||||||
|
IO.puts("==> Generating default index.md")
|
||||||
|
|
||||||
|
pages =
|
||||||
|
Path.join(content_dir, "**/*.md")
|
||||||
|
|> Path.wildcard()
|
||||||
|
|> Enum.map(fn path ->
|
||||||
|
slug = Path.relative_to(path, content_dir) |> Path.rootname()
|
||||||
|
|
||||||
|
title =
|
||||||
|
path
|
||||||
|
|> File.read!()
|
||||||
|
|> then(fn content ->
|
||||||
|
case Regex.run(~r/^title\s*=\s*"(.+)"/m, content) do
|
||||||
|
[_, t] -> t
|
||||||
|
_ -> slug
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
{slug, title}
|
||||||
|
end)
|
||||||
|
|> Enum.sort_by(fn {_, title} -> title end)
|
||||||
|
|> Enum.map(fn {slug, title} -> "- [#{title}](#{slug})" end)
|
||||||
|
|> Enum.join("\n")
|
||||||
|
|
||||||
|
File.write!(index_path, """
|
||||||
|
---
|
||||||
|
title: Index
|
||||||
|
---
|
||||||
|
|
||||||
|
#{pages}
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
|
||||||
|
IO.puts("==> Done. #{md_count} markdown files in #{content_dir}")
|
||||||
Reference in New Issue
Block a user