Skip to content

Conversation

@domingo2000
Copy link
Contributor

@domingo2000 domingo2000 commented Oct 18, 2025

Solves #667

Use --generate-todo to create a todo list that will ignore all the current offenses based on an offense count. Very similar of how rubocop does.

The implementation writes to a .herb-todo.yml file, that stores a map of all the files and rules with the counts of offenses and warnings. When the linter runs, reads from that file and ignore all ocurrences declarated as exclude.

The file is not dependant on a Herb Config file, because is autogenerated and can be very big, it should be consider as "private".

The --generate-todo name comes from standardrb recommendation on todo list.

Usage:

herb-lint --regenerate-todo <path> # Creates the file and show the offenses.
herb-lint <path> # Pass the linting without errors nor warnings.

TODO

  • Write documentation

@domingo2000 domingo2000 force-pushed the linter/add-todo-list-ignore-rules branch 4 times, most recently from f9b1910 to 2c29a62 Compare October 18, 2025 20:09

if (!this.linter) {
this.linter = new Linter(Herb)
this.linter = new Linter(Herb, undefined, this.linterTodo)
Copy link
Contributor Author

@domingo2000 domingo2000 Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing undefined to let the linter set the default rules is not very idiomatic. Maybe it is better to pass the default rules on each call 🤷🏻 .

Copy link
Owner

@marcoroth marcoroth Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can refactor the Linter to take in an options object as the second argument. So it could be:

Suggested change
this.linter = new Linter(Herb, undefined, this.linterTodo)
this.linter = new Linter(Herb, { rules: customRules, linterTodo: this.linterTodo })

or just:

Suggested change
this.linter = new Linter(Herb, undefined, this.linterTodo)
this.linter = new Linter(Herb, { linterTodo: this.linterTodo })

@domingo2000
Copy link
Contributor Author

@marcoroth happy to receive feedback on this. Currently a little WIP and can refactor a little more, but it works well, tried on buk-webapp monolith and is working good (4288 erb files).

Maybe i can follow up this PR with the following:

  1. Show how much rules are in .herb-todo.yml
  2. Have a command flag that ignore the todo file. This is useful when the you want to get the linter run in all the project, without loosing the current todo.

}
}

export class LinterTodo {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is not the best. Maybe TodoList? I belive that just Todo could fit too, but Todo could mean many more things.

@domingo2000 domingo2000 force-pushed the linter/add-todo-list-ignore-rules branch from 2c29a62 to ea420de Compare October 18, 2025 20:36
@domingo2000 domingo2000 changed the title Linter: Use --regenerate-todo to create a todo list for ignoring rules. Linter: Use --generate-todo to create a todo list for ignoring rules. Oct 18, 2025
--no-timing hide timing information
--no-wrap-lines disable line wrapping
--truncate-lines enable line truncation (mutually exclusive with line wrapping)
--generate-todo generate a .herb-todo.yml file with current diagnostics
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be --regenerate-todo as rubocop, or --generate-todo as standardrb. I think regenerate makes clear that you are destroying the old one. But generate-todo is a little less verbose and simple.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have both so there is no surprise.

--generate-todo can generate a file if none is present yet. If one is present it could back out and say something like: .herb-todo.yml already exists, run "--regenerate-todo" to overwrite it.

--regenerate-todo would just always overwrite the current one, no matter if .herb-todo.yml exists or not.

@marcoroth marcoroth added feature New feature or request linter labels Oct 19, 2025
@domingo2000 domingo2000 force-pushed the linter/add-todo-list-ignore-rules branch 2 times, most recently from 9d06eeb to dd6f773 Compare October 20, 2025 03:38
Using this parameter creates a .herb-todo.yml file that is used to
ignore rules massively, this is usefull for big projects adopting the
linter, to start ignoring all current offenses without having to ommit
all in the files manually.
@domingo2000 domingo2000 force-pushed the linter/add-todo-list-ignore-rules branch from dd6f773 to 30ecd96 Compare October 20, 2025 03:40
@marcoroth
Copy link
Owner

When you run --generate-todo it concludes the run with:

 Rule offenses:
  html-no-space-in-tag (82 offenses in 24 files)
  html-no-self-closing (46 offenses in 3 files)
  html-input-require-autocomplete (27 offenses in 2 files)
  erb-require-trailing-newline (3 offenses in 3 files)


 Summary:
  Checked      210 files
  Files        28 with offenses | 182 clean (210 total)
  Offenses     158 errors | 0 warnings (158 offenses across 28 files)
  Fixable      158 offenses | 131 autocorrectable using `--fix`
  Start at     12:14:46
  Duration     1323ms (40 rules)

Maybe we can mention that we generated a .herb-todo.yml file here too.

On subsequent runs, I think it would also be great to mention the amount of "suppressed offenses", so people are aware that the linter is only passing because of the todos in the .herb-todo.yml file.

❯ herb-lint ../../../../rubyevents

 Summary:
  Checked      210 files
  Files        210 clean (210 total)
  Offenses     0 offenses
  Start at     12:15:49
  Duration     1066ms (40 rules)

 ✓ All files are clean!

@marcoroth
Copy link
Owner

Another thing (which might not be as common in day-to-day use-cases), but I run the herb-lint out of my current Herb working directory, so I often use relative paths (but this could also happen when you run this within your app).

Running herb-lint ../../../../rubyevents --generate-todo generates the .herb-todo.yml file in the current working directory, and not at ../../../../rubyevents/.herb-todo.yml. I think we might want to resolve the path argument and

a) Put the .herb-todo.yml at the location you provide when running the linter (or if none is provided, use the current working directory like we do now)
b) Look at that path passed when running the linter for a config file first, then in the CWD
c) Inside the .herb-todo.yml: I think the paths should all be relative to the project root, currently they were generated as:

excludes:
  html-no-space-in-tag:
    ../../../../rubyevents/app/views/topics/show.html.erb:
      warning: 0
      error: 1
    ../../../../rubyevents/app/views/templates/new.html.erb:
      warning: 0
      error: 3

# ...

Maybe we can also mention the absolute path of which .herb-todo.yml file the linter is using when running the the linter, something like:

npx herb-lint 

Using TODO Config file at: /Users/[user]/[project]/.herb-todo.yml

Thank you so much for working on this @domingo2000! 🙏🏼

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

Labels

feature New feature or request linter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants