fdlint 0.1.3 → 0.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -9
  3. data/Gemfile.lock +14 -4
  4. data/Rakefile +9 -85
  5. data/bin/fdlint +71 -8
  6. data/lib/fdlint/cli.rb +102 -0
  7. data/lib/{file_validator.rb → fdlint/file_validator.rb} +0 -0
  8. data/lib/fdlint/helper/code_type.rb +55 -0
  9. data/lib/fdlint/helper/file_reader.rb +19 -0
  10. data/lib/fdlint/helper/logger.rb +35 -0
  11. data/lib/fdlint/log_entry.rb +51 -0
  12. data/lib/fdlint/parser/base_parser.rb +151 -0
  13. data/lib/{css/parser.rb → fdlint/parser/css/css_parser.rb} +31 -28
  14. data/lib/{css → fdlint/parser/css}/struct.rb +5 -5
  15. data/lib/{encoding_error.rb → fdlint/parser/encoding_error.rb} +2 -2
  16. data/lib/{html/parser.rb → fdlint/parser/html/html_parser.rb} +26 -20
  17. data/lib/{html → fdlint/parser/html}/query.rb +0 -0
  18. data/lib/{html → fdlint/parser/html}/rule/check_tag_rule.rb +0 -0
  19. data/lib/{html → fdlint/parser/html}/struct.rb +82 -38
  20. data/lib/fdlint/parser/js/expr/expr.rb +71 -0
  21. data/lib/fdlint/parser/js/expr/left_hand.rb +65 -0
  22. data/lib/fdlint/parser/js/expr/operate.rb +94 -0
  23. data/lib/fdlint/parser/js/expr/primary.rb +168 -0
  24. data/lib/{js/parser.rb → fdlint/parser/js/js_parser.rb} +11 -9
  25. data/lib/fdlint/parser/js/stat/if.rb +27 -0
  26. data/lib/fdlint/parser/js/stat/iter.rb +88 -0
  27. data/lib/fdlint/parser/js/stat/stat.rb +120 -0
  28. data/lib/fdlint/parser/js/stat/switch.rb +67 -0
  29. data/lib/fdlint/parser/js/stat/try.rb +30 -0
  30. data/lib/fdlint/parser/js/stat/var.rb +42 -0
  31. data/lib/fdlint/parser/js/struct.rb +257 -0
  32. data/lib/fdlint/parser/node.rb +27 -0
  33. data/lib/fdlint/parser/parse_error.rb +10 -0
  34. data/lib/fdlint/parser/parser_visitable.rb +134 -0
  35. data/lib/{position_info.rb → fdlint/parser/position_info.rb} +12 -1
  36. data/lib/fdlint/parser.rb +3 -0
  37. data/lib/fdlint/printer/base_printer.rb +20 -0
  38. data/lib/fdlint/printer/console_printer.rb +80 -0
  39. data/lib/fdlint/printer/nocolor_printer.rb +29 -0
  40. data/lib/fdlint/printer/vim_printer.rb +21 -0
  41. data/lib/fdlint/printer.rb +1 -0
  42. data/lib/fdlint/rule/dsl.rb +83 -0
  43. data/lib/fdlint/rule/validation.rb +79 -0
  44. data/lib/fdlint/rule.rb +81 -0
  45. data/lib/fdlint/support/core/array.rb +5 -0
  46. data/lib/fdlint/support/core/file.rb +8 -0
  47. data/lib/fdlint/support/core/hash.rb +5 -0
  48. data/lib/fdlint/support/core/nil.rb +7 -0
  49. data/lib/fdlint/support/core/string.rb +7 -0
  50. data/lib/fdlint/support/core_ext.rb +5 -0
  51. data/lib/fdlint/validator.rb +102 -0
  52. data/lib/fdlint/version.rb +3 -0
  53. data/lib/fdlint.rb +5 -0
  54. data/rules.d/css.rule.rb +100 -0
  55. data/rules.d/filename.rule.rb +49 -0
  56. data/rules.d/html.rule.rb +169 -0
  57. data/rules.d/js.jquery.rule.rb +49 -0
  58. data/rules.d/js.rule.rb +205 -0
  59. data/test/default_test.rb +14 -0
  60. data/test/fixtures/js/scope-test.js +15 -2
  61. data/test/test_helper.rb +9 -0
  62. metadata +269 -221
  63. data/lib/base_parser.rb +0 -143
  64. data/lib/cmd_runner.rb +0 -145
  65. data/lib/context.rb +0 -31
  66. data/lib/css/reader.rb +0 -30
  67. data/lib/css/rule/check_compression_rule.rb +0 -48
  68. data/lib/css/rule/checklist.rb +0 -45
  69. data/lib/helper/code_type.rb +0 -50
  70. data/lib/helper/color_string.rb +0 -44
  71. data/lib/helper/file_reader.rb +0 -22
  72. data/lib/helper/strenc.rb +0 -65
  73. data/lib/js/expr/expr.rb +0 -66
  74. data/lib/js/expr/left_hand.rb +0 -63
  75. data/lib/js/expr/operate.rb +0 -92
  76. data/lib/js/expr/primary.rb +0 -166
  77. data/lib/js/rule/all.rb +0 -35
  78. data/lib/js/rule/checklist.rb +0 -41
  79. data/lib/js/rule/file_checker.rb +0 -42
  80. data/lib/js/rule/helper.rb +0 -96
  81. data/lib/js/rule/no_global.rb +0 -87
  82. data/lib/js/stat/if.rb +0 -25
  83. data/lib/js/stat/iter.rb +0 -85
  84. data/lib/js/stat/stat.rb +0 -117
  85. data/lib/js/stat/switch.rb +0 -65
  86. data/lib/js/stat/try.rb +0 -28
  87. data/lib/js/stat/var.rb +0 -40
  88. data/lib/js/struct.rb +0 -248
  89. data/lib/log_entry.rb +0 -49
  90. data/lib/node.rb +0 -28
  91. data/lib/parse_error.rb +0 -13
  92. data/lib/parser_visitable.rb +0 -138
  93. data/lib/printer/base_printer.rb +0 -24
  94. data/lib/printer/console_printer.rb +0 -66
  95. data/lib/printer/nocolor_printer.rb +0 -27
  96. data/lib/printer/vim_printer.rb +0 -19
  97. data/lib/rule.rb +0 -241
  98. data/lib/rule_helper.rb +0 -14
  99. data/lib/runner.rb +0 -225
  100. data/rules.d/css.rule +0 -127
  101. data/rules.d/html.dtd.rule +0 -22
  102. data/rules.d/html.prop.rule +0 -51
  103. data/rules.d/html.tag.rule +0 -136
  104. data/rules.d/js.file.rule +0 -13
  105. data/rules.d/js.jquery.rule +0 -56
  106. data/rules.d/js.mergefile.rule +0 -71
  107. data/rules.d/js.rule +0 -84
