Skip to content

Commit d2eb7f5

Browse files
authored
Merge pull request #449 from control-toolbox/vitepress
Make exception display color-aware for DocumenterVitepress
2 parents 602b784 + 73e80c2 commit d2eb7f5

20 files changed

Lines changed: 1464 additions & 34 deletions

docs/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
build/
2+
node_modules/
3+
package-lock.json
4+
Manifest.toml

docs/MIGRATION.md

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# Migration from Documenter to DocumenterVitepress
2+
3+
## Migration Steps
4+
5+
### 1. Update dependencies
6+
7+
File **docs/Project.toml**
8+
9+
- Add `DocumenterVitepress` (UUID: `4710194d-e776-4893-9690-8d956a29c365`)
10+
- Add `LiveServer` for local preview
11+
- Keep `Documenter` as a dependency
12+
13+
```toml
14+
[deps]
15+
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
16+
DocumenterVitepress = "4710194d-e776-4893-9690-8d956a29c365"
17+
LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589"
18+
19+
[compat]
20+
Documenter = "1"
21+
DocumenterVitepress = "0.3"
22+
LiveServer = "1"
23+
```
24+
25+
### 2. Modify make.jl
26+
27+
File **docs/make.jl**
28+
29+
- Add `using DocumenterVitepress`
30+
- Replace `format=Documenter.HTML(...)` with `format=DocumenterVitepress.MarkdownVitepress(...)`
31+
- Replace `deploydocs` with `DocumenterVitepress.deploydocs`
32+
33+
```julia
34+
using Documenter
35+
using DocumenterVitepress
36+
37+
makedocs(;
38+
# ... other arguments ...
39+
format=DocumenterVitepress.MarkdownVitepress(;
40+
repo="https://github.com/control-toolbox/CTBase.jl",
41+
devbranch="main",
42+
devurl="dev",
43+
sidebar_drawer=true,
44+
),
45+
# ...
46+
)
47+
48+
DocumenterVitepress.deploydocs(;
49+
repo="github.com/control-toolbox/CTBase.jl.git",
50+
devbranch="main",
51+
push_preview=true,
52+
)
53+
```
54+
55+
### 3. Install Julia dependencies
56+
57+
After editing `docs/Project.toml`, resolve and instantiate (the Manifest must be regenerated to include the new packages):
58+
59+
```bash
60+
julia --project=docs -e 'using Pkg; Pkg.resolve(); Pkg.instantiate()'
61+
```
62+
63+
### 4. Generate Vitepress configuration files
64+
65+
`generate_template` requires `DocumenterVitepress` to be installed (step 3 must be done first).
66+
67+
```bash
68+
julia --project=docs -e 'using DocumenterVitepress; DocumenterVitepress.generate_template("docs", "CTBase")'
69+
```
70+
71+
This creates the following files (do not create them manually):
72+
73+
In `docs/src/`:
74+
75+
- `.vitepress/config.mts` - Main Vitepress configuration
76+
- `.vitepress/theme/index.ts` - Theme customization
77+
- `.vitepress/theme/style.css` - Custom CSS styles
78+
- `.vitepress/theme/docstrings.css` - Docstring block styles
79+
- `.vitepress/mathjax-plugin.ts` - MathJax plugin
80+
- `.vitepress/julia-repl-transformer.ts` - Julia REPL transformer
81+
- `components/VersionPicker.vue` - Version picker navbar component
82+
- `components/SidebarDrawerToggle.vue` - Sidebar collapse toggle
83+
- `components/AuthorBadge.vue` - Author badge component
84+
- `components/Authors.vue` - Authors list component
85+
86+
At the root of `docs/`:
87+
88+
- `package.json` - npm dependencies
89+
- `.gitignore` - ignores `build/`, `node_modules/`, `package-lock.json`, `Manifest.toml`
90+
91+
### 5. Patch config.mts for remote assets
92+
93+
The generated `docs/src/.vitepress/config.mts` does not include the control-toolbox CSS/JS assets. Edit the `head` section to add them.
94+
95+
Replace the generated `head` block:
96+
97+
```typescript
98+
head: [
99+
['link', { rel: 'icon', href: 'REPLACE_ME_DOCUMENTER_VITEPRESS_FAVICON' }],
100+
['script', {src: `${getBaseRepository(baseTemp.base)}versions.js`}],
101+
['script', {src: `${baseTemp.base}siteinfo.js`}]
102+
],
103+
```
104+
105+
With the remote assets version (Option B):
106+
107+
```typescript
108+
head: [
109+
['link', { rel: 'icon', href: 'REPLACE_ME_DOCUMENTER_VITEPRESS_FAVICON' }],
110+
['link', { rel: 'stylesheet', href: 'https://control-toolbox.org/assets/css/vitepress-documentation.css' }],
111+
['script', {src: `${getBaseRepository(baseTemp.base)}versions.js`}],
112+
['script', {src: 'https://control-toolbox.org/assets/js/vitepress-documentation.js'}],
113+
['script', {src: `${baseTemp.base}siteinfo.js`}]
114+
],
115+
```
116+
117+
#### Option A: Local assets (for development only)
118+
119+
If assets are not yet published remotely, use local files placed in `docs/src/assets/`. Add a Vite plugin in the `vite.plugins` section of `config.mts` to copy them at build time:
120+
121+
```typescript
122+
import { copyFileSync, mkdirSync } from 'fs'
123+
124+
let ctOutDir = ''
125+
126+
// inside vite.plugins:
127+
{
128+
name: 'ct-static-assets',
129+
apply: 'build' as const,
130+
configResolved(config: any) {
131+
if (!config.build.ssr) ctOutDir = config.build.outDir
132+
},
133+
closeBundle() {
134+
if (!ctOutDir) return
135+
const assetsDir = path.join(ctOutDir, 'assets')
136+
mkdirSync(assetsDir, { recursive: true })
137+
for (const file of [
138+
'vitepress-documentation.css',
139+
'vitepress-documentation.js',
140+
]) {
141+
try { copyFileSync(path.resolve(__dirname, '../assets', file), path.join(assetsDir, file)) } catch (_) {}
142+
}
143+
}
144+
},
145+
```
146+
147+
And reference them in `head` using `${baseTemp.base}assets/...` instead of the remote URLs.
148+
149+
### 6. Install npm dependencies
150+
151+
```bash
152+
cd docs && npm install
153+
```
154+
155+
### 7. Local build and preview
156+
157+
```bash
158+
# Generate documentation
159+
julia --project=docs docs/make.jl
160+
161+
# Local preview (output is in docs/build/1/, not docs/build/)
162+
julia --project=docs -e 'using LiveServer; LiveServer.serve(dir="docs/build/1")'
163+
```
164+
165+
## Important notes
166+
167+
- **ANSI color codes in @repl blocks**: DocumenterVitepress does not automatically convert ANSI escape codes to HTML in `@repl` blocks (unlike `@example` blocks which are converted to `ansi` code blocks). To avoid raw ANSI codes appearing in the generated markdown, wrap `showerror` calls with `IOContext(stdout, :color => false)`:
168+
169+
```julia
170+
try
171+
throw(CTBase.Exceptions.IncorrectArgument("n must be positive"; got="-1"))
172+
catch e
173+
showerror(IOContext(stdout, :color => false), e)
174+
end
175+
```
176+
177+
This is a known limitation tracked in [LuxDL/DocumenterVitepress.jl#321](https://github.com/LuxDL/DocumenterVitepress.jl/issues/321).
178+
179+
- **Color-aware display functions**: If your package has custom display functions that use ANSI codes (e.g., error display helpers), make them color-aware by checking `get(io, :color, false)`. For example, in CTBase we implemented a helper function and updated all ANSI styling primitives:
180+
181+
```julia
182+
# src/Exceptions/display.jl
183+
_apply_ansi(s, code, io::IO) = get(io, :color, false) ? "\033[$(code)m$(s)\033[0m" : s
184+
185+
_dim(s, io::IO) = _apply_ansi(s, "2", io)
186+
_bold(s, io::IO) = _apply_ansi(s, "1", io)
187+
_red(s, io::IO) = _apply_ansi(s, "1;31", io)
188+
_yellow(s, io::IO) = _apply_ansi(s, "33", io)
189+
_green(s, io::IO) = _apply_ansi(s, "32", io)
190+
```
191+
192+
Then propagate the `io` parameter to all call sites in display functions:
193+
194+
```julia
195+
function _format_user_friendly_error(io::IO, e::CTException)
196+
# ...
197+
print(io, _red(type_name, io)) # Pass io to the helper
198+
# ...
199+
end
200+
```
201+
202+
This ensures:
203+
- REPL / GitHub Actions → colors enabled (`:color => true` by default)
204+
- Documenter / VitePress → plain text when wrapped with `IOContext(stdout, :color => false)`)
205+
206+
- **Git repository required**: DocumenterVitepress requires a git repository to function
207+
- **Build output**: Documentation is generated in `docs/build/1/` (not `docs/build/`)
208+
- **Do not create Vitepress files manually**: always use `generate_template` (step 4) — it generates all config, theme, components, and npm files
209+
- **Symlinks**: Before deployment, remove symlinks on the `gh-pages` branch (stable, v1, etc.)
210+
211+
Documenter.jl uses symlinks on the `gh-pages` branch to manage documentation versions:
212+
213+
- `stable` → points to the current stable version (e.g., `v0.5.0`)
214+
- `v1` → points to the latest major version
215+
- `v0.1`, `v0.2`, etc. → point to specific versions
216+
217+
DocumenterVitepress cannot write to symlinks. If you are migrating from an existing Documenter documentation, your `gh-pages` branch likely contains these symlinks. They must be manually removed before the first deployment with DocumenterVitepress.
218+
219+
**How to remove symlinks:**
220+
221+
1. Go to GitHub: `https://github.com/control-toolbox/CTBase.jl/tree/gh-pages`
222+
2. Symlinks are identifiable by a small arrow ↗
223+
3. Click on each symlink (stable, v1, etc.)
224+
4. Delete them via the context menu
225+
226+
DocumenterVitepress handles versions differently, without using symlinks.
227+
- **Vitepress configuration**: The `REPLACE_ME_DOCUMENTER_VITEPRESS` strings are automatically replaced during the build
228+
- **TypeScript errors**: TypeScript errors in the IDE regarding `sidebar` and missing `node_modules` are normal before `npm install` — DocumenterVitepress replaces these values during the build
229+
230+
## Deployment
231+
232+
Deployment is done automatically via CI with `DocumenterVitepress.deploydocs`. Ensure that:
233+
234+
- The GitHub repository exists
235+
- The `gh-pages` branch does not contain symlinks
236+
- CI workflows are configured for DocumenterVitepress

