jslint_on_rails 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.markdown +167 -0
- data/config/jslint.yml +79 -0
- data/lib/jslint.rb +3 -0
- data/lib/jslint/errors.rb +9 -0
- data/lib/jslint/lint.rb +64 -0
- data/lib/jslint/rails.rb +2 -0
- data/lib/jslint/tasks.rb +26 -0
- data/lib/jslint/utils.rb +70 -0
- data/vendor/jslint.js +5533 -0
- data/vendor/rhino.jar +0 -0
- data/vendor/test.jar +0 -0
- metadata +66 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jakub Suder
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# JSLint on Rails
|
2
|
+
|
3
|
+
**JSLint on Rails** is a Ruby gem and Rails plugin which lets you run
|
4
|
+
the [JSLint JavaScript code checker](http://jslint.com) on your Javascript code easily.
|
5
|
+
|
6
|
+
It can be installed either as a Rails plugin (the recommended method for Rails), or as a gem (for other frameworks).
|
7
|
+
|
8
|
+
Note: to run JSLint on Rails, you need to have **Java** available on your machine - it's required because JSLint is
|
9
|
+
itself written in JavaScript, and is run using the [Rhino](http://www.mozilla.org/rhino) JavaScript engine (written in
|
10
|
+
Java). Any decent version of Java will do (and by decent I mean 5.0 or later).
|
11
|
+
|
12
|
+
|
13
|
+
## Installation (Rails)
|
14
|
+
|
15
|
+
If you use Rails, you can install the library as a plugin - it's less work to set it up this way.
|
16
|
+
To install the plugin, use this command:
|
17
|
+
|
18
|
+
./script/plugin install git://github.com/psionides/jslint_on_rails.git
|
19
|
+
|
20
|
+
This will create for you a sample `jslint.yml` config file in your config directory.
|
21
|
+
|
22
|
+
|
23
|
+
## Installation (other frameworks)
|
24
|
+
|
25
|
+
If you use Merb or some other framework, you need to install JSLint as a Ruby gem (you can do that in Rails too - the
|
26
|
+
advantage of this method is that it may be easier to update to newer versions later).
|
27
|
+
|
28
|
+
To use JSLint as a gem, follow these steps:
|
29
|
+
|
30
|
+
* install the gem in your application using whatever technique is recommended for your framework (e.g. using gem
|
31
|
+
bundler, or just plain old `gem install jslint_on_rails`)
|
32
|
+
* in your Rakefile, add a line to load the JSLint tasks:
|
33
|
+
|
34
|
+
require 'jslint/tasks'
|
35
|
+
|
36
|
+
* below that line, set JSLint's config_path variable to point it to a place where you want your JSLint configuration
|
37
|
+
file to be kept - for example:
|
38
|
+
|
39
|
+
JSLint.config_path = "config/jslint.yml"
|
40
|
+
|
41
|
+
* run a rake task which will generate a sample config file for you:
|
42
|
+
|
43
|
+
rake jslint:copy_config
|
44
|
+
|
45
|
+
|
46
|
+
## Installation (custom)
|
47
|
+
|
48
|
+
If you wish to write your own rake task to run JSLint, you can create and execute the JSLint object manually:
|
49
|
+
|
50
|
+
require 'jslint'
|
51
|
+
lint = JSLint::Lint.new(
|
52
|
+
:paths => ['public/javascripts/**/*.js'],
|
53
|
+
:exclude_paths => ['public/javascripts/vendor/**/*.js'],
|
54
|
+
:config_path => 'config/jslint.yml'
|
55
|
+
)
|
56
|
+
lint.run
|
57
|
+
|
58
|
+
|
59
|
+
## Configuration
|
60
|
+
|
61
|
+
Whatever method you use for installation, a YAML config file should be created for you. In this file, you can:
|
62
|
+
|
63
|
+
* define which Javascript files are checked by default; you'll almost certainly want to change that, because the default
|
64
|
+
is `public/javascripts/**/*.js` which means all Javascript files, and you probably don't want JSLint to check entire
|
65
|
+
jQuery, Prototype or whatever other libraries you use - so change this so that only your scripts are checked (you can
|
66
|
+
put multiple entries under "paths:" and "exclude_paths:")
|
67
|
+
* tweak JSLint options to enable or disable specific checks - I've set the defaults to what I believe is reasonable,
|
68
|
+
but what's reasonable for me may not be reasonable for you
|
69
|
+
|
70
|
+
|
71
|
+
## Running
|
72
|
+
|
73
|
+
To start the check, run the rake task:
|
74
|
+
|
75
|
+
rake jslint
|
76
|
+
|
77
|
+
You will get a result like this (if everything goes well):
|
78
|
+
|
79
|
+
Running JSLint:
|
80
|
+
|
81
|
+
checking public/javascripts/Event.js... OK
|
82
|
+
checking public/javascripts/Map.js... OK
|
83
|
+
checking public/javascripts/Marker.js... OK
|
84
|
+
checking public/javascripts/Reports.js... OK
|
85
|
+
|
86
|
+
No JS errors found.
|
87
|
+
|
88
|
+
If anything is wrong, you will get something like this instead:
|
89
|
+
|
90
|
+
Running JSLint:
|
91
|
+
|
92
|
+
checking public/javascripts/Event.js... 2 errors:
|
93
|
+
|
94
|
+
Lint at line 24 character 15: Use '===' to compare with 'null'.
|
95
|
+
if (a == null && b == null) {
|
96
|
+
|
97
|
+
Lint at line 72 character 6: Extra comma.
|
98
|
+
},
|
99
|
+
|
100
|
+
checking public/javascripts/Marker.js... 1 error:
|
101
|
+
|
102
|
+
Lint at line 275 character 27: Missing radix parameter.
|
103
|
+
var x = parseInt(mapX);
|
104
|
+
|
105
|
+
|
106
|
+
Found 3 errors.
|
107
|
+
rake aborted!
|
108
|
+
JSLint test failed.
|
109
|
+
|
110
|
+
If you want to test specific file or files (just once, without modifying the config), you can pass paths to include
|
111
|
+
and/or paths to exclude to the rake task:
|
112
|
+
|
113
|
+
rake jslint paths=public/javascripts/models/*.js,public/javascripts/lib/*.js exclude_paths=public/javascripts/lib/jquery.js
|
114
|
+
|
115
|
+
For the best effect, you should include JSLint check in your Continuous Integration build - that way, you'll get
|
116
|
+
immediate notification when you've committed JS code with errors.
|
117
|
+
|
118
|
+
|
119
|
+
## Additional options
|
120
|
+
|
121
|
+
I've added some additional options to JSLint to get rid of some warnings which I thought didn't make sense. They're all
|
122
|
+
disabled by default, but feel free to enable any or all of them if you feel abused by JSLint.
|
123
|
+
|
124
|
+
Here's a documentation for all the extra options:
|
125
|
+
|
126
|
+
|
127
|
+
### lastsemic
|
128
|
+
|
129
|
+
If set to true, this will ignore warnings about missing semicolon after a statement, if the statement is the last one in
|
130
|
+
a block or function. I've added this because I like to omit the semicolon in one-liner anonymous functions, in
|
131
|
+
situations like this:
|
132
|
+
|
133
|
+
var ids = $$('.entry').map(function(e) { return e.id });
|
134
|
+
|
135
|
+
|
136
|
+
### newstat
|
137
|
+
|
138
|
+
Allows you to use a call to 'new' as a whole statement, without assigning the result anywhere. Sometimes you want to
|
139
|
+
create an instance of a class, but you don't need to assign it anywhere - the call to constructor starts the action
|
140
|
+
automatically. This includes calls like `new Ajax.Request(...)` or `new Effect.Highlight(...)` used when working with
|
141
|
+
Prototype and Scriptaculous.
|
142
|
+
|
143
|
+
|
144
|
+
### statinexp
|
145
|
+
|
146
|
+
JSLint has a warning that says "Expected an assignment or function call and instead saw an expression" - you get it
|
147
|
+
when you write an expression and you don't use it for anything, like if you wrote such line:
|
148
|
+
|
149
|
+
$$('.entry').length;
|
150
|
+
|
151
|
+
Just checking the length without assigning it anywhere or passing to any function doesn't make any sense, so it's good
|
152
|
+
that JSLint complains. However, there are some cases where the code makes perfect sense, but JSLint still thinks it
|
153
|
+
doesn't. Examples:
|
154
|
+
|
155
|
+
element && element.show(); // call show only if element is not null
|
156
|
+
selected ? element.show() : element.hide(); // more readable than if & else with brackets
|
157
|
+
|
158
|
+
So I've tweaked the code that creates this warning so that it doesn't print it if the code makes sense. Specifically:
|
159
|
+
|
160
|
+
* expressions joined with && or || are accepted if the last one in the line is a statement
|
161
|
+
* expressions with ?: are accepted if both alternatives (before and after the colon) are statements
|
162
|
+
|
163
|
+
|
164
|
+
## Credits
|
165
|
+
|
166
|
+
* JSLint on Rails was created by [Jakub Suder](http://psionides.jogger.pl), licensed under MIT License
|
167
|
+
* JSLint was created by [Douglas Crockford](http://jslint.com)
|
data/config/jslint.yml
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# ------------ rake task options ------------
|
2
|
+
|
3
|
+
# JS files to check by default, if no parameters are passed to rake jslint
|
4
|
+
# (you may want to limit this only to your own scripts and exclude any external scripts and frameworks)
|
5
|
+
|
6
|
+
# this can be overridden by adding 'paths' and 'exclude_paths' parameter to rake command:
|
7
|
+
# rake jslint paths=path1,path2,... exclude_paths=library1,library2,...
|
8
|
+
|
9
|
+
paths:
|
10
|
+
- public/javascripts/**/*.js
|
11
|
+
|
12
|
+
exclude_paths:
|
13
|
+
|
14
|
+
|
15
|
+
# ------------ jslint options ------------
|
16
|
+
# see http://www.jslint.com/lint.html#options for more detailed explanations
|
17
|
+
|
18
|
+
# "enforce" type options (true means potentially more warnings)
|
19
|
+
|
20
|
+
adsafe: false # true if ADsafe rules should be enforced. See http://www.ADsafe.org/
|
21
|
+
bitwise: true # true if bitwise operators should not be allowed
|
22
|
+
newcap: true # true if Initial Caps must be used with constructor functions
|
23
|
+
eqeqeq: false # true if === should be required (for ALL equality comparisons)
|
24
|
+
immed: false # true if immediate function invocations must be wrapped in parens
|
25
|
+
nomen: false # true if initial or trailing underscore in identifiers should be forbidden
|
26
|
+
onevar: false # true if only one var statement per function should be allowed
|
27
|
+
plusplus: false # true if ++ and -- should not be allowed
|
28
|
+
regexp: false # true if . and [^...] should not be allowed in RegExp literals
|
29
|
+
safe: false # true if the safe subset rules are enforced (used by ADsafe)
|
30
|
+
strict: false # true if the ES5 "use strict"; pragma is required
|
31
|
+
undef: false # true if variables must be declared before used
|
32
|
+
white: false # true if strict whitespace rules apply (see also 'indent' option)
|
33
|
+
|
34
|
+
# "allow" type options (false means potentially more warnings)
|
35
|
+
|
36
|
+
cap: false # true if upper case HTML should be allowed
|
37
|
+
css: true # true if CSS workarounds should be tolerated
|
38
|
+
debug: false # true if debugger statements should be allowed (set to false before going into production)
|
39
|
+
evil: false # true if eval should be allowed
|
40
|
+
forin: true # true if unfiltered 'for in' statements should be allowed
|
41
|
+
fragment: true # true if HTML fragments should be allowed
|
42
|
+
laxbreak: false # true if statement breaks should not be checked
|
43
|
+
on: false # true if HTML event handlers (e.g. onclick="...") should be allowed
|
44
|
+
sub: false # true if subscript notation may be used for expressions better expressed in dot notation
|
45
|
+
|
46
|
+
# other options
|
47
|
+
|
48
|
+
maxlen: 120 # Maximum line length
|
49
|
+
indent: 2 # Number of spaces that should be used for indentation - used only if 'white' option is set
|
50
|
+
maxerr: 50 # The maximum number of warnings reported (per file)
|
51
|
+
passfail: false # true if the scan should stop on first error (per file)
|
52
|
+
# following are relevant only if undef = true
|
53
|
+
predef: '' # Names of predefined global variables - comma-separated string
|
54
|
+
browser: true # true if the standard browser globals should be predefined
|
55
|
+
rhino: false # true if the Rhino environment globals should be predefined
|
56
|
+
sidebar: false # true if the Windows Sidebar Gadgets globals should be predefined
|
57
|
+
widget: false # true if the Yahoo Widgets globals should be predefined
|
58
|
+
devel: true # true if functions like alert, confirm, console, prompt etc. are predefined
|
59
|
+
|
60
|
+
|
61
|
+
# ------------ jslint_on_rails custom lint options (switch to true to disable some annoying warnings) ------------
|
62
|
+
|
63
|
+
# ignores "missing semicolon" warning at the end of a function; this lets you write one-liners
|
64
|
+
# like: x.map(function(i) { return i + 1 }); without having to put a second semicolon inside the function
|
65
|
+
lastsemic: false
|
66
|
+
|
67
|
+
# allows you to use the 'new' expression as a statement (without assignment)
|
68
|
+
# so you can call e.g. new Ajax.Request(...), new Effect.Highlight(...) without assigning to a dummy variable
|
69
|
+
newstat: false
|
70
|
+
|
71
|
+
# ignores the "Expected an assignment or function call and instead saw an expression" warning,
|
72
|
+
# if the expression contains a proper statement and makes sense; this lets you write things like:
|
73
|
+
# element && element.show();
|
74
|
+
# valid || other || lastChance || alert('OMG!');
|
75
|
+
# selected ? show() : hide();
|
76
|
+
# although these will still cause a warning:
|
77
|
+
# element && link;
|
78
|
+
# selected ? 5 : 10;
|
79
|
+
statinexp: false
|
data/lib/jslint.rb
ADDED
data/lib/jslint/lint.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module JSLint
|
2
|
+
|
3
|
+
PATH = File.dirname(__FILE__)
|
4
|
+
|
5
|
+
TEST_JAR_FILE = File.expand_path("#{PATH}/../../vendor/test.jar")
|
6
|
+
RHINO_JAR_FILE = File.expand_path("#{PATH}/../../vendor/rhino.jar")
|
7
|
+
TEST_JAR_CLASS = "Test"
|
8
|
+
RHINO_JAR_CLASS = "org.mozilla.javascript.tools.shell.Main"
|
9
|
+
|
10
|
+
JSLINT_FILE = File.expand_path("#{PATH}/../../vendor/jslint.js")
|
11
|
+
|
12
|
+
class Lint
|
13
|
+
|
14
|
+
# available options:
|
15
|
+
# :paths => [list of paths...]
|
16
|
+
# :exclude_paths => [list of exluded paths...]
|
17
|
+
# :config_path => path to custom config file (can be set via JSLint.config_path too)
|
18
|
+
def initialize(options = {})
|
19
|
+
default_config = Utils.load_config_file(DEFAULT_CONFIG_FILE)
|
20
|
+
custom_config = Utils.load_config_file(options[:config_path] || JSLint.config_path)
|
21
|
+
@config = default_config.merge(custom_config)
|
22
|
+
|
23
|
+
included_files = files_matching_paths(options, :paths)
|
24
|
+
excluded_files = files_matching_paths(options, :exclude_paths)
|
25
|
+
@file_list = Utils.exclude_files(included_files, excluded_files)
|
26
|
+
|
27
|
+
['paths', 'exclude_paths', 'config_path'].each { |field| @config.delete(field) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
check_java
|
32
|
+
puts "Running JSLint:\n\n"
|
33
|
+
command = "java -cp #{RHINO_JAR_FILE} #{RHINO_JAR_CLASS} #{JSLINT_FILE} #{option_string} #{@file_list.join(' ')}"
|
34
|
+
success = system(command)
|
35
|
+
raise LintCheckFailure, "JSLint test failed." unless success
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def option_string
|
42
|
+
@config.map { |k, v| "#{k}=#{v.inspect}" }.join(',')
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_java
|
46
|
+
unless @java_ok
|
47
|
+
java_test = %x(java -cp #{TEST_JAR_FILE} #{TEST_JAR_CLASS})
|
48
|
+
if java_test.strip == "OK"
|
49
|
+
@java_ok = true
|
50
|
+
else
|
51
|
+
raise NoJavaException, "Please install Java before running JSLint."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def files_matching_paths(options, field)
|
57
|
+
path_list = options[field] || @config[field.to_s] || []
|
58
|
+
file_list = path_list.map { |p| Dir[p] }.flatten
|
59
|
+
Utils.unique_files(file_list)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/jslint/rails.rb
ADDED
data/lib/jslint/tasks.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../jslint')
|
2
|
+
|
3
|
+
desc "Run JSLint check on selected Javascript files"
|
4
|
+
task :jslint do
|
5
|
+
include_paths = JSLint::Utils.paths_from_command_line('paths')
|
6
|
+
exclude_paths = JSLint::Utils.paths_from_command_line('exclude_paths')
|
7
|
+
|
8
|
+
if include_paths && exclude_paths.nil?
|
9
|
+
# if you pass paths= on command line but not exclude_paths=, and you have exclude_paths
|
10
|
+
# set in the config file, then the old exclude pattern will be used against the new
|
11
|
+
# include pattern, which may be very confusing...
|
12
|
+
exclude_paths = []
|
13
|
+
end
|
14
|
+
|
15
|
+
lint = JSLint::Lint.new :paths => include_paths, :exclude_paths => exclude_paths
|
16
|
+
lint.run
|
17
|
+
end
|
18
|
+
|
19
|
+
namespace :jslint do
|
20
|
+
|
21
|
+
desc "Create a copy of the default JSLint config file in your config directory"
|
22
|
+
task :copy_config do
|
23
|
+
JSLint::Utils.copy_config_file
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/jslint/utils.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'ftools'
|
2
|
+
|
3
|
+
module JSLint
|
4
|
+
|
5
|
+
DEFAULT_CONFIG_FILE = File.expand_path(File.dirname(__FILE__) + "/../../config/jslint.yml")
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :config_path
|
9
|
+
end
|
10
|
+
|
11
|
+
module Utils
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def load_config_file(file_name)
|
15
|
+
if file_name && File.exists?(file_name) && File.file?(file_name) && File.readable?(file_name)
|
16
|
+
YAML.load_file(file_name)
|
17
|
+
else
|
18
|
+
{}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# workaround for a problem with case-insensitive file systems like HFS on Mac
|
23
|
+
def unique_files(list)
|
24
|
+
files = []
|
25
|
+
list.each do |entry|
|
26
|
+
files << entry unless files.any? { |f| File.identical?(f, entry) }
|
27
|
+
end
|
28
|
+
files
|
29
|
+
end
|
30
|
+
|
31
|
+
# workaround for a problem with case-insensitive file systems like HFS on Mac
|
32
|
+
def exclude_files(list, excluded)
|
33
|
+
list.reject { |entry| excluded.any? { |f| File.identical?(f, entry) }}
|
34
|
+
end
|
35
|
+
|
36
|
+
def paths_from_command_line(field)
|
37
|
+
argument = ENV[field] || ENV[field.upcase]
|
38
|
+
argument && argument.split(/,/)
|
39
|
+
end
|
40
|
+
|
41
|
+
def copy_config_file
|
42
|
+
raise ArgumentError, "Please set JSLint.config_path" if JSLint.config_path.nil?
|
43
|
+
print "Copying default config file to #{File.expand_path(JSLint.config_path)}... "
|
44
|
+
if File.exists?(JSLint.config_path)
|
45
|
+
puts "\n\nWarning: config file exists, so it won't be overwritten. " +
|
46
|
+
"You can copy it manually from the jslint_on_rails directory if you want to reset it."
|
47
|
+
else
|
48
|
+
File.copy(JSLint::DEFAULT_CONFIG_FILE, JSLint.config_path)
|
49
|
+
puts "OK."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove_config_file
|
54
|
+
raise ArgumentError, "Please set JSLint.config_path" if JSLint.config_path.nil?
|
55
|
+
print "Removing config file... "
|
56
|
+
if File.exists?(JSLint.config_path) && File.file?(JSLint.config_path)
|
57
|
+
if File.read(JSLint.config_path) == File.read(JSLint::DEFAULT_CONFIG_FILE)
|
58
|
+
File.delete(JSLint.config_path)
|
59
|
+
puts "OK."
|
60
|
+
else
|
61
|
+
puts "File was modified, so it won't be deleted automatically."
|
62
|
+
end
|
63
|
+
else
|
64
|
+
puts "OK (no config file found)."
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|