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.
- data/Gemfile +8 -0
- data/Gemfile.lock +14 -0
- data/README.md +68 -0
- data/Rakefile +92 -0
- data/bin/fdlint +17 -0
- data/lib/base_parser.rb +143 -0
- data/lib/cmd_runner.rb +145 -0
- data/lib/context.rb +31 -0
- data/lib/css/parser.rb +186 -0
- data/lib/css/reader.rb +30 -0
- data/lib/css/rule/check_compression_rule.rb +48 -0
- data/lib/css/rule/checklist.rb +45 -0
- data/lib/css/struct.rb +111 -0
- data/lib/encoding_error.rb +6 -0
- data/lib/file_validator.rb +38 -0
- data/lib/helper/code_type.rb +50 -0
- data/lib/helper/color_string.rb +44 -0
- data/lib/helper/file_reader.rb +22 -0
- data/lib/helper/strenc.rb +65 -0
- data/lib/html/parser.rb +212 -0
- data/lib/html/query.rb +96 -0
- data/lib/html/rule/check_tag_rule.rb +80 -0
- data/lib/html/struct.rb +291 -0
- data/lib/js/expr/expr.rb +66 -0
- data/lib/js/expr/left_hand.rb +63 -0
- data/lib/js/expr/operate.rb +92 -0
- data/lib/js/expr/primary.rb +166 -0
- data/lib/js/parser.rb +116 -0
- data/lib/js/rule/all.rb +35 -0
- data/lib/js/rule/checklist.rb +41 -0
- data/lib/js/rule/file_checker.rb +42 -0
- data/lib/js/rule/helper.rb +96 -0
- data/lib/js/rule/no_global.rb +87 -0
- data/lib/js/stat/if.rb +25 -0
- data/lib/js/stat/iter.rb +85 -0
- data/lib/js/stat/stat.rb +117 -0
- data/lib/js/stat/switch.rb +65 -0
- data/lib/js/stat/try.rb +28 -0
- data/lib/js/stat/var.rb +40 -0
- data/lib/js/struct.rb +248 -0
- data/lib/log_entry.rb +49 -0
- data/lib/node.rb +28 -0
- data/lib/parse_error.rb +13 -0
- data/lib/parser_visitable.rb +138 -0
- data/lib/position_info.rb +46 -0
- data/lib/printer/base_printer.rb +24 -0
- data/lib/printer/console_printer.rb +66 -0
- data/lib/printer/nocolor_printer.rb +27 -0
- data/lib/printer/vim_printer.rb +19 -0
- data/lib/rule.rb +241 -0
- data/lib/rule_helper.rb +14 -0
- data/lib/runner.rb +225 -0
- data/rules.d/css.rule +127 -0
- data/rules.d/html.dtd.rule +22 -0
- data/rules.d/html.prop.rule +51 -0
- data/rules.d/html.tag.rule +136 -0
- data/rules.d/js.file.rule +13 -0
- data/rules.d/js.jquery.rule +56 -0
- data/rules.d/js.mergefile.rule +71 -0
- data/rules.d/js.rule +84 -0
- data/test/all_tests.rb +84 -0
- data/test/cli/cli_test.rb +70 -0
- data/test/cli/log_level_test.rb +51 -0
- data/test/cli/output_format_test.rb +47 -0
- data/test/cli/type_test.rb +77 -0
- data/test/css/mac_line_end_support_test.rb +38 -0
- data/test/css/parser_test.rb +276 -0
- data/test/css/rule/check_encoding_test.rb +66 -0
- data/test/css/rule/check_list_rule_test.rb +167 -0
- data/test/css/rule/compression_test.rb +53 -0
- data/test/css/rule/file_name_test.rb +76 -0
- data/test/fixtures/css/broken.css +4 -0
- data/test/fixtures/css/cbu/36.css +52 -0
- data/test/fixtures/css/cbu/china_top.css +324 -0
- data/test/fixtures/css/cbu/default-merge.css +3 -0
- data/test/fixtures/css/cbu/default.css +13 -0
- data/test/fixtures/css/cbu/diy-merge.css +25 -0
- data/test/fixtures/css/cbu/fns-v1.css +27 -0
- data/test/fixtures/css/cbu/index_v0.1.css +12 -0
- data/test/fixtures/css/cbu/merge.css +11 -0
- data/test/fixtures/css/cbu/min.css +2 -0
- data/test/fixtures/css/cbu/my_home_admin.css +126 -0
- data/test/fixtures/css/cbu/nav.css +95 -0
- data/test/fixtures/css/cbu/pic_list.css +386 -0
- data/test/fixtures/css/cbu/quote-edit.css +18 -0
- data/test/fixtures/css/cbu/selloffer.shopwindow.css +1 -0
- data/test/fixtures/css/cbu/v1.css +9 -0
- data/test/fixtures/css/css3.css +30 -0
- data/test/fixtures/css/empty-min.css +0 -0
- data/test/fixtures/css/empty.css +0 -0
- data/test/fixtures/css/font-family.css +4 -0
- data/test/fixtures/css/gb-good.css +14 -0
- data/test/fixtures/css/gb_using_star.css +4 -0
- data/test/fixtures/css/import.css +18 -0
- data/test/fixtures/css/mac-line-sep-err-min.css +1 -0
- data/test/fixtures/css/mac-line-sep-err.css +1 -0
- data/test/fixtures/css/mac-line-sep-good-min.css +1 -0
- data/test/fixtures/css/mac-line-sep-good.css +1 -0
- data/test/fixtures/css/multi-encoding-in-a-file.css +0 -0
- data/test/fixtures/css/simple.css +1 -0
- data/test/fixtures/css/using_expr.css +8 -0
- data/test/fixtures/css/using_hack.css +21 -0
- data/test/fixtures/css/using_id.css +1 -0
- data/test/fixtures/css/using_star.css +4 -0
- data/test/fixtures/css/utf8_good.css +6 -0
- data/test/fixtures/css/utf8_good_declaring_charset.css +7 -0
- data/test/fixtures/css/utf8_using_star.css +5 -0
- data/test/fixtures/html/1-1.html +120 -0
- data/test/fixtures/html/1-2.html +120 -0
- data/test/fixtures/html/cms.html +373 -0
- data/test/fixtures/html/css_out_of_head.html +9 -0
- data/test/fixtures/html/fdev-template.html +22 -0
- data/test/fixtures/html/google.com.html +33 -0
- data/test/fixtures/html/mixed_log_levels.html +4 -0
- data/test/fixtures/html/mixed_types.html +13 -0
- data/test/fixtures/html/no_dtd.html +6 -0
- data/test/fixtures/html/readme.html +94 -0
- data/test/fixtures/html/review.board.html +163 -0
- data/test/fixtures/html/syntax_err.html +3 -0
- data/test/fixtures/html/train/detail/345/233/276/346/226/207/347/273/223/345/220/210.html +208 -0
- data/test/fixtures/html/train/detail/347/232/204Flash.html +212 -0
- data/test/fixtures/html/train/detail/347/232/204Vedio.html +212 -0
- data/test/fixtures/html/train/index.html +37 -0
- data/test/fixtures/html/train/test.html +1 -0
- 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
- data/test/fixtures/html/train//344/277/256/346/224/271/345/255/220/345/210/206/347/261/273.html +108 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/350/257/276/347/250/213.html +195 -0
- data/test/fixtures/html/train//345/215/232/345/256/242/350/256/276/347/275/256.html +142 -0
- data/test/fixtures/html/train//346/265/217/350/247/210/350/256/260/345/275/225.html +191 -0
- 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
- data/test/fixtures/html/train//346/267/273/345/212/240/345/255/220/345/210/206/347/261/273.html +112 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/350/257/276/347/250/213.html +195 -0
- data/test/fixtures/html/train//347/231/273/345/275/225.html +20 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/345/210/206/347/261/273.html +210 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/345/217/215/351/246/210.html +222 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/350/257/276/347/250/213.html +284 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/350/264/246/346/210/267.html +107 -0
- data/test/fixtures/html/train//347/275/221/344/270/212/345/237/271/350/256/255home/351/241/265.html +354 -0
- data/test/fixtures/html/train//347/275/221/345/225/206/345/237/271/350/256/255list/351/241/265.html +255 -0
- 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
- data/test/fixtures/html/train//350/257/264/346/230/216.txt +3 -0
- 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
- data/test/fixtures/html/unescaped.html +2 -0
- data/test/fixtures/html/view.vm +916 -0
- data/test/fixtures/js/jquery-1.7.js +9300 -0
- data/test/fixtures/js/scope-test.js +22 -0
- data/test/helper.rb +41 -0
- data/test/html/mixed_type_test.rb +35 -0
- data/test/html/parser/parse_comment_test.rb +47 -0
- data/test/html/parser/parse_dtd_test.rb +46 -0
- data/test/html/parser/parse_script_tag_test.rb +55 -0
- data/test/html/parser/parse_with_auto_close_tag_test.rb +41 -0
- data/test/html/parser/parse_with_diff_case_test.rb +38 -0
- data/test/html/parser/parse_with_emtpy_test.rb +22 -0
- data/test/html/parser/parse_with_multi_children_test.rb +27 -0
- data/test/html/parser/parse_with_multi_line_test.rb +41 -0
- data/test/html/parser/parse_with_prop_test.rb +88 -0
- data/test/html/parser/parse_with_script_tag_test.rb +26 -0
- data/test/html/parser/parse_with_selfclosing_test.rb +39 -0
- data/test/html/parser/parse_with_simple_tag_test.rb +44 -0
- data/test/html/parser/parse_with_simple_tree_test.rb +40 -0
- data/test/html/parser/parse_with_style_tag_test.rb +22 -0
- data/test/html/parser/parse_with_text_test.rb +45 -0
- data/test/html/parser_test.rb +52 -0
- data/test/html/query_test.rb +52 -0
- data/test/html/rule/check_block_level_element_test.rb +52 -0
- data/test/html/rule/check_button_test.rb +45 -0
- data/test/html/rule/check_class_count_test.rb +36 -0
- data/test/html/rule/check_css_in_head_test.rb +53 -0
- data/test/html/rule/check_dtd_test.rb +46 -0
- data/test/html/rule/check_form_element_name_test.rb +49 -0
- data/test/html/rule/check_head_contain_meta_and_title_test.rb +52 -0
- data/test/html/rule/check_html_template_test.rb +103 -0
- data/test/html/rule/check_hyperlink_with_target_test.rb +40 -0
- data/test/html/rule/check_hyperlink_with_title_test.rb +43 -0
- data/test/html/rule/check_id_n_class_downcase_test.rb +40 -0
- data/test/html/rule/check_img_with_alt_prop_test.rb +33 -0
- data/test/html/rule/check_no_import_css_test.rb +36 -0
- data/test/html/rule/check_prop_have_value_test.rb +32 -0
- data/test/html/rule/check_prop_seperator_test.rb +32 -0
- data/test/html/rule/check_style_prop_test.rb +30 -0
- data/test/html/rule/check_tag_closed_test.rb +59 -0
- data/test/html/rule/check_tag_downcase_test.rb +51 -0
- data/test/html/rule/check_unescape_char_test.rb +35 -0
- data/test/html/rule/check_unique_import_test.rb +56 -0
- data/test/html/rule_test.rb +62 -0
- data/test/js/expr/expr.rb +57 -0
- data/test/js/expr/left_hand.rb +25 -0
- data/test/js/expr/operate.rb +145 -0
- data/test/js/expr/primary.rb +89 -0
- data/test/js/parser_test.rb +98 -0
- data/test/js/rule/alert_check_test.rb +37 -0
- data/test/js/rule/all_test.rb +23 -0
- data/test/js/rule/base_test.rb +34 -0
- data/test/js/rule/file_checker_test.rb +131 -0
- data/test/js/rule/jq_check_test.rb +90 -0
- data/test/js/rule/nest_try_catch_test.rb +71 -0
- data/test/js/rule/new_object_and_new_array_test.rb +38 -0
- data/test/js/rule/no_eval_test.rb +34 -0
- data/test/js/rule/no_global_test.rb +88 -0
- data/test/js/rule/private_method_check_test.rb +58 -0
- data/test/js/rule/semicolon_test.rb +63 -0
- data/test/js/rule/stat_if_with_brace_test.rb +68 -0
- data/test/js/rule/stat_if_with_muti_else_test.rb +68 -0
- data/test/js/rule/use_strict_equal_test.rb +44 -0
- data/test/js/rule_test.rb +47 -0
- data/test/js/stat/if.rb +26 -0
- data/test/js/stat/iter.rb +115 -0
- data/test/js/stat/stat.rb +91 -0
- data/test/js/stat/switch.rb +37 -0
- data/test/js/stat/try.rb +32 -0
- data/test/js/stat/var.rb +38 -0
- data/test/parser_visitable_test.rb +102 -0
- data/test/position_info_test.rb +66 -0
- data/test/rule_dsl/dsl_basic_test.rb +91 -0
- data/test/rule_dsl/importing_test.rb +48 -0
- data/test/runner/log_level_test.rb +58 -0
- 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,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
|