Skip to content

Commit d165c9b

Browse files
authored
Add Strict locals missing linter (#175)
And here I propose another linter that we would love to use in our project. It checks for [Strict locals](https://guides.rubyonrails.org/action_view_overview.html#strict-locals) magic line and reports when the magic line is missing from the template. Strict locals are encouraged by some in Rails partials as they allow to define an explicit "API" for the templates showing clearly which variables should be passed into the template when rendering it vs. which ones are purely local for the template. They also allow to define default values. I wrote a little [post](https://dev.to/nejremeslnici/strict-locals-in-slim-haml-partials-in-rails-2f73) about them recently. Thanks for your considering this! ![image](https://github.com/sds/slim-lint/assets/462701/c34333ec-4dbe-42f4-8e34-fabf879ecc69) ![image](https://github.com/sds/slim-lint/assets/462701/96c491ee-4ccd-44fe-8bcb-4227770af5c5)
1 parent 77affdc commit d165c9b

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

config/default.yml

+6
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ linters:
8989
- Style/WhileUntilDo
9090
- Style/WhileUntilModifier
9191

92+
StrictLocalsMissing:
93+
enabled: false
94+
include:
95+
# Include only Rails partial templates by default
96+
- app/views/**/_*.html.slim
97+
9298
Tab:
9399
enabled: true
94100

lib/slim_lint/linter/README.md

+35
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Below is a list of linters supported by `slim-lint`, ordered alphabetically.
1313
* [LineLength](#linelength)
1414
* [RedundantDiv](#redundantdiv)
1515
* [RuboCop](#rubocop)
16+
* [StrictLocalsMissing](#strictlocalsmissing)
1617
* [Tab](#tab)
1718
* [TagCase](#tagcase)
1819
* [TrailingBlankLines](#trailingblanklines)
@@ -281,6 +282,40 @@ AllCops:
281282
DisplayCopNames: true
282283
```
283284

285+
## StrictLocalsMissing
286+
287+
Reports on missing [strict locals magic comment](https://guides.rubyonrails.org/action_view_overview.html#strict-locals)
288+
in Slim templates. Use the `include` configuration option to narrow down
289+
the files to e.g. only partial view templates in Rails:
290+
291+
```yaml
292+
linters:
293+
StrictLocalsMissing:
294+
enabled: true
295+
include:
296+
- app/views/**/_*.html.slim
297+
```
298+
299+
**Bad for the above configuration**
300+
301+
In `app/views/somewhere/_partial.html.slim`:
302+
303+
```slim
304+
= some_helper(foo, bar)
305+
```
306+
**Good for the above configuration**
307+
308+
In `app/views/somewhere/_partial.html.slim`:
309+
310+
```slim
311+
/# locals: (foo:, bar: 'default')
312+
= some_helper(foo, bar)
313+
```
314+
315+
By default, Rails partial templates accept any local variables.
316+
Strict locals, on the other hand, help define an explicit interface
317+
for the template that shows which local variables it accepts.
318+
284319
## Tab
285320

286321
Reports detection of tabs used for indentation.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module SlimLint
4+
# Reports on missing strict locals magic line in Slim templates.
5+
class Linter::StrictLocalsMissing < Linter
6+
include LinterRegistry
7+
8+
on_start do |_sexp|
9+
unless document.source =~ %r{/#\s+locals:\s+\(.*\)}
10+
dummy_node = Struct.new(:line)
11+
report_lint(dummy_node.new(1), 'Strict locals magic line is missing')
12+
end
13+
end
14+
end
15+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe SlimLint::Linter::StrictLocalsMissing do
6+
include_context 'linter'
7+
8+
context 'when magic line defines strict locals' do
9+
let(:slim) { '/# locals: (foo:, bar:)' }
10+
11+
it { should_not report_lint }
12+
end
13+
14+
context 'when magic line defines empty strict locals' do
15+
let(:slim) { '/# locals: ()' }
16+
17+
it { should_not report_lint }
18+
end
19+
20+
context 'when magic line is not at the top of the file' do
21+
let(:slim) { <<-SLIM }
22+
h1 Hello
23+
= call(foo, bar)
24+
/# locals: (foo:, bar:)
25+
SLIM
26+
27+
it { should_not report_lint }
28+
end
29+
30+
context 'when template has no strict locals definition' do
31+
let(:slim) { 'h1 hello' }
32+
33+
it { should report_lint }
34+
end
35+
end

0 commit comments

Comments
 (0)