fdlint 0.2.0.pre → 0.2.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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +36 -15
  4. data/Rakefile +1 -1
  5. data/bin/fdlint +30 -9
  6. data/lib/fdlint/cli.rb +62 -10
  7. data/lib/fdlint/helper/logger.rb +1 -1
  8. data/lib/fdlint/log_entry.rb +14 -0
  9. data/lib/fdlint/parser/html/html_parser.rb +10 -12
  10. data/lib/fdlint/parser/html/query.rb +64 -50
  11. data/lib/fdlint/parser/html/struct.rb +20 -3
  12. data/lib/fdlint/parser/parser_visitable.rb +11 -5
  13. data/lib/fdlint/printer/base_printer.rb +6 -0
  14. data/lib/fdlint/printer/console_printer.rb +15 -23
  15. data/lib/fdlint/rule.rb +4 -4
  16. data/lib/fdlint/support/core/object.rb +6 -0
  17. data/lib/fdlint/support/core_ext.rb +1 -0
  18. data/lib/fdlint/validator.rb +60 -8
  19. data/lib/fdlint/version.rb +1 -1
  20. data/lib/hook/git_pre_commit +18 -0
  21. data/rules.d/css.rule.rb +1 -1
  22. data/rules.d/filename.rule.rb +3 -1
  23. data/rules.d/html.rule.rb +45 -32
  24. data/rules.d/js.rule.rb +2 -2
  25. data/test/all_tests.rb +28 -42
  26. data/test/css/mac_line_end_support_test.rb +10 -12
  27. data/test/css/parser_test.rb +4 -5
  28. data/test/css/rule/check_encoding_test.rb +7 -8
  29. data/test/css/rule/check_list_rule_test.rb +85 -102
  30. data/test/css/rule/file_name_test.rb +18 -39
  31. data/test/default_test.rb +1 -14
  32. data/test/fixtures/css/ad.css +0 -0
  33. data/test/fixtures/css/adver/test.css +0 -0
  34. data/test/fixtures/css/camelCase.css +0 -0
  35. data/test/fixtures/css/min/ad.css +0 -0
  36. data/test/fixtures/css/min/ad.min.css +0 -0
  37. data/test/fixtures/html/mixed_log_levels.html +1 -0
  38. data/test/fixtures/html/mixed_types.html +3 -0
  39. data/test/helper.rb +116 -16
  40. data/test/html/mixed_type_test.rb +14 -20
  41. data/test/html/parser/parse_comment_test.rb +4 -4
  42. data/test/html/parser/parse_dtd_test.rb +7 -7
  43. data/test/html/parser/parse_script_tag_test.rb +21 -14
  44. data/test/html/parser/parse_with_auto_close_tag_test.rb +12 -10
  45. data/test/html/parser/parse_with_diff_case_test.rb +8 -9
  46. data/test/html/parser/parse_with_emtpy_test.rb +5 -3
  47. data/test/html/parser/parse_with_multi_children_test.rb +4 -4
  48. data/test/html/parser/parse_with_multi_line_test.rb +5 -5
  49. data/test/html/parser/parse_with_prop_test.rb +10 -10
  50. data/test/html/parser/parse_with_script_tag_test.rb +4 -4
  51. data/test/html/parser/parse_with_selfclosing_test.rb +11 -11
  52. data/test/html/parser/parse_with_simple_tag_test.rb +7 -7
  53. data/test/html/parser/parse_with_simple_tree_test.rb +6 -6
  54. data/test/html/parser/parse_with_style_tag_test.rb +4 -4
  55. data/test/html/parser/parse_with_text_test.rb +7 -7
  56. data/test/html/parser_test.rb +19 -19
  57. data/test/html/query_test.rb +18 -18
  58. data/test/html/rule/check_block_level_element_test.rb +22 -24
  59. data/test/html/rule/check_button_test.rb +9 -9
  60. data/test/html/rule/check_css_in_head_test.rb +11 -32
  61. data/test/html/rule/check_dtd_test.rb +15 -11
  62. data/test/html/rule/check_form_element_name_test.rb +10 -9
  63. data/test/html/rule/check_head_contain_meta_and_title_test.rb +28 -34
  64. data/test/html/rule/check_hyperlink_with_target_test.rb +23 -17
  65. data/test/html/rule/check_hyperlink_with_title_test.rb +13 -20
  66. data/test/html/rule/check_id_n_class_downcase_test.rb +8 -19
  67. data/test/html/rule/check_img_with_alt_prop_test.rb +11 -12
  68. data/test/html/rule/check_no_import_css_test.rb +20 -20
  69. data/test/html/rule/check_prop_have_value_test.rb +9 -13
  70. data/test/html/rule/check_prop_seperator_test.rb +9 -13
  71. data/test/html/rule/check_style_prop_test.rb +7 -12
  72. data/test/html/rule/check_tag_closed_test.rb +15 -40
  73. data/test/html/rule/check_tag_downcase_test.rb +31 -28
  74. data/test/html/rule/check_unescape_char_test.rb +17 -17
  75. data/test/html/rule/check_unique_import_test.rb +15 -33
  76. data/test/html/rule_test.rb +2 -6
  77. data/test/js/expr/expr.rb +1 -1
  78. data/test/js/expr/left_hand.rb +1 -1
  79. data/test/js/expr/operate.rb +1 -1
  80. data/test/js/expr/primary.rb +1 -1
  81. data/test/js/parser_test.rb +3 -6
  82. data/test/js/rule/alert_check_test.rb +5 -19
  83. data/test/js/rule/base_test.rb +5 -19
  84. data/test/js/rule/jq_check_test.rb +26 -58
  85. data/test/js/rule/nest_try_catch_test.rb +26 -43
  86. data/test/js/rule/new_object_and_new_array_test.rb +5 -21
  87. data/test/js/rule/no_eval_test.rb +3 -19
  88. data/test/js/rule/no_global_test.rb +34 -73
  89. data/test/js/rule/private_method_check_test.rb +14 -43
  90. data/test/js/rule/semicolon_test.rb +18 -47
  91. data/test/js/rule/stat_if_with_brace_test.rb +30 -53
  92. data/test/js/rule/stat_if_with_muti_else_test.rb +36 -53
  93. data/test/js/rule/use_strict_equal_test.rb +16 -29
  94. data/test/js/rule_test.rb +2 -4
  95. data/test/js/stat/if.rb +1 -1
  96. data/test/js/stat/iter.rb +1 -1
  97. data/test/js/stat/stat.rb +1 -1
  98. data/test/js/stat/switch.rb +1 -1
  99. data/test/js/stat/try.rb +1 -1
  100. data/test/js/stat/var.rb +1 -1
  101. data/test/parser_visitable_test.rb +14 -36
  102. data/test/position_info_test.rb +4 -5
  103. data/test/runner/log_level_test.rb +30 -25
  104. metadata +11 -14
  105. data/lib/fdlint/file_validator.rb +0 -38
  106. data/lib/fdlint/parser/html/rule/check_tag_rule.rb +0 -80
  107. data/test/css/rule/compression_test.rb +0 -53
  108. data/test/html/rule/check_class_count_test.rb +0 -36
  109. data/test/html/rule/check_html_template_test.rb +0 -103
  110. data/test/js/rule/all_test.rb +0 -23
  111. data/test/js/rule/file_checker_test.rb +0 -131
  112. data/test/rule_dsl/dsl_basic_test.rb +0 -91
  113. data/test/rule_dsl/importing_test.rb +0 -48
  114. data/test/test_helper.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9d6d40f7557d99e016ca0dfe7949ff1fa69d856
