fdlint 0.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.
Files changed (218) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +14 -0
  3. data/README.md +68 -0
  4. data/Rakefile +92 -0
  5. data/bin/fdlint +17 -0
  6. data/lib/base_parser.rb +143 -0
  7. data/lib/cmd_runner.rb +145 -0
  8. data/lib/context.rb +31 -0
  9. data/lib/css/parser.rb +186 -0
  10. data/lib/css/reader.rb +30 -0
  11. data/lib/css/rule/check_compression_rule.rb +48 -0
  12. data/lib/css/rule/checklist.rb +45 -0
  13. data/lib/css/struct.rb +111 -0
  14. data/lib/encoding_error.rb +6 -0
  15. data/lib/file_validator.rb +38 -0
  16. data/lib/helper/code_type.rb +50 -0
  17. data/lib/helper/color_string.rb +44 -0
  18. data/lib/helper/file_reader.rb +22 -0
  19. data/lib/helper/strenc.rb +65 -0
  20. data/lib/html/parser.rb +212 -0
  21. data/lib/html/query.rb +96 -0
  22. data/lib/html/rule/check_tag_rule.rb +80 -0
  23. data/lib/html/struct.rb +291 -0
  24. data/lib/js/expr/expr.rb +66 -0
  25. data/lib/js/expr/left_hand.rb +63 -0
  26. data/lib/js/expr/operate.rb +92 -0
  27. data/lib/js/expr/primary.rb +166 -0
  28. data/lib/js/parser.rb +116 -0
  29. data/lib/js/rule/all.rb +35 -0
  30. data/lib/js/rule/checklist.rb +41 -0
  31. data/lib/js/rule/file_checker.rb +42 -0
  32. data/lib/js/rule/helper.rb +96 -0
  33. data/lib/js/rule/no_global.rb +87 -0
  34. data/lib/js/stat/if.rb +25 -0
  35. data/lib/js/stat/iter.rb +85 -0
  36. data/lib/js/stat/stat.rb +117 -0
  37. data/lib/js/stat/switch.rb +65 -0
  38. data/lib/js/stat/try.rb +28 -0
  39. data/lib/js/stat/var.rb +40 -0
  40. data/lib/js/struct.rb +248 -0
  41. data/lib/log_entry.rb +49 -0
  42. data/lib/node.rb +28 -0
  43. data/lib/parse_error.rb +13 -0
  44. data/lib/parser_visitable.rb +138 -0
  45. data/lib/position_info.rb +46 -0
  46. data/lib/printer/base_printer.rb +24 -0
  47. data/lib/printer/console_printer.rb +66 -0
  48. data/lib/printer/nocolor_printer.rb +27 -0
  49. data/lib/printer/vim_printer.rb +19 -0
  50. data/lib/rule.rb +241 -0
  51. data/lib/rule_helper.rb +14 -0
  52. data/lib/runner.rb +225 -0
  53. data/rules.d/css.rule +127 -0
  54. data/rules.d/html.dtd.rule +22 -0
  55. data/rules.d/html.prop.rule +51 -0
  56. data/rules.d/html.tag.rule +136 -0
  57. data/rules.d/js.file.rule +13 -0
  58. data/rules.d/js.jquery.rule +56 -0
  59. data/rules.d/js.mergefile.rule +71 -0
  60. data/rules.d/js.rule +84 -0
  61. data/test/all_tests.rb +84 -0
  62. data/test/cli/cli_test.rb +70 -0
  63. data/test/cli/log_level_test.rb +51 -0
  64. data/test/cli/output_format_test.rb +47 -0
  65. data/test/cli/type_test.rb +77 -0
  66. data/test/css/mac_line_end_support_test.rb +38 -0
  67. data/test/css/parser_test.rb +276 -0
  68. data/test/css/rule/check_encoding_test.rb +66 -0
  69. data/test/css/rule/check_list_rule_test.rb +167 -0
  70. data/test/css/rule/compression_test.rb +53 -0
  71. data/test/css/rule/file_name_test.rb +76 -0
  72. data/test/fixtures/css/broken.css +4 -0
  73. data/test/fixtures/css/cbu/36.css +52 -0
  74. data/test/fixtures/css/cbu/china_top.css +324 -0
  75. data/test/fixtures/css/cbu/default-merge.css +3 -0
  76. data/test/fixtures/css/cbu/default.css +13 -0
  77. data/test/fixtures/css/cbu/diy-merge.css +25 -0
  78. data/test/fixtures/css/cbu/fns-v1.css +27 -0
  79. data/test/fixtures/css/cbu/index_v0.1.css +12 -0
  80. data/test/fixtures/css/cbu/merge.css +11 -0
  81. data/test/fixtures/css/cbu/min.css +2 -0
  82. data/test/fixtures/css/cbu/my_home_admin.css +126 -0
  83. data/test/fixtures/css/cbu/nav.css +95 -0
  84. data/test/fixtures/css/cbu/pic_list.css +386 -0
  85. data/test/fixtures/css/cbu/quote-edit.css +18 -0
  86. data/test/fixtures/css/cbu/selloffer.shopwindow.css +1 -0
  87. data/test/fixtures/css/cbu/v1.css +9 -0
  88. data/test/fixtures/css/css3.css +30 -0
  89. data/test/fixtures/css/empty-min.css +0 -0
  90. data/test/fixtures/css/empty.css +0 -0
  91. data/test/fixtures/css/font-family.css +4 -0
  92. data/test/fixtures/css/gb-good.css +14 -0
  93. data/test/fixtures/css/gb_using_star.css +4 -0
  94. data/test/fixtures/css/import.css +18 -0
  95. data/test/fixtures/css/mac-line-sep-err-min.css +1 -0
  96. data/test/fixtures/css/mac-line-sep-err.css +1 -0
  97. data/test/fixtures/css/mac-line-sep-good-min.css +1 -0
  98. data/test/fixtures/css/mac-line-sep-good.css +1 -0
  99. data/test/fixtures/css/multi-encoding-in-a-file.css +0 -0
  100. data/test/fixtures/css/simple.css +1 -0
  101. data/test/fixtures/css/using_expr.css +8 -0
  102. data/test/fixtures/css/using_hack.css +21 -0
  103. data/test/fixtures/css/using_id.css +1 -0
  104. data/test/fixtures/css/using_star.css +4 -0
  105. data/test/fixtures/css/utf8_good.css +6 -0
  106. data/test/fixtures/css/utf8_good_declaring_charset.css +7 -0
  107. data/test/fixtures/css/utf8_using_star.css +5 -0
  108. data/test/fixtures/html/1-1.html +120 -0
  109. data/test/fixtures/html/1-2.html +120 -0
  110. data/test/fixtures/html/cms.html +373 -0
  111. data/test/fixtures/html/css_out_of_head.html +9 -0
  112. data/test/fixtures/html/fdev-template.html +22 -0
  113. data/test/fixtures/html/google.com.html +33 -0
  114. data/test/fixtures/html/mixed_log_levels.html +4 -0
  115. data/test/fixtures/html/mixed_types.html +13 -0
  116. data/test/fixtures/html/no_dtd.html +6 -0
  117. data/test/fixtures/html/readme.html +94 -0
  118. data/test/fixtures/html/review.board.html +163 -0
  119. data/test/fixtures/html/syntax_err.html +3 -0
  120. data/test/fixtures/html/train/detail/345/233/276/346/226/207/347/273/223/345/220/210.html +208 -0
  121. data/test/fixtures/html/train/detail/347/232/204Flash.html +212 -0
  122. data/test/fixtures/html/train/detail/347/232/204Vedio.html +212 -0
  123. data/test/fixtures/html/train/index.html +37 -0
  124. data/test/fixtures/html/train/test.html +1 -0
  125. data/test/fixtures/html/train//344/277/256/346/224/271/344/270/200/347/272/247/345/210/206/347/261/273.html +112 -0
  126. data/test/fixtures/html/train//344/277/256/346/224/271/345/255/220/345/210/206/347/261/273.html +108 -0
  127. data/test/fixtures/html/train//344/277/256/346/224/271/350/257/276/347/250/213.html +195 -0
  128. data/test/fixtures/html/train//345/215/232/345/256/242/350/256/276/347/275/256.html +142 -0
  129. data/test/fixtures/html/train//346/265/217/350/247/210/350/256/260/345/275/225.html +191 -0
  130. data/test/fixtures/html/train//346/267/273/345/212/240/344/270/200/347/272/247/345/210/206/347/261/273.html +113 -0
  131. data/test/fixtures/html/train//346/267/273/345/212/240/345/255/220/345/210/206/347/261/273.html +112 -0
  132. data/test/fixtures/html/train//346/267/273/345/212/240/350/257/276/347/250/213.html +195 -0
  133. data/test/fixtures/html/train//347/231/273/345/275/225.html +20 -0
  134. data/test/fixtures/html/train//347/256/241/347/220/206/345/210/206/347/261/273.html +210 -0
  135. data/test/fixtures/html/train//347/256/241/347/220/206/345/217/215/351/246/210.html +222 -0
  136. data/test/fixtures/html/train//347/256/241/347/220/206/350/257/276/347/250/213.html +284 -0
  137. data/test/fixtures/html/train//347/256/241/347/220/206/350/264/246/346/210/267.html +107 -0
  138. data/test/fixtures/html/train//347/275/221/344/270/212/345/237/271/350/256/255home/351/241/265.html +354 -0
  139. data/test/fixtures/html/train//347/275/221/345/225/206/345/237/271/350/256/255list/351/241/265.html +255 -0
  140. data/test/fixtures/html/train//350/256/276/347/275/256/351/246/226/351/241/265/346/216/250/350/215/220.html +168 -0
  141. data/test/fixtures/html/train//350/257/264/346/230/216.txt +3 -0
  142. data/test/fixtures/html/train//351/246/226/351/241/265/345/271/277/345/221/212/350/256/276/347/275/256.html +297 -0
  143. data/test/fixtures/html/unescaped.html +2 -0
  144. data/test/fixtures/html/view.vm +916 -0
  145. data/test/fixtures/js/jquery-1.7.js +9300 -0
  146. data/test/fixtures/js/scope-test.js +22 -0
  147. data/test/helper.rb +41 -0
  148. data/test/html/mixed_type_test.rb +35 -0
  149. data/test/html/parser/parse_comment_test.rb +47 -0
  150. data/test/html/parser/parse_dtd_test.rb +46 -0
  151. data/test/html/parser/parse_script_tag_test.rb +55 -0
  152. data/test/html/parser/parse_with_auto_close_tag_test.rb +41 -0
  153. data/test/html/parser/parse_with_diff_case_test.rb +38 -0
  154. data/test/html/parser/parse_with_emtpy_test.rb +22 -0
  155. data/test/html/parser/parse_with_multi_children_test.rb +27 -0
  156. data/test/html/parser/parse_with_multi_line_test.rb +41 -0
  157. data/test/html/parser/parse_with_prop_test.rb +88 -0
  158. data/test/html/parser/parse_with_script_tag_test.rb +26 -0
  159. data/test/html/parser/parse_with_selfclosing_test.rb +39 -0
  160. data/test/html/parser/parse_with_simple_tag_test.rb +44 -0
  161. data/test/html/parser/parse_with_simple_tree_test.rb +40 -0
  162. data/test/html/parser/parse_with_style_tag_test.rb +22 -0
  163. data/test/html/parser/parse_with_text_test.rb +45 -0
  164. data/test/html/parser_test.rb +52 -0
  165. data/test/html/query_test.rb +52 -0
  166. data/test/html/rule/check_block_level_element_test.rb +52 -0
  167. data/test/html/rule/check_button_test.rb +45 -0
  168. data/test/html/rule/check_class_count_test.rb +36 -0
  169. data/test/html/rule/check_css_in_head_test.rb +53 -0
  170. data/test/html/rule/check_dtd_test.rb +46 -0
  171. data/test/html/rule/check_form_element_name_test.rb +49 -0
  172. data/test/html/rule/check_head_contain_meta_and_title_test.rb +52 -0
  173. data/test/html/rule/check_html_template_test.rb +103 -0
  174. data/test/html/rule/check_hyperlink_with_target_test.rb +40 -0
  175. data/test/html/rule/check_hyperlink_with_title_test.rb +43 -0
  176. data/test/html/rule/check_id_n_class_downcase_test.rb +40 -0
  177. data/test/html/rule/check_img_with_alt_prop_test.rb +33 -0
  178. data/test/html/rule/check_no_import_css_test.rb +36 -0
  179. data/test/html/rule/check_prop_have_value_test.rb +32 -0
  180. data/test/html/rule/check_prop_seperator_test.rb +32 -0
  181. data/test/html/rule/check_style_prop_test.rb +30 -0
  182. data/test/html/rule/check_tag_closed_test.rb +59 -0
  183. data/test/html/rule/check_tag_downcase_test.rb +51 -0
  184. data/test/html/rule/check_unescape_char_test.rb +35 -0
  185. data/test/html/rule/check_unique_import_test.rb +56 -0
  186. data/test/html/rule_test.rb +62 -0
  187. data/test/js/expr/expr.rb +57 -0
  188. data/test/js/expr/left_hand.rb +25 -0
  189. data/test/js/expr/operate.rb +145 -0
  190. data/test/js/expr/primary.rb +89 -0
  191. data/test/js/parser_test.rb +98 -0
  192. data/test/js/rule/alert_check_test.rb +37 -0
  193. data/test/js/rule/all_test.rb +23 -0
  194. data/test/js/rule/base_test.rb +34 -0
  195. data/test/js/rule/file_checker_test.rb +131 -0
  196. data/test/js/rule/jq_check_test.rb +90 -0
  197. data/test/js/rule/nest_try_catch_test.rb +71 -0
  198. data/test/js/rule/new_object_and_new_array_test.rb +38 -0
  199. data/test/js/rule/no_eval_test.rb +34 -0
  200. data/test/js/rule/no_global_test.rb +88 -0
  201. data/test/js/rule/private_method_check_test.rb +58 -0
  202. data/test/js/rule/semicolon_test.rb +63 -0
  203. data/test/js/rule/stat_if_with_brace_test.rb +68 -0
  204. data/test/js/rule/stat_if_with_muti_else_test.rb +68 -0
  205. data/test/js/rule/use_strict_equal_test.rb +44 -0
  206. data/test/js/rule_test.rb +47 -0
  207. data/test/js/stat/if.rb +26 -0
  208. data/test/js/stat/iter.rb +115 -0
  209. data/test/js/stat/stat.rb +91 -0
  210. data/test/js/stat/switch.rb +37 -0
  211. data/test/js/stat/try.rb +32 -0
  212. data/test/js/stat/var.rb +38 -0
  213. data/test/parser_visitable_test.rb +102 -0
  214. data/test/position_info_test.rb +66 -0
  215. data/test/rule_dsl/dsl_basic_test.rb +91 -0
  216. data/test/rule_dsl/importing_test.rb +48 -0
  217. data/test/runner/log_level_test.rb +58 -0
  218. metadata +317 -0
