jsduck 5.0.0.beta2 → 5.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/Rakefile +14 -4
  2. data/bin/jsduck +3 -1
  3. data/jsduck.gemspec +2 -2
  4. data/lib/jsduck/app.rb +8 -0
  5. data/lib/jsduck/assets.rb +3 -0
  6. data/lib/jsduck/batch_processor.rb +2 -0
  7. data/lib/jsduck/categories/class_name.rb +2 -26
  8. data/lib/jsduck/categories/factory.rb +5 -43
  9. data/lib/jsduck/columns.rb +56 -0
  10. data/lib/jsduck/doc/delimited_parser.rb +105 -0
  11. data/lib/jsduck/doc/scanner.rb +2 -1
  12. data/lib/jsduck/doc/standard_tag_parser.rb +37 -71
  13. data/lib/jsduck/guide_anchors.rb +32 -0
  14. data/lib/jsduck/guide_toc.rb +49 -0
  15. data/lib/jsduck/guides.rb +14 -32
  16. data/lib/jsduck/inline/video.rb +2 -8
  17. data/lib/jsduck/js/ast.rb +13 -305
  18. data/lib/jsduck/js/class.rb +245 -0
  19. data/lib/jsduck/js/event.rb +34 -0
  20. data/lib/jsduck/js/fires.rb +42 -0
  21. data/lib/jsduck/js/method.rb +94 -0
  22. data/lib/jsduck/js/method_calls.rb +40 -0
  23. data/lib/jsduck/js/node.rb +29 -0
  24. data/lib/jsduck/js/property.rb +64 -0
  25. data/lib/jsduck/js/{function.rb → returns.rb} +8 -3
  26. data/lib/jsduck/js/scoped_traverser.rb +42 -0
  27. data/lib/jsduck/logger.rb +13 -1
  28. data/lib/jsduck/merger.rb +34 -27
  29. data/lib/jsduck/news.rb +128 -0
  30. data/lib/jsduck/options.rb +59 -2
  31. data/lib/jsduck/params_merger.rb +47 -0
  32. data/lib/jsduck/process/accessors.rb +8 -2
  33. data/lib/jsduck/process/fires.rb +71 -0
  34. data/lib/jsduck/process/importer.rb +19 -1
  35. data/lib/jsduck/render/class.rb +11 -4
  36. data/lib/jsduck/render/signature_util.rb +14 -0
  37. data/lib/jsduck/tag/alias.rb +0 -20
  38. data/lib/jsduck/tag/alternate_class_names.rb +0 -5
  39. data/lib/jsduck/tag/cfg.rb +30 -5
  40. data/lib/jsduck/tag/class.rb +45 -2
  41. data/lib/jsduck/tag/css_mixin.rb +8 -4
  42. data/lib/jsduck/tag/css_var.rb +26 -5
  43. data/lib/jsduck/tag/default.rb +2 -8
  44. data/lib/jsduck/tag/enum.rb +7 -10
  45. data/lib/jsduck/tag/event.rb +12 -4
  46. data/lib/jsduck/tag/extends.rb +0 -6
  47. data/lib/jsduck/tag/fires.rb +53 -0
  48. data/lib/jsduck/tag/icons/cfg.png +0 -0
  49. data/lib/jsduck/tag/icons/css_mixin.png +0 -0
  50. data/lib/jsduck/tag/icons/css_var.png +0 -0
  51. data/lib/jsduck/tag/icons/event.png +0 -0
  52. data/lib/jsduck/tag/icons/method.png +0 -0
  53. data/lib/jsduck/tag/icons/property.png +0 -0
  54. data/lib/jsduck/tag/member_tag.rb +130 -0
  55. data/lib/jsduck/tag/method.rb +44 -4
  56. data/lib/jsduck/tag/param.rb +8 -60
  57. data/lib/jsduck/tag/property.rb +28 -5
  58. data/lib/jsduck/tag/tag.rb +3 -75
  59. data/lib/jsduck/tag/type.rb +1 -11
  60. data/lib/jsduck/tag_registry.rb +6 -48
  61. data/lib/jsduck/web/css.rb +8 -1
  62. data/lib/jsduck/web/data.rb +2 -1
  63. data/lib/jsduck/web/index_html.rb +1 -0
  64. data/lib/jsduck/web/member_icons.rb +43 -0
  65. data/lib/jsduck/web/search.rb +3 -2
  66. data/lib/jsduck/web/writer.rb +8 -0
  67. metadata +31 -27
  68. data/lib/jsduck/docs_code_comparer.rb +0 -44
  69. data/lib/jsduck/render/signature.rb +0 -94
  70. data/lib/jsduck/tag/autodetected.rb +0 -21
  71. data/lib/jsduck/tag/name.rb +0 -36