@@ -0,0 +1,79 @@
1
+ module Fdlint; module Rule
2
+
3
+ class Validation < Struct.new( :scope, :validate_block )
4
+
5
+ attr_accessor :desc, :uri, :group
6
+
7
+ def long_desc
8
+ if desc
9
+ uri ? desc + ", more info: #{uri}" : desc
10
+ end
11
+ end
12
+
13
+ def to_s
14
+ "<Validation #{scope} #{long_desc}>"
15
+ end
16
+
17
+ def inspect
18
+ to_s
19
+ end
20
+
21
+ def to_visitor( opt={} )
22
+ [
23
+ scope,
24
+ proc { |node, source, parser|
25
+ exec( node, source, opt[:file], parser )
26
+ }
27
+ ]
28
+ end
29
+
30
+ def exec( node, source, file = nil, parser = nil )
31
+ Runner.new.exec( node, source, file, parser, validate_block ) do |results|
32
+ results.map do |msg, level, opt|
33
+ pos = if node.respond_to?( :position ) && node.position
34
+ node.position
35
+ else
36
+ opt[:pos]
37
+ end
38
+
39
+ if pos
40
+ pos.column += opt[:column_offset] if opt[:column_offset]
41
+ pos.row += opt[:row_offset] if opt[:row_offset]
42
+ row, column = pos.row, pos.column
43
+ end
44
+
45
+ LogEntry.new( msg, level, row, column ).tap do |entry|
46
+ entry.validation = self
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ class Runner
53
+
54
+ def exec( node, source, file, parser, validate_block, &block )
55
+ instance_exec( node, source, file, parser, &validate_block )
56
+ yield results if block_given?
57
+ end
58
+
59
+ def results
60
+ @results ||= []
61
+ end
62
+
63
+ def fatal( msg, opt={} )
64
+ results << [msg, :fatal, opt]
65
+ end
66
+
67
+ def error( msg, opt={} )
68
+ results << [msg, :error, opt]
69
+ end
70
+
71
+ def warn( msg, opt={} )
72
+ results << [msg, :warn, opt]
73
+ end
74
+
75
+
76
+ end
77
+ end
78
+
79
+ end; end
@@ -0,0 +1,81 @@
1
+ require 'fdlint/rule/validation'
2
+ require 'fdlint/rule/dsl'
3
+
4
+ module Fdlint
5
+
6
+ module Rule
7
+
8
+ RULE_PATH = File.expand_path '../../rules.d', File.dirname(__FILE__)
9
+
10
+ class << self
11
+
12
+ include ::Fdlint::Helper::Logger
13
+
14
+ SYNTAXES = [:css, :html, :js]
15
+
16
+ # Public: Rules for file validation
17
+ #
18
+ # opt - options including syntax
19
+ #
20
+ # Returns an Array holding all validations for this file
21
+ def validations_for_file( opt={} )
22
+ (all[opt[:code_type]] || []).select do |validation|
23
+ validation.scope == :file
24
+ end
25
+ end
26
+
27
+ alias_method :for_file, :validations_for_file
28
+
29
+ SYNTAXES.each do |syntax|
30
+ # Public: Rules for css content validation
31
+ #
32
+ # Returns an Array holding all rules for content validation.
33
+ define_method "validations_for_#{syntax}_content" do
34
+ (all[syntax] || []).select do |validation|
35
+ validation.scope != :file
36
+ end
37
+ end
38
+
39
+ alias_method :"for_#{syntax}_content", :"validations_for_#{syntax}_content"
40
+ end
41
+
42
+ def all
43
+ @rules ||= {}
44
+ import unless @imported
45
+ @rules
46
+ end
47
+
48
+ alias_method :rules, :all
49
+
50
+ def import
51
+ debug { "importing rules" }
52
+ Dir.glob( File.join RULE_PATH, '**', '*.rule.rb' ) do |rule|
53
+ begin
54
+ DSL.instance_eval File.read(rule), rule
55
+ rescue SyntaxError, NoMethodError => e
56
+ fatal { "Error parsing rule file:".red }
57
+ fatal { " -> #{rule}" }
58
+ fatal { "" }
59
+ raise
60
+ end
61
+ end
62
+ @imported = true
63
+ debug { "done" }
64
+ end
65
+
66
+ def add( code_type, scope, opt )
67
+ debug { " -> adding rule: #{code_type.inspect} - #{scope.inspect} - #{opt[:desc]}" }
68
+ cache = @rules[code_type] ||= []
69
+ cache << Validation.new( scope, opt[:block] ).tap do |v|
70
+ v.desc = opt[:desc]
71
+ v.uri = opt[:uri]
72
+ end
73
+ @rules
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
@@ -0,0 +1,5 @@
1
+ class Array
2
+
3
+ alias_method :blank?, :empty?
4
+
5
+ end
@@ -0,0 +1,8 @@
1
+ class File
2
+
3
+ def name
4
+ File.basename( path )
5
+ end
6
+
7
+ end
8
+
@@ -0,0 +1,5 @@
1
+ class Hash
2
+
3
+ alias_method :blank?, :empty?
4
+
5
+ end
@@ -0,0 +1,7 @@
1
+ class NilClass
2
+
3
+ def blank?
4
+ true
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class String
2
+
3
+ def blank?
4
+ !(self =~ /\S/)
5
+ end
6
+
7
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'core/nil'
2
+ require_relative 'core/string'
3
+ require_relative 'core/array'
4
+ require_relative 'core/hash'
5
+ require_relative 'core/file'
@@ -0,0 +1,102 @@
1
+ require 'fdlint/rule'
2
+ require 'fdlint/helper/code_type'
3
+ require 'fdlint/parser'
4
+ require 'fdlint/parser/js/js_parser'
5
+ require 'fdlint/parser/html/html_parser'
6
+ require 'fdlint/parser/css/css_parser'
7
+
8
+ module Fdlint
9
+
10
+ class Validator
11
+
12
+ attr_reader :file, :source, :results, :code_type
13
+ include Fdlint::Helper::Logger
14
+
15
+ def initialize( path = nil, options = {} )
16
+ @file = path
17
+ @source = options[:text]
18
+ @code_type = options[:code_type] || Helper::CodeType.guess( source, file )
19
+ end
20
+
21
+ def validate
22
+ @results = []
23
+
24
+ begin
25
+ @source ||= read_file
26
+
27
+ validate_file if file
28
+ validate_content
29
+ rescue EncodingError
30
+ results << InvalidFileEncoding.new
31
+ end
32
+
33
+ yield file, source, results
34
+ end
35
+
36
+ def validate_file
37
+ debug { "validating file: " << file }
38
+ file = File.new( self.file )
39
+ entries = file_level_rules.map do |validation|
40
+ validation.exec( file, source, file )
41
+ end
42
+ self << entries.flatten.compact
43
+ end
44
+
45
+ def validate_content
46
+ if source.valid_encoding?
47
+ self << parse.flatten
48
+ else
49
+ self << [InvalidFileEncoding.new]
50
+ end
51
+ end
52
+
53
+ protected
54
+
55
+ def << results
56
+ @results ||= []
57
+ if results
58
+ @results.concat results
59
+ end
60
+ @results
61
+ end
62
+
63
+ def read_file
64
+ ::Fdlint::Helper::FileReader.readfile( file, force_utf8: true )
65
+ end
66
+
67
+ def parse
68
+ parser.parse_no_throw
69
+ parser.results
70
+ end
71
+
72
+ def parser
73
+ @parser ||= build_parser
74
+ end
75
+
76
+ def build_parser
77
+ base_parser = {
78
+ :js => ::Fdlint::Parser::JS::JsParser,
79
+ :css => ::Fdlint::Parser::CSS::CssParser,
80
+ :html => ::Fdlint::Parser::HTML::HtmlParser
81
+ }.fetch( code_type ).new( source )
82
+
83
+ file = File.new(self.file) if self.file
84
+
85
+ base_parser.tap do |parser|
86
+ content_level_rules.each do |validation|
87
+ parser.add_visitor *validation.to_visitor( file: file )
88
+ end
89
+ end
90
+ end
91
+
92
+ def content_level_rules
93
+ Fdlint::Rule.send( :"for_#{code_type}_content" )
94
+ end
95
+
96
+ def file_level_rules
97
+ Fdlint::Rule.for_file( :code_type => code_type )
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,3 @@
1
+ module Fdlint
2
+ VERSION = '0.2.0.pre'
3
+ end
data/lib/fdlint.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'fdlint/version'
2
+ require 'fdlint/support/core_ext'
3
+ require 'fdlint/helper/logger'
4
+ require 'fdlint/validator'
5
+ require 'fdlint/printer'
@@ -0,0 +1,100 @@
1
+ # CSS review rules
2
+ # Author: @qhwa
3
+
4
+ css_rules
5
+
6
+ review( 'selector' ) {
7
+
8
+ desc "页面级别样式请用class选择符代替id选择符,以免在重用时影响其他页面"
9
+ rule { |selector, source, file|
10
+ if file && file.page_level? && selector =~ /^#/
11
+ error '页面级别样式不使用id'
12
+ end
13
+ }
14
+
15
+ desc "页面级别的样式如果使用 body、p 等全局标签样式,会增加复用难度"
16
+ rule { |selector, source, file|
17
+ if file && file.page_level? && selector =~ /^\w+$/
18
+ error '页面级别样式不能全局定义标签样式'
19
+ end
20
+ }
21
+
22
+ desc "过多的 '>' 层级会影响性能"
23
+ rule { |selector|
24
+ if selector.text.split(/[>\s]/).length > 4
25
+ error 'CSS级联深度不能超过4层'
26
+ end
27
+ }
28
+
29
+ rule { |selector|
30
+ if selector =~ /^\*/
31
+ error '禁止使用星号选择符'
32
+ end
33
+ }
34
+
35
+ rule { |selector|
36
+ if selector =~ /\S\*/
37
+ error '合理使用hack'
38
+ end
39
+ }
40
+ }
41
+
42
+ review( 'declaration' ) {
43
+ rule { |declaration|
44
+ if declaration.property =~ /^font(-family)?$/ && declaration.value =~ /\p{Han}+/u
45
+ error '字体名称中的中文必须用ascii字符表示'
46
+ end
47
+ }
48
+ }
49
+
50
+ review( 'ruleset' ) {
51
+
52
+ uri 'http://wd.alibaba-inc.com/doc/page/regulations/css'
53
+ rule { |ruleset|
54
+ list = [
55
+ %w(position display visible z-index overflow float clear),
56
+ %w(width height top right bottom left margin padding border),
57
+ %w(background opacity),
58
+ %w(font color text line-height vertical-align)
59
+ ]
60
+
61
+ now = 0
62
+ ruleset.declarations.each do |dec|
63
+ prop_text = dec.property.text
64
+ index = list.find_index do |bag|
65
+ bag.any? { |field| prop_text.include? field }
66
+ end
67
+
68
+ if index
69
+ if index < now
70
+ warn '建议使用Mozilla推荐CSS书写顺序'
71
+ break
72
+ end
73
+ now = index
74
+ end
75
+ end
76
+ nil
77
+ }
78
+ }
79
+
80
+ review( 'property' ) {
81
+ rule { |property|
82
+ if property =~ /[^-a-z]/
83
+ error '合理使用hack'
84
+ end
85
+ }
86
+ }
87
+
88
+ review( 'value' ) {
89
+ rule { |value|
90
+
91
+ if value =~ /^expression\(/
92
+ error '禁止使用CSS表达式'
93
+ end
94
+
95
+ if value =~ /\\\d$/
96
+ error '合理使用hack'
97
+ end
98
+ }
99
+ }
100
+
@@ -0,0 +1,49 @@
1
+ rules_for 'css', 'js'
2
+
3
+ review( 'file' ) {
4
+ rule { |file|
5
+ if file.name =~ /(?:[^a-z0-9%_-]|^)
6
+ #以ad开头,与后面文字组合,构成常见的广告单词
7
+ ad
8
+ (?:
9
+ [sv][^a-z\r=\?]+
10
+ |[^a-z\r_-]*[\.\/]
11
+ |bot|c_|client|council|gifs|graph|images|img
12
+ |fshow|pic|vert|view|info|click|sponsor|banner
13
+ |click|ver|name|x|log|
14
+ )/x
15
+ error '路径和文件名中不应该出现ad'
16
+ end
17
+ }
18
+
19
+ desc '使用规范的文件名可以避免不同操作系统上的编码问题'
20
+ uri '#file-name'
21
+ rule { |file|
22
+
23
+ unless file.name =~ /\A[_\-a-zA-Z0-9\.]+\Z/
24
+ error "文件名只能是英文字符"
25
+ end
26
+
27
+ if file.name =~ /[_]/
28
+ warn "文件名中的连字符建议使用“-”而不是“_”"
29
+ end
30
+
31
+ win_disk = /^[a-zA-Z]:/
32
+ if file.name.sub( win_disk, '') =~ /[A-Z]/
33
+ error '文件夹和文件命名必须用小写字母'
34
+ end
35
+ }
36
+
37
+ }
38
+
39
+ helpers( 'file' ) {
40
+ def page_level?
41
+ !library_level?
42
+ end
43
+
44
+ def library_level?
45
+ name =~ %r{/lib/}
46
+ end
47
+ }
48
+
49
+
@@ -0,0 +1,169 @@
1
+ # encoding: utf-8
2
+ html_rules
3
+
4
+ review( 'doc' ) { |doc|
5
+ unless doc.have_dtd?
6
+ error "必须存在文档类型声明"
7
+ end
8
+ }
9
+
10
+ review( 'dtd' ) { |dtd|
11
+ unless dtd.type =~ /^html$/i
12
+ warn '推荐使用HTML 5 DTD'
13
+ end
14
+
15
+ unless dtd =~ /^<!DOCTYPE/
16
+ warn '必须使用大写的"DOCTYPE"'
17
+ end
18
+ }
19
+
20
+ review( 'property' ) { |prop|
21
+
22
+ # Bad case: <div style="padding:0">...</div>
23
+ if prop =~ /^style$/im
24
+ error "不能定义内嵌样式style"
25
+ end
26
+
27
+ if prop =~ /[A-Z_]/
28
+ error "属性名必须小写,连字符用中横线"
29
+ end
30
+
31
+ if prop =~ 'id' and prop.value =~ /[A-Z]/
32
+ error "id名称全部小写,单词分隔使用中横线"
33
+ end
34
+
35
+ if prop =~ 'class' and prop.value =~ /[A-Z]/
36
+ error "class名称全部小写,单词分隔使用中横线"
37
+ end
38
+
39
+ if prop.value and prop.sep != '"'
40
+ error "属性值必须使用双引号"
41
+ end
42
+
43
+ if prop.value.nil?
44
+ error "不能仅有属性名"
45
+ end
46
+
47
+ }
48
+
49
+ review( 'tag' ) { |tag|
50
+
51
+ if tag =~ /[A-Z]/ || tag.ending =~ /[A-Z]/
52
+ error "标签名必须小写"
53
+ end
54
+
55
+ if tag =~ 'img'
56
+ unless tag.has_prop?( 'alt' )
57
+ error "img标签必须加上alt属性"
58
+ end
59
+ end
60
+
61
+ if tag =~ 'a' and tag['href'] =~ /^[^#]/
62
+ unless tag['title']
63
+ error '非功能能点的a标签必须加上title属性'
64
+ end
65
+ end
66
+ }
67
+
68
+ review( 'tag' ) {
69
+
70
+ # <base target='_self'>
71
+ after( 'tag' ) { |tag|
72
+ if tag =~ 'base' and tag.prop_value('target')
73
+ @has_target = true
74
+ end
75
+ }
76
+
77
+ rule { |tag|
78
+ unless @has_target
79
+ if tag =~ 'a' and tag.prop_value('href') =~ /^#/ and tag.prop_value(:target) != '_self'
80
+ warn '功能a必须加target="_self",除非preventDefault过'
81
+ end
82
+ end
83
+ }
84
+
85
+ }
86
+
87
+ review( 'tag' ) {
88
+
89
+ after( 'tag' ) {|tag|
90
+ if tag =~ 'script'
91
+ src = tag['src'].to_s
92
+ if !src.empty?
93
+ @scripts_used ||= []
94
+ @scripts_used << src
95
+ end
96
+ elsif tag.stylesheet_link?
97
+ src = tag['href'].to_s
98
+ if !src.empty?
99
+ @styles_used ||= []
100
+ @styles_used << src
101
+ end
102
+ end
103
+ }
104
+
105
+ rule { |tag|
106
+ if tag =~ 'script'
107
+ src = tag['src'].to_s
108
+ if !src.empty? && (@script_used || []).include?( src )
109
+ error "避免重复引用同一或相同功能文件"
110
+ end
111
+ elsif tag.stylesheet_link?
112
+ src = tag['src'].to_s
113
+ if !src.empty? && (@styles_used || []).include?( src )
114
+ error "避免重复引用同一或相同功能文件"
115
+ end
116
+ end
117
+ }
118
+ }
119
+
120
+
121
+ review( 'tag' ) { |tag|
122
+
123
+ if tag =~ 'style' and tag.inner_text =~ /@import\s/m
124
+ error "不通过@import在页面上引入CSS"
125
+ end
126
+
127
+ if tag =~ 'head'
128
+
129
+ children = tag.children
130
+ has_meta = children.any? { |e| e =~ 'meta' and e['charset'] }
131
+ has_title = children.any? { |e| e =~ 'title' }
132
+
133
+ unless has_meta and has_title
134
+ error "head必须包含字符集meta和title"
135
+ end
136
+ end
137
+
138
+ if !(tag =~ 'a') and tag.inline? and tag.children.any? { |e| !e.inline? }
139
+ error "行内标签不得包含块级标签,a标签例外"
140
+ end
141
+
142
+ if (tag =~ 'input') and tag['type'].to_s =~ /text|radio|checkbox/ or
143
+ tag =~ /^(select|textarea)$/
144
+
145
+ if tag['name'].blank?
146
+ error "text、radio、checkbox、textarea、select必须加name属性"
147
+ end
148
+ end
149
+
150
+ if tag =~ /input/ and tag['type'].to_s =~ /button|submit|reset/
151
+ error "所有按钮必须用button(button/submit/reset)"
152
+ end
153
+
154
+ if tag.stylesheet_link?
155
+ if tag.has_scope? and !tag.in_scope?('head')
156
+ error "外链CSS置于head里(例外:应用里的footer样式)"
157
+ end
158
+ end
159
+
160
+ if !tag.closed?
161
+ error "标签必须正确闭合"
162
+ end
163
+ }
164
+
165
+ review( 'text_tag' ) do |text_tag|
166
+ if text_tag.text =~ /[<>]/
167
+ error "特殊HTML符号(>和<)必须转义"
168
+ end
169
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ # vi: filetype=ruby
3
+
4
+ js_rules
5
+
6
+ JQ_IDS = %w(jQuery $ jQ jq)
7
+
8
+ review( 'expr_member' ) {
9
+
10
+ desc '闭包写法:"(function($, NS){....})(jQuery,Namespace);"'
11
+ rule { |expr|
12
+ if %w{. ( [}.include?(expr.type) && expr.left.text == "jQuery"
13
+ unless expr.type == "." && expr.right.text == 'namespace'
14
+ error '禁止直接使用jQuery变量,使用全局闭包写法,jQuery.namespace例外'
15
+ end
16
+
17
+ end
18
+ }
19
+
20
+ rule { |expr|
21
+ if expr.type == '.' &&
22
+ JQ_IDS.include?( expr.left.text ) &&
23
+ %w[sub noConflict].include?( expr.right.text )
24
+
25
+ error '禁止使用jQuery.sub()和jQuery.noConflict方法'
26
+ end
27
+ }
28
+
29
+ rule { |expr|
30
+ if expr.type == '('
31
+ name = expr.left
32
+ if name.type == '.' && name.right.text == 'data'
33
+ param = expr.right[0]
34
+ if param && param.text =~ /[-_]/
35
+ error '使用".data()"读写自定义属性时需要转化成驼峰形式'
36
+ end
37
+ end
38
+ end
39
+ }
40
+
41
+ rule { |expr|
42
+ if expr.type == '(' && JQ_IDS.include?(expr.left.text)
43
+ param = expr.right[0]
44
+ if param && param.type == 'string' && param.text !~ /^['"][#\w<]/
45
+ warn '使用选择器时,能确定tagName的,必须加上tagName'
46
+ end
47
+ end
48
+ }
49
+ }