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.
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