4
- data.tar.gz: 558d82918d7b4f6e1b4901fa69201e5e7539056d
3
+ metadata.gz: 5010fb8a0b8b08e6b4ebd130f35c425d62cfef80
4
+ data.tar.gz: d01fccdb793cbc4900f291c74599e2dfef319509
5
5
  SHA512:
6
- metadata.gz: 50b3ac1298038655d9fd0a89295f22d865780f79528f9b77ed65f47d967ee07d13aa1ecb2d156b53ffc82db4939c94660bdb1c02191232b024138ad4e27333e6
7
- data.tar.gz: 3f44299dbd47fdca59e2dbe0c5b6769f287bd20d865338f40669d41540479e4c024f2cd5b39a48e9958040e3daee29f32b46f04e5df248e0efd8331e80f74051
6
+ metadata.gz: b21da480340bc0c23af7851ec02baf2b16e424fbc544e5da2ae963f32b5c0001ad4b54ca87a25db756321a5b88b179df025ee48d86d1de26efe118d255aef988
7
+ data.tar.gz: 99aa6f8d89edabee672f97202ecfffbe359d88c1eeab7b9445e20006c3ab1b57263b64d7ac31193c343bb6da2a036c019b61a846213f79e8efbbe6257e3e6491
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fdlint (0.2.0.pre)
4
+ fdlint (0.2.0)
5
5
  colored (~> 1.2)
