eslint-rails-ee 1.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.
@@ -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