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
18 changes: 17 additions & 1 deletion Cargo.lock

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

8 changes: 4 additions & 4 deletions apps/oxfmt/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const PATHS_ERROR_MESSAGE: &str = "PATH must not contain \"..\"";
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version(VERSION))]
pub struct FormatCommand {
#[bpaf(external, fallback(OutputOptions::DefaultWrite))]
#[bpaf(external, fallback(OutputOptions::DefaultWrite), hide_usage)]
pub output_options: OutputOptions,
#[bpaf(external)]
pub basic_options: BasicOptions,
Expand All @@ -31,7 +31,7 @@ pub struct FormatCommand {
pub misc_options: MiscOptions,
/// Single file, single path or list of paths.
/// If not provided, current working directory is used.
/// Glob is supported only for exclude patterns like `'!**/fixtures/*.js'.
/// Glob is supported only for exclude patterns like `'!**/fixtures/*.js'`.
// `bpaf(fallback)` seems to have issues with `many` or `positional`,
// so we implement the fallback behavior in code instead.
#[bpaf(positional("PATH"), many, guard(validate_paths, PATHS_ERROR_MESSAGE))]
Expand Down Expand Up @@ -65,14 +65,14 @@ pub struct BasicOptions {
pub struct IgnoreOptions {
/// Path to ignore file(s). Can be specified multiple times.
/// If not specified, .gitignore and .prettierignore in the current directory are used.
#[bpaf(argument("PATH"), many)]
#[bpaf(argument("PATH"), many, hide_usage)]
pub ignore_path: Vec<PathBuf>,
/// Format code in node_modules directory (skipped by default)
#[bpaf(switch, hide_usage)]
pub with_node_modules: bool,
}

/// Miscellaneous
/// Misc Options
#[derive(Debug, Clone, Bpaf)]
pub struct MiscOptions {
/// Start language server protocol (LSP) server
Expand Down
9 changes: 5 additions & 4 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,14 @@ watch-playground:
# When testing changes to the website documentation, you may also want to run `dprint fmt --staged`
# in the website directory.
website path:
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)
cargo run -p website -- linter-cli > {{path}}/src/docs/guide/usage/linter/generated-cli.md
cargo run -p website -- linter-schema-markdown > {{path}}/src/docs/guide/usage/linter/generated-config.md
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)
cargo run -p website_linter cli > {{path}}/src/docs/guide/usage/linter/generated-cli.md
cargo run -p website_linter schema-markdown > {{path}}/src/docs/guide/usage/linter/generated-config.md
cargo run -p website_formatter cli > {{path}}/src/docs/guide/usage/formatter/generated-cli.md

# Generate linter schema json for `npm/oxlint/configuration_schema.json`
linter-schema-json:
cargo run -p website -- linter-schema-json > npm/oxlint/configuration_schema.json
cargo run -p website_linter schema-json > npm/oxlint/configuration_schema.json

# Automatically DRY up Cargo.toml manifests in a workspace
autoinherit:
Expand Down
3 changes: 0 additions & 3 deletions tasks/website/src/lib.rs

This file was deleted.

64 changes: 0 additions & 64 deletions tasks/website/src/linter/cli.rs

This file was deleted.

9 changes: 0 additions & 9 deletions tasks/website/src/linter/mod.rs

This file was deleted.

11 changes: 11 additions & 0 deletions tasks/website_common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "website_common"
version = "0.0.0"
edition.workspace = true
license.workspace = true
publish = false

[lints]
workspace = true

[dependencies]
62 changes: 62 additions & 0 deletions tasks/website_common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/// Generate CLI documentation from bpaf-generated markdown.
///
/// Takes raw markdown from bpaf's `render_markdown()` and processes it into
/// website-ready format with proper frontmatter and section headers.
///
/// # Arguments
/// * `raw_markdown` - The markdown string from bpaf's render_markdown()
/// * `tool_name` - The name of the tool (e.g., "oxlint", "oxfmt") used to strip the header
/// * `gitignore_note_anchor` - Optional section header after which to insert the gitignore note
///
/// # Returns
/// Processed markdown ready for the website
#[expect(clippy::disallowed_methods)]
pub fn generate_cli_docs(
raw_markdown: &str,
tool_name: &str,
gitignore_note_anchor: Option<&str>,
) -> String {
// Remove the extra header
let header = format!("# {tool_name}\n");
let markdown = raw_markdown.trim_start_matches(header.as_str());

// Add ---\nsearch: false\n---\n at the top to prevent Vitepress from indexing this file.
let markdown = format!("---\nsearch: false\n---\n\n{markdown}");

// Hack usage line
let markdown = markdown.replacen("**Usage**:", "## Usage\n", 1);

let markdown = markdown
.split('\n')
.flat_map(|line| {
// Hack the bug on the line containing `###`
if line.contains("###") {
line.split("###").map(str::trim).chain(["\n"]).collect::<Vec<_>>()
} else {
vec![line]
}
})
.map(|line| {
// Make `** title **` to `## title`
if let Some(line) = line.strip_prefix("**")
&& let Some(line) = line.strip_suffix("**")
{
format!("## {line}")
} else {
line.to_string()
}
})
.collect::<Vec<_>>()
.join("\n");

// Add note about .gitignore only being respected inside Git repositories
if let Some(anchor) = gitignore_note_anchor {
let search_pattern = format!("\n\n## {anchor}\n");
let replacement = format!(
"\n\n> [!NOTE]\n> `.gitignore` is only respected inside a Git repository.\n\n## {anchor}\n"
);
markdown.replace(&search_pattern, &replacement)
} else {
markdown
}
}
26 changes: 26 additions & 0 deletions tasks/website_formatter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "website_formatter"
version = "0.0.0"
edition.workspace = true
license.workspace = true
publish = false

[lints]
workspace = true

[[bin]]
name = "website_formatter"
test = false
doctest = false

[dependencies]
bpaf = { workspace = true, features = ["docgen"] }
oxfmt = { path = "../../apps/oxfmt", default-features = false }
pico-args = { workspace = true }
website_common = { path = "../website_common" }

[dev-dependencies]
insta = { workspace = true }

[package.metadata.cargo-shear]
ignored = ["bpaf"]
29 changes: 29 additions & 0 deletions tasks/website_formatter/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use oxfmt::format_command;
use website_common::generate_cli_docs;

#[test]
fn test_cli() {
let snapshot = generate_cli();
insta::with_settings!({ prepend_module_to_snapshot => false }, {
insta::assert_snapshot!(snapshot);
});
}

#[test]
fn test_cli_terminal() {
let snapshot = oxfmt::format_command().run_inner(&["--help"]).unwrap_err().unwrap_stdout();
insta::with_settings!({ prepend_module_to_snapshot => false }, {
insta::assert_snapshot!(snapshot);
});
}

// <https://oxc.rs/docs/guide/usage/formatter/cli.html>
#[expect(clippy::print_stdout)]
pub fn print_cli() {
println!("{}", generate_cli());
}

fn generate_cli() -> String {
let markdown = format_command().render_markdown("oxfmt");
generate_cli_docs(&markdown, "oxfmt", None)
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
#![expect(clippy::print_stderr)]

use pico_args::Arguments;
use website::linter;

mod cli;

fn main() {
let mut args = Arguments::from_env();
let command = args.subcommand().expect("subcommands");

let task = command.as_deref().unwrap_or("default");

match task {
"linter-schema-json" => linter::print_schema_json(),
"linter-schema-markdown" => linter::print_schema_markdown(),
"linter-cli" => linter::print_cli(),
"linter-rules" => linter::print_rules(args),
"cli" => cli::print_cli(),
_ => eprintln!("Missing task command."),
}
}
14 changes: 5 additions & 9 deletions tasks/website/Cargo.toml → tasks/website_linter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "website"
name = "website_linter"
version = "0.0.0"
edition.workspace = true
license.workspace = true
Expand All @@ -9,34 +9,30 @@ publish = false
workspace = true

[[bin]]
name = "website"
name = "website_linter"
test = false
doctest = false

[lib]
doctest = false

[dependencies]
bpaf = { workspace = true, features = ["docgen"] }
handlebars = { workspace = true }
itertools = { workspace = true }
oxc_linter = { workspace = true, features = ["ruledocs"] }
# Disable default features to avoid `napi-rs` dependency, which causes linker errors
oxlint = { path = "../../apps/oxlint", default-features = false }
pico-args = { workspace = true }
project-root = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
website_common = { path = "../website_common" }

[dev-dependencies]
insta = { workspace = true }
markdown = { workspace = true }
oxc_allocator = { workspace = true }
oxc_diagnostics = { workspace = true }
oxc_parser = { workspace = true }
oxc_span = { workspace = true }

insta = { workspace = true }
markdown = { workspace = true }

[package.metadata.cargo-shear]
ignored = ["bpaf"]
29 changes: 29 additions & 0 deletions tasks/website_linter/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use oxlint::cli::lint_command;
use website_common::generate_cli_docs;

#[test]
fn test_cli() {
let snapshot = generate_cli();
insta::with_settings!({ prepend_module_to_snapshot => false }, {
insta::assert_snapshot!(snapshot);
});
}

#[test]
fn test_cli_terminal() {
let snapshot = oxlint::cli::lint_command().run_inner(&["--help"]).unwrap_err().unwrap_stdout();
insta::with_settings!({ prepend_module_to_snapshot => false }, {
insta::assert_snapshot!(snapshot);
});
}

// <https://oxc.rs/docs/guide/usage/linter/cli.html>
#[expect(clippy::print_stdout)]
pub fn print_cli() {
println!("{}", generate_cli());
}

fn generate_cli() -> String {
let markdown = lint_command().render_markdown("oxlint");
generate_cli_docs(&markdown, "oxlint", Some("Handle Warnings"))
}
Loading
Loading