6
6
  gli (~> 2.9.0)
7
7
  string_utf8 (~> 0.1.1)
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  fdlint -- 让前端code review更轻松
2
2
  =================================
3
3
 
4
- ![fdlint-logo](http://q.pnq.cc/wp-content/uploads/2012/02/fdlint-logo-white.png)
4
+ ![fdlint-logo](http://fdlint.oss-cn-hangzhou.aliyuncs.com/fdlint-logo-white-small.png)
5
5
 
6
- fdlint (开发代号xray) 是根据阿里巴巴前端开发checklist开发的自动代码扫描工具。
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: fdlint
44
- --css 在扫描模式下仅检查CSS文件,在管道模式下指定内容为CSS
45
- --js 在扫描模式下仅检查JS文件,在管道模式下指定内容为JS
46
- --html 在扫描模式下仅检查HTML文件,在管道模式下指定内容为HTML
47
- -c, --charset set 指定文件默认的编码(本参数已废弃,目前自动判断字符集)
48
- -d, --debug 输出调试信息
49
- -l, --list 无彩色输出,等同于 '--format=nocolor'
50
- -m, --checkmin 检查压缩后的js或css文件。如不指定改选项,会跳过*-min.css或*-min.js文件。
51
- (e.g. *-min.js; *-min.css)
52
- --format [type] 输出模式:console (默认)、nocolor 或 vim
53
- --level [log_level] 输出时消息的过滤等级:warn(默认)、error 或 fatal
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
@@ -13,4 +13,4 @@ Rake::TestTask.new do |t|
13
13
  t.test_files = FileList['test/*_test.rb']
14
14
  end
15
15
 
16
- task :default => [:test,:features]
16
+ task :default => [:test]
data/bin/fdlint CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- require 'gli'
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 "ignore minified files"
39
+ c.desc "check minified files too"
36
40
  c.default_value false
37
- c.switch [:m, :"ignore-min"], negatable: false
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
- def run
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
- validate_file f unless File.directory? f
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
- ::Fdlint::Validator.new( path ).validate do |file, source, results|
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
@@ -27,7 +27,7 @@ module Fdlint; module Helper
27
27
  end
28
28
 
29
29
  def logger
30
- $logger ||= ::Logger.new(STDOUT)
30
+ $logger ||= ::Logger.new(STDOUT, ::Logger::WARN)
31
31
  end
32
32
 
33
33
  end
@@ -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
- nodes = batch(:parse_element)
37
- case nodes.size
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
- el = Tag.new(tag, prop, children, close_type, ending)
169
- el.scopes = scopes.dup
170
- el
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 = Element.new(tag, prop, [], :self)
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 XRay
4
- module HTML
3
+ module Fdlint; module Parser; module HTML
5
4
 
6
- class Element
7
-
8
- WORD = /[A-Za-z][-_\w]*/
9
- CLASS = %r(\.#{WORD})
10
- ID = %r(##{WORD})
11
- PROP_PAIR = %r(\s*,?\s*#{WORD}(==[^\s,])?)
12
- PROP = %r(\[#{WORD}.*?\])
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
- # This method implemented CSS selector for
16
- # HTML (like Sizzle) very simply. It is not
17
- # fully supported CSS selector.
18
- #
19
- # TODO: support full CSS3 selector
20
- ###
21
- def match?(str)
22
- return false if is_a?(TextElement)
23
- obj = query_obj(str)
24
- tag = obj[:tag]
25
- cls = obj[:classes]
26
- props = obj[:properties]
27
- unless tag.nil? or tag_name_equal? tag
28
- return false
29
- end
30
- classes = prop_value(:class)
31
- cls.each { |c| return false unless classes.include? c }
32
- props.each do |n, v|
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
- alias_method :===, :match?
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
- public
77
- def query( selector, &block )
78
- ret = []
79
- if match?(selector)
80
- ret << self
81
- yield self if block_given?
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
- children && children.each do |node|
85
- ret += node.query(selector, &block)
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
- ret
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
- end
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 < Element
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 < Element
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, *args
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.each do |target, visitor|
78
- add_visitor target, visitor
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
 
@@ -10,6 +10,12 @@ module Fdlint
10
10
 
11
11
  attr_accessor :file, :source, :results
12
12
 
13
+ def pre_validate( file )
14
+ end
15
+
16
+ def post_validate( file )
17
+ end
18
+
13
19
  def print( file, source, results )
14
20
  @file, @source, @results = file, source, results || []
15
21
  end