Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 44 additions & 20 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10

jobs:
changes:
Expand All @@ -37,6 +36,9 @@ jobs:
- 'crates/**'
- 'ndg/**'
- 'ndg-commonmark/**'
- 'flake.nix'
- 'flake.lock'
- 'nix/internal/shell.nix'
build:
name: Build with Cargo
needs: changes
Expand All @@ -53,15 +55,31 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Nix
if: runner.os == 'Linux'
uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31

- name: Expose devshell toolchain to cache action
if: runner.os == 'Linux'
run: echo "$PATH" >> "$GITHUB_PATH"
shell: nix develop --command bash -e -o pipefail {0}

- name: Install Rust
if: runner.os == 'macOS'
uses: dtolnay/rust-toolchain@be39649afda95dbf70f87cce95f68b8d5797b296 # stable
with:
targets: ${{ matrix.target }}

- name: Cache dependencies
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Build
- name: Build with devshell
if: runner.os == 'Linux'
run: cargo build --release --target ${{ matrix.target }}
shell: nix develop --command bash -e -o pipefail {0}

- name: Build with Cargo
if: runner.os == 'macOS'
run: cargo build --release --target ${{ matrix.target }}

features:
Expand All @@ -72,21 +90,22 @@ jobs:
matrix:
backend: [syntastica, syntect]
runs-on: ubuntu-latest
defaults:
run:
shell: nix develop --command bash -e -o pipefail {0}
steps:
- name: Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Rust
uses: dtolnay/rust-toolchain@be39649afda95dbf70f87cce95f68b8d5797b296 # stable
- name: Install Nix
uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31

- name: Expose devshell toolchain to cache action
run: echo "$PATH" >> "$GITHUB_PATH"

- name: Cache dependencies
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Install cargo-nextest
uses: taiki-e/install-action@9983c65e42da123ff25d1f78505eb6de315aa172 # v2.87.20
with:
tool: cargo-nextest

- name: Check backend without Markdown extensions
run: cargo check -p ndg-commonmark --no-default-features --features ${{ matrix.backend }} --lib

Expand All @@ -98,14 +117,18 @@ jobs:
needs: changes
if: ${{ needs.changes.outputs.files_changed == 'true' }}
runs-on: ubuntu-latest
defaults:
run:
shell: nix develop --command bash -e -o pipefail {0}
steps:
- name: Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Rust
uses: dtolnay/rust-toolchain@be39649afda95dbf70f87cce95f68b8d5797b296 # stable
with:
components: clippy, rustfmt
- name: Install Nix
uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31

- name: Expose devshell toolchain to cache action
run: echo "$PATH" >> "$GITHUB_PATH"

- name: Cache dependencies
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
Expand All @@ -124,6 +147,9 @@ jobs:
name: Test
runs-on: ubuntu-latest
needs: build
defaults:
run:
shell: nix develop --command bash -e -o pipefail {0}
permissions:
contents: write
outputs:
Expand All @@ -136,17 +162,15 @@ jobs:
with:
fetch-depth: 2

- name: Install Rust
uses: dtolnay/rust-toolchain@be39649afda95dbf70f87cce95f68b8d5797b296 # stable
- name: Install Nix
uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31

- name: Expose devshell toolchain to cache action
run: echo "$PATH" >> "$GITHUB_PATH"

- name: Cache Dependencies
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Install cargo-nextest
uses: taiki-e/install-action@9983c65e42da123ff25d1f78505eb6de315aa172 # v2.87.20
with:
tool: cargo-nextest

