erb-linter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7852edfabee0e8808f79b8722b4256a3ddb37cd0d4dcd5581ebd4c505eeb7aca
4
+ data.tar.gz: 7988dbbceb52230a05fbb67a6ab638b37ea5eb068b1d6963b5fe278e66abdf66
5
+ SHA512:
6
+ metadata.gz: 39f296bfb232df44446f560f44e27ff8c67a4f9302fd64eb3f73005a35bf60de786c55d59a520e84fa08ac5c241ea5534b26d16ddcfba5d00847e34542651b70
7
+ data.tar.gz: f02389ff2ab6cfd34ff41596c26f93be8e8d2bec559a50c0978282e2de47997fae104ec59b498128ae27423531325d5e2750ccf0ae8ce92b01573dbd06b1e628
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in erb-linter.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ erb-linter (0.0.1)
5
+ rake
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ minitest (5.14.4)
11
+ rake (13.0.3)
12
+
13
+ PLATFORMS
14
+ x86_64-darwin-20
15
+
16
+ DEPENDENCIES
17
+ erb-linter!
18
+ minitest (~> 5.0)
19
+ rake (~> 13.0)
20
+
21
+ BUNDLED WITH
22
+ 2.2.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Nebulab srls
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/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # ERB::Linter
2
+
3
+ <a href="https://nebulab.it?utm_source=github&utm_medium=badge"><img src="https://img.shields.io/static/v1?label=Nebulab&message=Open+Source&color=%235dbefd&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMQSURBVHgBrZZNTxNRFIbfczsgIMQiLBCRFDaGhUr9A9iiIa5EEneIwE/AlRsNv8KlIKwFl35RTUBX8rFBdiVKAgqmXSCY0M71nJl+zJTOdKh9k6Yzc8/cZ86555x7CQE0mji5hSyGlQoNaOgIPwrnhtIArWszu4EQFudjdR8rzUV+gw8/ZMZB9IwvIwimJJGafhmjWZwFOJ7QkYzWCwTdj+qUDJGKz8Rou3RAlT4YS+hHWW2u/QdM1MNzrI6+zwyXDrg8FANStIDaSXOIJ5whLgAljOIZiglRK6U4vDfz4S2ElGGJWsEaQkCTUbhtNbV+lb+xgFY2Bs9ET0h/GzBxlfAkqnCUKY5xKfVLbsi1/R126lcF6WgCYp2ES42EBp6tvQFY+alLTUlrUxizJEVNWiVwBkVagGg7oe+CDclLYOfrgMdfTBz8PfWa1lkzbsDEsH/5FyF9YUK0zQ1xwpoZtsm9pwxMRLyA9wyi0A2Jcjl1NNqeeEFEimxYPkmWd014ikIDnDTeBb53DOweaRxnvWGyhnmYfPZWGt487sNi6lsK67/lZ1oZGOtUaD3nhtU7etXXfe0VzrzCBgLKCR68rNDX6oaJlvd0xXnklbSfgSTL/QghXF8EP980cVKyVL/Ys9UDVFJa8Tdt+1lYmcmJM3Vd4UEvWeslRf32h9ubrVRl77gBrCto85OfUU+LXTMGx+JuN2Hoin3/Zkfjj6ObBAknV+KG4jpc9BqXMEpiCMz6Z9ZQ12kvJZxb6co4Zr1W83esY8F2OYsIe+eEyfTiVXczCl7uM2wliHfMEJaRc3Wa++mLUotrF4EW7h6f94Dvh6aVFM60Fy8Xkya+BfBOjh5yUWhqY0vmKi9q1GnVxZ7sHKIWSs7FQ71yUagkRTTCfymnVY1gsgHHC5z8hbUjaz0Fr8ZanXhX0pPOw5SrV8wNGjNscMrTKpXKaj05f9twVYHnMZGPHEuwTwEBNi+3NGiNt6GRcsfEIAfhp2cAV3cQLtXoOz7q8+ZJRLx3kmxn4dy7aas1SrfiBpKraV/9A+PSJLDAXLUvAAAAAElFTkSuQmCC"></a>
4
+
5
+ It's all to easy to introduce errors inside ERB files, unclosed tags, wrong attribute names, bad indentation.
6
+
7
+ ERB::Linter can check for those errors by turning your ERB tags into HTML-ish tags and run a proper HTML linter on the result:
8
+
9
+ *original ERB*
10
+
11
+ ```erb
12
+ <div>
13
+ <% if @foo == :bar %>
14
+ <%= image_tag(item[:value], alt: item[:value]) %>
15
+ <% elsif @type == :other %>
16
+ <span><%= item[:value] %></span>
17
+ <% else %>
18
+ <% raise "unknown type" %>
19
+ <% end %>
20
+ </div>
21
+ ```
22
+
23
+ *generated HTML, for linter consumption*
24
+
25
+ ```html
26
+ <div>
27
+ <erb silent erb-code=" if @foo == :bar ">
28
+ <erb loud erb-code=" image_tag(item[:value], alt: item[:value]) "></erb>
29
+ </erb>
30
+ <erb silent erb-code=" elsif @type == :other ">
31
+ <span><erb loud erb-code=" item[:value] "></erb></span>
32
+ </erb>
33
+ <erb silent erb-code=" else ">
34
+ <erb silent erb-code=" raise &quot;unknown type&quot; "></erb>
35
+ </erb>
36
+ </div>
37
+ ```
38
+
39
+
40
+ ## Installation
41
+
42
+ Add this line to your application's Gemfile:
43
+
44
+ ```ruby
45
+ gem 'erb-linter'
46
+ ```
47
+
48
+ And then execute:
49
+
50
+ $ bundle install
51
+
52
+ Or install it yourself as:
53
+
54
+ $ gem install erb-linter
55
+
56
+ ## Usage
57
+
58
+ ### From the command line
59
+
60
+ ```bash
61
+ ruby -rbundler/setup -rerb/linter -e "exit ERB::Linter::Checker.check_files('app/views/**/*.html.erb')"
62
+ ```
63
+
64
+ ### As a Rake task
65
+
66
+ Add a task to your Rakefile:
67
+
68
+ ```ruby
69
+ ERB::Linter::Task.new do |task|
70
+ # Change the task name.
71
+ # Default: :erb_linter
72
+ # task.name = 'linter:html'
73
+
74
+ # Use a different glob for listing ERB files.
75
+ # Default: '**/*.erb'
76
+ # task.glob = 'app/components/**/*.html.erb'
77
+
78
+ # Use a different temp dir for storing the HTML version of your ERB files.
79
+ # Default: Dir.tmpdir
80
+ # task.tmpdir = Rails.root.join('tmp')
81
+ end
82
+ ```
83
+
84
+ Add a `linthtml` configuration:
85
+
86
+ `.linthtmlrc.js`
87
+
88
+ ```js
89
+ module.exports = {
90
+ maxerr: false,
91
+ "raw-ignore-regex": false,
92
+ "attr-bans": [
93
+ "align",
94
+ "background",
95
+ "bgcolor",
96
+ "border",
97
+ "frameborder",
98
+ "longdesc",
99
+ "marginwidth",
100
+ "marginheight",
101
+ "scrolling",
102
+ "style",
103
+ "width",
104
+ ],
105
+ "indent-delta": false,
106
+ "indent-style": "spaces",
107
+ "indent-width": 2,
108
+ "indent-width-cont": false,
109
+ "spec-char-escape": true,
110
+ "text-ignore-regex": false,
111
+ "tag-bans": ["style", "b"],
112
+ "tag-close": true,
113
+ "tag-name-lowercase": true,
114
+ "tag-name-match": true,
115
+ "tag-self-close": false,
116
+ "doctype-first": false,
117
+ "doctype-html5": false,
118
+ "attr-name-style": "dash",
119
+ "attr-name-ignore-regex": false,
120
+ "attr-no-dup": true,
121
+ "attr-no-unsafe-char": true,
122
+ "attr-order": false,
123
+ "attr-quote-style": "double",
124
+ "attr-req-value": false,
125
+ "attr-new-line": false,
126
+ "attr-validate": true,
127
+ "id-no-dup": true,
128
+ "id-class-no-ad": false,
129
+ "id-class-style": false, // "lowercase", "underscore", "dash", "camel", "bem"
130
+ "class-no-dup": true,
131
+ "class-style": false,
132
+ "id-class-ignore-regex": false,
133
+ "img-req-alt": true,
134
+ "img-req-src": true,
135
+ "html-valid-content-model": true,
136
+ "head-valid-content-model": true,
137
+ "href-style": false,
138
+ "link-req-noopener": true,
139
+ "label-req-for": false,
140
+ "line-end-style": "lf",
141
+ "line-no-trailing-whitespace": true,
142
+ "line-max-len": false,
143
+ "line-max-len-ignore-regex": false,
144
+ "head-req-title": true,
145
+ "title-no-dup": true,
146
+ "title-max-len": 60,
147
+ "html-req-lang": false,
148
+ "lang-style": "case",
149
+ "fig-req-figcaption": false,
150
+ "focusable-tabindex-style": false,
151
+ "input-radio-req-name": true,
152
+ "input-req-label": false,
153
+ "table-req-caption": false,
154
+ "table-req-header": false,
155
+ "tag-req-attr": false,
156
+ };
157
+ ```
158
+
159
+ ## Development
160
+
161
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
162
+
163
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
164
+
165
+ ## Contributing
166
+
167
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nebulab/erb-linter.
168
+
169
+ ## License
170
+
171
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "erb/linter"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/erb/linter"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "erb-linter"
7
+ spec.version = ERB::Linter::VERSION
8
+ spec.authors = ["Elia Schito"]
9
+ spec.email = ["elia@schito.me"]
10
+
11
+ spec.summary = "Check your ERB files for closing tags, indentation, bad attributes etc."
12
+ spec.homepage = "https://github.com/nebulab/erb-linter#readme"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5")
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/nebulab/erb-linter"
18
+ spec.metadata["changelog_uri"] = "https://github.com/nebulab/erb-linter/releases"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ files = Dir.chdir(File.expand_path(__dir__)) { `git ls-files -z`.split("\x0") }
23
+
24
+ spec.files = files.reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
+ spec.bindir = "exe"
26
+ spec.executables = files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "rake"
30
+ end
data/lib/erb/linter.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module ERB::Linter
6
+ VERSION = "0.0.1"
7
+
8
+ class Error < StandardError; end
9
+
10
+ autoload :Task, "erb/linter/task"
11
+ autoload :Converter, "erb/linter/converter"
12
+ autoload :Checker, "erb/linter/checker"
13
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "tempfile"
5
+
6
+ module ERB::Linter::Checker
7
+ extend self
8
+
9
+ def check_files(glob = "**/*.erb", tmpdir: Dir.tmpdir, root: Dir.pwd)
10
+ Dir.chdir root do
11
+ print "Checking linthtml version... "
12
+ unless system("yarn -s linthtml -v")
13
+ raise ERB::Linter::Error, "please install linthtml in yarn with `yarn add --dev @linthtml/linthtml`"
14
+ end
15
+
16
+ root = File.expand_path(root)
17
+ tmpdir = File.expand_path(File.join(tmpdir, "erb-linter"))
18
+ FileUtils.rm_rf tmpdir
19
+
20
+ paths_to_check = []
21
+
22
+ paths = Dir[glob]
23
+
24
+ paths.sort.map do |path|
25
+ puts "Checking #{path}..."
26
+ Thread.new(path) do |erb_path|
27
+ html_path = File.expand_path("#{tmpdir}/#{erb_path}")
28
+ paths_to_check << html_path
29
+ FileUtils.mkdir_p File.dirname(html_path)
30
+ File.write html_path, ERB::Linter::Converter.erb2html(File.read(erb_path))
31
+ end
32
+ end.each(&:join)
33
+
34
+ system "yarn", "-s", "linthtml", *paths_to_check
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+ require "ripper"
5
+
6
+ module ERB::Linter::Converter
7
+ extend self
8
+
9
+ # Credit to the Deface gem
10
+ def erb2html(source)
11
+ source = +source
12
+
13
+ # all opening html tags that contain <% %> blocks
14
+ source.scan(/<\w+[^<>]+(?:<%.*?%>[^<>]*)+/m).each do |line|
15
+ # regexs to catch <% %> inside attributes id="<% something %>" - with double, single or no quotes
16
+ erb_attrs_regexs = [
17
+ /([\w-]+)(\s?=\s?)(")([^"]*<%.*?%>[^"]*)/m,
18
+ /([\w-]+)(\s?=\s?)(')([^']*<%.*?%>[^']*)'/m,
19
+ /([\w-]+)(\s?=\s?)()(<%.*?%>)(?:\s|>|\z)/m,
20
+ ]
21
+
22
+ # rubocop:disable Linter/ShadowingOuterLocalVariable
23
+ replace_line = erb_attrs_regexs.inject(line.clone) do |replace_line, regex|
24
+ replace_line = line.scan(regex).inject(replace_line) do |replace_line, match|
25
+ replace_line.sub("#{match[0]}#{match[1]}#{match[2]}#{match[3]}#{match[2]}") { "data-erb-#{match[0]}=\"#{CGI.escapeHTML(match[3])}\"" }
26
+ end
27
+
28
+ replace_line
29
+ end
30
+ # rubocop:enable Linter/ShadowingOuterLocalVariable
31
+
32
+ i = -1
33
+
34
+ # catch all <% %> inside tags id <p <%= test %>> , not inside attrs
35
+ replace_line.scan(/(<%.*?%>)/m).each do |match|
36
+ replace_line.sub!(match[0], " data-erb-#{i += 1}=\"#{CGI.escapeHTML(match[0])}\"")
37
+ end
38
+
39
+ source.sub!(line, replace_line)
40
+ end
41
+
42
+ # replaces all <% %> not inside opening html tags
43
+ replacements = [
44
+ { %r{<%\s*end\s*-?%>} => "</erb>" },
45
+ { %r{(^\s*)<%(\s*(?:else|elsif\b.*)\s*)-?%>} => "\\1</erb>\n\\1<erb silent erb-code-start\\2erb-code-end>" },
46
+ { "<%=" => "<erb loud erb-code-start" },
47
+ { "<%" => "<erb silent erb-code-start" },
48
+ { "-%>" => "erb-code-end>" },
49
+ { "%>" => "erb-code-end>" },
50
+ ]
51
+
52
+ replacements.each{ |h| h.each { |replace, with| source.gsub! replace, with } }
53
+
54
+ source.scan(/(erb-code-start)((?:(?!erb-code-end)[\s\S])*)(erb-code-end)/).each do |match|
55
+ source.sub!("#{match[0]}#{match[1]}#{match[2]}") do |_m|
56
+ code = match[1]
57
+
58
+ # is nil when the parsing is broken, meaning it's an open expression
59
+ if Ripper.sexp(code).nil?
60
+ "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", "&#10;"))}\""
61
+ else
62
+ "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", "&#10;"))}\"></erb"
63
+ end
64
+ end
65
+ end
66
+
67
+ source
68
+ end
69
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tempfile"
4
+
5
+ class ERB::Linter::Task < Rake::TaskLib
6
+ attr_accessor :name, :glob, :root, :tmpdir
7
+
8
+ def initialize(*args, &block)
9
+ @name = args.shift || :erb_linter
10
+ @glob = "**/*.erb"
11
+ @root = Dir.pwd
12
+ @tmpdir = Dir.tmpdir
13
+
14
+ yield self
15
+
16
+ desc 'Check ERB files for HTML correcteness'
17
+ task(name, *args) do |_, task_args|
18
+ run
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def run
25
+ success = ERB::Linter::Checker.check_files(glob, root: root, tmpdir: tmpdir)
26
+
27
+ exit(success)
28
+ rescue ERB::Linter::Error => error
29
+ abort error.message
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb/linter'
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: erb-linter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Elia Schito
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email:
29
+ - elia@schito.me
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - Gemfile
36
+ - Gemfile.lock
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - bin/console
41
+ - bin/setup
42
+ - erb-linter.gemspec
43
+ - lib/erb/linter.rb
44
+ - lib/erb/linter/checker.rb
45
+ - lib/erb/linter/converter.rb
46
+ - lib/erb/linter/task.rb
47
+ - lib/erb/linter/version.rb
48
+ homepage: https://github.com/nebulab/erb-linter#readme
49
+ licenses:
50
+ - MIT
51
+ metadata:
52
+ homepage_uri: https://github.com/nebulab/erb-linter#readme
53
+ source_code_uri: https://github.com/nebulab/erb-linter
54
+ changelog_uri: https://github.com/nebulab/erb-linter/releases
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '2.5'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubygems_version: 3.2.3
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Check your ERB files for closing tags, indentation, bad attributes etc.
74
+ test_files: []