Skip to content

Commit 116e0d1

Browse files
committed
feat(website): auto generate oxfmt docs (#15985)
Corresponding PR: oxc-project/oxc-project.github.io#643 The next PR will get the json schema auto generated as well.
1 parent 38b7bc4 commit 116e0d1

File tree

23 files changed

+220
-102
lines changed

23 files changed

+220
-102
lines changed

Cargo.lock

Lines changed: 17 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/oxfmt/src/command.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const PATHS_ERROR_MESSAGE: &str = "PATH must not contain \"..\"";
2121
#[derive(Debug, Clone, Bpaf)]
2222
#[bpaf(options, version(VERSION))]
2323
pub struct FormatCommand {
24-
#[bpaf(external, fallback(OutputOptions::DefaultWrite))]
24+
#[bpaf(external, fallback(OutputOptions::DefaultWrite), hide_usage)]
2525
pub output_options: OutputOptions,
2626
#[bpaf(external)]
2727
pub basic_options: BasicOptions,
@@ -31,7 +31,7 @@ pub struct FormatCommand {
3131
pub misc_options: MiscOptions,
3232
/// Single file, single path or list of paths.
3333
/// If not provided, current working directory is used.
34-
/// Glob is supported only for exclude patterns like `'!**/fixtures/*.js'.
34+
/// Glob is supported only for exclude patterns like `'!**/fixtures/*.js'`.
3535
// `bpaf(fallback)` seems to have issues with `many` or `positional`,
3636
// so we implement the fallback behavior in code instead.
3737
#[bpaf(positional("PATH"), many, guard(validate_paths, PATHS_ERROR_MESSAGE))]
@@ -65,14 +65,14 @@ pub struct BasicOptions {
6565
pub struct IgnoreOptions {
6666
/// Path to ignore file(s). Can be specified multiple times.
6767
/// If not specified, .gitignore and .prettierignore in the current directory are used.
68-
#[bpaf(argument("PATH"), many)]
68+
#[bpaf(argument("PATH"), many, hide_usage)]
6969
pub ignore_path: Vec<PathBuf>,
7070
/// Format code in node_modules directory (skipped by default)
7171
#[bpaf(switch, hide_usage)]
7272
pub with_node_modules: bool,
7373
}
7474

75-
/// Miscellaneous
75+
/// Misc Options
7676
#[derive(Debug, Clone, Bpaf)]
7777
pub struct MiscOptions {
7878
/// Start language server protocol (LSP) server

justfile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,14 @@ watch-playground:
247247
# When testing changes to the website documentation, you may also want to run `dprint fmt --staged`
248248
# in the website directory.
249249
website path:
250-
cargo run -p website -- linter-rules --table {{path}}/src/docs/guide/usage/linter/generated-rules.md --rule-docs {{path}}/src/docs/guide/usage/linter/rules --git-ref $(git rev-parse HEAD)
251-
cargo run -p website -- linter-cli > {{path}}/src/docs/guide/usage/linter/generated-cli.md
252-
cargo run -p website -- linter-schema-markdown > {{path}}/src/docs/guide/usage/linter/generated-config.md
250+
cargo run -p website_linter rules --table {{path}}/src/docs/guide/usage/linter/generated-rules.md --rule-docs {{path}}/src/docs/guide/usage/linter/rules --git-ref $(git rev-parse HEAD)
251+
cargo run -p website_linter cli > {{path}}/src/docs/guide/usage/linter/generated-cli.md
252+
cargo run -p website_linter schema-markdown > {{path}}/src/docs/guide/usage/linter/generated-config.md
253+
cargo run -p website_formatter cli > {{path}}/src/docs/guide/usage/formatter/generated-cli.md
253254

254255
# Generate linter schema json for `npm/oxlint/configuration_schema.json`
255256
linter-schema-json:
256-
cargo run -p website -- linter-schema-json > npm/oxlint/configuration_schema.json
257+
cargo run -p website_linter schema-json > npm/oxlint/configuration_schema.json
257258

258259
# Automatically DRY up Cargo.toml manifests in a workspace
259260
autoinherit:

tasks/website/src/lib.rs

Lines changed: 0 additions & 3 deletions
This file was deleted.

tasks/website/src/linter/cli.rs

Lines changed: 0 additions & 64 deletions
This file was deleted.

tasks/website/src/linter/mod.rs

Lines changed: 0 additions & 9 deletions
This file was deleted.

tasks/website_common/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "website_common"
3+
version = "0.0.0"
4+
edition.workspace = true
5+
license.workspace = true
6+
publish = false
7+
8+
[lints]
9+
workspace = true
10+
11+
[dependencies]

tasks/website_common/src/lib.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/// Generate CLI documentation from bpaf-generated markdown.
2+
///
3+
/// Takes raw markdown from bpaf's `render_markdown()` and processes it into
4+
/// website-ready format with proper frontmatter and section headers.
5+
///
6+
/// # Arguments
7+
/// * `raw_markdown` - The markdown string from bpaf's render_markdown()
8+
/// * `tool_name` - The name of the tool (e.g., "oxlint", "oxfmt") used to strip the header
9+
/// * `gitignore_note_anchor` - Optional section header after which to insert the gitignore note
10+
///
11+
/// # Returns
12+
/// Processed markdown ready for the website
13+
#[expect(clippy::disallowed_methods)]
14+
pub fn generate_cli_docs(
15+
raw_markdown: &str,
16+
tool_name: &str,
17+
gitignore_note_anchor: Option<&str>,
18+
) -> String {
19+
// Remove the extra header
20+
let header = format!("# {tool_name}\n");
21+
let markdown = raw_markdown.trim_start_matches(header.as_str());
22+
23+
// Add ---\nsearch: false\n---\n at the top to prevent Vitepress from indexing this file.
24+
let markdown = format!("---\nsearch: false\n---\n\n{markdown}");
25+
26+
// Hack usage line
27+
let markdown = markdown.replacen("**Usage**:", "## Usage\n", 1);
28+
29+
let markdown = markdown
30+
.split('\n')
31+
.flat_map(|line| {
32+
// Hack the bug on the line containing `###`
33+
if line.contains("###") {
34+
line.split("###").map(str::trim).chain(["\n"]).collect::<Vec<_>>()
35+
} else {
36+
vec![line]
37+
}
38+
})
39+
.map(|line| {
40+
// Make `** title **` to `## title`
41+
if let Some(line) = line.strip_prefix("**")
42+
&& let Some(line) = line.strip_suffix("**")
43+
{
44+
format!("## {line}")
45+
} else {
46+
line.to_string()
47+
}
48+
})
49+
.collect::<Vec<_>>()
50+
.join("\n");
51+
52+
// Add note about .gitignore only being respected inside Git repositories
53+
if let Some(anchor) = gitignore_note_anchor {
54+
let search_pattern = format!("\n\n## {anchor}\n");
55+
let replacement = format!(
56+
"\n\n> [!NOTE]\n> `.gitignore` is only respected inside a Git repository.\n\n## {anchor}\n"
57+
);
58+
markdown.replace(&search_pattern, &replacement)
59+
} else {
60+
markdown
61+
}
62+
}

tasks/website_formatter/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "website_formatter"
3+
version = "0.0.0"
4+
edition.workspace = true
5+
license.workspace = true
6+
publish = false
7+
8+
[lints]
9+
workspace = true
10+
11+
[[bin]]
12+
name = "website_formatter"
13+
test = false
14+
doctest = false
15+
16+
[dependencies]
17+
bpaf = { workspace = true, features = ["docgen"] }
18+
oxfmt = { path = "../../apps/oxfmt", default-features = false }
19+
pico-args = { workspace = true }
20+
website_common = { path = "../website_common" }
21+
22+
[dev-dependencies]
23+
insta = { workspace = true }
24+
25+
[package.metadata.cargo-shear]
26+
ignored = ["bpaf"]

tasks/website_formatter/src/cli.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use oxfmt::format_command;
2+
use website_common::generate_cli_docs;
3+
4+
#[test]
5+
fn test_cli() {
6+
let snapshot = generate_cli();
7+
insta::with_settings!({ prepend_module_to_snapshot => false }, {
8+
insta::assert_snapshot!(snapshot);
9+
});
10+
}
11+
12+
#[test]
13+
fn test_cli_terminal() {
14+
let snapshot = oxfmt::format_command().run_inner(&["--help"]).unwrap_err().unwrap_stdout();
15+
insta::with_settings!({ prepend_module_to_snapshot => false }, {
16+
insta::assert_snapshot!(snapshot);
17+
});
18+
}
19+
20+
// <https://oxc.rs/docs/guide/usage/formatter/cli.html>
21+
#[expect(clippy::print_stdout)]
22+
pub fn print_cli() {
23+
println!("{}", generate_cli());
24+
}
25+
26+
fn generate_cli() -> String {
27+
let markdown = format_command().render_markdown("oxfmt");
28+
generate_cli_docs(&markdown, "oxfmt", None)
29+
}

0 commit comments

Comments
 (0)