super_slim 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ba8d431a17e14eb0624e5f6612d3c1475cd58e70ef5c01dedea459be72523d9a
4
+ data.tar.gz: 14e1f9dbfdf8525f134e0cf92223a55f3f0cb38990c88c0138c56c8b9cf44477
5
+ SHA512:
6
+ metadata.gz: a457a4a0e06d98f6d975a7ccabfbc33421e3c98d3f7b2379f292c97f3ad7bea68ff94481a8c33c6b75153cf2df153514b1821cd02c5bfa87091a9ba15b477f67
7
+ data.tar.gz: bb1a7f2754771c0acf1eef3a9b204df75df8b68490cdcdd3f54c2c159db5cf08ad975da40f52d41661c8f7327df4323541d88434099e35667eca7bacf214118a
data/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## [2.0.0] — 2026-11-05
6
+
7
+ SlimX becomes SuperSlim, sheds what a Rails app doesn't need, and `super-slim fmt` wraps long lines.
8
+
9
+ **Breaking**
10
+ - The gem is renamed: `super_slim` in the Gemfile, the `SuperSlim` module, the `super-slim lint|fmt` command,
11
+ `.super-slim.yml` and `plugins: [super_slim]` for RuboCop. Compiled templates name their variables
12
+ `_super_slim1` instead of `_slimx1`.
13
+ - No more Tilt adapter: `require "slimx/tilt"` has no replacement. Rails and `SuperSlim.compile` are unchanged.
14
+ - No more strict mode (`SlimX.strict`, `strict: true`): run `super-slim fmt --check` in CI instead.
15
+ - No more custom shortcuts: only `#id` and `.class`.
16
+ - Only `doctype 5`, `doctype html` and `doctype xml` remain; HTML 4 and XHTML doctypes, such as
17
+ `doctype transitional`, raise `Invalid doctype`.
18
+
19
+ **New**
20
+ - `super-slim fmt` wraps lines longer than `fmt: max_line_length` in `.super-slim.yml`, off by default: a tag's
21
+ attributes one per line inside `fmt: attribute_delimiters` (`"()"`, `"[]"` or `"{}"`), then the arguments of
22
+ its Ruby code after each comma, four spaces deep when the line opens a block. A wrap is kept only when the
23
+ compiled Ruby differs by whitespace alone.
24
+
25
+ ## [1.1.0] — 2026-11-04
26
+
27
+ A RuboCop plugin that lints the Ruby inside templates with SlimX's own parser, and templates compiled up to 10% faster.
28
+
29
+ **New**
30
+ - A RuboCop plugin (`plugins: [slimx]`) lints the Ruby of templates: `-` and `=` lines, code attributes, splats,
31
+ `#{}` interpolations and `ruby:` blocks. Offenses point at their line and column in the `.slim` file, and
32
+ autocorrection writes back into it. It replaces rubocop-slim, and with it slimi and Temple; unlike slimi, it
33
+ also reads the output of a tag with whitespace markers and attributes (`span> title="x" = name`).
34
+
35
+ **Faster**
36
+ - Templates compile 7% faster without JIT and 10% faster with YJIT, with 11% to 16% fewer allocations (6,097
37
+ public templates, Ruby 3.3 and 4.0). Rendering is unchanged.
38
+
39
+ **Fixed**
40
+ - `slimx lint` reports the offenses of one line in the same order on every Ruby version.
41
+
42
+ ## [1.0.0] — 2026-11-04
43
+
44
+ First stable release: a Slim 5 engine written from scratch, without Temple. 0.1.0 and 0.1.1 were previews. It
45
+ renders what Slim 5 renders with `format: :html, sort_attrs: false`, byte for byte; XML formats (`.xml`, `.rss`,
46
+ `.atom`) render as XML.
47
+
48
+ **New**
49
+ - Rails through a railtie, Tilt through `require "slimx/tilt"`, and `SlimX.compile` for plain Ruby.
50
+ - Every `-` and `=` stays on its template line, so backtraces point at the right one.
51
+ - `slimx fmt`, which refuses any change to the compiled Ruby, and strict mode (`SlimX.strict`), which
52
+ refuses unformatted templates at compile time.
53
+ - `slimx lint` with 29 rules from slim-lint, haml-lint, erb_lint, Herb and eslint-plugin-jsx-a11y,
54
+ configured in `.slimx.yml`.
55
+ - `tailwind_classes`, off by default: class shortcuts read Tailwind class names.
56
+
57
+ **Left out**
58
+ - XHTML, `sort_attrs` and the other Temple options, the `coffee:`, `sass:`, `scss:`, `less:`, `erb:`,
59
+ `builder:` and wiki engines, conditional comments, the `logic_less`, `translator`, `include` and `smart`
60
+ plugins, and lambda shortcuts: each raises at compile time.
61
+
62
+ **Requires**
63
+ - Ruby 3.3 or later, every Ruby still maintained, Rails 8.0 or 8.1 for the railtie, and strscan 3.1.1 or later,
64
+ newer than the one Ruby 3.3 ships.
65
+
66
+ [Unreleased]: https://github.com/levups/super-slim/compare/v2.0.0...HEAD
67
+ [2.0.0]: https://github.com/levups/super-slim/compare/v1.1.0...v2.0.0
68
+ [1.1.0]: https://github.com/next-gen-gems/slimx/compare/v1.0.0...v1.1.0
69
+ [1.0.0]: https://github.com/next-gen-gems/slimx/releases/tag/v1.0.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2026 Level UP Solutions
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/LICENSE-slim ADDED
@@ -0,0 +1,22 @@
1
+ SuperSlim's parser follows the grammar and the parsing algorithm of Slim
2
+ (https://github.com/slim-template/slim), released under the MIT License:
3
+
4
+ Copyright (c) 2010 - 2026 Slim Team
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # SuperSlim
2
+
3
+ A Slim template engine rewritten from scratch: no Temple, no filter pipeline, a single-pass compiler and a
4
+ render-time runtime of a few methods. Templates written for [Slim](https://github.com/slim-template/slim) 5
5
+ render byte for byte the same, faster, with fewer allocations.
6
+
7
+ https://levups.github.io/super-slim/
8
+
9
+ ## Installation
10
+
11
+ ```ruby
12
+ # Gemfile
13
+ gem "super_slim" # instead of slim-rails
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ Rails picks it up through its railtie. Outside Rails, `SuperSlim.compile(source)` returns the Ruby code.
19
+
20
+ ## What SuperSlim renders
21
+
22
+ HTML5, with attributes in the order you write them — what Slim renders with
23
+ `format: :html, sort_attrs: false`. Templates whose format is XML (`.xml`, `.rss`, `.atom`) render as XML
24
+ (`<link />`, `doctype xml`) from their format alone.
25
+
26
+ The whole Slim 5 grammar is supported: tags and `#id`/`.class` shortcuts, attributes with
27
+ any delimiter, boolean attributes, `*splat`, `data`/`aria` hashes, `class` merging, `<`/`>` whitespace
28
+ markers, `|` and `'` text, inline HTML, `/` and `/!` comments, `-` control code, `=`/`==` output, `#{}` and
29
+ `#{{}}` interpolation, `doctype 5`/`html`/`xml`, and the `ruby:`, `javascript:`, `css:` and `markdown:` (through
30
+ [Markly](https://github.com/ioquatix/markly)) embedded engines.
31
+
32
+ ## Tailwind class names
33
+
34
+ Slim cuts `.rounded-b-[3px]` at the bracket, and refuses `.lg:[&:nth-child(3)]:hover:underline`. Turn on
35
+ `tailwind_classes` and the `.` shortcut takes any Tailwind class name:
36
+
37
+ ```slim
38
+ .p-1.rounded-b-[3px].lg:[&:nth-child(3)]:hover:underline
39
+ .font-['Open_Sans'].bg-(--brand).mx-0.5.w-1/2.!font-bold.@lg:flex
40
+ ul.*:p-2
41
+ ```
42
+
43
+ ```ruby
44
+ SuperSlim.tailwind_classes = true # config/initializers/super_slim.rb, or tailwind_classes: true per compile
45
+ ```
46
+
47
+ ```yaml
48
+ # .super-slim.yml, for super-slim lint and super-slim fmt
49
+ tailwind_classes: true
50
+ ```
51
+
52
+ Attribute lists keep their meaning: Tailwind never puts a bracket right after a letter or a digit, so
53
+ `a.btn[href="/"]` is still a link with a class and an `href`. A colon followed by a space still nests a tag
54
+ (`li.item: a`), and a final `/` still closes one. What changes is what Slim mangles: it renders `.size-2.5` as
55
+ `class="size-2 5"`, the option as `class="size-2.5"`. On 7,505 templates from 31 apps, 16 compile differently
56
+ with the option, all of them Tailwind classes Slim breaks. It stays off by default all the same, so that
57
+ turning SuperSlim on changes nothing.
58
+
59
+ The option only changes how templates are read: compiling 1,408 templates takes the same time (+0.5%, within
60
+ noise), and the compiled Ruby, so the time it takes to render, is the same.
61
+
62
+ ## What SuperSlim leaves out
63
+
64
+ Each fails at compile time with a clear error instead of being silently ignored:
65
+
66
+ - the XHTML format and its doctypes, the HTML 4 doctypes, `sort_attrs`, `attr_quote`, `pretty`, `js_wrapper` and every Temple option;
67
+ - the `coffee:`, `sass:`, `scss:`, `less:`, `erb:`, `builder:`, `textile:`, `rdoc:`, `asciidoc:` and wiki
68
+ engines, and `/[if IE]` conditional comments;
69
+ - the `logic_less`, `translator`, `include` and `smart` plugins, and custom shortcuts: only `#id` and `.class`.
70
+
71
+ They can come back if people ask, as long as they cost nothing at render time.
72
+
73
+ ## Migrating from Slim
74
+
75
+ Slim's defaults are `format: :xhtml` and `sort_attrs: true`. If you never changed them, first switch Slim
76
+ to `format: :html, sort_attrs: false` and check your test suite: that is the output SuperSlim gives. Then swap
77
+ `slim-rails` for `super_slim`, and drop any `Slim::Engine.set_options`/`with_options` call.
78
+
79
+ ## Formatter and linter
80
+
81
+ One parser serves the compiler, the formatter and the linter, so all three read templates the same way.
82
+
83
+ `super-slim fmt [--check] [PATH…]` rewrites templates in their canonical layout: two spaces per level, `- code`,
84
+ `= output` and `p = output` spacing, no redundant `div`, no trailing whitespace, single blank lines, one final
85
+ newline. Text blocks move as a whole, so the whitespace they render stays put — and before writing anything,
86
+ the formatter compiles both versions and refuses if the Ruby differs. Formatting cannot change a page.
87
+
88
+ With `fmt: max_line_length: 120` in `.super-slim.yml`, `fmt` also wraps longer lines: a tag's attributes one
89
+ per line inside `attribute_delimiters` (`"()"` by default, `"[]"` or `"{}"`), then the arguments of its Ruby code after each comma, four spaces deep when the line opens a block. It keeps a wrap only
90
+ when the compiled Ruby differs by whitespace alone, and leaves lines already spread over several lines alone.
91
+
92
+ `super-slim lint [PATH…]` reads `.super-slim.yml` and checks, with about a third of the CPU time slim-lint takes:
93
+
94
+ | Rule | From | Default |
95
+ |---|---|---|
96
+ | Indentation, Tab, TrailingWhitespace, EmptyLines, TrailingBlankLines, ControlStatementSpacing, RedundantDiv | slim-lint, fixed by `super-slim fmt` | on |
97
+ | LineLength (`max: 120`), CommentControlStatement, EmptyControlStatement, ConsecutiveControlStatements (`max: 2`), TagCase | slim-lint | on |
98
+ | InstanceVariables (in partials) | haml-lint, Rails conventions | on |
99
+ | StrictLocals (partials declare `/# locals:`) | Rails 7.1 strict locals | off |
100
+ | DuplicateId, ImageAlt, IframeTitle, AnchorHref, NestedLinks, PositiveTabindex, BooleanAttributeValue, AriaRole, AriaAttribute | Herb, eslint-plugin-jsx-a11y | on |
101
+ | ButtonType, InlineStyles | eslint-plugin-react, haml-lint | off |
102
+ | DeprecatedClasses (`classes: {old: new}`) | erb_lint | on, empty |
103
+ | UnnecessaryInterpolation, UnnecessaryStringOutput | haml-lint | on |
104
+ | Zwsp (invisible characters) | slim-lint | on |
105
+
106
+ ```yaml
107
+ # .super-slim.yml
108
+ linters:
109
+ LineLength:
110
+ max: 150
111
+ ButtonType:
112
+ enabled: true
113
+ DeprecatedClasses:
114
+ classes:
115
+ text-muted: text-body-secondary
116
+ ```
117
+
118
+ The Ruby inside templates is RuboCop's job: SuperSlim ships a RuboCop plugin that hands it each `-` and `=` line,
119
+ code attribute, splat, `#{}` interpolation and `ruby:` block, read by SuperSlim's own parser. Offenses point at their
120
+ line and column in the `.slim` file, and autocorrection writes back into it. It replaces rubocop-slim, and with
121
+ it slimi and Temple:
122
+
123
+ ```yaml
124
+ # .rubocop-slim.yml, run with rubocop -c .rubocop-slim.yml
125
+ inherit_from: .rubocop.yml
126
+ plugins:
127
+ - super_slim
128
+ AllCops:
129
+ Include:
130
+ - "**/*.slim"
131
+ ```
132
+
133
+ The cops that cannot hold in a template — indentation and alignment across lines, frozen string comments,
134
+ `Lint/UselessAssignment` on a variable a later line reads — are turned off for `.slim` files, as rubocop-slim
135
+ does. Unlike slimi, SuperSlim also reads the output of a tag with whitespace markers and attributes
136
+ (`span> title="x" = name`), so a few offenses appear when switching.
137
+
138
+ ## How it is tested
139
+
140
+ - Slim's own test suite and its literate specification (`TESTS.md`) run against SuperSlim: every template renders
141
+ through Slim and SuperSlim, and both outputs must match.
142
+ - A seeded fuzzer generates thousands of random templates from the grammar and compares both engines; CI adds
143
+ 10,000 from a new seed on every run.
144
+ - Golden snapshots (`test/golden`) hold the rendered HTML, compiled Ruby, formatting and lint offenses of
145
+ templates covering the grammar, its errors and Tailwind class names: `UPDATE_GOLDEN=1` rewrites them.
146
+ - `bench/compiled_digests.rb` proves a refactoring leaves the compiled Ruby of a whole corpus unchanged.
147
+
148
+ Real templates come from 30 public apps pinned in `corpus/manifest.yml`: `rake corpus:fetch` downloads only
149
+ their `.slim` files into the ignored `corpus/cache`, `rake corpus:check` compiles, formats and lints each one
150
+ against Slim, and `rake corpus:diff BASE=rev` reports what changes between a revision and the working tree:
151
+ compiled Ruby, formatting, lint offenses. Downloaded templates are never rendered, as rendering runs their
152
+ Ruby; directories you trust, such as your own app, render too when passed in `SUPER_SLIM_CORPUS`.
153
+
154
+ ## Benchmarks
155
+
156
+ `bench/compare.rb` renders the same blog page with SuperSlim, Slim, Haml, Erubi and Herb, checks that they all
157
+ produce the same HTML, and writes compile and render speed and allocations to `docs/benchmarks.json`, which
158
+ the website reads. Run it before each release, on a quiet machine:
159
+
160
+ ```sh
161
+ bundle exec rake bench # YJIT, 5 seconds per measurement
162
+ SUPER_SLIM_CORPUS=path/to/app/views bundle exec rake bench # adds the compile time of a whole app against Slim
163
+ BENCH_LOCK=path/to/.bench.lock bundle exec rake bench # waits for other benchmarks holding that lock
164
+ ```
165
+
166
+ With `corpus/cache` fetched, it also times the open-source corpus, and `super-slim lint` and `fmt` there against
167
+ slim-lint when it is installed. `bundle exec rake docs` rewrites the linter rules on the website
168
+ (`docs/tools.html`), after checking every example against the linter.
169
+
170
+ ## Development
171
+
172
+ ```sh
173
+ bundle install
174
+ bundle exec rake test # SuperSlim's tests, then Slim's own suites
175
+ BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rake test # the same on Rails 8.0
176
+ bundle exec rubocop
177
+ FUZZ_SEED=42 FUZZ_CASES=20000 bundle exec ruby -Ilib test/fuzz_test.rb
178
+ bundle exec ruby -Ilib bench/corpus.rb path/to/app # compile time and allocations against Slim
179
+ bundle exec ruby --yjit -Ilib bench/render.rb # render speed and allocations against Slim
180
+ ```
181
+
182
+ ### Releasing
183
+
184
+ Pull requests leave `CHANGELOG.md` alone: a release starts by writing its notes under `## [Unreleased]`, from the
185
+ pull requests merged since the last tag, without committing them. Refresh the website's numbers on a quiet machine
186
+ (`SUPER_SLIM_CORPUS=path/to/app/views bundle exec rake bench`) and merge them through a pull request, then, from an
187
+ up-to-date `main` whose CI is green:
188
+
189
+ ```sh
190
+ bin/release minor --dry-run # the notes and the changes, nothing written
191
+ bin/release minor # or patch, major, or an exact version such as 1.2.0
192
+ ```
193
+
194
+ `main` takes no direct push, so `bin/release` dates the Unreleased section and bumps the version on a branch
195
+ `release/vX.Y.Z`, opens its pull request, which merges itself once CI is green, and waits for it. It then pushes a
196
+ signed tag `vX.Y.Z` on the merge commit (`bin/release --tag X.Y.Z` does it if you stopped waiting). The tag starts
197
+ the Release workflow, which publishes the gem to RubyGems through trusted publishing and creates the GitHub release
198
+ with the version's notes and the `.gem`.
199
+
200
+ ## License
201
+
202
+ MIT, by Level UP Solutions, part of [Next Gen Gems](https://github.com/next-gen-gems). The parser follows the
203
+ grammar and parsing algorithm of [Slim](https://github.com/slim-template/slim), by the Slim Team (MIT).
@@ -0,0 +1,212 @@
1
+ inherit_mode:
2
+ merge:
3
+ - Exclude
4
+ - Include
5
+
6
+ AllCops:
7
+ Include:
8
+ - "**/*.slim"
9
+
10
+ Layout/ArgumentAlignment:
11
+ Exclude:
12
+ - "**/*.slim"
13
+
14
+ Layout/ArrayAlignment:
15
+ Exclude:
16
+ - "**/*.slim"
17
+
18
+ Layout/BlockAlignment:
19
+ Exclude:
20
+ - "**/*.slim"
21
+
22
+ Layout/ClosingParenthesisIndentation:
23
+ Exclude:
24
+ - "**/*.slim"
25
+
26
+ Layout/EmptyLineAfterGuardClause:
27
+ Exclude:
28
+ - "**/*.slim"
29
+
30
+ Layout/EndAlignment:
31
+ Exclude:
32
+ - "**/*.slim"
33
+
34
+ Layout/FirstArgumentIndentation:
35
+ Exclude:
36
+ - "**/*.slim"
37
+
38
+ Layout/FirstArrayElementIndentation:
39
+ Exclude:
40
+ - "**/*.slim"
41
+
42
+ Layout/FirstArrayElementLineBreak:
43
+ Exclude:
44
+ - "**/*.slim"
45
+
46
+ Layout/FirstHashElementIndentation:
47
+ Exclude:
48
+ - "**/*.slim"
49
+
50
+ Layout/FirstHashElementLineBreak:
51
+ Exclude:
52
+ - "**/*.slim"
53
+
54
+ Layout/FirstMethodArgumentLineBreak:
55
+ Exclude:
56
+ - "**/*.slim"
57
+
58
+ Layout/FirstParameterIndentation:
59
+ Exclude:
60
+ - "**/*.slim"
61
+
62
+ Layout/HashAlignment:
63
+ Exclude:
64
+ - "**/*.slim"
65
+
66
+ Layout/InitialIndentation:
67
+ Exclude:
68
+ - "**/*.slim"
69
+
70
+ Layout/IndentationConsistency:
71
+ Exclude:
72
+ - "**/*.slim"
73
+
74
+ Layout/IndentationWidth:
75
+ Exclude:
76
+ - "**/*.slim"
77
+
78
+ Layout/LineEndStringConcatenationIndentation:
79
+ Exclude:
80
+ - "**/*.slim"
81
+
82
+ Layout/LineLength:
83
+ Exclude:
84
+ - "**/*.slim"
85
+
86
+ Layout/MultilineArrayBraceLayout:
87
+ Exclude:
88
+ - "**/*.slim"
89
+
90
+ Layout/MultilineAssignmentLayout:
91
+ Exclude:
92
+ - "**/*.slim"
93
+
94
+ Layout/MultilineHashBraceLayout:
95
+ Exclude:
96
+ - "**/*.slim"
97
+
98
+ Layout/MultilineMethodCallBraceLayout:
99
+ Exclude:
100
+ - "**/*.slim"
101
+
102
+ Layout/MultilineMethodCallIndentation:
103
+ Exclude:
104
+ - "**/*.slim"
105
+
106
+ Layout/MultilineMethodDefinitionBraceLayout:
107
+ Exclude:
108
+ - "**/*.slim"
109
+
110
+ Layout/MultilineOperationIndentation:
111
+ Exclude:
112
+ - "**/*.slim"
113
+
114
+ Layout/ParameterAlignment:
115
+ Exclude:
116
+ - "**/*.slim"
117
+
118
+ Layout/TrailingEmptyLines:
119
+ Exclude:
120
+ - "**/*.slim"
121
+
122
+ Layout/TrailingWhitespace:
123
+ Exclude:
124
+ - "**/*.slim"
125
+
126
+ Lint/EmptyFile:
127
+ Exclude:
128
+ - "**/*.slim"
129
+
130
+ Lint/Syntax:
131
+ Exclude:
132
+ - "**/*.slim"
133
+
134
+ Lint/UselessAssignment:
135
+ Exclude:
136
+ - "**/*.slim"
137
+
138
+ Lint/Void:
139
+ Exclude:
140
+ - "**/*.slim"
141
+
142
+ Metrics/BlockLength:
143
+ Exclude:
144
+ - "**/*.slim"
145
+
146
+ Metrics/BlockNesting:
147
+ Exclude:
148
+ - "**/*.slim"
149
+
150
+ Naming/FileName:
151
+ Exclude:
152
+ - "**/*.slim"
153
+
154
+ Style/FrozenStringLiteralComment:
155
+ Exclude:
156
+ - "**/*.slim"
157
+
158
+ Style/IdenticalConditionalBranches:
159
+ Exclude:
160
+ - "**/*.slim"
161
+
162
+ Style/IfUnlessModifier:
163
+ Exclude:
164
+ - "**/*.slim"
165
+
166
+ Style/MultilineTernaryOperator:
167
+ Exclude:
168
+ - "**/*.slim"
169
+
170
+ Style/NestedTernaryOperator:
171
+ Exclude:
172
+ - "**/*.slim"
173
+
174
+ Style/Next:
175
+ Exclude:
176
+ - "**/*.slim"
177
+
178
+ Style/ParallelAssignment:
179
+ Exclude:
180
+ - "**/*.slim"
181
+
182
+ Style/RedundantParentheses:
183
+ Exclude:
184
+ - "**/*.slim"
185
+
186
+ Style/RescueModifier:
187
+ Exclude:
188
+ - "**/*.slim"
189
+
190
+ Style/Semicolon:
191
+ Exclude:
192
+ - "**/*.slim"
193
+
194
+ Style/TrailingCommaInArguments:
195
+ Exclude:
196
+ - "**/*.slim"
197
+
198
+ Style/TrailingCommaInArrayLiteral:
199
+ Exclude:
200
+ - "**/*.slim"
201
+
202
+ Style/TrailingCommaInHashLiteral:
203
+ Exclude:
204
+ - "**/*.slim"
205
+
206
+ Style/WhileUntilDo:
207
+ Exclude:
208
+ - "**/*.slim"
209
+
210
+ Style/WhileUntilModifier:
211
+ Exclude:
212
+ - "**/*.slim"
data/exe/super-slim ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/super_slim/cli"
5
+
6
+ exit SuperSlim::CLI.new.call(ARGV)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SuperSlim
4
+ module AST
5
+ NEWLINE = :newline
6
+
7
+ # Structs, not Data: Data.new allocates two more objects per node and is 2.8x slower, on every template line.
8
+ Static = Struct.new(:text)
9
+ Text = Struct.new(:text)
10
+ TextBlock = Struct.new(:children)
11
+ InlineHtml = Struct.new(:text, :children)
12
+ Tag = Struct.new(:name, :attributes, :content)
13
+ Control = Struct.new(:code, :children)
14
+ Output = Struct.new(:code, :escape, :children)
15
+ Comment = Struct.new(:children)
16
+ Doctype = Struct.new(:type)
17
+ Embedded = Struct.new(:engine, :children, :attributes)
18
+
19
+ StaticAttribute = Struct.new(:name, :value)
20
+ QuotedAttribute = Struct.new(:name, :escape, :text)
21
+ CodeAttribute = Struct.new(:name, :escape, :code)
22
+ BooleanAttribute = Struct.new(:name)
23
+ Splat = Struct.new(:code)
24
+
25
+ def self.empty?(nodes)
26
+ nodes.all?(NEWLINE)
27
+ end
28
+
29
+ # Once compiled, a text block with no text leaves nothing behind: Slim then closes a void tag holding one.
30
+ def self.renders_nothing?(nodes)
31
+ nodes.all? { |node| node == NEWLINE || (node.is_a?(TextBlock) && renders_nothing?(node.children)) }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../super_slim"
4
+ require_relative "linter"
5
+ require_relative "parallel"
6
+
7
+ module SuperSlim
8
+ class CLI
9
+ USAGE = <<~TEXT
10
+ Usage: super-slim lint [PATH…] report what breaks the rules of .super-slim.yml
11
+ super-slim fmt [--check] [PATH…] rewrite templates in their canonical layout
12
+ super-slim version
13
+ TEXT
14
+ SKIPPED_DIRECTORIES = %r{(\A|/)(\.git|node_modules|tmp|vendor)/}
15
+ EX_USAGE = 64
16
+
17
+ def call(arguments)
18
+ command, *arguments = arguments
19
+ configuration = Linter.configuration
20
+ SuperSlim.tailwind_classes = configuration["tailwind_classes"] if configuration.key?("tailwind_classes")
21
+ case command
22
+ when "lint" then lint(arguments, configuration)
23
+ when "fmt" then format(arguments, configuration.fetch("fmt", {}))
24
+ when "version", "--version" then puts(VERSION) || 0
25
+ else warn(USAGE) || EX_USAGE
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def lint(paths, configuration)
32
+ linter = Linter.new(configuration.fetch("linters", {}))
33
+ offenses = Parallel.map(templates(paths)) { |path| linter.call(path, File.read(path)) }.flatten
34
+ offenses.each { |offense| puts "#{offense.path}:#{offense.lineno}: [#{offense.rule}] #{offense.message}" }
35
+ offenses.empty? ? 0 : 1
36
+ end
37
+
38
+ def format(arguments, options)
39
+ check = arguments.delete("--check")
40
+ formatter = Formatter.new(max_line_length: options["max_line_length"], attribute_delimiters: options.fetch("attribute_delimiters", "()"))
41
+ outcomes = Parallel.map(templates(arguments)) do |path|
42
+ source = File.read(path)
43
+ formatted = formatter.call(source)
44
+ next if formatted == source
45
+
46
+ File.write(path, formatted) unless check
47
+ [:reformatted, path]
48
+ rescue Error => exception
49
+ [:refused, "#{path}: #{exception.message.lines.first.strip}"]
50
+ end
51
+ outcomes.compact.each { |outcome, text| (outcome == :reformatted) ? puts(text) : warn(text) }
52
+ (outcomes.any? { |outcome,| outcome == :refused || (outcome == :reformatted && check) }) ? 1 : 0
53
+ end
54
+
55
+ def templates(paths)
56
+ paths = ["."] if paths.empty?
57
+ paths.flat_map do |path|
58
+ next [path] unless File.directory?(path)
59
+
60
+ Dir.glob("**/*.slim", base: path).filter_map { |template| File.join(path, template) unless template.match?(SKIPPED_DIRECTORIES) }
61
+ end.sort
62
+ end
63
+ end
64
+ end