eslint-rails-ee 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +216 -0
- data/Rakefile +1 -0
- data/app/controllers/eslint_controller.rb +23 -0
- data/app/views/eslint/show.html.erb +55 -0
- data/app/views/eslint/source.html.erb +80 -0
- data/build-plugins.sh +21 -0
- data/config/eslint.json +160 -0
- data/config/routes.rb +7 -0
- data/eslint-rails-ee.gemspec +30 -0
- data/lib/eslint-rails-ee.rb +8 -0
- data/lib/eslint-rails-ee/config.rb +29 -0
- data/lib/eslint-rails-ee/engine.rb +4 -0
- data/lib/eslint-rails-ee/runner.rb +113 -0
- data/lib/eslint-rails-ee/text_formatter.rb +43 -0
- data/lib/eslint-rails-ee/version.rb +3 -0
- data/lib/eslint-rails-ee/warning.rb +34 -0
- data/lib/tasks/eslint.rake +50 -0
- data/vendor/assets/javascripts/eslint.js +107296 -0
- data/vendor/assets/javascripts/plugins/eslint-plugin-react.js +12862 -0
- metadata +154 -0
data/config/eslint.json
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
{
|
2
|
+
"ecmaFeatures": {},
|
3
|
+
"parser": "espree",
|
4
|
+
"env": {
|
5
|
+
"browser": false,
|
6
|
+
"node": false,
|
7
|
+
"amd": false,
|
8
|
+
"mocha": false,
|
9
|
+
"jasmine": false
|
10
|
+
},
|
11
|
+
|
12
|
+
"rules": {
|
13
|
+
"no-alert": 2,
|
14
|
+
"no-array-constructor": 2,
|
15
|
+
"no-bitwise": 0,
|
16
|
+
"no-caller": 2,
|
17
|
+
"no-catch-shadow": 2,
|
18
|
+
"no-comma-dangle": 0,
|
19
|
+
"no-cond-assign": 2,
|
20
|
+
"no-console": 2,
|
21
|
+
"no-constant-condition": 2,
|
22
|
+
"no-control-regex": 2,
|
23
|
+
"no-debugger": 2,
|
24
|
+
"no-delete-var": 2,
|
25
|
+
"no-div-regex": 0,
|
26
|
+
"no-dupe-keys": 2,
|
27
|
+
"no-dupe-args": 2,
|
28
|
+
"no-else-return": 0,
|
29
|
+
"no-empty": 2,
|
30
|
+
"no-empty-character-class": 2,
|
31
|
+
"no-eq-null": 0,
|
32
|
+
"no-eval": 2,
|
33
|
+
"no-ex-assign": 2,
|
34
|
+
"no-extend-native": 2,
|
35
|
+
"no-extra-bind": 2,
|
36
|
+
"no-extra-boolean-cast": 2,
|
37
|
+
"no-extra-parens": 0,
|
38
|
+
"no-extra-semi": 2,
|
39
|
+
"no-fallthrough": 2,
|
40
|
+
"no-floating-decimal": 0,
|
41
|
+
"no-func-assign": 2,
|
42
|
+
"no-implied-eval": 2,
|
43
|
+
"no-inline-comments": 0,
|
44
|
+
"no-inner-declarations": [2, "functions"],
|
45
|
+
"no-invalid-regexp": 2,
|
46
|
+
"no-irregular-whitespace": 2,
|
47
|
+
"no-iterator": 2,
|
48
|
+
"no-label-var": 2,
|
49
|
+
"no-labels": 2,
|
50
|
+
"no-lone-blocks": 2,
|
51
|
+
"no-lonely-if": 0,
|
52
|
+
"no-loop-func": 2,
|
53
|
+
"no-mixed-requires": [0, false],
|
54
|
+
"no-mixed-spaces-and-tabs": [2, false],
|
55
|
+
"no-multi-spaces": 2,
|
56
|
+
"no-multi-str": 2,
|
57
|
+
"no-multiple-empty-lines": [0, { "max": 2 }],
|
58
|
+
"no-native-reassign": 2,
|
59
|
+
"no-negated-in-lhs": 2,
|
60
|
+
"no-nested-ternary": 0,
|
61
|
+
"no-new": 2,
|
62
|
+
"no-new-func": 2,
|
63
|
+
"no-new-object": 2,
|
64
|
+
"no-new-require": 0,
|
65
|
+
"no-new-wrappers": 2,
|
66
|
+
"no-obj-calls": 2,
|
67
|
+
"no-octal": 2,
|
68
|
+
"no-octal-escape": 2,
|
69
|
+
"no-path-concat": 0,
|
70
|
+
"no-plusplus": 0,
|
71
|
+
"no-process-env": 0,
|
72
|
+
"no-process-exit": 2,
|
73
|
+
"no-proto": 2,
|
74
|
+
"no-redeclare": 2,
|
75
|
+
"no-regex-spaces": 2,
|
76
|
+
"no-reserved-keys": 0,
|
77
|
+
"no-restricted-modules": 0,
|
78
|
+
"no-return-assign": 2,
|
79
|
+
"no-script-url": 2,
|
80
|
+
"no-self-compare": 0,
|
81
|
+
"no-sequences": 2,
|
82
|
+
"no-shadow": 2,
|
83
|
+
"no-shadow-restricted-names": 2,
|
84
|
+
"no-space-before-semi": 0,
|
85
|
+
"no-spaced-func": 2,
|
86
|
+
"no-sparse-arrays": 2,
|
87
|
+
"no-sync": 0,
|
88
|
+
"no-ternary": 0,
|
89
|
+
"no-trailing-spaces": 2,
|
90
|
+
"no-throw-literal": 0,
|
91
|
+
"no-undef": 1,
|
92
|
+
"no-undef-init": 2,
|
93
|
+
"no-undefined": 0,
|
94
|
+
"no-underscore-dangle": 2,
|
95
|
+
"no-unreachable": 2,
|
96
|
+
"no-unused-expressions": 2,
|
97
|
+
"no-unused-vars": [2, { "vars": "all", "args": "after-used" }],
|
98
|
+
"no-use-before-define": 2,
|
99
|
+
"no-void": 0,
|
100
|
+
"no-var": 0,
|
101
|
+
"no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
|
102
|
+
"no-with": 2,
|
103
|
+
|
104
|
+
"block-scoped-var": 0,
|
105
|
+
"brace-style": [0, "1tbs"],
|
106
|
+
"camelcase": 2,
|
107
|
+
"comma-dangle": [2, "never"],
|
108
|
+
"comma-spacing": 2,
|
109
|
+
"comma-style": 0,
|
110
|
+
"complexity": [0, 11],
|
111
|
+
"consistent-return": 2,
|
112
|
+
"consistent-this": [0, "that"],
|
113
|
+
"curly": [2, "all"],
|
114
|
+
"default-case": 0,
|
115
|
+
"dot-notation": [2, { "allowKeywords": true }],
|
116
|
+
"eol-last": 2,
|
117
|
+
"eqeqeq": 2,
|
118
|
+
"func-names": 0,
|
119
|
+
"func-style": [0, "declaration"],
|
120
|
+
"generator-star": 0,
|
121
|
+
"guard-for-in": 0,
|
122
|
+
"handle-callback-err": 0,
|
123
|
+
"indent": 0,
|
124
|
+
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
125
|
+
"keyword-spacing": 2,
|
126
|
+
"max-depth": [0, 4],
|
127
|
+
"max-len": [0, 80, 4],
|
128
|
+
"max-nested-callbacks": [0, 2],
|
129
|
+
"max-params": [0, 3],
|
130
|
+
"max-statements": [0, 10],
|
131
|
+
"new-cap": 2,
|
132
|
+
"new-parens": 2,
|
133
|
+
"one-var": 0,
|
134
|
+
"operator-assignment": [0, "always"],
|
135
|
+
"padded-blocks": 0,
|
136
|
+
"quote-props": 0,
|
137
|
+
"quotes": [1, "double"],
|
138
|
+
"radix": 0,
|
139
|
+
"semi": 2,
|
140
|
+
"semi-spacing": [2, { "before": false, "after": true }],
|
141
|
+
"sort-vars": 0,
|
142
|
+
"space-after-function-name": [0, "never"],
|
143
|
+
"space-after-keywords": [0, "always"],
|
144
|
+
"space-before-blocks": [0, "always"],
|
145
|
+
"space-before-function-parentheses": [0, "always"],
|
146
|
+
"space-in-brackets": [0, "never"],
|
147
|
+
"space-in-parens": [0, "never"],
|
148
|
+
"space-infix-ops": 2,
|
149
|
+
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
150
|
+
"spaced-line-comment": [0, "always"],
|
151
|
+
"strict": 2,
|
152
|
+
"use-isnan": 2,
|
153
|
+
"valid-jsdoc": 0,
|
154
|
+
"valid-typeof": 2,
|
155
|
+
"vars-on-top": 0,
|
156
|
+
"wrap-iife": 0,
|
157
|
+
"wrap-regex": 0,
|
158
|
+
"yoda": [2, "never"]
|
159
|
+
}
|
160
|
+
}
|
data/config/routes.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'eslint-rails-ee/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'eslint-rails-ee'
|
8
|
+
spec.version = ESLintRails::VERSION
|
9
|
+
spec.authors = ['Justin Force', 'Jon Kessler', 'David Valentine']
|
10
|
+
spec.email = ['justin.force@appfolio.com', 'jon.kessler@appfolio.com', 'davidlewisrogers3@gmail.com']
|
11
|
+
spec.summary = %q{A Rails wrapper for ESLint, but ENHANCED!}
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = 'https://github.com/dlvalentine/eslint-rails-ee'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.required_ruby_version = '>= 2.0.0'
|
22
|
+
|
23
|
+
spec.add_dependency 'railties', '>= 3.2'
|
24
|
+
spec.add_dependency 'execjs'
|
25
|
+
spec.add_dependency 'colorize'
|
26
|
+
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
29
|
+
spec.add_development_dependency 'therubyracer'
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ESLintRails
|
2
|
+
class Config
|
3
|
+
|
4
|
+
def self.read(force_default: false)
|
5
|
+
self.new(force_default: force_default).send(:read)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
CONFIG_PATH = 'config/eslint.json'
|
11
|
+
private_constant :CONFIG_PATH
|
12
|
+
|
13
|
+
def initialize(force_default: nil)
|
14
|
+
raise(ArgumentError, 'force_default is required') if force_default.nil?
|
15
|
+
|
16
|
+
@force_default = force_default
|
17
|
+
@custom_file = Rails.root.join(CONFIG_PATH)
|
18
|
+
@default_file = ESLintRails::Engine.root.join(CONFIG_PATH)
|
19
|
+
end
|
20
|
+
|
21
|
+
def read
|
22
|
+
config_file.read
|
23
|
+
end
|
24
|
+
|
25
|
+
def config_file
|
26
|
+
(@custom_file.exist? && !@force_default) ? @custom_file : @default_file
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
module ESLintRails
|
4
|
+
class Runner
|
5
|
+
include ActionView::Helpers::JavaScriptHelper
|
6
|
+
|
7
|
+
JAVASCRIPT_EXTENSIONS = %w(.js .jsx .es6)
|
8
|
+
|
9
|
+
def initialize(file)
|
10
|
+
@file = normalize_infile(file)
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(should_autocorrect=false)
|
14
|
+
warnings = assets.map do |asset|
|
15
|
+
generate_warnings(asset, should_autocorrect).tap { |warnings| output_progress(warnings) }
|
16
|
+
end
|
17
|
+
|
18
|
+
puts
|
19
|
+
|
20
|
+
warnings.flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def normalize_infile(file)
|
26
|
+
file = file.to_s.gsub(/^app\/assets\/javascripts\//, '') # Remove beginning of asset path
|
27
|
+
file = Pathname.new("#{Dir.pwd}/app/assets/javascripts/#{file}") # Ensure path is absolute
|
28
|
+
file = Pathname.new("#{file}.js") if !file.directory? && file.extname.empty? # Make sure it has an extension
|
29
|
+
file
|
30
|
+
end
|
31
|
+
|
32
|
+
def assets
|
33
|
+
all_js_assets = Rails.application.assets.each_file.to_a.map { |path| Pathname.new(path) }.select do |asset|
|
34
|
+
JAVASCRIPT_EXTENSIONS.include?(asset.extname)
|
35
|
+
end
|
36
|
+
|
37
|
+
assets = all_js_assets.select{|a| is_descendant?(@file, a)}
|
38
|
+
|
39
|
+
assets.reject{|a| a.to_s =~ /eslint.js|vendor|gems|min.js|editorial/ }
|
40
|
+
end
|
41
|
+
|
42
|
+
def eslint_js
|
43
|
+
@eslint_js ||= Rails.application.assets['eslint'].to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def eslint_plugin_js
|
47
|
+
@eslint_plugin_js ||= begin
|
48
|
+
plugins.map do |plugin_name|
|
49
|
+
Rails.application.assets["plugins/eslint-plugin-#{plugin_name}"].to_s
|
50
|
+
end.join('\n')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def plugins
|
55
|
+
JSON.parse(Config.read)['plugins'] || []
|
56
|
+
end
|
57
|
+
|
58
|
+
def warning_hashes(file_content, relative_path, should_autocorrect=false)
|
59
|
+
if !should_autocorrect
|
60
|
+
ExecJS.eval <<-JS
|
61
|
+
function () {
|
62
|
+
window = this;
|
63
|
+
#{eslint_js};
|
64
|
+
#{eslint_plugin_js};
|
65
|
+
return new eslint().verify('#{escape_javascript(file_content)}', #{Config.read});
|
66
|
+
}()
|
67
|
+
JS
|
68
|
+
else
|
69
|
+
hsh = ExecJS.eval <<-JS
|
70
|
+
function () {
|
71
|
+
window = this;
|
72
|
+
#{eslint_js};
|
73
|
+
#{eslint_plugin_js};
|
74
|
+
return new eslint().verifyAndFix('#{escape_javascript(file_content)}', #{Config.read});
|
75
|
+
}()
|
76
|
+
JS
|
77
|
+
File.write(relative_path, hsh['output']) if !hsh['output'].nil?
|
78
|
+
hsh['messages']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def generate_warnings(asset, should_autocorrect=false)
|
83
|
+
relative_path = asset.relative_path_from(Pathname.new(Dir.pwd))
|
84
|
+
file_content = asset.read
|
85
|
+
|
86
|
+
warning_hashes(file_content, relative_path, should_autocorrect).map do |hash|
|
87
|
+
ESLintRails::Warning.new(relative_path, hash)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def output_progress(warnings)
|
92
|
+
print case file_severity(warnings)
|
93
|
+
when :high
|
94
|
+
'H'.red
|
95
|
+
when :low
|
96
|
+
'L'.yellow
|
97
|
+
else
|
98
|
+
'.'.green
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def file_severity(warnings)
|
103
|
+
warnings.map(&:severity).uniq.sort.first
|
104
|
+
end
|
105
|
+
|
106
|
+
def is_descendant?(a, b)
|
107
|
+
a_list = a.to_s.split('/')
|
108
|
+
b_list = b.to_s.split('/')
|
109
|
+
|
110
|
+
b_list[0..a_list.size-1] == a_list
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'active_support/core_ext/string/strip'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module ESLintRails
|
5
|
+
class TextFormatter
|
6
|
+
|
7
|
+
def initialize(warnings)
|
8
|
+
@warnings = warnings
|
9
|
+
end
|
10
|
+
|
11
|
+
def format(should_autocorrect=false)
|
12
|
+
max_line_column_length = max_length_of_attribute(:location)
|
13
|
+
max_rule_id_length = max_length_of_attribute(:rule_id)
|
14
|
+
max_message_length = max_length_of_attribute(:message)
|
15
|
+
@warnings.each do |warning|
|
16
|
+
message = [
|
17
|
+
warning.location.ljust(max_line_column_length + 1),
|
18
|
+
warning.severity.to_s.ljust(6),
|
19
|
+
warning.rule_id.ljust(max_rule_id_length),
|
20
|
+
warning.message.ljust(max_message_length)
|
21
|
+
].join(" ")
|
22
|
+
colorized_message =
|
23
|
+
case warning.severity
|
24
|
+
when :low
|
25
|
+
message.yellow
|
26
|
+
when :high
|
27
|
+
message.red
|
28
|
+
else
|
29
|
+
raise 'Couldn\'t figure out how to format this mess. Stahp.'
|
30
|
+
end
|
31
|
+
puts colorized_message
|
32
|
+
end
|
33
|
+
|
34
|
+
puts "#{@warnings.size} warning(s) found #{should_autocorrect ? 'after auto-correcting some issues' : ''}"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def max_length_of_attribute(attr_key)
|
40
|
+
@warnings.map { |warning| warning.send(attr_key).size }.max
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ESLintRails
|
2
|
+
class Warning
|
3
|
+
attr_reader :filename, :line, :column, :node_type
|
4
|
+
|
5
|
+
SEVERITY = [ :low, :high ].freeze
|
6
|
+
private_constant :SEVERITY
|
7
|
+
|
8
|
+
def initialize(filename, warning_hash)
|
9
|
+
@filename = filename
|
10
|
+
@rule_id = warning_hash['ruleId'] || "unexpected error"
|
11
|
+
@severity = warning_hash['severity']
|
12
|
+
@message = warning_hash['message']
|
13
|
+
@line = warning_hash['line']
|
14
|
+
@column = warning_hash['column']
|
15
|
+
@node_type = warning_hash['nodeType']
|
16
|
+
end
|
17
|
+
|
18
|
+
def severity
|
19
|
+
SEVERITY[@severity-1]
|
20
|
+
end
|
21
|
+
|
22
|
+
def location
|
23
|
+
"#{filename}:#{line}:#{column}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def rule_id
|
27
|
+
@rule_id || 'N/A'
|
28
|
+
end
|
29
|
+
|
30
|
+
def message
|
31
|
+
@message || 'N/A'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
ENV['EXECJS_RUNTIME'] = 'RubyRacer'
|
2
|
+
|
3
|
+
require 'eslint-rails-ee'
|
4
|
+
|
5
|
+
namespace :eslint do
|
6
|
+
def run_and_print_results(file, should_autocorrect=false)
|
7
|
+
warnings = ESLintRails::Runner.new(file).run(should_autocorrect)
|
8
|
+
raiseError = false
|
9
|
+
|
10
|
+
warnings.each do |warning|
|
11
|
+
next if warning.severity.nil?
|
12
|
+
if warning.severity.to_s.casecmp('high').zero?
|
13
|
+
raiseError = true
|
14
|
+
break
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if warnings.empty?
|
19
|
+
puts 'All files passed! Any issues that might have existed may have been auto-corrected :)'.green
|
20
|
+
exit 0
|
21
|
+
elsif !raiseError
|
22
|
+
formatter = ESLintRails::TextFormatter.new(warnings)
|
23
|
+
puts formatter.format(should_autocorrect)
|
24
|
+
puts 'eslint reports some warnings, but they are minor enough to be passable. Might want to fix them up before release, though. :/'.yellow
|
25
|
+
exit 0
|
26
|
+
else
|
27
|
+
formatter = ESLintRails::TextFormatter.new(warnings)
|
28
|
+
puts formatter.format(should_autocorrect)
|
29
|
+
puts 'Major issues exist according to provided eslint rules. You *MUST* git gud and correct these issues before release :('.red
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc %{Run ESLint against the specified JavaScript file and report warnings (default is 'application')}
|
35
|
+
task :run, [:filename, :should_autocorrect] => :environment do |_, args|
|
36
|
+
formatted_should_autocorrect = ['true'].include?(args[:should_autocorrect]) ? true : false
|
37
|
+
run_and_print_results(args[:filename] || 'application', formatted_should_autocorrect)
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'Run ESLint against all project javascript files and report warnings'
|
41
|
+
task :run_all, [:should_autocorrect] => :environment do |_, args|
|
42
|
+
formatted_should_autocorrect = ['true'].include?(args[:should_autocorrect]) ? true : false
|
43
|
+
run_and_print_results(nil, formatted_should_autocorrect) # Run all
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Print the current configuration file (Uses local config/eslint.json if it exists; uses default config/eslint.json if it does not; optionally force default by passing a parameter)'
|
47
|
+
task :print_config, [:force_default] => :environment do |_, args|
|
48
|
+
puts ESLintRails::Config.read(force_default: args[:force_default])
|
49
|
+
end
|
50
|
+
end
|