docs/Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
[deps]
22
Coverage = "a2441757-f6aa-5fb2-8edb-039e3f45d037"
33
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
4+
DocumenterVitepress = "4710194d-e776-4893-9690-8d956a29c365"
5+
LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589"
46
MarkdownAST = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"
57

68
[compat]
79
Coverage = "1"
810
Documenter = "1"
11+
DocumenterVitepress = "0.3"
12+
LiveServer = "1"
913
MarkdownAST = "0.1"
1014
julia = "1.10"

docs/make.jl

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pushfirst!(LOAD_PATH, joinpath(@__DIR__))
44
pushfirst!(LOAD_PATH, joinpath(@__DIR__, ".."))
55

66
using Documenter
7+
using DocumenterVitepress
78
using CTBase
89
using Markdown
910
using MarkdownAST: MarkdownAST
@@ -56,14 +57,11 @@ with_api_reference(src_dir) do api_pages
5657
remotes=nothing, # Disable remote links. Needed for DocumenterReference
5758
warnonly=[:cross_references],
5859
sitename="CTBase.jl",
59-
format=Documenter.HTML(;
60-
repolink="https://" * repo_url,
61-
prettyurls=false,
62-
size_threshold_ignore=["api.md", "dev.md"],
63-
assets=[
64-
asset("https://control-toolbox.org/assets/css/documentation.css"),
65-
asset("https://control-toolbox.org/assets/js/documentation.js"),
66-
],
60+
format=DocumenterVitepress.MarkdownVitepress(;
61+
repo="https://" * repo_url,
62+
devbranch="main",
63+
devurl="dev",
64+
sidebar_drawer=true,
6765
),
6866
pages=[
6967
"Introduction" => "index.md",
@@ -83,4 +81,8 @@ end
8381
# ═══════════════════════════════════════════════════════════════════════════════
8482
# Deploy documentation to GitHub Pages
8583
# ═══════════════════════════════════════════════════════════════════════════════
86-
deploydocs(; repo=repo_url * ".git", devbranch="main")
84+
DocumenterVitepress.deploydocs(;
85+
repo=repo_url * ".git",
86+
devbranch="main",
87+
push_preview=true,
88+
)

docs/package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"devDependencies": {
3+
"@types/markdown-it-footnote": "^3.0.4",
4+
"@types/node": "^25.3.5"
5+
},
6+
"scripts": {
7+
"docs:dev": "vitepress dev build/.documenter",
8+
"docs:build": "vitepress build build/.documenter",
9+
"docs:preview": "vitepress preview build/.documenter"
10+
},
11+
"dependencies": {
12+
"@mdit/plugin-mathjax": "^0.26.1",
13+
"@mdit/plugin-tex": "^0.24.1",
14+
"@nolebase/vitepress-plugin-enhanced-readabilities": "^2.18.2",
15+
"markdown-it": "^14.1.0",
16+
"markdown-it-footnote": "^4.0.0",
17+
"vitepress": "^1.6.4",
18+
"vitepress-plugin-tabs": "^0.9.0"
19+
}
20+
}

0 commit comments

Comments
 (0)