- name: Run nextest
id: nextest
run: |
Expand Down
32 changes: 31 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ changes.
`sidebar.group_by_dir` report errors
([#290](https://github.com/feel-co/ndg/issues/290)).

### Changed

- `ndg init` now generates configuration from the config types and defaults,
keeping TOML field comments and optional-setting examples in sync as settings
are added. JSON uses the same starter values without activating example paths
or sidebar rules. Long time coming honestly.

## [2.10.2]

### Fixed

- Pages now link each script in `script_paths` at `assets/<file name>`, where
Expand All @@ -60,6 +69,25 @@ changes.
navigation links synchronized when non-heading anchors share their IDs, and
preserves explicitly assigned IDs that collide with generated suffixes
([#286](https://github.com/feel-co/ndg/issues/286)).
- Sidebar and per-page TOC links now match rendered heading IDs for headings
containing quoted inline code, such as `"/package"`. Existing heading IDs and
deep links remain unchanged
([#293](https://github.com/feel-co/ndg/issues/293)).

### Changed

- The documentation site now includes the ndg-commonmark syntax and highlighting
guides, with navigation entries and searchable content
([#291](https://github.com/feel-co/ndg/issues/291)).

## [2.10.1]

### Fixed

- Headings excluded from the per-page TOC now still follow the configured
duplicate-anchor policy. Exclusions no longer bypass duplicate validation or
deduplication, including for cached Markdown renders.
- Toggling the sidebar no longer shifts the main content horizontally.

## [2.10.0]

Expand Down Expand Up @@ -712,7 +740,9 @@ documentation using the old hyphen-based format will need to be updated**.
- Prevented panic in theme name fallback logic in ndg-commonmark
- Prevented panic on empty fence character extraction in ndg-commonmark

[Unreleased]: https://github.com/feel-co/ndg/compare/v2.10.0..HEAD
[Unreleased]: https://github.com/feel-co/ndg/compare/v2.10.2..HEAD
[2.10.2]: https://github.com/feel-co/ndg/compare/v2.10.1..v2.10.2
[2.10.1]: https://github.com/feel-co/ndg/compare/v2.10.0..v2.10.1
[2.10.0]: https://github.com/feel-co/ndg/compare/v2.9.0..v2.10.0
[2.9.0]: https://github.com/feel-co/ndg/compare/v2.8.2...v2.9.0
[2.8.2]: https://github.com/feel-co/ndg/compare/v2.8.1...v2.8.2
Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/ndg-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ serde.workspace = true
serde_json.workspace = true
serde_path_to_error.workspace = true
thiserror.workspace = true
toml.workspace = true
toml = { workspace = true, features = [ "display" ] }

[dev-dependencies]
tempfile.workspace = true
Expand Down
6 changes: 4 additions & 2 deletions crates/ndg-config/src/anchor.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::str::FromStr;

use ndg_macros::Configurable;
use ndg_macros::{ConfigTemplate, Configurable};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Default, Serialize, Deserialize, Configurable)]
#[derive(
Debug, Clone, Default, Serialize, Deserialize, Configurable, ConfigTemplate,
)]
#[serde(default, deny_unknown_fields)]
pub struct AnchorConfig {
#[config(key = "legacy_option_id_format")]
Expand Down
6 changes: 4 additions & 2 deletions crates/ndg-config/src/assets.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use ndg_macros::Configurable;
use ndg_macros::{ConfigTemplate, Configurable};
use serde::{Deserialize, Serialize};

/// Configuration for custom assets copying
///
/// Controls how custom assets from the assets directory are copied.
#[derive(Debug, Clone, Serialize, Deserialize, Configurable)]
#[derive(
Debug, Clone, Serialize, Deserialize, Configurable, ConfigTemplate,
)]
#[serde(default, deny_unknown_fields)]
pub struct AssetsConfig {
/// Follow symbolic links when copying assets
Expand Down
57 changes: 54 additions & 3 deletions crates/ndg-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
path::{Path, PathBuf},
};

use ndg_macros::Configurable;
use ndg_macros::{ConfigTemplate, Configurable};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -41,7 +41,9 @@ pub const DEFAULT_TAB_STYLE: &str = "none";
/// generation, including input/output directories, template customization,
/// search, syntax highlighting, and more. Fields are typically loaded from a
/// TOML or JSON config file, but can also be set via CLI arguments.
#[derive(Debug, Clone, Serialize, Deserialize, Configurable)]
#[derive(
Debug, Clone, Serialize, Deserialize, Configurable, ConfigTemplate,
)]
#[serde(default, deny_unknown_fields)]
pub struct Config {
/// Input directory containing markdown files.
Expand All @@ -54,24 +56,30 @@ pub struct Config {

/// Path to options.json file (optional).
#[config(key = "module_options", allow_empty)]
#[template(example = PathBuf::from("options.json"))]
pub module_options: Option<PathBuf>,

/// Path to custom template file.
#[config(key = "template_path", allow_empty)]
#[template(example = PathBuf::from("templates/custom.html"))]
pub template_path: Option<PathBuf>,

/// Path to template directory containing all template files.
#[config(key = "template_dir", allow_empty)]
#[template(example = PathBuf::from("templates"))]
pub template_dir: Option<PathBuf>,

/// Paths to custom stylesheets.
#[template(example = vec![PathBuf::from("assets/custom.css")])]
pub stylesheet_paths: Vec<PathBuf>,

/// Paths to custom JavaScript files.
#[template(example = vec![PathBuf::from("assets/custom.js")])]
pub script_paths: Vec<PathBuf>,

/// Directory containing additional assets.
#[config(key = "assets_dir", allow_empty)]
#[template(example = PathBuf::from("assets"))]
pub assets_dir: Option<PathBuf>,

/// Options for copying custom assets.
Expand All @@ -80,10 +88,12 @@ pub struct Config {

/// Path to manpage URL mappings JSON file.
#[config(key = "manpage_urls_path", allow_empty)]
#[template(example = PathBuf::from("manpage-urls.json"))]
pub manpage_urls_path: Option<PathBuf>,

/// Path to user-defined Tree-sitter query overrides.
#[config(key = "syntax_queries_path", allow_empty)]
#[template(example = PathBuf::from("queries"))]
pub syntax_queries_path: Option<PathBuf>,

/// Title for the documentation.
Expand All @@ -92,6 +102,7 @@ pub struct Config {

/// Number of threads to use for parallel processing.
#[config(key = "jobs", allow_empty)]
#[template(example = 4usize)]
pub jobs: Option<usize>,

/// Whether to generate anchors for headings.
Expand Down Expand Up @@ -158,6 +169,7 @@ pub struct Config {
/// recursively for `.nix` files. Entries with nixdoc comments (`/** ... */`)
/// are extracted and rendered as a library reference page (`lib.html`).
#[serde(default)]
#[template(example = vec![PathBuf::from("lib")])]
pub nixdoc_inputs: Vec<PathBuf>,

/// Index page configuration.
Expand Down Expand Up @@ -645,12 +657,52 @@ impl Config {
None
}

/// Validate filenames used when copying custom scripts into `assets/`.
///
/// This checks destinations without reading source files, so rendering can
/// enforce the same rules as asset copying.
///
/// # Errors
///
/// Returns an error if a script has no filename or two scripts would share
/// the same destination filename.
///
/// # Examples
///
/// ```
/// let config = ndg_config::Config::default();
/// config.validate_script_destinations()?;
/// # Ok::<(), ndg_config::ConfigError>(())
/// ```
pub fn validate_script_destinations(&self) -> Result<(), ConfigError> {
let mut destinations = FxHashMap::default();
for script_path in &self.script_paths {
let filename = script_path.file_name().ok_or_else(|| {
ConfigError::Config(format!(
"Invalid script_paths entry '{}': expected a path with a filename",
script_path.display()
))
})?;
if let Some(previous) = destinations.insert(filename, script_path) {
return Err(ConfigError::Config(format!(
"script_paths entries '{}' and '{}' both copy to 'assets/{}'; \
script filenames must be unique",
previous.display(),
script_path.display(),
filename.to_string_lossy()
)));
}
}
Ok(())
}

/// Validate all paths specified in the configuration
///
/// # Errors
///
/// Returns an error if any configured path does not exist or is invalid.
pub fn validate_paths(&self) -> Result<(), ConfigError> {
self.validate_script_destinations()?;
let mut errors = Vec::new();

// Module options file should exist if specified
Expand Down Expand Up @@ -822,7 +874,6 @@ impl Config {
format: &str,
path: &Path,
) -> Result<(), ConfigError> {
// Get template from the templates module
let config_content = crate::templates::get_template(format)
.map_err(|e| ConfigError::Template(e.to_string()))?;

Expand Down
Loading
Loading