@@ -80,6 +80,35 @@ module JsDuck
80
80
  end
81
81
  end
82
82
 
83
+ # Returns the type of node.
84
+ def type
85
+ @node["type"]
86
+ end
87
+
88
+ # Extracts all sub-statements and sub-expressions from AST node.
89
+ # Without looking at the type of node, we just take all the
90
+ # sub-hashes and -arrays.
91
+ #
92
+ # A downside of this simple algorithm is that the statements can
93
+ # end up in different order than they are in source code. For
94
+ # example the IfStatement has three parts in the following
95
+ # order: "test", "consequent", "alternate": But because we're
96
+ # looping over a hash, they might end up in a totally different
97
+ # order.
98
+ def body
99
+ body = []
100
+ @node.each_pair do |key, value|
101
+ if key == "type" || key == "range"
102
+ # ignore
103
+ elsif value.is_a?(Array)
104
+ body.concat(value.map {|v| Js::Node.create(v) })
105
+ elsif value.is_a?(Hash)
106
+ body << Js::Node.create(value)
107
+ end
108
+ end
109
+ body
110
+ end
111
+
83
112
  # Iterates over keys and values in ObjectExpression. The keys
84
113
  # are turned into strings, but values are left as is for further
85
114
  # processing.
@@ -0,0 +1,64 @@
1
+ require "jsduck/util/singleton"
2
+
3
+ module JsDuck
4
+ module Js
5
+
6
+ # Auto-detection of properties.
7
+ class Property
8
+ include Util::Singleton
9
+
10
+ # Checks if AST node is a property, and if so, returns doc-hash
11
+ # with property name and various auto-detected attributes.
12
+ # When not a property returns nil.
13
+ def detect(ast)
14
+ exp = ast.expression_statement? ? ast["expression"] : nil
15
+ var = ast.variable_declaration? ? ast["declarations"][0] : nil
16
+
17
+ # foo = ...
18
+ if exp && exp.assignment_expression?
19
+ make(exp["left"].to_s, exp["right"])
20
+
21
+ # var foo = ...
22
+ elsif var
23
+ make(var["id"].to_s, var["init"])
24
+
25
+ # foo: ...
26
+ elsif ast.property?
27
+ make(ast["key"].key_value, ast["value"])
28
+
29
+ # foo;
30
+ elsif exp && exp.identifier?
31
+ make(exp.to_s)
32
+
33
+ # "foo" (inside some expression)
34
+ elsif ast.string?
35
+ make(ast.to_value)
36
+
37
+ # "foo"; (as a statement of it's own)
38
+ elsif exp && exp.string?
39
+ make(exp.to_value)
40
+
41
+ else
42
+ nil
43
+ end
44
+ end
45
+
46
+ # Produces a doc-hash for a property.
47
+ def make(name=nil, ast=nil)
48
+ return {
49
+ :tagname => :property,
50
+ :name => name,
51
+ :type => ast && ast.value_type,
52
+ :default => ast && default(ast),
53
+ }
54
+ end
55
+
56
+ private
57
+
58
+ def default(ast)
59
+ ast.to_value != nil ? ast.to_s : nil
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -3,10 +3,15 @@ require "jsduck/util/singleton"
3
3
  module JsDuck
4
4
  module Js
