eslint-rails-ee 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+ Rails.application.routes.draw do
2
+ unless Rails.env.production?
3
+ get '/eslint' => 'eslint#show'
4
+ get '/eslint/source' => 'eslint#source', as: :eslint_source
5
+ get '/eslint/eslint.json' => 'eslint#config_file', as: :config_file
6
+ end
7
+ end
@@ -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,8 @@
1
+ require 'execjs'
2
+
3
+ require 'eslint-rails-ee/config'
4
+ require 'eslint-rails-ee/engine'
5
+ require 'eslint-rails-ee/runner'
6
+ require 'eslint-rails-ee/text_formatter'
7
+ require 'eslint-rails-ee/version'
8
+ require 'eslint-rails-ee/warning'
@@ -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,4 @@
1
+ module ESLintRails
2
+ class Engine < Rails::Engine
3
+ end
4
+ 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,3 @@
1
+ module ESLintRails
2
+ VERSION = '1.0'.freeze
3
+ 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