data/lib/runner.rb ADDED
@@ -0,0 +1,225 @@
1
+ # encoding: utf-8
2
+
3
+ #require 'profile'
4
+
5
+ require 'logger'
6
+ require_relative 'base_parser'
7
+ require_relative 'rule'
8
+ require_relative 'parser_visitable'
9
+ require_relative 'file_validator'
10
+ require_relative 'log_entry'
11
+ require_relative 'css/reader'
12
+ require_relative 'css/parser'
13
+ require_relative 'css/rule/checklist'
14
+ require_relative 'html/parser'
15
+ require_relative 'html/rule/check_tag_rule'
16
+ require_relative 'js/parser'
17
+ require_relative 'js/rule/all'
18
+ require_relative 'js/rule/file_checker'
19
+ require_relative 'helper/file_reader'
20
+ require_relative 'helper/code_type'
21
+
22
+
23
+ module XRay
24
+
25
+ module CSS
26
+ class VisitableParser < Parser
27
+ include XRay::ParserVisitable
28
+ end
29
+ end
30
+
31
+ module HTML
32
+ class VisitableParser < Parser
33
+ include XRay::ParserVisitable
34
+ end
35
+ end
36
+
37
+ module JS
38
+ class VisitableParser < Parser
39
+ include XRay::ParserVisitable
40
+ end
41
+ end
42
+
43
+
44
+ class Runner
45
+
46
+ include Helper::FileReader
47
+
48
+ attr_reader :source, :rules
49
+
50
+ def initialize(opt={})
51
+ @opt = {
52
+ :encoding => 'utf-8',
53
+ :debug => false,
54
+ :log_level => :warn
55
+ }.merge opt
56
+
57
+ @rules = []
58
+
59
+ if @opt[:debug]
60
+ @logger = Logger.new(STDOUT)
61
+ @logger.level = Logger::INFO
62
+ end
63
+
64
+ Rule.import_all
65
+
66
+
67
+ end
68
+
69
+ def check_css(css, opt = {})
70
+ @source = css
71
+ parser = XRay::CSS::VisitableParser.new(css, @logger)
72
+ visitor = XRay::CSS::Rule::CheckListRule.new( opt )
73
+ parser.add_visitor visitor
74
+ run_parser( parser )
75
+ end
76
+
77
+ def check_css_file( file, opt={} )
78
+ results = []
79
+ begin
80
+ "".extend(XRay::Rule).check_css_file(file).each do |msg, level, *pos|
81
+ results.unshift LogEntry.new( msg, level, pos[0] || 0, pos[1] || 0 )
82
+ end
83
+
84
+ source = XRay::CSS::Reader.read( file, @opt )
85
+ results.concat check_css( source, opt.merge({
86
+ :scope => CodeType.scope(file)
87
+ }))
88
+ rescue EncodingError => e
89
+ results.unshift LogEntry.new( "File can't be read as #{@opt[:encoding]} charset", :fatal)
90
+ rescue => e
91
+ if @opt[:debug]
92
+ puts e
93
+ puts e.backtrace
94
+ end
95
+ results.unshift LogEntry.new( e.to_s, :fatal)
96
+ end
97
+ results
98
+ end
99
+
100
+ def check_js(js, opt={})
101
+ @source = js
102
+ parser = JS::VisitableParser.new(js, @logger)
103
+ parser.add_visitors JS::Rule::All.new
104
+ run_parser( parser )
105
+ end
106
+
107
+ def check_js_file(file, opt = {})
108
+ results = []
109
+ begin
110
+ results.concat JS::Rule::FileChecker.new.check_file(file)
111
+
112
+ source, encoding = readfile(file, opt)
113
+ results.concat check_js(source)
114
+ rescue EncodingError => e
115
+ results.unshift LogEntry.new( "File can't be read as #{@opt[:encoding]} charset", :fatal)
116
+ rescue => e
117
+ if @opt[:debug]
118
+ puts e
119
+ puts e.backtrace
120
+ end
121
+ results.unshift LogEntry.new( e.to_s, :fatal )
122
+ end
123
+ results
124
+ end
125
+
126
+ def check_html(text, opt={})
127
+ @source = text
128
+ parser = HTML::VisitableParser.new(text, @logger)
129
+ visitor = HTML::Rule::CheckTagRule.new( opt )
130
+ parser.add_visitor visitor
131
+ results = run_parser( parser )
132
+ unless @parsed_element.nil? or @parsed_element.empty?
133
+ results += check_scripts_in_html(opt)
134
+ results += check_styles_in_html(opt)
135
+ end
136
+ results
137
+ end
138
+
139
+ def check_scripts_in_html(opt={})
140
+ results = []
141
+ @parsed_element.query('script') do |script|
142
+ row = script.position.row
143
+ col = script.position.column
144
+ Runner.new.check_js(script.text, opt).each do |ret|
145
+ ret.column += script.outer_html[/^\s*<script*?>/].size if ret.row == 1
146
+ ret.row += row - 1
147
+ results << ret
148
+ end
149
+ end
150
+
151
+ results
152
+ end
153
+
154
+ def check_styles_in_html(opt={})
155
+ results = []
156
+ @parsed_element.query('style') do |style|
157
+ row = style.position.row
158
+ col = style.position.column
159
+ Runner.new.check_css(style.text, opt.merge({:scope => :in_page })).each do |ret|
160
+ ret.column += style.outer_html[/^\s*<style*?>/].size if ret.row == 1
161
+ ret.row += row - 1
162
+ results << ret
163
+ end
164
+ end
165
+
166
+ results
167
+ end
168
+
169
+ def check_html_file(file, opt={})
170
+ source, encoding = readfile file
171
+ check_html source
172
+ end
173
+
174
+ def check_file( file )
175
+ type = CodeType.guess_by_name(file)
176
+ send( "check_#{type}_file", file ) if CodeType.is_style_file? file
177
+ end
178
+
179
+ def check(text, filename="")
180
+ type = CodeType.guess(text, filename)
181
+ send "check_#{type}", text
182
+ end
183
+
184
+ def valid_file? file
185
+ CodeType.is_style_file?(file) and !minified_and_ignored(file) and type_ok?(file)
186
+ end
187
+
188
+ def minified_and_ignored(file)
189
+ !@opt[:check_min] && file.to_s =~ /-min\.(css|js)$/
190
+ end
191
+
192
+ def type_ok?(file)
193
+ if @opt[:type]
194
+ CodeType.guess_by_name(file) == @opt[:type]
195
+ else
196
+ true
197
+ end
198
+ end
199
+
200
+ def run_parser(parser)
201
+ @parsed_element = parser.parse_no_throw
202
+ filter_results( parser.results ).sort_by!(&:row)
203
+ end
204
+
205
+ def filter_results(results)
206
+ case log_level
207
+ when :warn
208
+ results
209
+ when :error
210
+ results.select {|r| r.level == :error || r.level == :fatal }
211
+ when :fatal
212
+ results.select {|r| r.level == :fatal }
213
+ end
214
+ end
215
+
216
+ def log_level
217
+ @opt[:log_level]
218
+ end
219
+
220
+ def log_level=(lvl)
221
+ @opt[:log_level] = lvl
222
+ end
223
+
224
+ end
225
+ end
data/rules.d/css.rule ADDED
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+ # vi: filetype=ruby
3
+ css
4
+
5
+ check_file_name_without_ad do |name|
6
+ unless defined? ADVERTISMENT
7
+ ADVERTISMENT = /(?:[^a-z0-9%_-]|^)ad(?:[sv][^a-z\r=\?]+|banner|click|ver|name|x|log|[^a-z\r_-]*[\.\/]|bot|c_|client|council|gifs|graph|images|img|fshow|pic|vert|view|info|click|sponsor)/
8
+ end
9
+ if name =~ ADVERTISMENT
10
+ ['路径和文件名中不应该出现ad', :error]
11
+ end
12
+ end
13
+
14
+ check_file_name_without_underscore do |name|
15
+ if File.basename(name) =~ /_/
16
+ ['文件名中单词的分隔符应该使用中横线“-”', :error]
17
+ end
18
+ end
19
+
20
+ check_file_name_seperator do |name|
21
+ if File.dirname(name) =~ /-(?!v?[\d.])/
22
+ ['文件夹只有需要版本区分时才可用中横线分隔,如fdev-v3', :error]
23
+ end
24
+ end
25
+
26
+ check_file_name_downcase do |name|
27
+ win_disk = /^[a-zA-Z]:/
28
+ if name.sub( win_disk, '') =~ /[A-Z]/
29
+ ['文件夹和文件命名必须用小写字母', :error]
30
+ end
31
+ end
32
+
33
+ check_selector_with_id do |selector|
34
+ if context.page_level? &&
35
+ selector =~ /#[-\w]+/
36
+ ['页面级别样式不使用id', :error]
37
+ end
38
+ end
39
+
40
+ check_selector_with_global_tag do |selector|
41
+ if context.page_level? &&
42
+ selector =~ /^\w+$/
43
+ ['页面级别样式不能全局定义标签样式', :error]
44
+ end
45
+ end
46
+
47
+ check_selector_level do |selector|
48
+ if selector.text.split(/[>\s]/).length > 4
49
+ ['CSS级联深度不能超过4层', :error]
50
+ end
51
+ end
52
+
53
+ check_selector_with_star do |selector|
54
+ if selector =~ /^\*/
55
+ ['禁止使用星号选择符', :error]
56
+ end
57
+ end
58
+
59
+ check_selector_redefine_lib_css do |selector|
60
+ if !context.lib? &&
61
+ selector =~ /^\.fd-\w+$/
62
+ ['禁止修改或重载type中的样式', :error]
63
+ end
64
+ end
65
+
66
+ check_declaration_font do |dec|
67
+ if dec.property =~ /^font(-family)?$/ &&
68
+ dec.value.to_s.each_byte.any? {|b| b>127}
69
+ ['字体名称中的中文必须用ascii字符表示', :error]
70
+ end
71
+ end
72
+
73
+ check_ruleset_redefine_a_hover_color do |ruleset|
74
+ if ruleset.selector &&
75
+ ruleset.selector.text == 'a:hover' &&
76
+ ruleset.declarations.find { |dec| dec.property.text == 'color' }
77
+ ['禁止重写reset中定义的a标签的hover色(现为#ff7300)', :error]
78
+ end
79
+ end
80
+
81
+ check_declarations_sequence do |ruleset|
82
+ list = [
83
+ %w(position display visible z-index overflow float clear),
84
+ %w(width height top right bottom left margin padding border),
85
+ %w(background opacity),
86
+ %w(font color text line-height vertical-align)
87
+ ]
88
+
89
+ now = 0
90
+ ruleset.declarations.each do |dec|
91
+ prop_text = dec.property.text
92
+ index = list.find_index do |bag|
93
+ bag.any? { |field| prop_text.index field }
94
+ end
95
+
96
+ next unless index
97
+ return ['建议使用Mozilla推荐CSS书写顺序'\
98
+ 'http://wd.alibaba-inc.com/doc/page/regulations/css', :warn] if index < now
99
+ now = index
100
+ end
101
+ nil
102
+ end
103
+
104
+ check_selector_using_hack do |selector|
105
+ if selector =~ /\S\*/
106
+ ['合理使用hack', :error]
107
+ end
108
+ end
109
+
110
+ check_property_hack do |property|
111
+ if property =~ /[^-a-z]/
112
+ ['合理使用hack', :error]
113
+ end
114
+ end
115
+
116
+ check_value_use_css_expression do |value|
117
+ if value =~ /^expression\(/
118
+ ['禁止使用CSS表达式', :error]
119
+ end
120
+ end
121
+
122
+ check_value_use_hack do |value|
123
+ if value =~ /\\\d$/
124
+ ['合理使用hack', :error]
125
+ end
126
+ end
127
+
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ # vi: filetype=ruby
3
+ html
4
+
5
+ check_doc do |doc|
6
+ unless context.have_dtd?
7
+ ["必须存在文档类型声明", :error]
8
+ end
9
+ end
10
+
11
+ check_dtd_type_must_be_html5 do |dtd|
12
+ unless dtd.type =~ /^html$/i
13
+ ['新页面统一使用HTML 5 DTD', :warn]
14
+ end
15
+ end
16
+
17
+ check_dtd_upcase do |dtd|
18
+ unless dtd =~ /^<!DOCTYPE/
19
+ ['必须使用大写的"DOCTYPE"', :warn]
20
+ end
21
+ end
22
+
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ # vi: filetype=ruby
3
+
4
+ html
5
+
6
+ check_property_no_inline_style do |prop|
7
+ if prop.name =~ /^style$/im
8
+ ["不能定义内嵌样式style", :error]
9
+ end
10
+ end
11
+
12
+ check_property_name_downcase do |prop|
13
+ if prop.name =~ /[A-Z]/
14
+ ["属性名必须小写,连字符用中横线", :error]
15
+ end
16
+ end
17
+
18
+ check_property_id_prop_value_downcase do |prop|
19
+ if prop.name_equal? 'id' and prop.value =~ /[A-Z]/
20
+ ["id名称全部小写,单词分隔使用中横线", :error]
21
+ end
22
+ end
23
+
24
+ check_property_class_prop_value_downcase do |prop|
25
+ if prop.name_equal? 'class' and prop.value =~ /[A-Z]/
26
+ ["class名称全部小写,单词分隔使用中横线", :error]
27
+ end
28
+ end
29
+
30
+ check_property_value_sep do |prop|
31
+ if prop.value and prop.sep != '"'
32
+ ["属性值必须使用双引号", :error]
33
+ end
34
+ end
35
+
36
+ check_property_value_exsit do |prop|
37
+ if prop.value.nil?
38
+ ["不能仅有属性名", :error]
39
+ end
40
+ end
41
+
42
+ check_property_class_count do |prop|
43
+ if prop.name_equal? 'class'
44
+ names = prop.value.split /\s+/
45
+ names.reject! { |s| s =~ /^(fd-|layout|grid|w952)\b/ }
46
+ if names.size > 3
47
+ ["一个节点上定义的class个数最多不超过3个(不含lib中的class)", :error]
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,136 @@
1
+ # encoding: utf-8
2
+ # vi: filetype=ruby
3
+
4
+ html
5
+
6
+ check_tag_name_downcase do |tag|
7
+ if tag.tag_name =~ /[A-Z]/ || tag.ending =~ /[A-Z]/
8
+ ["标签名必须小写", :error]
9
+ end
10
+ end
11
+
12
+ check_tag_img_must_have_alt do |tag|
13
+ if tag.tag_name_equal? 'img'
14
+ unless tag.has_prop?(:alt)
15
+ ["img标签必须加上alt属性", :error]
16
+ end
17
+ end
18
+ end
19
+
20
+ check_tag_hyperlink_with_target do |tag|
21
+
22
+ if tag.tag_name_equal? 'base' and tag.prop_value(:target)
23
+ @has_target = true
24
+ end
25
+
26
+ unless @has_target
27
+ if tag.tag_name_equal? 'a' and tag.prop_value(:href) =~ /^#/ and tag.prop_value(:target) != '_self'
28
+ ['功能a必须加target="_self",除非preventDefault过', :warn]
29
+ end
30
+ end
31
+ end
32
+
33
+ check_tag_hyperlink_with_title do |tag|
34
+ if tag.tag_name_equal? 'a' and tag.prop_value(:href) =~ /^[^#]/
35
+ unless (prop = tag.prop_value(:title)) and !prop.empty?
36
+ ['非功能能点的a标签必须加上title属性', :error]
37
+ end
38
+ end
39
+ end
40
+
41
+ check_tag_unique_script_import do |tag|
42
+ if tag.tag_name_equal? 'script'
43
+ src = tag.prop_value(:src).to_s
44
+ if !src.empty? and context.script_used? src
45
+ ["避免重复引用同一或相同功能文件", :error]
46
+ end
47
+ end
48
+ end
49
+
50
+ check_tag_unique_style_import do |tag|
51
+ if tag.tag_name_equal? 'link' and tag.prop_value(:rel) =~ /stylesheet/i
52
+ src = tag.prop_value(:href).to_s
53
+ if !src.empty? and context.style_used? src
54
+ ["避免重复引用同一或相同功能文件", :error]
55
+ end
56
+ end
57
+ end
58
+
59
+ check_tag_no_import_in_style_tag do |tag|
60
+ if tag.tag_name_equal? 'style' and tag.inner_text =~ /@import\s/m
61
+ ["不通过@import在页面上引入CSS", :error]
62
+ end
63
+ end
64
+
65
+ check_tag_head_contain_meta_and_title do |tag|
66
+ if tag.tag_name_equal? 'head'
67
+ children = tag.children
68
+ has_meta = children.any? { |e| e.tag_name_equal? 'meta' and e.prop_value('charset') }
69
+ has_title = children.any? { |e| e.tag_name_equal? 'title' }
70
+ unless has_meta and has_title
71
+ ["head必须包含字符集meta和title", :error]
72
+ end
73
+ end
74
+ end
75
+
76
+ check_tag_block_in_block do |tag|
77
+ if !tag.tag_name_equal?('a') and tag.inline? and tag.children.any? { |e| !e.inline? }
78
+ ["行内标签不得包含块级标签,a标签例外", :error]
79
+ end
80
+ end
81
+
82
+ check_tag_form_element_with_name do |tag|
83
+ if (tag.tag_name_equal?('input') and %w(text radio checkbox).include?(tag.prop_value('type').to_s.downcase) or
84
+ tag.tag_name_equal?('select') or
85
+ tag.tag_name_equal?('textarea'))
86
+ val = tag.prop_value('name')
87
+ unless val and !val.empty?
88
+ ["text、radio、checkbox、textarea、select必须加name属性", :error]
89
+ end
90
+ end
91
+ end
92
+
93
+ check_tag_form_button do |tag|
94
+ if tag.tag_name_equal?('input') and %w(button submit reset).include?(tag.prop_value('type').to_s.downcase)
95
+ ["所有按钮必须用button(button/submit/reset)", :error]
96
+ end
97
+ end
98
+
99
+ check_tag_css_in_head do |tag|
100
+ if tag.tag_name_equal?('link') and tag.prop_value(:rel) =~ /stylesheet/i
101
+ if tag.has_scope? and !tag.in_scope?('head')
102
+ ["外链CSS置于head里(例外:应用里的footer样式)", :warn]
103
+ end
104
+ end
105
+ end
106
+
107
+ check_tag_closed do |tag|
108
+ if !tag.closed? or (tag.auto_close? and !tag.self_closed?)
109
+ ["标签必须正确闭合", :error]
110
+ end
111
+ end
112
+
113
+ check_tag_html_template do |tag|
114
+ info = ["新页面按库中的HTML基本结构模板书写基本页面结构", :warn]
115
+ if tag.tag_name_equal?('head')
116
+ if !tag.children.any? { |e| e === 'meta[name==description]' } or
117
+ !tag.children.any? { |e| e === 'meta[name==keywords]' }
118
+ info
119
+ end
120
+ elsif tag.tag_name_equal?('body')
121
+ info unless tag.children.any? { |e| e === 'div#doc' }
122
+ elsif tag === 'div#doc'
123
+ if !tag.children.any? { |e| e === 'div#header' } or
124
+ !tag.children.any? { |e| e === 'div#footer' } or
125
+ !tag.children.any? { |e| e === 'div#alibar' } or
126
+ !tag.children.any? { |e| e === 'div#content' }
127
+ info
128
+ end
129
+ end
130
+ end
131
+
132
+ check_text_should_escape do |el|
133
+ if el.text =~ /[<>]/
134
+ ["特殊HTML符号(>和<)必须转义", :error]
135
+ end
136
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ # vi: filetype=ruby
3
+
4
+ js
5
+
6
+ check_file_functional_file_must_have_comment do |file, src|
7
+ if context.func_file? file
8
+ unless context.has_doc_comment? src
9
+ ['功能文件头必须有文档注释(以/**开始的多行注释)', :warn]
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ # vi: filetype=ruby
3
+
4
+ js
5
+
6
+ JQ_IDS = %w(jQuery $ jQ jq)
7
+
8
+ check_expr_member_direct_jquery_call do |expr|
9
+ expr = context.find_expr_member(expr) do |expr|
10
+ ['.', '(', '['].include?(expr.type) && expr.left.text == 'jQuery'
11
+ end
12
+
13
+ if expr
14
+ unless expr.type == '.' && expr.right.text == 'namespace'
15
+ ['禁止直接使用jQuery变量,使用全局闭包写法"(function($, NS){....})(jQuery,Namespace);",jQuery.namespace例外', :error]
16
+ end
17
+ end
18
+ end
19
+
20
+ check_expr_member_forbit_method_call do |expr|
21
+ methods = %w(sub noConflict)
22
+ expr = context.find_expr_member(expr) do |expr|
23
+ expr.type == '.' && JQ_IDS.include?(expr.left.text) &&
24
+ methods.include?(expr.right.text)
25
+ end
26
+
27
+ ['禁止使用jQuery.sub()和jQuery.noConflict方法', :error] if expr
28
+ end
29
+
30
+ check_expr_member_data_call_param do |expr|
31
+ expr = context.find_expr_member(expr) do |expr|
32
+ if expr.type == '('
33
+ name = expr.left
34
+ if name.is_a?(XRay::JS::Expression) && name.type == '.' &&
35
+ name.right.text == 'data'
36
+ param = expr.right[0]
37
+ param && param.text =~ /[-_]/
38
+ end
39
+ end
40
+ end
41
+
42
+ ['使用".data()"读写自定义属性时需要转化成驼峰形式', :error] if expr
43
+ end
44
+
45
+ check_expr_member_ctor_selector do |expr|
46
+ expr = context.find_expr_member(expr) do |expr|
47
+ expr.type == '(' && JQ_IDS.include?(expr.left.text)
48
+ end
49
+
50
+ if expr
51
+ param = expr.right[0]
52
+ if param && param.type == 'string' && param.text !~ /^['"][#\w<]/
53
+ ['使用选择器时,能确定tagName的,必须加上tagName', :warn]
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+ # vi: filetype=ruby
3
+
4
+ js
5
+
6
+ check_merge_importing_one_line_one_import do |import, pathes, file|
7
+ same_line = pathes.select { |path| path[:row] == import[:row] }
8
+ if same_line.size > 1 and same_line.index(import) > 0
9
+ ['一行只能有一个import文件', :error, import[:row], import[:col]]
10
+ end
11
+ end
12
+
13
+ check_merge_importing_strict_import_js do |import, pathes, file|
14
+ url = import[:url]
15
+ if context.lib_scope?(url) or context.relative?(url)
16
+ break
17
+ end
18
+
19
+ unless /ImportJavscript\.url\(['"]?([^'"]+)['"]?\);/ =~ import[:text]
20
+ ['merge文件格式不正确', :error, import[:row], import[:col]]
21
+ end
22
+ end
23
+
24
+ check_merge_importing_global_cannot_import_page do |import, pathes, file|
25
+ url = import[:url]
26
+ if context.lib_scope?(url) or context.relative?(url)
27
+ break
28
+ end
29
+
30
+ if context.global_scope? file and context.page_scope?(url)
31
+ ['产品线merge文件不能引用page文件', :error, import[:row], import[:col]]
32
+ end
33
+ end
34
+
35
+ check_merge_importing_can_only_import_from_same_app do |import, pathes, file|
36
+ url = import[:url]
37
+ if context.lib_scope?(url) or context.relative?(url)
38
+ break
39
+ end
40
+
41
+ app = context.app(file)
42
+ if app && app != context.app(url)
43
+ ['merge文件引用非该产品线文件', :error, import[:row], import[:col]]
44
+ end
45
+ end
46
+
47
+ check_merge_importing_page_can_only_import_same_page do |import, pathes, file|
48
+ if context.page_scope?(file)
49
+ path = file.sub /\\/, '/'
50
+ url = import[:url]
51
+ prefix = File.basename(path).sub /-merge([-_]\d+)?\.js/, ''
52
+ page_pattern = %r"/page/#{prefix}(-min|(/.+))?\.js"
53
+ if context.page_scope?(file) && context.page_scope?(url) && url !~ page_pattern
54
+ ['页面级别merge文件只允许merge当前页面所属js文件', :error, import[:row], import[:col]]
55
+ end
56
+ end
57
+ end
58
+
59
+ ##Style 拆分之后可能merge的文件已经不存在
60
+ ##stop checking this rule
61
+ #check_merge_importing_exist do |import, pathes, file|
62
+ #end
63
+
64
+ check_merge_importing_must_be_minified do |import, pathes, file|
65
+ if !context.min_file?(import[:url])
66
+ ['merge文件需要引用压缩版的js, 如a-min.js', :warn,
67
+ import[:row],
68
+ import[:col] + import[:text].rindex(/\/|\\/) + 1
69
+ ]
70
+ end
71
+ end