5
5
 
6
- # Analyzes the AST of a FunctionDeclaration or FunctionExpression.
7
- class Function
6
+ # Analyzes the AST of a Function for possible return values.
7
+ class Returns
8
8
  include Util::Singleton
9
9
 
10
+ # True when function always finishes with returning this.
11
+ def chainable?(ast)
12
+ detect(ast) == [:this]
13
+ end
14
+
10
15
  # Detects possible return types of the given function.
11
16
  #
12
17
  # For now there are three possible detected return values:
@@ -18,7 +23,7 @@ module JsDuck
18
23
  #
19
24
  # * :other - some other value is returned.
20
25
  #
21
- def return_types(ast)
26
+ def detect(ast)
22
27
  h = return_types_hash(ast["body"]["body"])
23
28
 
24
29
  # Replace the special :void value that signifies possibility of
@@ -0,0 +1,42 @@
1
+ module JsDuck
2
+ module Js
3
+
4
+ # Traverses syntax tree while keeping track of variables that are
5
+ # bound to `this`.
6
+ class ScopedTraverser
7
+ def initialize
8
+ @this_map = {
9
+ "this" => true
10
+ }
11
+ end
12
+
13
+ # Loops recursively over all the sub-nodes of the given node,
14
+ # calling the provided block for each sub-node.
15
+ def traverse(node, &block)
16
+ node.body.each do |child|
17
+ yield child
18
+
19
+ if this_var?(child)
20
+ var_name = child["id"].to_s
21
+ @this_map[var_name] = true
22
+ end
23
+
24
+ traverse(child, &block)
25
+ end
26
+ end
27
+
28
+ # True when variable with given name is bound to `this`.
29
+ def this?(var_name)
30
+ @this_map[var_name]
31
+ end
32
+
33
+ private
34
+
35
+ # True when initialization of variable with `this`
36
+ def this_var?(node)
37
+ node.type == "VariableDeclarator" && node["init"].type == "ThisExpression"
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -10,8 +10,14 @@ module JsDuck
10
10
  # Set to true to enable verbose logging
11
11
  attr_accessor :verbose
12
12
 
13
+ # Set true to force colored output.
14
+ # Set false to force no colors.
15
+ attr_accessor :colors
16
+
13
17
  def initialize
14
18
  @verbose = false
