lintrunner 0.0.1
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/.gitignore +18 -0
- data/.lintrunner_config +30 -0
- data/.rubocop.yml +1057 -0
- data/.scss-lint.yml +191 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/bin/lintrunner +8 -0
- data/lib/lintrunner/cli.rb +97 -0
- data/lib/lintrunner/executor.rb +23 -0
- data/lib/lintrunner/git_helpers.rb +11 -0
- data/lib/lintrunner/message.rb +14 -0
- data/lib/lintrunner/options.rb +78 -0
- data/lib/lintrunner/parser/base.rb +19 -0
- data/lib/lintrunner/parser/eslint.rb +28 -0
- data/lib/lintrunner/parser/rubocop.rb +57 -0
- data/lib/lintrunner/parser/scss_lint.rb +36 -0
- data/lib/lintrunner/plugin.rb +5 -0
- data/lib/lintrunner/reporter/base.rb +30 -0
- data/lib/lintrunner/reporter/context.rb +24 -0
- data/lib/lintrunner/reporter/file.rb +11 -0
- data/lib/lintrunner/reporter/text.rb +29 -0
- data/lib/lintrunner/runner/base.rb +21 -0
- data/lib/lintrunner/runner/changed_file.rb +24 -0
- data/lib/lintrunner/runner/diff.rb +89 -0
- data/lib/lintrunner/runner/new_file.rb +26 -0
- data/lib/lintrunner/runner/repo.rb +31 -0
- data/lib/lintrunner/version.rb +3 -0
- data/lib/lintrunner.rb +34 -0
- data/lintrunner.gemspec +26 -0
- data/test.rb +43 -0
- data/test2.rb +7 -0
- metadata +134 -0
data/.scss-lint.yml
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
# Default application configuration that all configurations inherit from.
|
2
|
+
|
3
|
+
scss_files: "**/*.scss"
|
4
|
+
|
5
|
+
linters:
|
6
|
+
BangFormat:
|
7
|
+
enabled: true
|
8
|
+
space_before_bang: true
|
9
|
+
space_after_bang: false
|
10
|
+
|
11
|
+
BorderZero:
|
12
|
+
enabled: true
|
13
|
+
convention: none
|
14
|
+
|
15
|
+
ColorKeyword:
|
16
|
+
enabled: true
|
17
|
+
|
18
|
+
ColorVariable:
|
19
|
+
enabled: true
|
20
|
+
|
21
|
+
Comment:
|
22
|
+
enabled: true
|
23
|
+
|
24
|
+
DebugStatement:
|
25
|
+
enabled: true
|
26
|
+
|
27
|
+
DeclarationOrder:
|
28
|
+
enabled: false
|
29
|
+
|
30
|
+
DuplicateProperty:
|
31
|
+
enabled: false
|
32
|
+
|
33
|
+
ElsePlacement:
|
34
|
+
enabled: true
|
35
|
+
style: same_line
|
36
|
+
|
37
|
+
EmptyLineBetweenBlocks:
|
38
|
+
enabled: true
|
39
|
+
ignore_single_line_blocks: false
|
40
|
+
|
41
|
+
EmptyRule:
|
42
|
+
enabled: true
|
43
|
+
|
44
|
+
FinalNewline:
|
45
|
+
enabled: true
|
46
|
+
present: true
|
47
|
+
|
48
|
+
HexLength:
|
49
|
+
enabled: true
|
50
|
+
style: long
|
51
|
+
|
52
|
+
HexNotation:
|
53
|
+
enabled: true
|
54
|
+
style: lowercase
|
55
|
+
|
56
|
+
HexValidation:
|
57
|
+
enabled: true
|
58
|
+
|
59
|
+
IdSelector:
|
60
|
+
enabled: false
|
61
|
+
|
62
|
+
ImportantRule:
|
63
|
+
enabled: true
|
64
|
+
|
65
|
+
ImportPath:
|
66
|
+
enabled: true
|
67
|
+
leading_underscore: false
|
68
|
+
filename_extension: false
|
69
|
+
|
70
|
+
Indentation:
|
71
|
+
enabled: true
|
72
|
+
allow_non_nested_indentation: false
|
73
|
+
character: space
|
74
|
+
width: 2
|
75
|
+
|
76
|
+
LeadingZero:
|
77
|
+
enabled: false
|
78
|
+
style: exclude_zero
|
79
|
+
|
80
|
+
MergeableSelector:
|
81
|
+
enabled: true
|
82
|
+
force_nesting: false
|
83
|
+
|
84
|
+
NameFormat:
|
85
|
+
enabled: true
|
86
|
+
allow_leading_underscore: true
|
87
|
+
convention: hyphenated_lowercase
|
88
|
+
|
89
|
+
NestingDepth:
|
90
|
+
enabled: true
|
91
|
+
max_depth: 4
|
92
|
+
|
93
|
+
PlaceholderInExtend:
|
94
|
+
enabled: true
|
95
|
+
|
96
|
+
PropertyCount:
|
97
|
+
enabled: false
|
98
|
+
include_nested: false
|
99
|
+
max_properties: 10
|
100
|
+
|
101
|
+
PropertySortOrder:
|
102
|
+
enabled: true
|
103
|
+
ignore_unspecified: false
|
104
|
+
separate_groups: false
|
105
|
+
|
106
|
+
PropertySpelling:
|
107
|
+
enabled: true
|
108
|
+
extra_properties: []
|
109
|
+
|
110
|
+
QualifyingElement:
|
111
|
+
enabled: false
|
112
|
+
allow_element_with_attribute: false
|
113
|
+
allow_element_with_class: false
|
114
|
+
allow_element_with_id: false
|
115
|
+
|
116
|
+
SelectorDepth:
|
117
|
+
enabled: true
|
118
|
+
max_depth: 3
|
119
|
+
|
120
|
+
SelectorFormat:
|
121
|
+
enabled: true
|
122
|
+
convention: hyphenated_BEM
|
123
|
+
|
124
|
+
Shorthand:
|
125
|
+
enabled: true
|
126
|
+
|
127
|
+
SingleLinePerProperty:
|
128
|
+
enabled: true
|
129
|
+
allow_single_line_rule_sets: false
|
130
|
+
|
131
|
+
SingleLinePerSelector:
|
132
|
+
enabled: true
|
133
|
+
|
134
|
+
SpaceAfterComma:
|
135
|
+
enabled: true
|
136
|
+
|
137
|
+
SpaceAfterPropertyColon:
|
138
|
+
enabled: true
|
139
|
+
style: one_space
|
140
|
+
|
141
|
+
SpaceAfterPropertyName:
|
142
|
+
enabled: true
|
143
|
+
|
144
|
+
SpaceBeforeBrace:
|
145
|
+
enabled: true
|
146
|
+
style: space # or 'new_line'
|
147
|
+
allow_single_line_padding: false
|
148
|
+
|
149
|
+
SpaceBetweenParens:
|
150
|
+
enabled: true
|
151
|
+
spaces: 0
|
152
|
+
|
153
|
+
StringQuotes:
|
154
|
+
enabled: false
|
155
|
+
style: single_quotes
|
156
|
+
|
157
|
+
TrailingSemicolon:
|
158
|
+
enabled: true
|
159
|
+
|
160
|
+
TrailingZero:
|
161
|
+
enabled: true
|
162
|
+
|
163
|
+
UnnecessaryMantissa:
|
164
|
+
enabled: true
|
165
|
+
|
166
|
+
UnnecessaryParentReference:
|
167
|
+
enabled: true
|
168
|
+
|
169
|
+
UrlFormat:
|
170
|
+
enabled: true
|
171
|
+
|
172
|
+
UrlQuotes:
|
173
|
+
enabled: true
|
174
|
+
|
175
|
+
VariableForProperty:
|
176
|
+
enabled: true
|
177
|
+
properties:
|
178
|
+
- color
|
179
|
+
- font
|
180
|
+
|
181
|
+
VendorPrefixes:
|
182
|
+
enabled: false
|
183
|
+
identifier_list: base
|
184
|
+
include: []
|
185
|
+
exclude: []
|
186
|
+
|
187
|
+
ZeroUnit:
|
188
|
+
enabled: true
|
189
|
+
|
190
|
+
Compass::*:
|
191
|
+
enabled: false
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Ivan Tse
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Lintrunner
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'lintrunner'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install lintrunner
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/lintrunner/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/lintrunner
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require "lintrunner/options"
|
2
|
+
|
3
|
+
module Lintrunner
|
4
|
+
class CLI
|
5
|
+
|
6
|
+
# Takes an array of arguments
|
7
|
+
# Returns exit code
|
8
|
+
def run(args)
|
9
|
+
options = Options.new.parse(args)
|
10
|
+
|
11
|
+
handle_options(options)
|
12
|
+
|
13
|
+
path = options[:path]
|
14
|
+
context = options[:context]
|
15
|
+
|
16
|
+
# If context is different from path that we want to lint, check that it's up to date
|
17
|
+
path != context && is_up_to_date?(context)
|
18
|
+
|
19
|
+
warnings = []
|
20
|
+
reporter = initialize_reporter(options[:reporter], path)
|
21
|
+
|
22
|
+
config = JSON.parse(File.read(Dir.pwd + "/.lintrunner_config"))
|
23
|
+
include_paths(config["include_paths"])
|
24
|
+
require_files(config["require"])
|
25
|
+
|
26
|
+
config["linters"].each do |name, config|
|
27
|
+
next if config["disabled"]
|
28
|
+
if config["command"] && config["parser"]
|
29
|
+
parser = Lintrunner::Parser.const_get(config["parser"]).new
|
30
|
+
executor = Lintrunner::Executor.new(
|
31
|
+
command: config["command"],
|
32
|
+
parser: parser
|
33
|
+
)
|
34
|
+
else
|
35
|
+
executor = Lintrunner::Executor.new(
|
36
|
+
plugin: Lintrunner::Plugin.const_get(config["plugin"])
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
runner = Lintrunner::Runner.const_get(config["runner"]).new(
|
41
|
+
path: path,
|
42
|
+
match: config["match"],
|
43
|
+
executor: executor
|
44
|
+
)
|
45
|
+
reporter.start(name)
|
46
|
+
results = runner.run(reporter)
|
47
|
+
reporter.finish(results)
|
48
|
+
warnings.concat(results)
|
49
|
+
end
|
50
|
+
exit warnings.empty? ? 0 : 1
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def initialize_reporter(reporter, path)
|
56
|
+
Lintrunner::Reporter.const_get(reporter.capitalize).new(path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def handle_options(options)
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
def is_up_to_date?(path)
|
64
|
+
context_repo = Rugged::Repository.new(path)
|
65
|
+
return unless context_repo.branches['master'].head?
|
66
|
+
return if context_repo.branches['master'].upstream.nil?
|
67
|
+
Dir.chdir path do
|
68
|
+
# update remote ref
|
69
|
+
`git fetch #{context_repo.branches['master'].upstream.name.split("/").join(" ")} > /dev/null 2>&1`
|
70
|
+
status_output = `git status`
|
71
|
+
if status_output =~ /Your branch is behind .* and can be fast-forwarded./
|
72
|
+
|
73
|
+
puts "Repo is not up to date!".color(:red)
|
74
|
+
puts "Would you like to update it (Y/N)?"
|
75
|
+
input = STDIN.gets
|
76
|
+
if input.strip == "Y"
|
77
|
+
`git pull --ff-only #{context_repo.branches['master'].upstream.name.split("/").join(" ")}`
|
78
|
+
else
|
79
|
+
exit 1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
rescue Rugged::RepositoryError => e
|
84
|
+
# noop
|
85
|
+
end
|
86
|
+
|
87
|
+
def include_paths(include_paths = [])
|
88
|
+
$:.unshift(*include_paths)
|
89
|
+
end
|
90
|
+
|
91
|
+
def require_files(files)
|
92
|
+
files.each do |file|
|
93
|
+
require file
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Encapsulates how to execute a lint either through a command line string with a parser or with a plugin
|
2
|
+
module Lintrunner
|
3
|
+
class Executor
|
4
|
+
|
5
|
+
attr_reader :command, :parser, :plugin
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@command = options[:command]
|
9
|
+
@parser = options[:parser]
|
10
|
+
@plugin = options[:plugin]
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(filename, options = {})
|
14
|
+
if command && parser
|
15
|
+
output = `#{command} #{filename} 2>/dev/null`
|
16
|
+
exit_code = $?.exitstatus
|
17
|
+
parser.parse(output, exit_code, options)
|
18
|
+
elsif plugin
|
19
|
+
plugin.new(filename).run(options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Lintrunner
|
2
|
+
class Message
|
3
|
+
|
4
|
+
attr_accessor :filename, :line, :name, :description
|
5
|
+
|
6
|
+
def initialize(filename:, line:, name:, description:)
|
7
|
+
self.filename = filename
|
8
|
+
self.line = line
|
9
|
+
self.name = name
|
10
|
+
self.description = description
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
module Lintrunner
|
4
|
+
class Options
|
5
|
+
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def test; require 'pry'; binding.pry;end
|
9
|
+
def initialize
|
10
|
+
@options = {}
|
11
|
+
@option_parser = OptionParser.new do |opts|
|
12
|
+
add_banner(opts)
|
13
|
+
add_config_option(opts)
|
14
|
+
add_context_option(opts)
|
15
|
+
add_include_path_option(opts)
|
16
|
+
add_reporter_option(opts)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse(args)
|
21
|
+
@option_parser.parse!(args)
|
22
|
+
add_defaults
|
23
|
+
options[:path] = args.first if args.first
|
24
|
+
options
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def add_defaults
|
30
|
+
options[:config] ||= ".lintrunner_config"
|
31
|
+
options[:context] ||= Dir.pwd
|
32
|
+
options[:include_paths] = Array(options[:include_paths]) << options[:context]
|
33
|
+
options[:include_paths].uniq!
|
34
|
+
options[:reporter] ||= "text"
|
35
|
+
options[:path] = Dir.pwd
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_banner(opts)
|
39
|
+
opts.banner = unindent(<<-BANNER)
|
40
|
+
Run multiple linters with various runners
|
41
|
+
Usage: #{opts.program_name} [options] [path]
|
42
|
+
BANNER
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_config_option(opts)
|
46
|
+
message = "the configuration file for lintrunner (default: .lintrunner_config)"
|
47
|
+
opts.on("-c", "--config config", message, String) do |config|
|
48
|
+
self.options[:config] = config
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_context_option(opts)
|
53
|
+
message = "the path on which lintrunner will execute in (default: current path)"
|
54
|
+
opts.on("-x", "--context path", message, String) do |path|
|
55
|
+
self.options[:context] = Pathname.new(path).realpath.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_include_path_option(opts)
|
60
|
+
message = "the paths to add to load paths (the context is in the load path)"
|
61
|
+
opts.on("--include_path path1,...", message, Array) do |paths|
|
62
|
+
self.options[:include_paths] = paths
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_reporter_option(opts)
|
67
|
+
message = "the reporter that lintrunner uses to report results"
|
68
|
+
opts.on("--reporter reporter", message, String) do |reporter|
|
69
|
+
self.options[:reporter] = reporter
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def unindent(str)
|
74
|
+
str.gsub(/^#{str.scan(/^[ ]+(?=\S)/).min}/, "")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Parsers are responsible for parsing the output of a linter command and returning an array of
|
2
|
+
# Lintrunner::Message for each warning produced
|
3
|
+
module Lintrunner
|
4
|
+
module Parser
|
5
|
+
class Base
|
6
|
+
|
7
|
+
# Parse the output of a linter so that lintrunner can understand what the warnings are
|
8
|
+
# @param [String] output The output of running the linter command
|
9
|
+
# @param [Integer] exit_code The exit code of the linter command
|
10
|
+
# @param [Hash] options
|
11
|
+
# @option opts [String] :filename Allow the caller to set the filename of the results
|
12
|
+
# @return [Array<Lintrunner::Message>] An array of warnings produced by the linter command
|
13
|
+
def parse(output, exit_code, options = {})
|
14
|
+
raise "Parser must implement #parser method"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Lintrunner
|
2
|
+
module Parser
|
3
|
+
class Eslint < Base
|
4
|
+
|
5
|
+
# Example output of eslint (using the compact formatter):
|
6
|
+
# app/javascripts/delivery_page/components/add_guests.js: line 256, col 5, Error - Missing semicolon. (semi)
|
7
|
+
# app/javascripts/delivery_page/components/add_guests.js: line 277, col 18, Error - Props should be sorted alphabetically (react/jsx-sort-props)
|
8
|
+
def parse(output, exit_code, options = {})
|
9
|
+
return [] unless exit_code == 1
|
10
|
+
|
11
|
+
messages = []
|
12
|
+
output.each_line do |line|
|
13
|
+
|
14
|
+
match = /(?<filename>.*): line (?<line>\d*), .*Error - (?<description>.*) \((?<name>.*)\)/.match(line)
|
15
|
+
if match
|
16
|
+
messages << Lintrunner::Message.new(
|
17
|
+
filename: options[:filename] || match[:filename],
|
18
|
+
line: match["line"].to_i,
|
19
|
+
name: match["name"],
|
20
|
+
description: match["description"])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
messages
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Lintrunner
|
2
|
+
module Parser
|
3
|
+
class Rubocop
|
4
|
+
|
5
|
+
# Example output of rubocop (using the json formatter):
|
6
|
+
# {
|
7
|
+
# "metadata": {
|
8
|
+
# "rubocop_version": "0.32.1",
|
9
|
+
# "ruby_engine": "ruby",
|
10
|
+
# "ruby_version": "2.1.1",
|
11
|
+
# "ruby_patchlevel": "76",
|
12
|
+
# "ruby_platform": "x86_64-darwin14.0"
|
13
|
+
# },
|
14
|
+
# "files": [
|
15
|
+
# {
|
16
|
+
# "path": "lib\/lintrunner\/options.rb",
|
17
|
+
# "offenses": [
|
18
|
+
# {
|
19
|
+
# "severity": "warning",
|
20
|
+
# "message": "Remove debugger entry point `binding.pry`.",
|
21
|
+
# "cop_name": "Lint\/Debugger",
|
22
|
+
# "corrected": null,
|
23
|
+
# "location": {
|
24
|
+
# "line": 73,
|
25
|
+
# "column": 22,
|
26
|
+
# "length": 11
|
27
|
+
# }
|
28
|
+
# }
|
29
|
+
# ]
|
30
|
+
# }
|
31
|
+
# ],
|
32
|
+
# "summary": {
|
33
|
+
# "offense_count": 1,
|
34
|
+
# "target_file_count": 1,
|
35
|
+
# "inspected_file_count": 1
|
36
|
+
# }
|
37
|
+
# }
|
38
|
+
def parse(output, exit_code, options = {})
|
39
|
+
return [] unless exit_code == 1
|
40
|
+
|
41
|
+
messages = []
|
42
|
+
JSON.parse(output)["files"].each do |file_results|
|
43
|
+
filename = file_results["path"]
|
44
|
+
file_results["offenses"].each do |lint|
|
45
|
+
messages << Lintrunner::Message.new(
|
46
|
+
filename: options[:filename] || filename,
|
47
|
+
line: lint["location"]["line"],
|
48
|
+
name: lint["cop_name"],
|
49
|
+
description: lint["message"])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
messages
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Lintrunner
|
2
|
+
module Parser
|
3
|
+
class SCSSLint
|
4
|
+
|
5
|
+
# Example output of scss-lint (using the json formatter):
|
6
|
+
# {
|
7
|
+
# "app/styles/pages/delivery/_add_guests.scss": [
|
8
|
+
# {
|
9
|
+
# "line": 9,
|
10
|
+
# "column": 14,
|
11
|
+
# "length": 16,
|
12
|
+
# "severity": "warning",
|
13
|
+
# "reason": "Shorthand form for property `padding` should be written more concisely as `32px 70px 0` instead of `32px 70px 0 70px`",
|
14
|
+
# "linter": "Shorthand"
|
15
|
+
# }
|
16
|
+
# ]
|
17
|
+
# }
|
18
|
+
def parse(output, exit_code, options = {})
|
19
|
+
return [] unless exit_code == 1
|
20
|
+
|
21
|
+
messages = []
|
22
|
+
JSON.parse(output).each do |filename, lints|
|
23
|
+
lints.each do |lint|
|
24
|
+
messages << Lintrunner::Message.new(
|
25
|
+
filename: options[:filename] || filename,
|
26
|
+
line: lint["line"],
|
27
|
+
name: lint["linter"],
|
28
|
+
description: lint["reason"])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
messages
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Reporters are responsible for generating the output of lintrunner
|
2
|
+
# Default behavior is noop for #start, #report, and #finish
|
3
|
+
module Lintrunner
|
4
|
+
module Reporter
|
5
|
+
class Base
|
6
|
+
|
7
|
+
attr_reader :path
|
8
|
+
|
9
|
+
def initialize(path)
|
10
|
+
@path = path
|
11
|
+
end
|
12
|
+
|
13
|
+
# Called when a lintrunner is started
|
14
|
+
# @param [String] name The name of the lintrunner (ex. "binding.pry check")
|
15
|
+
def start(name); end
|
16
|
+
|
17
|
+
# Called when a message is discovered from the lintrunner
|
18
|
+
# @param [Lintrunner::Message] message
|
19
|
+
def report(message); end
|
20
|
+
|
21
|
+
# Called when the lintrunner is finished executing
|
22
|
+
# @param [Array<Lintrunner::Message>] messages
|
23
|
+
def finish(messages); end
|
24
|
+
|
25
|
+
# The description of this reporter. Used for CLI when listing possible reporters
|
26
|
+
def description; end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Lintrunner
|
2
|
+
module Reporter
|
3
|
+
class Context < Text
|
4
|
+
|
5
|
+
def report(message)
|
6
|
+
puts "#{location(message)} #{message.description} #{message_name(message)}"
|
7
|
+
puts context(message)
|
8
|
+
puts
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def context(message)
|
14
|
+
lineno = message.line - 1
|
15
|
+
start = lineno > 2 ? lineno - 3 : 0
|
16
|
+
range = start..(message.line + 2)
|
17
|
+
lines = ::File.readlines(::File.join(path, message.filename))[range]
|
18
|
+
message_index = lineno - start
|
19
|
+
lines[message_index] = lines[message_index].color(:red)
|
20
|
+
lines.join
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|