super-slim 3.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 +7 -0
- data/CHANGELOG.md +118 -0
- data/LICENSE +21 -0
- data/LICENSE-slim +22 -0
- data/README.md +217 -0
- data/config/rubocop.yml +212 -0
- data/exe/super-slim +6 -0
- data/lib/super-slim.rb +4 -0
- data/lib/super_slim/ast.rb +34 -0
- data/lib/super_slim/cli.rb +71 -0
- data/lib/super_slim/compiler.rb +467 -0
- data/lib/super_slim/formatter/line_wrapper.rb +131 -0
- data/lib/super_slim/formatter.rb +125 -0
- data/lib/super_slim/linter/rules.rb +585 -0
- data/lib/super_slim/linter.rb +81 -0
- data/lib/super_slim/parallel.rb +36 -0
- data/lib/super_slim/parser.rb +556 -0
- data/lib/super_slim/rails_handler.rb +31 -0
- data/lib/super_slim/railtie.rb +12 -0
- data/lib/super_slim/rubocop.rb +128 -0
- data/lib/super_slim/runtime.rb +103 -0
- data/lib/super_slim/shortcuts.rb +28 -0
- data/lib/super_slim/version.rb +5 -0
- data/lib/super_slim.rb +53 -0
- metadata +86 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7c27c1a3bdc54556d0e0418a316838423719f9a0da47bba2be27b4a2812498c7
|
|
4
|
+
data.tar.gz: d5a779c6fa56691f985b3a82516769118dcb17a883eacfc6bbbe23b6afb14da0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0d5e8d990ae3886c94cbd198f1ccfbf51722e121cf2529bfc239da0b52b6639aaf9dec2609396eef4ab43f938d9d15d0f1913085c79b23704c8373e17bac4269
|
|
7
|
+
data.tar.gz: e7eef329f3c7adef4ba9b7f53e697a35e8aec0501c72304517c590ade320641555ec7167cf6f0ff1fa8f693fe22b45bb99da3a69e6808afcc13cdfa630a5123f
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [3.0.0] — 2026-11-05
|
|
6
|
+
|
|
7
|
+
SuperSlim is published as super-slim, the name of its command.
|
|
8
|
+
|
|
9
|
+
**Breaking**
|
|
10
|
+
- The gem is renamed `super-slim`: write `gem "super-slim"` in the Gemfile and `plugins: [super-slim]` in RuboCop's
|
|
11
|
+
configuration. The `SuperSlim` module, `require "super_slim"`, the `super-slim` command and `.super-slim.yml` are
|
|
12
|
+
unchanged.
|
|
13
|
+
|
|
14
|
+
## [2.2.0] — 2026-11-05
|
|
15
|
+
|
|
16
|
+
Six linter rules and options, `super-slim format`, and a formatter that no longer wraps a line RuboCop then refuses.
|
|
17
|
+
|
|
18
|
+
**New**
|
|
19
|
+
- `super-slim format` and the `format:` key of `.super-slim.yml`; `fmt` remains an alias for both.
|
|
20
|
+
- Every linter rule takes `include:` and `exclude:` globs.
|
|
21
|
+
- DeprecatedAttributes (`attributes: {old: new}`), and prefixes in DeprecatedClasses (`ml-*: ms-*`).
|
|
22
|
+
- UnescapedOutput (`==`, `html_safe`, `raw`, `#{{}}`), EmbeddedEngines (`forbidden_engines:`) and HardcodedText, off
|
|
23
|
+
by default.
|
|
24
|
+
|
|
25
|
+
**Fixed**
|
|
26
|
+
- `super-slim format` keeps a line ending with an `if`, `unless`, `while`, `until` or `rescue` modifier on one line:
|
|
27
|
+
wrapped, RuboCop refused it (Style/MultilineIfModifier).
|
|
28
|
+
|
|
29
|
+
## [2.1.0] — 2026-11-05
|
|
30
|
+
|
|
31
|
+
Slim's doctypes are back, for email layouts.
|
|
32
|
+
|
|
33
|
+
**New**
|
|
34
|
+
- `doctype transitional`, `strict` and `frameset` render the HTML 4.01 doctypes again, and XML templates take `1.1`,
|
|
35
|
+
`basic`, `mobile`, `svg` and the XHTML 1.0 ones, as in Slim 5. Email layouts need `doctype transitional`:
|
|
36
|
+
`<!DOCTYPE html>` switches Apple Mail and iOS Mail to standards mode, which opens a gap under the images of table
|
|
37
|
+
cells.
|
|
38
|
+
|
|
39
|
+
## [2.0.1] — 2026-11-05
|
|
40
|
+
|
|
41
|
+
Two fixes to `super-slim`: braces around wrapped attributes, and `--help`.
|
|
42
|
+
|
|
43
|
+
**Fixed**
|
|
44
|
+
- `super-slim fmt` writes a space before `{` when it wraps attributes inside `{}` (`a.btn {`), as templates
|
|
45
|
+
usually do; `(` and `[` stay attached to the tag.
|
|
46
|
+
- `super-slim --help`, `-h` and `help` print the usage on stdout and exit 0, after a command too
|
|
47
|
+
(`super-slim fmt --help`). An unknown option, such as `fmt --chek`, fails with the usage and status 64
|
|
48
|
+
instead of crashing.
|
|
49
|
+
|
|
50
|
+
## [2.0.0] — 2026-11-05
|
|
51
|
+
|
|
52
|
+
SlimX becomes SuperSlim, sheds what a Rails app doesn't need, and `super-slim fmt` wraps long lines.
|
|
53
|
+
|
|
54
|
+
**Breaking**
|
|
55
|
+
- The gem is renamed: `super_slim` in the Gemfile, the `SuperSlim` module, the `super-slim lint|fmt` command,
|
|
56
|
+
`.super-slim.yml` and `plugins: [super_slim]` for RuboCop. Compiled templates name their variables
|
|
57
|
+
`_super_slim1` instead of `_slimx1`.
|
|
58
|
+
- No more Tilt adapter: `require "slimx/tilt"` has no replacement. Rails and `SuperSlim.compile` are unchanged.
|
|
59
|
+
- No more strict mode (`SlimX.strict`, `strict: true`): run `super-slim fmt --check` in CI instead.
|
|
60
|
+
- No more custom shortcuts: only `#id` and `.class`.
|
|
61
|
+
- Only `doctype 5`, `doctype html` and `doctype xml` remain; HTML 4 and XHTML doctypes, such as
|
|
62
|
+
`doctype transitional`, raise `Invalid doctype`.
|
|
63
|
+
|
|
64
|
+
**New**
|
|
65
|
+
- `super-slim fmt` wraps lines longer than `fmt: max_line_length` in `.super-slim.yml`, off by default: a tag's
|
|
66
|
+
attributes one per line inside `fmt: attribute_delimiters` (`"()"`, `"[]"` or `"{}"`), then the arguments of
|
|
67
|
+
its Ruby code after each comma, four spaces deep when the line opens a block. A wrap is kept only when the
|
|
68
|
+
compiled Ruby differs by whitespace alone.
|
|
69
|
+
|
|
70
|
+
## [1.1.0] — 2026-11-04
|
|
71
|
+
|
|
72
|
+
A RuboCop plugin that lints the Ruby inside templates with SlimX's own parser, and templates compiled up to 10% faster.
|
|
73
|
+
|
|
74
|
+
**New**
|
|
75
|
+
- A RuboCop plugin (`plugins: [slimx]`) lints the Ruby of templates: `-` and `=` lines, code attributes, splats,
|
|
76
|
+
`#{}` interpolations and `ruby:` blocks. Offenses point at their line and column in the `.slim` file, and
|
|
77
|
+
autocorrection writes back into it. It replaces rubocop-slim, and with it slimi and Temple; unlike slimi, it
|
|
78
|
+
also reads the output of a tag with whitespace markers and attributes (`span> title="x" = name`).
|
|
79
|
+
|
|
80
|
+
**Faster**
|
|
81
|
+
- Templates compile 7% faster without JIT and 10% faster with YJIT, with 11% to 16% fewer allocations (6,097
|
|
82
|
+
public templates, Ruby 3.3 and 4.0). Rendering is unchanged.
|
|
83
|
+
|
|
84
|
+
**Fixed**
|
|
85
|
+
- `slimx lint` reports the offenses of one line in the same order on every Ruby version.
|
|
86
|
+
|
|
87
|
+
## [1.0.0] — 2026-11-04
|
|
88
|
+
|
|
89
|
+
First stable release: a Slim 5 engine written from scratch, without Temple. 0.1.0 and 0.1.1 were previews. It
|
|
90
|
+
renders what Slim 5 renders with `format: :html, sort_attrs: false`, byte for byte; XML formats (`.xml`, `.rss`,
|
|
91
|
+
`.atom`) render as XML.
|
|
92
|
+
|
|
93
|
+
**New**
|
|
94
|
+
- Rails through a railtie, Tilt through `require "slimx/tilt"`, and `SlimX.compile` for plain Ruby.
|
|
95
|
+
- Every `-` and `=` stays on its template line, so backtraces point at the right one.
|
|
96
|
+
- `slimx fmt`, which refuses any change to the compiled Ruby, and strict mode (`SlimX.strict`), which
|
|
97
|
+
refuses unformatted templates at compile time.
|
|
98
|
+
- `slimx lint` with 29 rules from slim-lint, haml-lint, erb_lint, Herb and eslint-plugin-jsx-a11y,
|
|
99
|
+
configured in `.slimx.yml`.
|
|
100
|
+
- `tailwind_classes`, off by default: class shortcuts read Tailwind class names.
|
|
101
|
+
|
|
102
|
+
**Left out**
|
|
103
|
+
- XHTML, `sort_attrs` and the other Temple options, the `coffee:`, `sass:`, `scss:`, `less:`, `erb:`,
|
|
104
|
+
`builder:` and wiki engines, conditional comments, the `logic_less`, `translator`, `include` and `smart`
|
|
105
|
+
plugins, and lambda shortcuts: each raises at compile time.
|
|
106
|
+
|
|
107
|
+
**Requires**
|
|
108
|
+
- 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,
|
|
109
|
+
newer than the one Ruby 3.3 ships.
|
|
110
|
+
|
|
111
|
+
[Unreleased]: https://github.com/levups/super-slim/compare/v3.0.0...HEAD
|
|
112
|
+
[3.0.0]: https://github.com/levups/super-slim/compare/v2.2.0...v3.0.0
|
|
113
|
+
[2.2.0]: https://github.com/levups/super-slim/compare/v2.1.0...v2.2.0
|
|
114
|
+
[2.1.0]: https://github.com/levups/super-slim/compare/v2.0.1...v2.1.0
|
|
115
|
+
[2.0.1]: https://github.com/levups/super-slim/compare/v2.0.0...v2.0.1
|
|
116
|
+
[2.0.0]: https://github.com/levups/super-slim/compare/v1.1.0...v2.0.0
|
|
117
|
+
[1.1.0]: https://github.com/next-gen-gems/slimx/compare/v1.0.0...v1.1.0
|
|
118
|
+
[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,217 @@
|
|
|
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, Slim's doctypes (`html`, `transitional` for email clients, their XHTML versions in XML
|
|
30
|
+
templates), and the `ruby:`, `javascript:`, `css:` and `markdown:` (through
|
|
31
|
+
[Markly](https://github.com/ioquatix/markly)) embedded engines.
|
|
32
|
+
|
|
33
|
+
## Tailwind class names
|
|
34
|
+
|
|
35
|
+
Slim cuts `.rounded-b-[3px]` at the bracket, and refuses `.lg:[&:nth-child(3)]:hover:underline`. Turn on
|
|
36
|
+
`tailwind_classes` and the `.` shortcut takes any Tailwind class name:
|
|
37
|
+
|
|
38
|
+
```slim
|
|
39
|
+
.p-1.rounded-b-[3px].lg:[&:nth-child(3)]:hover:underline
|
|
40
|
+
.font-['Open_Sans'].bg-(--brand).mx-0.5.w-1/2.!font-bold.@lg:flex
|
|
41
|
+
ul.*:p-2
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
SuperSlim.tailwind_classes = true # config/initializers/super_slim.rb, or tailwind_classes: true per compile
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
# .super-slim.yml, for super-slim lint and super-slim format
|
|
50
|
+
tailwind_classes: true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Attribute lists keep their meaning: Tailwind never puts a bracket right after a letter or a digit, so
|
|
54
|
+
`a.btn[href="/"]` is still a link with a class and an `href`. A colon followed by a space still nests a tag
|
|
55
|
+
(`li.item: a`), and a final `/` still closes one. What changes is what Slim mangles: it renders `.size-2.5` as
|
|
56
|
+
`class="size-2 5"`, the option as `class="size-2.5"`. On 7,505 templates from 31 apps, 16 compile differently
|
|
57
|
+
with the option, all of them Tailwind classes Slim breaks. It stays off by default all the same, so that
|
|
58
|
+
turning SuperSlim on changes nothing.
|
|
59
|
+
|
|
60
|
+
The option only changes how templates are read: compiling 1,408 templates takes the same time (+0.5%, within
|
|
61
|
+
noise), and the compiled Ruby, so the time it takes to render, is the same.
|
|
62
|
+
|
|
63
|
+
## What SuperSlim leaves out
|
|
64
|
+
|
|
65
|
+
Each fails at compile time with a clear error instead of being silently ignored:
|
|
66
|
+
|
|
67
|
+
- the XHTML format, `sort_attrs`, `attr_quote`, `pretty`, `js_wrapper` and every Temple option;
|
|
68
|
+
- the `coffee:`, `sass:`, `scss:`, `less:`, `erb:`, `builder:`, `textile:`, `rdoc:`, `asciidoc:` and wiki
|
|
69
|
+
engines, and `/[if IE]` conditional comments;
|
|
70
|
+
- the `logic_less`, `translator`, `include` and `smart` plugins, and custom shortcuts: only `#id` and `.class`.
|
|
71
|
+
|
|
72
|
+
They can come back if people ask, as long as they cost nothing at render time.
|
|
73
|
+
|
|
74
|
+
## Migrating from Slim
|
|
75
|
+
|
|
76
|
+
Slim's defaults are `format: :xhtml` and `sort_attrs: true`. If you never changed them, first switch Slim
|
|
77
|
+
to `format: :html, sort_attrs: false` and check your test suite: that is the output SuperSlim gives. Then swap
|
|
78
|
+
`slim-rails` for `super-slim`, and drop any `Slim::Engine.set_options`/`with_options` call.
|
|
79
|
+
|
|
80
|
+
## Formatter and linter
|
|
81
|
+
|
|
82
|
+
One parser serves the compiler, the formatter and the linter, so all three read templates the same way.
|
|
83
|
+
|
|
84
|
+
`super-slim format [--check] [PATH…]` (or `fmt`) rewrites templates in their canonical layout: two spaces per level, `- code`,
|
|
85
|
+
`= output` and `p = output` spacing, no redundant `div`, no trailing whitespace, single blank lines, one final
|
|
86
|
+
newline. Text blocks move as a whole, so the whitespace they render stays put — and before writing anything,
|
|
87
|
+
the formatter compiles both versions and refuses if the Ruby differs. Formatting cannot change a page.
|
|
88
|
+
|
|
89
|
+
With `format: max_line_length: 120` in `.super-slim.yml`, `format` also wraps longer lines: a tag's attributes one
|
|
90
|
+
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
|
|
91
|
+
when the compiled Ruby differs by whitespace alone, and leaves lines already spread over several lines alone.
|
|
92
|
+
|
|
93
|
+
`super-slim lint [PATH…]` reads `.super-slim.yml` and checks, with about a third of the CPU time slim-lint takes:
|
|
94
|
+
|
|
95
|
+
| Rule | From | Default |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| Indentation, Tab, TrailingWhitespace, EmptyLines, TrailingBlankLines, ControlStatementSpacing, RedundantDiv | slim-lint, fixed by `super-slim format` | on |
|
|
98
|
+
| LineLength (`max: 120`), CommentControlStatement, EmptyControlStatement, ConsecutiveControlStatements (`max: 2`), TagCase | slim-lint | on |
|
|
99
|
+
| InstanceVariables (in partials) | haml-lint, Rails conventions | on |
|
|
100
|
+
| StrictLocals (partials declare `/# locals:`) | Rails 7.1 strict locals | off |
|
|
101
|
+
| DuplicateId, ImageAlt, IframeTitle, AnchorHref, NestedLinks, PositiveTabindex, BooleanAttributeValue, AriaRole, AriaAttribute | Herb, eslint-plugin-jsx-a11y | on |
|
|
102
|
+
| ButtonType, InlineStyles | eslint-plugin-react, haml-lint | off |
|
|
103
|
+
| DeprecatedClasses (`classes: {old: new}`, `old-*: new-*` for a prefix), DeprecatedAttributes (`attributes: {old: new}`) | erb_lint | on, empty |
|
|
104
|
+
| UnescapedOutput (`==`, `html_safe`, `raw`, `#{{}}`) | Brakeman, erb_lint | off |
|
|
105
|
+
| EmbeddedEngines (`forbidden_engines: [javascript, css]`) | slim-lint | off |
|
|
106
|
+
| HardcodedText (text to translate) | erb_lint | off |
|
|
107
|
+
| UnnecessaryInterpolation, UnnecessaryStringOutput | haml-lint | on |
|
|
108
|
+
| Zwsp (invisible characters) | slim-lint | on |
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
# .super-slim.yml
|
|
112
|
+
linters:
|
|
113
|
+
LineLength:
|
|
114
|
+
max: 150
|
|
115
|
+
ButtonType:
|
|
116
|
+
enabled: true
|
|
117
|
+
DeprecatedClasses:
|
|
118
|
+
classes:
|
|
119
|
+
text-muted: text-body-secondary
|
|
120
|
+
ml-*: ms-*
|
|
121
|
+
InlineStyles:
|
|
122
|
+
enabled: true
|
|
123
|
+
exclude: ["app/views/*_mailer/**/*"]
|
|
124
|
+
HardcodedText:
|
|
125
|
+
enabled: true
|
|
126
|
+
include: ["app/views/shop/**/*.slim"]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Every rule takes `include:` and `exclude:`, lists of globs matched against paths relative to `.super-slim.yml`
|
|
130
|
+
(`**/` spans directories).
|
|
131
|
+
|
|
132
|
+
The Ruby inside templates is RuboCop's job: SuperSlim ships a RuboCop plugin that hands it each `-` and `=` line,
|
|
133
|
+
code attribute, splat, `#{}` interpolation and `ruby:` block, read by SuperSlim's own parser. Offenses point at their
|
|
134
|
+
line and column in the `.slim` file, and autocorrection writes back into it. It replaces rubocop-slim, and with
|
|
135
|
+
it slimi and Temple:
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
# .rubocop-slim.yml, run with rubocop -c .rubocop-slim.yml
|
|
139
|
+
inherit_from: .rubocop.yml
|
|
140
|
+
plugins:
|
|
141
|
+
- super-slim
|
|
142
|
+
AllCops:
|
|
143
|
+
Include:
|
|
144
|
+
- "**/*.slim"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The cops that cannot hold in a template — indentation and alignment across lines, frozen string comments,
|
|
148
|
+
`Lint/UselessAssignment` on a variable a later line reads — are turned off for `.slim` files, as rubocop-slim
|
|
149
|
+
does. Unlike slimi, SuperSlim also reads the output of a tag with whitespace markers and attributes
|
|
150
|
+
(`span> title="x" = name`), so a few offenses appear when switching.
|
|
151
|
+
|
|
152
|
+
## How it is tested
|
|
153
|
+
|
|
154
|
+
- Slim's own test suite and its literate specification (`TESTS.md`) run against SuperSlim: every template renders
|
|
155
|
+
through Slim and SuperSlim, and both outputs must match.
|
|
156
|
+
- A seeded fuzzer generates thousands of random templates from the grammar and compares both engines; CI adds
|
|
157
|
+
10,000 from a new seed on every run.
|
|
158
|
+
- Golden snapshots (`test/golden`) hold the rendered HTML, compiled Ruby, formatting and lint offenses of
|
|
159
|
+
templates covering the grammar, its errors and Tailwind class names: `UPDATE_GOLDEN=1` rewrites them.
|
|
160
|
+
- `bench/compiled_digests.rb` proves a refactoring leaves the compiled Ruby of a whole corpus unchanged.
|
|
161
|
+
|
|
162
|
+
Real templates come from 30 public apps pinned in `corpus/manifest.yml`: `rake corpus:fetch` downloads only
|
|
163
|
+
their `.slim` files into the ignored `corpus/cache`, `rake corpus:check` compiles, formats and lints each one
|
|
164
|
+
against Slim, and `rake corpus:diff BASE=rev` reports what changes between a revision and the working tree:
|
|
165
|
+
compiled Ruby, formatting, lint offenses. Downloaded templates are never rendered, as rendering runs their
|
|
166
|
+
Ruby; directories you trust, such as your own app, render too when passed in `SUPER_SLIM_CORPUS`.
|
|
167
|
+
|
|
168
|
+
## Benchmarks
|
|
169
|
+
|
|
170
|
+
`bench/compare.rb` renders the same blog page with SuperSlim, Slim, Haml, Erubi and Herb, checks that they all
|
|
171
|
+
produce the same HTML, and writes compile and render speed and allocations to `docs/benchmarks.json`, which
|
|
172
|
+
the website reads. Run it before each release, on a quiet machine:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
bundle exec rake bench # YJIT, 5 seconds per measurement
|
|
176
|
+
SUPER_SLIM_CORPUS=path/to/app/views bundle exec rake bench # adds the compile time of a whole app against Slim
|
|
177
|
+
BENCH_LOCK=path/to/.bench.lock bundle exec rake bench # waits for other benchmarks holding that lock
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
With `corpus/cache` fetched, it also times the open-source corpus, and `super-slim lint` and `format` there against
|
|
181
|
+
slim-lint when it is installed. `bundle exec rake docs` rewrites the linter rules on the website
|
|
182
|
+
(`docs/tools.html`), after checking every example against the linter.
|
|
183
|
+
|
|
184
|
+
## Development
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
bundle install
|
|
188
|
+
bundle exec rake test # SuperSlim's tests, then Slim's own suites
|
|
189
|
+
BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rake test # the same on Rails 8.0
|
|
190
|
+
bundle exec rubocop
|
|
191
|
+
FUZZ_SEED=42 FUZZ_CASES=20000 bundle exec ruby -Ilib test/fuzz_test.rb
|
|
192
|
+
bundle exec ruby -Ilib bench/corpus.rb path/to/app # compile time and allocations against Slim
|
|
193
|
+
bundle exec ruby --yjit -Ilib bench/render.rb # render speed and allocations against Slim
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Releasing
|
|
197
|
+
|
|
198
|
+
Pull requests leave `CHANGELOG.md` alone: a release starts by writing its notes under `## [Unreleased]`, from the
|
|
199
|
+
pull requests merged since the last tag, without committing them. Refresh the website's numbers on a quiet machine
|
|
200
|
+
(`SUPER_SLIM_CORPUS=path/to/app/views bundle exec rake bench`) and merge them through a pull request, then, from an
|
|
201
|
+
up-to-date `main` whose CI is green:
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
bin/release minor --dry-run # the notes and the changes, nothing written
|
|
205
|
+
bin/release minor # or patch, major, or an exact version such as 1.2.0
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`main` takes no direct push, so `bin/release` dates the Unreleased section and bumps the version on a branch
|
|
209
|
+
`release/vX.Y.Z`, opens its pull request, which merges itself once CI is green, and waits for it. It then pushes a
|
|
210
|
+
signed tag `vX.Y.Z` on the merge commit (`bin/release --tag X.Y.Z` does it if you stopped waiting). The tag starts
|
|
211
|
+
the Release workflow, which publishes the gem to RubyGems through trusted publishing and creates the GitHub release
|
|
212
|
+
with the version's notes and the `.gem`.
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT, by Level UP Solutions, part of [Next Gen Gems](https://github.com/next-gen-gems). The parser follows the
|
|
217
|
+
grammar and parsing algorithm of [Slim](https://github.com/slim-template/slim), by the Slim Team (MIT).
|
data/config/rubocop.yml
ADDED
|
@@ -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
data/lib/super-slim.rb
ADDED
|
@@ -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
|