19
+ @colors = nil
20
+
15
21
  @warning_docs = [
16
22
  [:global, "Member doesn't belong to any class"],
17
23
  [:inheritdoc, "@inheritdoc referring to unknown class or member"],
@@ -36,6 +42,7 @@ module JsDuck
36
42
  [:type_syntax, "Syntax error in {type definition}"],
37
43
  [:type_name, "Unknown type referenced in {type definition}"],
38
44
  [:enum, "Enum with invalid values or no values at all"],
45
+ [:fires, "@fires references unknown event"],
39
46
 
40
47
  [:image, "{@img} referring to missing file"],
41
48
  [:image_unused, "An image exists in --images dir that's not used"],
@@ -173,6 +180,11 @@ module JsDuck
173
180
  $stderr.puts error.backtrace
174
181
  end
175
182
 
183
+ # True when at least one warning was logged.
184
+ def warnings_logged?
185
+ @shown_warnings.length > 0
186
+ end
187
+
176
188
  private
177
189
 
178
190
  COLORS = {
@@ -193,7 +205,7 @@ module JsDuck
193
205
  # Only does color output when STDERR is attached to TTY
194
206
  # i.e. is not piped/redirected.
195
207
  def paint(color_name, msg)
196
- if Util::OS.windows? || !$stderr.tty?
208
+ if @colors == false || @colors == nil && (Util::OS.windows? || !$stderr.tty?)
197
209
  msg
198
210
  else
199
211
  COLORS[color_name] + msg + CLEAR
@@ -14,15 +14,17 @@ module JsDuck
14
14
  # producing hash as a result.
15
15
  def merge(docset, filename="", linenr=0)
16
16
  docs = docset[:comment]
17
- code = docset[:code]
17
+ code = process_code(docset[:tagname], docset[:code])
18
18
 
19
19
  h = {
20
20
  :tagname => docset[:tagname],
21
+ :name => docs[:name] || code[:name] || "",
22
+ :autodetected => code[:autodetected] || {},
21
23
  :files => [{:filename => filename, :linenr => linenr}],
22
24
  }
23
25
 
24
- invoke_merge_in_tags(h, docs, code)
25
26
  general_merge(h, docs, code)
27
+ invoke_merge_in_member_tag(h, docs, code)
26
28
 
27
29
  # Needs to be calculated last, as it relies on the existance of
28
30
  # :name, :static and :tagname fields.
@@ -33,42 +35,47 @@ module JsDuck
33
35
 
34
36
  private
35
37
 
36
- # Invokes the #merge methods of tags registered for the given
37
- # merge context.
38
- def invoke_merge_in_tags(h, docs, code)
39
- TagRegistry.mergers(h[:tagname]).each do |tag|
40
- tag.merge(h, docs, code)
41
- end
38
+ # Applies processing to extract fields relevant to the member type.
39
+ def process_code(tagname, code)
40
+ TagRegistry.get_by_name(tagname).process_code(code)
41
+ end
42
+
43
+ # Invokes the #merge method in corresponding member or :class tag.
44
+ def invoke_merge_in_member_tag(h, docs, code)
45
+ TagRegistry.get_by_name(h[:tagname]).merge(h, docs, code)
42
46
  end
43
47
 
44
48
  # Applies default merge algorithm to the rest of the data.
45
49
  def general_merge(h, docs, code)
46
- # Merge in all items in docs that don't occour already in result.
50
+ # Add all items in docs not already in result.
47
51
  docs.each_pair do |key, value|
48
- h[key] = value unless h.has_key?(key) || Merger::explicit?(key)
52
+ h[key] = value unless h[key]
49
53
  end
50
- # Then add all in the items from code not already in result.
51
- code.each_pair do |key, value|
52
- h[key] = value unless h.has_key?(key) || Merger::explicit?(key)
54
+
55
+ # Add all items in code not already in result and mark them as
56
+ # auto-detected. But only if the explicit and auto-detected
57
+ # names don't conflict.
58
+ if Merger.can_be_autodetected?(docs, code)
59
+ code.each_pair do |key, value|
60
+ unless h[key]
61
+ h[key] = value
62
+ mark_autodetected(h, key)
63
+ end
64
+ end
53
65
  end
54
66
  end
55
67
 
56
- # True when given key gets merged explicitly and should therefore
57
- # be skipped when auto-merging.
58
- def self.explicit?(key)
59
- @explicit = explictly_merged_fields unless @explicit
60
- @explicit[key]
68
+ # True if the name detected from code matches with explicitly
69
+ # documented name. Also true when no explicit name documented.
70
+ #
71
+ # Note: This method is also called from ParamsMerger.
72
+ def self.can_be_autodetected?(docs, code)
73
+ docs[:name] == nil || docs[:name] == code[:name]
61
74
  end
62
75
 
63
- # Generates a lookup-hash of tagnames which are explicitly merged.
64
- def self.explictly_merged_fields
65
- mergers = {}
66
- member_types = TagRegistry.member_type_names + [:class]
67
- tags = member_types.map {|type| TagRegistry.mergers(type) }.flatten.uniq
68
- tags.each do |tag|
69
- mergers[tag.tagname] = true
70
- end
71
- mergers
76
+ # Stores the key as flag into h[:autodetected]
77
+ def mark_autodetected(h, key)
78
+ h[:autodetected][key] = true
72
79
  end
73
80
 
74
81
  end
@@ -0,0 +1,128 @@
1
+ require 'jsduck/util/null_object'
2
+ require 'jsduck/columns'
3
+
4
+ module JsDuck
5
+
6
+ class News
7
+ # Creates News object from relations data when --import option
8
+ # specified.
9
+ def self.create(relations, doc_formatter, opts)
10
+ if opts[:imports].length > 0
11
+ News.new(relations, doc_formatter)
12
+ else
13
+ Util::NullObject.new(:to_html => "")
14
+ end
15
+ end
16
+
17
+ # Generates list of new classes & members in this version.
18
+ def initialize(relations, doc_formatter)
19
+ @doc_formatter = doc_formatter
20
+ @columns = Columns.new(:members)
21
+ @new_items = filter_new_items(relations)
22
+ end
23
+
24
+ # Returns the HTML
25
+ def to_html(style="")
26
+ return [
27
+ "<div id='news-content' style='#{style}'>",
28
+ "<div class='section'>",
29
+ "<h1>New in this version</h1>",
30
+ render_columns(@new_items),
31
+ "<div style='clear:both'></div>",
32
+ "</div>",
33
+ "</div>",
34
+ ].flatten.join("\n")
35
+ end
36
+
37
+ private
38
+
39
+ def filter_new_items(relations)
40
+ classes = []
41
+ new_items = []
42
+
43
+ relations.each do |cls|
44
+ if !cls[:private]
45
+ if cls[:new]
46
+ classes << cls
47
+ else
48
+ members = filter_new_members(cls)
49
+ if members.length > 0
50
+ new_items << {:name => cls[:name], :members => members}
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ new_items.sort! {|a, b| a[:name] <=> b[:name] }
57
+
58
+ # Place the new classes section at the beginning
59
+ if classes.length > 0
60
+ new_items.unshift({:name => "New classes", :members => classes})
61
+ end
62
+
63
+ new_items
64
+ end
65
+
66
+ def filter_new_members(cls)
67
+ members = []
68
+ cls.all_local_members.each do |m|
69
+ members << m if m[:new] && visible?(m)
70
+ end
71
+ members = discard_accessors(members)
72
+ members.sort! {|a, b| a[:name] <=> b[:name] }
73
+ end
74
+
75
+ def visible?(member)
76
+ !member[:private] && !member[:hide]
77
+ end
78
+
79
+ def discard_accessors(members)
80
+ accessors = {}
81
+ members.find_all {|m| m[:accessor] }.each do |cfg|
82
+ accessors["set" + upcase_first(cfg[:name])] = true
83
+ accessors["get" + upcase_first(cfg[:name])] = true
84
+ accessors[cfg[:name].downcase + "change"] = true if cfg[:evented]
85
+ end
86
+
87
+ members.reject {|m| accessors[m[:name]] }
88
+ end
89
+
90
+ def upcase_first(str)
91
+ str[0,1].upcase + str[1..-1]
92
+ end
93
+
94
+ def render_columns(new_items)
95
+ align = ["left-column", "middle-column", "right-column"]
96
+ i = -1
97
+ return @columns.split(new_items, 3).map do |col|
98
+ i += 1
99
+ [
100
+ "<div class='#{align[i]}'>",
101
+ render_col(col),
102
+ "</div>",
103
+ ]
104
+ end
105
+ end
106
+
107
+ def render_col(col)
108
+ return col.map do |item|
109
+ [
110
+ "<h3>#{item[:name]}</h3>",
111
+ "<ul class='links'>",
112
+ item[:members].map {|m| "<li>" + link(m) + "</li>" },
113
+ "</ul>",
114
+ ]
115
+ end
116
+ end
117
+
118
+ def link(m)
119
+ if m[:tagname] == :class
120
+ @doc_formatter.link(m[:name], nil, m[:name])
121
+ else
122
+ @doc_formatter.link(m[:owner], m[:name], m[:name], m[:tagname], m[:static])
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -42,9 +42,11 @@ module JsDuck
42
42
  attr_accessor :tests
43
43
  attr_accessor :comments_url
44
44
  attr_accessor :comments_domain
45
+ attr_accessor :search
45
46
  attr_accessor :ignore_html
46
47
 
47
48
  # Debugging
49
+ attr_accessor :warnings_exit_nonzero
48
50
  attr_accessor :template_dir
49
51
  attr_accessor :template_links
50
52
  attr_accessor :extjs_path
@@ -92,8 +94,7 @@ module JsDuck
92
94
  ]
93
95
  @ext4_events = nil
94
96
 
95
- @version = "5.0.0.beta2"
96
-
97
+ @version = "5.0.0.beta3"
97
98
  # Customizing output
98
99
  @title = "Documentation - JSDuck"
99
100
  @header = "<strong>Documentation</strong> JSDuck"
@@ -121,9 +122,11 @@ module JsDuck
121
122
  @tests = false
122
123
  @comments_url = nil
123
124
  @comments_domain = nil
125
+ @search = {}
124
126
  @ignore_html = {}
125
127
 
126
128
  # Debugging
129
+ @warnings_exit_nonzero = false
127
130
  @root_dir = File.dirname(File.dirname(File.dirname(__FILE__)))
128
131
  @template_dir = @root_dir + "/template-min"
129
132
  @template_links = false
@@ -140,6 +143,7 @@ module JsDuck
140
143
  Logger.set_warning(:all, true)
141
144
  Logger.set_warning(:link_auto, false)
142
145
  Logger.set_warning(:param_count, false)
146
+ Logger.set_warning(:fires, false)
143
147
 
144
148
  @optparser = create_option_parser
145
149
  end
@@ -450,6 +454,8 @@ module JsDuck
450
454
  end
451
455
 
452
456
  opts.on('--comments-domain=STRING',
457
+ "A name identifying the subset of comments.",
458
+ "",
453
459
  "A string consisting of <name>/<version>.",
454
460
  "",
455
461
  "For example: ext-js/4",
@@ -458,6 +464,37 @@ module JsDuck
458
464
  @comments_domain = domain
459
465
  end
460
466
 
467
+ opts.on('--search-url=URL',
468
+ "Address of guides search server.",
469
+ "",
470
+ "When supplied, the search for guides is performed through this",
471
+ "external service and the results merged together with API search.",
472
+ "The search server must respond to JSONP requests.",
473
+ "",
474
+ "For example: http://sencha.com/docsearch",
475
+ "",
476
+ "Must be used together with --search-domain option.",
477
+ "",
478
+ "This option is EXPERIMENTAL.") do |url|
479
+ @search[:url] = url
480
+ end
481
+
482
+ opts.on('--search-domain=STRING',
483
+ "A name identifying the subset to search from.",
484
+ "",
485
+ "A string consisting of <name>/<version>.",
486
+ "",
487
+ "Tells the search engine which product and version",
488
+ "to include into search.",
489
+ "",
490
+ "For example: Ext JS/4.2.0",
491
+ "",
492
+ "Must be used together with --search-url option.",
493
+ "",
494
+ "This option is EXPERIMENTAL.") do |domain|
495
+ @search[:product], @search[:version] = domain.split(/\//)
496
+ end
497
+
461
498
  opts.separator ""
462
499
  opts.separator "Tweaking:"
463
500
  opts.separator ""
@@ -653,6 +690,26 @@ module JsDuck
653
690
  end
654
691
  end
655
692
 
693
+ opts.on('--warnings-exit-nonzero',
694
+ "Exits with non-zero exit code on warnings.",
695
+ "",
696
+ "By default JSDuck only exits with non-zero exit code",
697
+ "when a fatal error is encountered (code 1).",
698
+ "",
699
+ "With this option the exit code will be 2 when any warning",
700
+ "gets printed.") do
701
+ @warnings_exit_nonzero = true
702
+ end
703
+
704
+ opts.on('--[no-]color',
705
+ "Turn on/off colorized terminal output.",
706
+ "",
707
+ "By default the colored output is on, but gets disabled",
708
+ "automatically when output is not an interactive terminal",
709
+ "(or when running on Windows system).") do |on|
710
+ Logger.colors = on
711
+ end
712
+
656
713
  opts.on('-p', '--processes=COUNT',
657
714
  "The number of parallel processes to use.",
658
715
  "",