fdlint 0.2.0.pre → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +36 -15
- data/Rakefile +1 -1
- data/bin/fdlint +30 -9
- data/lib/fdlint/cli.rb +62 -10
- data/lib/fdlint/helper/logger.rb +1 -1
- data/lib/fdlint/log_entry.rb +14 -0
- data/lib/fdlint/parser/html/html_parser.rb +10 -12
- data/lib/fdlint/parser/html/query.rb +64 -50
- data/lib/fdlint/parser/html/struct.rb +20 -3
- data/lib/fdlint/parser/parser_visitable.rb +11 -5
- data/lib/fdlint/printer/base_printer.rb +6 -0
- data/lib/fdlint/printer/console_printer.rb +15 -23
- data/lib/fdlint/rule.rb +4 -4
- data/lib/fdlint/support/core/object.rb +6 -0
- data/lib/fdlint/support/core_ext.rb +1 -0
- data/lib/fdlint/validator.rb +60 -8
- data/lib/fdlint/version.rb +1 -1
- data/lib/hook/git_pre_commit +18 -0
- data/rules.d/css.rule.rb +1 -1
- data/rules.d/filename.rule.rb +3 -1
- data/rules.d/html.rule.rb +45 -32
- data/rules.d/js.rule.rb +2 -2
- data/test/all_tests.rb +28 -42
- data/test/css/mac_line_end_support_test.rb +10 -12
- data/test/css/parser_test.rb +4 -5
- data/test/css/rule/check_encoding_test.rb +7 -8
- data/test/css/rule/check_list_rule_test.rb +85 -102
- data/test/css/rule/file_name_test.rb +18 -39
- data/test/default_test.rb +1 -14
- data/test/fixtures/css/ad.css +0 -0
- data/test/fixtures/css/adver/test.css +0 -0
- data/test/fixtures/css/camelCase.css +0 -0
- data/test/fixtures/css/min/ad.css +0 -0
- data/test/fixtures/css/min/ad.min.css +0 -0
- data/test/fixtures/html/mixed_log_levels.html +1 -0
- data/test/fixtures/html/mixed_types.html +3 -0
- data/test/helper.rb +116 -16
- data/test/html/mixed_type_test.rb +14 -20
- data/test/html/parser/parse_comment_test.rb +4 -4
- data/test/html/parser/parse_dtd_test.rb +7 -7
- data/test/html/parser/parse_script_tag_test.rb +21 -14
- data/test/html/parser/parse_with_auto_close_tag_test.rb +12 -10
- data/test/html/parser/parse_with_diff_case_test.rb +8 -9
- data/test/html/parser/parse_with_emtpy_test.rb +5 -3
- data/test/html/parser/parse_with_multi_children_test.rb +4 -4
- data/test/html/parser/parse_with_multi_line_test.rb +5 -5
- data/test/html/parser/parse_with_prop_test.rb +10 -10
- data/test/html/parser/parse_with_script_tag_test.rb +4 -4
- data/test/html/parser/parse_with_selfclosing_test.rb +11 -11
- data/test/html/parser/parse_with_simple_tag_test.rb +7 -7
- data/test/html/parser/parse_with_simple_tree_test.rb +6 -6
- data/test/html/parser/parse_with_style_tag_test.rb +4 -4
- data/test/html/parser/parse_with_text_test.rb +7 -7
- data/test/html/parser_test.rb +19 -19
- data/test/html/query_test.rb +18 -18
- data/test/html/rule/check_block_level_element_test.rb +22 -24
- data/test/html/rule/check_button_test.rb +9 -9
- data/test/html/rule/check_css_in_head_test.rb +11 -32
- data/test/html/rule/check_dtd_test.rb +15 -11
- data/test/html/rule/check_form_element_name_test.rb +10 -9
- data/test/html/rule/check_head_contain_meta_and_title_test.rb +28 -34
- data/test/html/rule/check_hyperlink_with_target_test.rb +23 -17
- data/test/html/rule/check_hyperlink_with_title_test.rb +13 -20
- data/test/html/rule/check_id_n_class_downcase_test.rb +8 -19
- data/test/html/rule/check_img_with_alt_prop_test.rb +11 -12
- data/test/html/rule/check_no_import_css_test.rb +20 -20
- data/test/html/rule/check_prop_have_value_test.rb +9 -13
- data/test/html/rule/check_prop_seperator_test.rb +9 -13
- data/test/html/rule/check_style_prop_test.rb +7 -12
- data/test/html/rule/check_tag_closed_test.rb +15 -40
- data/test/html/rule/check_tag_downcase_test.rb +31 -28
- data/test/html/rule/check_unescape_char_test.rb +17 -17
- data/test/html/rule/check_unique_import_test.rb +15 -33
- data/test/html/rule_test.rb +2 -6
- data/test/js/expr/expr.rb +1 -1
- data/test/js/expr/left_hand.rb +1 -1
- data/test/js/expr/operate.rb +1 -1
- data/test/js/expr/primary.rb +1 -1
- data/test/js/parser_test.rb +3 -6
- data/test/js/rule/alert_check_test.rb +5 -19
- data/test/js/rule/base_test.rb +5 -19
- data/test/js/rule/jq_check_test.rb +26 -58
- data/test/js/rule/nest_try_catch_test.rb +26 -43
- data/test/js/rule/new_object_and_new_array_test.rb +5 -21
- data/test/js/rule/no_eval_test.rb +3 -19
- data/test/js/rule/no_global_test.rb +34 -73
- data/test/js/rule/private_method_check_test.rb +14 -43
- data/test/js/rule/semicolon_test.rb +18 -47
- data/test/js/rule/stat_if_with_brace_test.rb +30 -53
- data/test/js/rule/stat_if_with_muti_else_test.rb +36 -53
- data/test/js/rule/use_strict_equal_test.rb +16 -29
- data/test/js/rule_test.rb +2 -4
- data/test/js/stat/if.rb +1 -1
- data/test/js/stat/iter.rb +1 -1
- data/test/js/stat/stat.rb +1 -1
- data/test/js/stat/switch.rb +1 -1
- data/test/js/stat/try.rb +1 -1
- data/test/js/stat/var.rb +1 -1
- data/test/parser_visitable_test.rb +14 -36
- data/test/position_info_test.rb +4 -5
- data/test/runner/log_level_test.rb +30 -25
- metadata +11 -14
- data/lib/fdlint/file_validator.rb +0 -38
- data/lib/fdlint/parser/html/rule/check_tag_rule.rb +0 -80
- data/test/css/rule/compression_test.rb +0 -53
- data/test/html/rule/check_class_count_test.rb +0 -36
- data/test/html/rule/check_html_template_test.rb +0 -103
- data/test/js/rule/all_test.rb +0 -23
- data/test/js/rule/file_checker_test.rb +0 -131
- data/test/rule_dsl/dsl_basic_test.rb +0 -91
- data/test/rule_dsl/importing_test.rb +0 -48
- data/test/test_helper.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5010fb8a0b8b08e6b4ebd130f35c425d62cfef80
|
4
|
+
data.tar.gz: d01fccdb793cbc4900f291c74599e2dfef319509
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b21da480340bc0c23af7851ec02baf2b16e424fbc544e5da2ae963f32b5c0001ad4b54ca87a25db756321a5b88b179df025ee48d86d1de26efe118d255aef988
|
7
|
+
data.tar.gz: 99aa6f8d89edabee672f97202ecfffbe359d88c1eeab7b9445e20006c3ab1b57263b64d7ac31193c343bb6da2a036c019b61a846213f79e8efbbe6257e3e6491
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
fdlint -- 让前端code review更轻松
|
2
2
|
=================================
|
3
3
|
|
4
|
-
![fdlint-logo](http://
|
4
|
+
![fdlint-logo](http://fdlint.oss-cn-hangzhou.aliyuncs.com/fdlint-logo-white-small.png)
|
5
5
|
|
6
|
-
fdlint
|
6
|
+
fdlint 是根据阿里巴巴前端开发checklist开发的自动代码扫描工具。
|
7
7
|
可以扫描出前端程序中不符合开发规范的地方。
|
8
8
|
|
9
9
|
[![Build Status](https://secure.travis-ci.org/qhwa/fdlint.png)](http://travis-ci.org/qhwa/fdlint)
|
@@ -29,28 +29,49 @@ fdlint (开发代号xray) 是根据阿里巴巴前端开发checklist开发的自
|
|
29
29
|
|
30
30
|
gem install fdlint
|
31
31
|
|
32
|
-
运行方式
|
32
|
+
运行方式(命令行):
|
33
33
|
|
34
34
|
fdlint [参数] <目标文件或目录>
|
35
|
+
fdlint [全局参数] 命令 [命令选项] [路径...]
|
35
36
|
|
36
37
|
或者使用管道:
|
37
38
|
|
38
|
-
echo '* {}' | fdlint
|
39
|
+
echo '* {}' | fdlint check
|
39
40
|
|
40
41
|
参数列表:
|
41
42
|
|
42
43
|
~~~
|
43
|
-
Usage:
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
-d, --debug
|
49
|
-
-
|
50
|
-
-
|
51
|
-
|
52
|
-
|
53
|
-
|
44
|
+
Usage:
|
45
|
+
fdlint [全局参数] 命令 [命令参数] [路径...]
|
46
|
+
|
47
|
+
全局参数
|
48
|
+
|
49
|
+
-d, --debug - 打印调试信息
|
50
|
+
--help - 显示帮助信息
|
51
|
+
--version - 显示版本号
|
52
|
+
|
53
|
+
子命令
|
54
|
+
|
55
|
+
help - 打印某个子命令的帮助,例如 fdlint help fdlint
|
56
|
+
review, check - 检查代码。例如:
|
57
|
+
* fdlint check test.js
|
58
|
+
* fdlint check public/app/
|
59
|
+
* echo '<body></body>' | fdlint check
|
60
|
+
* fdlint check
|
61
|
+
rule - 显示导入的规则
|
62
|
+
~~~
|
63
|
+
|
64
|
+
`fdlint check` 的参数:
|
65
|
+
|
66
|
+
~~~
|
67
|
+
--format=arg - 输出格式,可以是'vim', 'console' 或 'nocolor'. (默认: console)
|
68
|
+
--html - 仅检查 html 文件,或指定语法为 html
|
69
|
+
--css - 仅检查 css 文件,或指定语法为 css
|
70
|
+
--js - 仅检查 js 文件,或指定语法为 js
|
71
|
+
-l, --list - 等同于 '--format=list'
|
72
|
+
--loglevel=arg - 指定位于某个级别或更严重的错误才显示,可以是 'warn', 'error' 或 'fatal'
|
73
|
+
(默认: warn)
|
74
|
+
-m, --[no-]checkmin - 检查已经被压缩的 js 或 css 文件。当扫描路径时,默认会忽略压缩文件
|
54
75
|
~~~
|
55
76
|
|
56
77
|
### Web
|
data/Rakefile
CHANGED
data/bin/fdlint
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
|
3
3
|
begin # XXX: Remove this begin/rescue before distributing your app
|
4
4
|
require 'fdlint'
|
5
5
|
require 'fdlint/cli'
|
@@ -10,6 +10,10 @@ rescue LoadError
|
|
10
10
|
exit 64
|
11
11
|
end
|
12
12
|
|
13
|
+
require 'gli'
|
14
|
+
require 'fdlint'
|
15
|
+
require 'fdlint/cli'
|
16
|
+
|
13
17
|
include GLI::App
|
14
18
|
|
15
19
|
program_desc 'Code reviewer for web developing. Check your HTML/JS/CSS codes against bad smells.'
|
@@ -32,17 +36,13 @@ command [:review, :check] do |c|
|
|
32
36
|
c.default_value :console
|
33
37
|
c.flag :format, must_match: %w[console nocolor vim]
|
34
38
|
|
35
|
-
c.desc "
|
39
|
+
c.desc "check minified files too"
|
36
40
|
c.default_value false
|
37
|
-
c.switch [:m, :"
|
41
|
+
c.switch [:m, :"checkmin"]
|
38
42
|
|
39
43
|
c.desc "shortcut for '--format=list'"
|
40
44
|
c.switch [:l, :list], negatable: false
|
41
45
|
|
42
|
-
c.desc "charset for reading files"
|
43
|
-
c.default_value 'utf-8'
|
44
|
-
c.flag [:c, :charset]
|
45
|
-
|
46
46
|
c.desc "the log level for filter results"
|
47
47
|
c.default_value 'warn'
|
48
48
|
c.flag [:loglevel], must_match: %s[warn error faltal]
|
@@ -59,7 +59,7 @@ command [:review, :check] do |c|
|
|
59
59
|
|
60
60
|
options = {
|
61
61
|
|
62
|
-
:log_level => opt[:loglevel],
|
62
|
+
:log_level => opt[:loglevel].intern,
|
63
63
|
:format => opt[:list] ? :nocolor : opt[:format],
|
64
64
|
:encoding => opt[:encoding],
|
65
65
|
:syntax => opt[:css] ? :css :
|
@@ -67,11 +67,32 @@ command [:review, :check] do |c|
|
|
67
67
|
opt[:html] ? :html : nil,
|
68
68
|
:debug => global[:debug],
|
69
69
|
:files => args,
|
70
|
-
:text => source
|
70
|
+
:text => source,
|
71
|
+
:checkmin => opt[:m]
|
71
72
|
}
|
72
73
|
|
73
74
|
Fdlint::CLI.run options
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
78
|
+
desc 'list imported rules'
|
79
|
+
command 'rule' do |c|
|
80
|
+
c.action do |global, opt, args|
|
81
|
+
Fdlint::CLI.list_rules debug: global[:debug]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
desc 'add git hook (svn hook is not supported yet)'
|
86
|
+
command 'hook' do |c|
|
87
|
+
c.action do |global, opt|
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
on_error do |err|
|
92
|
+
if defined? Bundler
|
93
|
+
puts err.message
|
94
|
+
puts err.backtrace.join( "\n" )
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
77
98
|
exit run(ARGV)
|
data/lib/fdlint/cli.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'optparse'
|
3
3
|
require 'find'
|
4
4
|
require 'logger'
|
5
|
+
require 'fdlint/helper/logger'
|
5
6
|
require 'fdlint/printer'
|
6
7
|
require 'fdlint/printer/vim_printer'
|
7
8
|
require 'fdlint/printer/nocolor_printer'
|
@@ -15,16 +16,23 @@ module Fdlint
|
|
15
16
|
Runner.new( options ).run
|
16
17
|
end
|
17
18
|
|
19
|
+
def self.list_rules( options )
|
20
|
+
Runner.new( options ).list_rules
|
21
|
+
end
|
22
|
+
|
18
23
|
class Runner
|
19
24
|
|
25
|
+
include Fdlint::Helper::Logger
|
26
|
+
|
20
27
|
attr_reader :options, :files
|
21
28
|
|
22
29
|
def initialize( options={} )
|
23
|
-
@options = options
|
24
|
-
@files = options[:files]
|
25
|
-
end
|
26
30
|
|
27
|
-
|
31
|
+
@options = options
|
32
|
+
@files = options[:files]
|
33
|
+
# 默认不检查已经被压缩的文件
|
34
|
+
@checkmin = options[:checkmin]
|
35
|
+
|
28
36
|
# Windows 下默认是ANSI编码
|
29
37
|
# 我们需要使用 UTF-8 编码输出
|
30
38
|
IO.popen "chcp 65001" if ENV['OS'] =~ /windows/i
|
@@ -32,6 +40,9 @@ module Fdlint
|
|
32
40
|
$logger = Logger.new(STDOUT).tap do |l|
|
33
41
|
l.level = options[:debug] ? Logger::DEBUG : Logger::FATAL
|
34
42
|
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def run
|
35
46
|
|
36
47
|
trap("SIGINT") { puts "\nGoodbye!"; exit! }
|
37
48
|
|
@@ -42,13 +53,21 @@ module Fdlint
|
|
42
53
|
end
|
43
54
|
end
|
44
55
|
|
56
|
+
def list_rules
|
57
|
+
::Fdlint::Rule.rules.each do |syntax, rules|
|
58
|
+
puts "----> #{syntax} (#{rules.size})\n\n"
|
59
|
+
rules.each do |rule|
|
60
|
+
print " " * 6
|
61
|
+
print rule
|
62
|
+
print "\n"
|
63
|
+
end
|
64
|
+
print "\n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
45
68
|
protected
|
46
69
|
|
47
70
|
def validate_content
|
48
|
-
opt_for_validator = {
|
49
|
-
:code_type => options[:code_type],
|
50
|
-
:text => options[:text]
|
51
|
-
}
|
52
71
|
::Fdlint::Validator.new( nil, opt_for_validator ).validate do |file, source, results|
|
53
72
|
printer.print( file, source, results )
|
54
73
|
end
|
@@ -72,16 +91,23 @@ module Fdlint
|
|
72
91
|
|
73
92
|
def validate_directory( dir )
|
74
93
|
Find.find dir do |f|
|
75
|
-
|
94
|
+
unless File.directory? f
|
95
|
+
if need_check? f
|
96
|
+
validate_file f
|
97
|
+
end
|
98
|
+
end
|
76
99
|
end
|
77
100
|
end
|
78
101
|
|
79
102
|
def validate_file( path )
|
80
|
-
|
103
|
+
printer.pre_validate path
|
104
|
+
::Fdlint::Validator.new( path, opt_for_validator ).validate do |file, source, results|
|
81
105
|
printer.print( file, source, results )
|
82
106
|
end
|
107
|
+
printer.post_validate path
|
83
108
|
end
|
84
109
|
|
110
|
+
|
85
111
|
private
|
86
112
|
|
87
113
|
def printer
|
@@ -97,6 +123,32 @@ module Fdlint
|
|
97
123
|
mapping[format]
|
98
124
|
end
|
99
125
|
|
126
|
+
def need_check? path
|
127
|
+
if path =~ /([\.-_])min\.(css|js|html?)$/i && !@checkmin
|
128
|
+
info { "ignore minified file '#{path}'" }
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
|
132
|
+
reg = case options[:syntax]
|
133
|
+
when :js, :css
|
134
|
+
/\.#{options[:syntax]}$/i
|
135
|
+
when :html
|
136
|
+
/\.html?$/i
|
137
|
+
else
|
138
|
+
/\.(js|css|html?)$/i
|
139
|
+
end
|
140
|
+
|
141
|
+
path =~ reg
|
142
|
+
end
|
143
|
+
|
144
|
+
def opt_for_validator
|
145
|
+
{
|
146
|
+
:syntax => options[:syntax],
|
147
|
+
:text => options[:text],
|
148
|
+
:log_level => options[:log_level]
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
100
152
|
end
|
101
153
|
end
|
102
154
|
end
|
data/lib/fdlint/helper/logger.rb
CHANGED
data/lib/fdlint/log_entry.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'colored'
|
2
3
|
|
3
4
|
module Fdlint
|
@@ -38,6 +39,19 @@ module Fdlint
|
|
38
39
|
validation && validation.long_desc
|
39
40
|
end
|
40
41
|
|
42
|
+
LEVEL_CONST = {
|
43
|
+
:warn => 1,
|
44
|
+
:error => 2,
|
45
|
+
:fatal => 3
|
46
|
+
}
|
47
|
+
|
48
|
+
def self.level_greater_or_equal?( a, b )
|
49
|
+
level_number(a) >= level_number(b)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.level_number( symb )
|
53
|
+
LEVEL_CONST[symb]
|
54
|
+
end
|
41
55
|
|
42
56
|
end
|
43
57
|
|
@@ -8,6 +8,7 @@ module Fdlint; module Parser; module HTML
|
|
8
8
|
class HtmlParser < ::Fdlint::Parser::BaseParser
|
9
9
|
|
10
10
|
include ::Fdlint::Parser::ParserVisitable
|
11
|
+
include ::Fdlint::Helper::Logger
|
11
12
|
|
12
13
|
def self.parse(src, &block)
|
13
14
|
parser = self.new(src)
|
@@ -33,13 +34,8 @@ module Fdlint; module Parser; module HTML
|
|
33
34
|
end
|
34
35
|
|
35
36
|
def parse_doc
|
36
|
-
|
37
|
-
|
38
|
-
when 0 then nil
|
39
|
-
when 1 then nodes[0]
|
40
|
-
else
|
41
|
-
::Fdlint::Parser::HTML::Document.new( nodes )
|
42
|
-
end
|
37
|
+
debug { "parse doc" }
|
38
|
+
::Fdlint::Parser::HTML::Document.new( batch(:parse_element) )
|
43
39
|
end
|
44
40
|
|
45
41
|
def parse_element
|
@@ -59,6 +55,7 @@ module Fdlint; module Parser; module HTML
|
|
59
55
|
end
|
60
56
|
|
61
57
|
def parse_dtd
|
58
|
+
debug { "parse dtd" }
|
62
59
|
node = scan(DTD)
|
63
60
|
DTDElement.new(@scanner[2], @scanner[1], node.position)
|
64
61
|
end
|
@@ -76,9 +73,10 @@ module Fdlint; module Parser; module HTML
|
|
76
73
|
text << "#{@scanner.scan(TEXT)}"
|
77
74
|
|
78
75
|
# TODO: make this detection a rule
|
79
|
-
parse_warn "'#{$~}' not escaped" if text =~ /<|>/
|
76
|
+
parse_warn "'#{$~}' not escaped" if text =~ /<|>/ && !@parsing_script
|
80
77
|
end
|
81
78
|
TextElement.new( text ).tap do |text|
|
79
|
+
text.scopes = scopes.dup
|
82
80
|
text.position = pos
|
83
81
|
end
|
84
82
|
end
|
@@ -165,9 +163,9 @@ module Fdlint; module Parser; module HTML
|
|
165
163
|
|
166
164
|
scopes.pop
|
167
165
|
|
168
|
-
|
169
|
-
|
170
|
-
|
166
|
+
Tag.new(tag, prop, children, close_type, ending).tap do |el|
|
167
|
+
el.scopes = scopes.dup
|
168
|
+
end
|
171
169
|
end
|
172
170
|
|
173
171
|
def scopes
|
@@ -183,7 +181,7 @@ module Fdlint; module Parser; module HTML
|
|
183
181
|
tag = scan(TAG_NAME)
|
184
182
|
prop = parse_properties
|
185
183
|
skip /\/>/
|
186
|
-
el =
|
184
|
+
el = Tag.new(tag, prop, [], :self)
|
187
185
|
el.scopes = scopes.dup
|
188
186
|
el
|
189
187
|
end
|
@@ -1,47 +1,37 @@
|
|
1
1
|
require 'strscan'
|
2
2
|
|
3
|
-
module
|
4
|
-
module HTML
|
3
|
+
module Fdlint; module Parser; module HTML
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
module Query
|
6
|
+
|
7
|
+
WORD = /[A-Za-z][-_\w]*/
|
8
|
+
CLASS = %r(\.#{WORD})
|
9
|
+
ID = %r(##{WORD})
|
10
|
+
PROP_PAIR = %r(\s*,?\s*#{WORD}(==[^\s,])?)
|
11
|
+
PROP = %r(\[#{WORD}.*?\])
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
if v.nil?
|
34
|
-
return false unless has_prop? n
|
35
|
-
else
|
36
|
-
return false unless prop_value(n) == v
|
37
|
-
end
|
38
|
-
end
|
39
|
-
true
|
40
|
-
end
|
13
|
+
###
|
14
|
+
# This method implemented CSS selector for
|
15
|
+
# HTML (like Sizzle) very simply. It is not
|
16
|
+
# fully supported CSS selector.
|
17
|
+
#
|
18
|
+
# TODO: support full CSS3 selector
|
19
|
+
###
|
20
|
+
def match?(str)
|
21
|
+
query = query_obj(str)
|
22
|
+
tag_query = query[:tag]
|
23
|
+
class_query = query[:classes]
|
24
|
+
prop_query = query[:properties]
|
25
|
+
|
26
|
+
(tag_query.blank? || match_tag?( tag_query )) &&
|
27
|
+
(class_query.blank? || match_class?( class_query )) &&
|
28
|
+
(prop_query.blank? || match_prop?( prop_query ))
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method :===, :match?
|
41
32
|
|
42
|
-
|
33
|
+
private
|
43
34
|
|
44
|
-
private
|
45
35
|
def query_obj(str)
|
46
36
|
classes = []
|
47
37
|
props = {}
|
@@ -73,24 +63,48 @@ module XRay
|
|
73
63
|
{:tag => tag, :classes => classes, :properties => props }
|
74
64
|
end
|
75
65
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
66
|
+
def match_tag?( tag_query )
|
67
|
+
tag_name_equal? tag_query
|
68
|
+
end
|
69
|
+
|
70
|
+
def match_class?( class_query )
|
71
|
+
classes = prop_value(:class)
|
72
|
+
if classes.blank?
|
73
|
+
false
|
74
|
+
else
|
75
|
+
class_query.all? do |c|
|
76
|
+
classes.include? c
|
77
|
+
end
|
82
78
|
end
|
79
|
+
end
|
83
80
|
|
84
|
-
|
85
|
-
|
81
|
+
def match_prop?( prop_query )
|
82
|
+
prop_query.all? do |n, v|
|
83
|
+
if v.nil?
|
84
|
+
has_prop? n
|
85
|
+
else
|
86
|
+
prop_value(n) == v
|
87
|
+
end
|
86
88
|
end
|
89
|
+
end
|
90
|
+
public
|
91
|
+
|
92
|
+
def query( selector, &block )
|
93
|
+
ret = []
|
94
|
+
if match?(selector)
|
95
|
+
ret << self
|
96
|
+
yield self if block_given?
|
97
|
+
end
|
87
98
|
|
88
|
-
|
99
|
+
children && children.each do |node|
|
100
|
+
ret += node.query(selector, &block)
|
89
101
|
end
|
90
|
-
|
91
|
-
alias_method :*, :query
|
92
102
|
|
103
|
+
ret
|
93
104
|
end
|
105
|
+
|
106
|
+
alias_method :*, :query
|
94
107
|
|
95
108
|
end
|
96
|
-
|
109
|
+
|
110
|
+
end; end; end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
require 'fdlint/parser/html/query'
|
2
|
+
|
1
3
|
module Fdlint; module Parser
|
4
|
+
|
2
5
|
module HTML
|
3
6
|
|
4
7
|
Node = ::Fdlint::Parser::Node
|
@@ -12,9 +15,12 @@ module Fdlint; module Parser
|
|
12
15
|
input td ins time kbd var)
|
13
16
|
module Matchable
|
14
17
|
def =~ patten
|
18
|
+
name = respond_to?(:name) ? self.name :
|
19
|
+
respond_to?(:tag_name) ? tag_name :
|
20
|
+
nil
|
15
21
|
if name
|
16
22
|
case patten
|
17
|
-
when String
|
23
|
+
when String, Symbol
|
18
24
|
name =~ Regexp.new("^#{patten}$", Regexp::IGNORECASE)
|
19
25
|
when Regexp
|
20
26
|
name =~ patten
|
@@ -183,9 +189,12 @@ module Fdlint; module Parser
|
|
183
189
|
end
|
184
190
|
|
185
191
|
class Tag < Element
|
192
|
+
|
186
193
|
include Matchable
|
194
|
+
include ::Fdlint::Parser::HTML::Query
|
187
195
|
|
188
196
|
alias_method :name, :tag_name
|
197
|
+
alias_method :tag_name_equal?, :=~
|
189
198
|
end
|
190
199
|
|
191
200
|
class Document < Tag
|
@@ -215,6 +224,10 @@ module Fdlint; module Parser
|
|
215
224
|
|
216
225
|
alias_method :have_dtd?, :has_dtd?
|
217
226
|
|
227
|
+
def outer_html
|
228
|
+
children.map(&:outer_html).to_a.join
|
229
|
+
end
|
230
|
+
|
218
231
|
end
|
219
232
|
|
220
233
|
class TextElement < Tag
|
@@ -231,6 +244,10 @@ module Fdlint; module Parser
|
|
231
244
|
alias_method :inner_html, :text
|
232
245
|
alias_method :outer_html, :text
|
233
246
|
|
247
|
+
def match?( query )
|
248
|
+
false
|
249
|
+
end
|
250
|
+
|
234
251
|
def ==(other)
|
235
252
|
text == other.text
|
236
253
|
end
|
@@ -247,7 +264,7 @@ module Fdlint; module Parser
|
|
247
264
|
end
|
248
265
|
|
249
266
|
|
250
|
-
class CommentElement <
|
267
|
+
class CommentElement < Tag
|
251
268
|
def initialize(text)
|
252
269
|
super(nil)
|
253
270
|
@text = text.to_s
|
@@ -278,7 +295,7 @@ module Fdlint; module Parser
|
|
278
295
|
|
279
296
|
end
|
280
297
|
|
281
|
-
class DTDElement <
|
298
|
+
class DTDElement < Tag
|
282
299
|
|
283
300
|
attr_accessor :type
|
284
301
|
|
@@ -46,7 +46,7 @@ module Fdlint
|
|
46
46
|
alias_method old_method, method
|
47
47
|
|
48
48
|
define_method(method) do |*args, &block|
|
49
|
-
before name
|
49
|
+
before name
|
50
50
|
node = self.send(old_method, *args, &block)
|
51
51
|
node && visit(name, node)
|
52
52
|
node
|
@@ -74,8 +74,14 @@ module Fdlint
|
|
74
74
|
#
|
75
75
|
# Returns Parser's visitors
|
76
76
|
def add_visitors(visitors)
|
77
|
-
visitors.
|
78
|
-
|
77
|
+
if visitors.is_a? Array
|
78
|
+
visitors.each do |target, visitor|
|
79
|
+
add_visitor target, visitor
|
80
|
+
end
|
81
|
+
else
|
82
|
+
visitors.public_methods(false).grep(/^visit_/).map do |method|
|
83
|
+
add_visitor method.to_s.sub( /^visit_/, '' ), visitors.method( method )
|
84
|
+
end
|
79
85
|
end
|
80
86
|
self.visitors
|
81
87
|
end
|
@@ -110,13 +116,13 @@ module Fdlint
|
|
110
116
|
|
111
117
|
def visit(name, node)
|
112
118
|
walk(name, node) do |result|
|
113
|
-
results << result
|
119
|
+
results << result if result.present?
|
114
120
|
end
|
115
121
|
end
|
116
122
|
|
117
123
|
def before(name)
|
118
124
|
walk 'before_parse_' << name, nil do |result|
|
119
|
-
results << result
|
125
|
+
results << result if result.present?
|
120
126
|
end
|
121
127
|
end
|
122
128
|
|