Skip to content

Conversation

@Bugg4
Copy link
Contributor

@Bugg4 Bugg4 commented Aug 14, 2025

This PR introduces tools to perform linting on Markdown docs using custom rules.
I opted to use remark with its remark-lint plugins.

Resolves #1193

If anyone wants to try this out:

  • Install pnpm, then:

To lint the whole content/ folder:

cd lint
pnpm i
pnpm run checkall

Or just one file:

pnpm run check ../content/Configuring/Animations.md

TODO:

  • GitHub CI action
  • Find or write a plugin to enforce specific casing on all words (not just in headings) (see related issue for more info) remark-lint-word-case
  • Finalize initial lists of words for case enforcement and exclusion form title-case
  • Add notes/instructions about this in contributing section
  • Fix reported odd escaping behavior on links and hugo shortcodes when running pnpm run fix

Bugg4 added 4 commits August 7, 2025 19:16
Allows to reuse partial word lists for different plugins
If I don't find a suitable alternative, I'll write this better and then publish it on npm or pull it as a dependency from my GH
@Bugg4 Bugg4 changed the title Backend: Introduce linting tools Meta: Introduce linting tools Aug 14, 2025
@vaxerski vaxerski requested a review from fufexan August 16, 2025 13:15
Bugg4 added 6 commits August 17, 2025 20:30
- Add more commit format examples
- Break guidelines about PR, issues and commits into their own section.
- This prepares the section for adding info about using the linting tools
Includes sections broken off from README
@fufexan
Copy link
Member

fufexan commented Aug 21, 2025

Great work here! I went ahead and created a Nix package & devshell along with a CI check action (which I'll have to fix).

@Bugg4
Copy link
Contributor Author

Bugg4 commented Aug 21, 2025

Thank you!

Adds a Nix flake which contains remark with plugins as a Nix package,
along with a dev shell.
Checks the whole repo using the Nix lint package.
Fails with exit code 1 even on warnings. Makes it easy to catch errors in CI.
@fufexan
Copy link
Member

fufexan commented Sep 3, 2025

I think I've fixed CI now, sorry for the long wait.

I've tried to run fix but it does some weird stuff at times, for example the diff below which removes the link

-| exec | executes a shell command | command (supports rules, see [below]({{< relref "#executing-with-rules" >}})) |
+| exec | executes a shell command | command (supports rules, see \[below]\({{< relref "#executing-with-rules" >}})) |

The thing is I don't even know why that was there, could've just used the #link directly. I'll check if anything else is sus and update this comment.

More examples

-{{</ callout >}}
+{{\</ callout >}}
-[module options](https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=hyprland).
-
+[module options](https://search.nixos.org/options?channel=unstable\&from=0\&size=50\&sort=relevance\&type=packages\&query=hyprland).

@Bugg4
Copy link
Contributor Author

Bugg4 commented Sep 4, 2025

Thank you, I'll look into it

@Bugg4
Copy link
Contributor Author

Bugg4 commented Sep 13, 2025

@fufexan
I was looking into that backslashed callout behaviour, and I was wondering, is there a reason why we're not using native GFM callouts? e.g.:

> [!NOTE]
> For binding keys without a modkey, leave it empty:
> 
> ```ini
> bind = , Print, exec, grim
> ```

instead of:

{{< callout type=info >}}

For binding keys without a modkey, leave it empty:

```ini
bind = , Print, exec, grim
```

{{< /callout >}}

They render pretty much the same (hugo shortcode above, native below):
(And we could add some custom CSS if we want to change the icons, although imho they're fine)

image

@fufexan
Copy link
Member

fufexan commented Sep 14, 2025

I didn't know GFM was supported. We could do that, sure.

@Bugg4
Copy link
Contributor Author

Bugg4 commented Sep 14, 2025

I didn't know GFM was supported. We could do that, sure.

Neither did I 😄

I made this perl script to convert all hugo callouts shortcodes into native GMF:

#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;

# Mapping from hugo callout type -> GFM alert type
my %map = (
    info      => 'NOTE',
    warning   => 'WARNING',
    important => 'IMPORTANT',
);

sub transform {
    my ($text) = @_;

    $text =~ s{
        \{\{<\s*callout(?:\s+type=(\w+))?\s*>\}\}   # opening shortcode
        (.*?)                                       # body
        \{\{<\s*/?\s*callout\s*>\}\}                # closing shortcode (forgiving of malformed closing tags)
    }{
        my $type = $1 // 'warning';
        my %map  = (info=>'NOTE', warning=>'WARNING', important=>'IMPORTANT');
        my $hdr  = $map{lc $type} // uc($type);
        my $body = $2;

        # Trim leading/trailing newlines inside the callout
        $body =~ s/^\n+//;
        $body =~ s/\n+$//;

        # If the very first line is empty, drop it
        $body =~ s/^\s*\n//;

        # Prefix all lines (including blanks) with "> "
        $body =~ s/^/> /mg;

        "> [!$hdr]\n$body"
    }egsx;

    return $text;
}


sub process_file {
    my ($file) = @_;
    return unless $file =~ /\.md$/i;

    local $/ = undef;
    open my $fh, '<', $file or die "Cannot open $file: $!";
    my $content = <$fh>;
    close $fh;

    my $new = transform($content);

    if ($new ne $content) {
        open my $out, '>', $file or die "Cannot write $file: $!";
        print $out $new;
        close $out;
        print "Transformed: $file\n";
    }
}

my $root = shift @ARGV // 'content';
find(
    {
        wanted   => sub { process_file($File::Find::name) if -f },
        no_chdir => 1,
    },
    $root
);

Diffs look ok to me, but please try it out on your end too, just to make sure it's all good.
Then I'll make a dedicate PR with the conversion.
Also let me know if I should push this script too, and where to put it

you can run it fro m the hugo root like so:

hugo-callouts-to-gfm.pl content/

Removed by mistake during merge
@vdawg-git
Copy link
Member

I think this could cause generally cause problems with custom syntax.
For example Hugo, but also .mdx (which we might use in the future if I get Vax to it).

@Bugg4
Copy link
Contributor Author

Bugg4 commented Oct 29, 2025

I think this could cause generally cause problems with custom syntax.
For example Hugo, but also .mdx (which we might use in the future if I get Vax to it).

Ideally we'd wanna reduce hugo-specific syntax to a minimum.
I've seen callouts can be transformed to standard GFM, and links can probably be adapted too.

I'm not familiar with .mdx, so could you please expand on how you intended to use it?

@vdawg-git
Copy link
Member

Nevermind, I remark lint should support mdx out of the box.
Its all JS ecosystem after all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal: introduce Markdown linter

3 participants