Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: css linter docs #2151

Merged
merged 9 commits into from
Mar 29, 2024
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
3 changes: 3 additions & 0 deletions Cargo.lock

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

Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@ use biome_css_syntax::CssDeclarationOrRuleBlock;
use biome_rowan::AstNode;

declare_rule! {
/// Succinct description of the rule.
/// **[WIP] This rule hasn't been implemented yet.**
///
/// Put context and details about the rule.
/// As a starting point, you can take the description of the corresponding _ESLint_ rule (if any).
///
/// Try to stay consistent with the descriptions of implemented rules.
///
/// Add a link to the corresponding stylelint rule (if any):
/// This is sample rule
///
/// ## Examples
///
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_service/src/configuration/linter/rules.rs

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

10 changes: 3 additions & 7 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,10 @@ new-js-lintrule rulename:
just gen-lint
just documentation

# WIP: Creates a new css lint rule in the given path, with the given name. Name has to be camel case.
# Creates a new css lint rule in the given path, with the given name. Name has to be camel case.
new-css-lintrule rulename:
cargo run -p xtask_codegen -- new-css-lintrule --kind=css --name={{rulename}}
cargo codegen analyzer
cargo codegen-configuration
just gen-bindings
just format
# TODO: lintdoc, website, cargo doc
cargo run -p xtask_codegen -- new-lintrule --kind=css --name={{rulename}}
just gen-lint

# Promotes a rule from the nursery group to a new group
promote-rule rulename group:
Expand Down
2 changes: 1 addition & 1 deletion packages/@biomejs/backend-jsonrpc/src/workspace.ts

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

2 changes: 1 addition & 1 deletion packages/@biomejs/biome/configuration_schema.json

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

6 changes: 6 additions & 0 deletions website/src/assets/svg/css-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions website/src/assets/svg/js-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions website/src/assets/svg/json-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion website/src/components/generated/NumberOfRules.astro

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

1 change: 1 addition & 0 deletions website/src/content/docs/linter/rules/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ Rules that belong to this group <strong>are not subject to semantic version</str
| Rule name | Description | Properties |
| --- | --- | --- |
| [noBarrelFile](/linter/rules/no-barrel-file) | Disallow the use of barrel file. | |
| [noColorInvalidHex](/linter/rules/no-color-invalid-hex) | <strong>[WIP] This rule hasn't been implemented yet.</strong> | |
| [noConsole](/linter/rules/no-console) | Disallow the use of <code>console</code>. | <span aria-label="The rule has an unsafe fix" role="img" title="The rule has an unsafe fix">⚠️ </span> |
| [noDoneCallback](/linter/rules/no-done-callback) | Disallow using a callback in asynchronous tests and hooks. | |
| [noDuplicateElseIf](/linter/rules/no-duplicate-else-if) | Disallow duplicate conditions in if-else-if chains | |
Expand Down
50 changes: 50 additions & 0 deletions website/src/content/docs/linter/rules/no-color-invalid-hex.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: noColorInvalidHex (not released)
---

**Diagnostic Category: `lint/nursery/noColorInvalidHex`**

:::danger
This rule hasn't been released yet.
:::

:::caution
This rule is part of the [nursery](/linter/rules/#nursery) group.
:::

**[WIP] This rule hasn't been implemented yet.**

This is sample rule

## Examples

### Invalid

```css
p {}
```

<pre class="language-text"><code class="language-text">nursery/noColorInvalidHex.js:1:3 <a href="https://biomejs.dev/linter/rules/no-color-invalid-hex">lint/nursery/noColorInvalidHex</a> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

<strong><span style="color: Orange;"> </span></strong><strong><span style="color: Orange;">⚠</span></strong> <span style="color: Orange;">Unexpected empty block is not allowed</span>

<strong><span style="color: Tomato;"> </span></strong><strong><span style="color: Tomato;">&gt;</span></strong> <strong>1 │ </strong>p {}
<strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong>
<strong>2 │ </strong>

<strong><span style="color: lightgreen;"> </span></strong><strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This note will give you more information.</span>

</code></pre>

### Valid

```css
p {
color: red;
}
```

## Related links

- [Disable a rule](/linter/#disable-a-lint-rule)
- [Rule options](/linter/#rule-options)
24 changes: 17 additions & 7 deletions xtask/codegen/src/generate_new_lintrule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ pub enum RuleKind {
Css,
}

impl RuleKind {
fn as_str(&self) -> &str {
match self {
Self::Js => "js",
Self::Json => "json",
Self::Css => "css",
}
}
}

impl FromStr for RuleKind {
type Err = &'static str;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Expand Down Expand Up @@ -188,11 +198,8 @@ impl Rule for {rule_name_upper_camel} {{
}

pub fn generate_new_lintrule(kind: RuleKind, rule_name: &str) {
let crate_folder = match kind {
RuleKind::Js => project_root().join("crates/biome_js_analyze"),
RuleKind::Json => project_root().join("crates/biome_json_analyze"),
RuleKind::Css => project_root().join("crates/biome_css_analyze"),
};
let rule_kind = kind.as_str();
let crate_folder = project_root().join(format!("crates/biome_{rule_kind}_analyze"));
let rule_folder = crate_folder.join("src/lint/nursery");
let test_folder = crate_folder.join("tests/specs/nursery");
let rule_name_upper_camel = rule_name.to_camel();
Expand Down Expand Up @@ -235,7 +242,10 @@ pub fn generate_new_lintrule(kind: RuleKind, rule_name: &str) {
let tests_path = format!("{}/{rule_name_lower_camel}", test_folder.display());
let _ = std::fs::create_dir_all(tests_path);

let test_file = format!("{}/{rule_name_lower_camel}/valid.js", test_folder.display());
let test_file = format!(
"{}/{rule_name_lower_camel}/valid.{rule_kind}",
test_folder.display()
);
if std::fs::File::open(&test_file).is_err() {
let _ = std::fs::write(
test_file,
Expand All @@ -244,7 +254,7 @@ pub fn generate_new_lintrule(kind: RuleKind, rule_name: &str) {
}

let test_file = format!(
"{}/{rule_name_lower_camel}/invalid.js",
"{}/{rule_name_lower_camel}/invalid.{rule_kind}",
test_folder.display()
);
if std::fs::File::open(&test_file).is_err() {
Expand Down
3 changes: 3 additions & 0 deletions xtask/lintdoc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ version = "0.0.0"
[dependencies]
biome_analyze = { workspace = true }
biome_console = { workspace = true }
biome_css_analyze = { workspace = true }
biome_css_parser = { workspace = true }
biome_css_syntax = { workspace = true }
biome_diagnostics = { workspace = true }
biome_js_analyze = { workspace = true }
biome_js_parser = { workspace = true }
Expand Down
97 changes: 97 additions & 0 deletions xtask/lintdoc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use biome_console::{
fmt::{Formatter, HTML},
markup, Console, Markup, MarkupBuf,
};
use biome_css_parser::CssParserOptions;
use biome_css_syntax::CssLanguage;
use biome_diagnostics::termcolor::NoColor;
use biome_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic};
use biome_js_parser::JsParserOptions;
Expand Down Expand Up @@ -130,9 +132,31 @@ fn main() -> Result<()> {
}
}

impl RegistryVisitor<CssLanguage> for LintRulesVisitor {
fn record_category<C: GroupCategory<Language = CssLanguage>>(&mut self) {
if matches!(C::CATEGORY, RuleCategory::Lint) {
C::record_groups(self);
}
}

fn record_rule<R>(&mut self)
where
R: Rule + 'static,
R::Query: Queryable<Language = CssLanguage>,
<R::Query as Queryable>::Output: Clone,
{
self.number_or_rules += 1;
self.groups
.entry(<R::Group as RuleGroup>::NAME)
.or_default()
.insert(R::METADATA.name, R::METADATA);
}
}

let mut visitor = LintRulesVisitor::default();
biome_js_analyze::visit_registry(&mut visitor);
biome_json_analyze::visit_registry(&mut visitor);
biome_css_analyze::visit_registry(&mut visitor);

let mut recommended_rules = String::new();

Expand Down Expand Up @@ -261,6 +285,7 @@ fn generate_group(
main_page_buffer,
"| [{rule}](/linter/rules/{dashed_rule}) | {summary_html} | {properties} |"
)?;

writeln!(main_page_buffer)?;
}
Err(err) => {
Expand Down Expand Up @@ -407,6 +432,7 @@ fn parse_documentation(
}
}
BlockType::Json => write!(content, "json")?,
BlockType::Css => write!(content, "css")?,
}
}
writeln!(content)?;
Expand Down Expand Up @@ -561,6 +587,7 @@ fn parse_documentation(
enum BlockType {
Js(JsFileSource),
Json,
Css,
}

struct CodeBlockTest {
Expand Down Expand Up @@ -617,6 +644,10 @@ impl FromStr for CodeBlockTest {
test.block_type = BlockType::Json;
}

"css" => {
test.block_type = BlockType::Css;
}

_ => {
bail!("unknown code block attribute {token:?}")
}
Expand Down Expand Up @@ -837,6 +868,72 @@ fn assert_lint(
write_diagnostic(code, diagnostic)?;
}

if test.expect_diagnostic && rule_has_code_action && !has_fix_kind {
bail!("The rule '{}' emitted code actions via `action` function, but you didn't mark rule with `fix_kind`.", rule)
}
}
}
BlockType::Css => {
let parse = biome_css_parser::parse_css(code, CssParserOptions::default());

if parse.has_errors() {
for diag in parse.into_diagnostics() {
let error = diag
.with_file_path(file.clone())
.with_file_source_code(code);
write_diagnostic(code, error)?;
}
} else {
let root = parse.tree();

let settings = WorkspaceSettings::default();

let rule_filter = RuleFilter::Rule(group, rule);
let filter = AnalysisFilter {
enabled_rules: Some(slice::from_ref(&rule_filter)),
..AnalysisFilter::default()
};

let options = AnalyzerOptions::default();
let (_, diagnostics) = biome_css_analyze::analyze(
&root,
filter,
&options,
|signal| {
if let Some(mut diag) = signal.diagnostic() {
let category = diag.category().expect("linter diagnostic has no code");
let severity = settings.get_severity_from_rule_code(category).expect(
"If you see this error, it means you need to run cargo codegen-configuration",
);

for action in signal.actions() {
if !action.is_suppression() {
rule_has_code_action = true;
diag = diag.add_code_suggestion(action.into());
}
}

let error = diag
.with_severity(severity)
.with_file_path(file.clone())
.with_file_source_code(code);
let res = write_diagnostic(code, error);

// Abort the analysis on error
if let Err(err) = res {
return ControlFlow::Break(err);
}
}

ControlFlow::Continue(())
},
);

// Result is Some(_) if analysis aborted with an error
for diagnostic in diagnostics {
write_diagnostic(code, diagnostic)?;
}

if test.expect_diagnostic && rule_has_code_action && !has_fix_kind {
bail!("The rule '{}' emitted code actions via `action` function, but you didn't mark rule with `fix_kind`.", rule)
}
Expand Down
Loading