jsduck 5.0.0.beta2 → 5.0.0.beta3

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 (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
@@ -0,0 +1,32 @@
1
+ module JsDuck
2
+
3
+ # Transforms in-page links so they won't break docs app #!-navigation.
4
+ #
5
+ # For example a link to "#automation" in testing guide will be
6
+ # replaced with "#!/guide/testing-section-automation" and the link
7
+ # target ID will be transformed into "testing-section-automation".
8
+ class GuideAnchors
9
+
10
+ def self.transform(html, guide_name)
11
+ html.gsub(/(<a\s+(?:[^<>]*\s+)?href=['"]#)([^!\/].*?)(['"])/i) do |m|
12
+ "#{$1}!/guide/#{guide_name}-section-#{$2}#{$3}"
13
+
14
+ end.gsub(/(<a\s+(?:[^<>]*\s+)?name=['"])(.*?)(['"])/i) do |m|
15
+ $1 + transform_id($2, guide_name) + $3
16
+
17
+ end.gsub(/(<\w+\s+(?:[^<>]*\s+)?id=['"])(.*?)(['"])/i) do |m|
18
+ $1 + transform_id($2, guide_name) + $3
19
+ end
20
+ end
21
+
22
+ def self.transform_id(id, guide_name)
23
+ if id =~ /^#{guide_name}-section-/
24
+ id
25
+ else
26
+ "#{guide_name}-section-#{id}"
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,49 @@
1
+ require 'jsduck/util/html'
2
+
3
+ module JsDuck
4
+
5
+ # Adds Table of Contents section to guide HTML.
6
+ class GuideToc
7
+
8
+ # Inserts table of contents at the top of guide HTML by looking
9
+ # for <h2> elements.
10
+ def self.inject(html, guide_name)
11
+ toc = []
12
+ new_html = []
13
+
14
+ html.each_line do |line|
15
+ if line =~ /^\s*<(h[1-6])>(.*?)<\/h[1-6]>$/
16
+ tag = $1
17
+ text = Util::HTML.strip_tags($2)
18
+ id = guide_name + "-section-" + title_to_id(text)
19
+ if tag == "h2"
20
+ toc << "<li><a href='#!/guide/#{id}'>#{text}</a></li>\n"
21
+ end
22
+ new_html << "<#{tag} id='#{id}'>#{text}</#{tag}>\n"
23
+ else
24
+ new_html << line
25
+ end
26
+ end
27
+
28
+ # Inject TOC below first heading if at least 2 items in TOC
29
+ if toc.length >= 2
30
+ new_html.insert(1, [
31
+ "<div class='toc'>\n",
32
+ "<p><strong>Contents</strong></p>\n",
33
+ "<ol>\n",
34
+ toc,
35
+ "</ol>\n",
36
+ "</div>\n",
37
+ ])
38
+ end
39
+
40
+ new_html.flatten.join
41
+ end
42
+
43
+ def self.title_to_id(title)
44
+ title.downcase.gsub(/[^\w]+/, "-")
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -4,7 +4,8 @@ require 'jsduck/util/io'
4
4
  require 'jsduck/util/null_object'
5
5
  require 'jsduck/logger'
6
6
  require 'jsduck/grouped_asset'
7
- require 'jsduck/util/html'
7
+ require 'jsduck/guide_toc'
8
+ require 'jsduck/guide_anchors'
8
9
  require 'jsduck/img/dir'
9
10
  require 'fileutils'
10
11
 
@@ -55,6 +56,10 @@ module JsDuck
55
56
  def load_guide(guide)
56
57
  return Logger.warn(:guide, "Guide not found", guide["url"]) unless File.exists?(guide["url"])
57
58
  return Logger.warn(:guide, "Guide not found", guide[:filename]) unless File.exists?(guide[:filename])
59
+ unless js_ident?(guide["name"])
60
+ # Guide name is also used as JSONP callback method name.
61
+ return Logger.warn(:guide, "Guide name is not valid JS identifier: #{guide["name"]}", guide[:filename])
62
+ end
58
63
 
59
64
  begin
60
65
  return format_guide(guide)
@@ -67,7 +72,9 @@ module JsDuck
67
72
  def format_guide(guide)
68
73
  @formatter.doc_context = {:filename => guide[:filename], :linenr => 0}
69
74
  @formatter.images = Img::Dir.new(guide["url"], "guides/#{guide["name"]}")
70
- html = add_toc(guide, @formatter.format(Util::IO.read(guide[:filename])))
75
+ html = @formatter.format(Util::IO.read(guide[:filename]))
76
+ html = GuideToc.inject(html, guide['name'])
77
+ html = GuideAnchors.transform(html, guide['name'])
71
78
 
72
79
  # Report unused images (but ignore the icon files)
73
80
  @formatter.images.get("icon.png")
@@ -102,6 +109,11 @@ module JsDuck
102
109
  end
103
110
  end
104
111
 
112
+ # True when string is valid JavaScript identifier
113
+ def js_ident?(str)
114
+ /\A[$\w]+\z/ =~ str
115
+ end
116
+
105
117
  # Ensures the guide dir contains icon.png.
106
118
  # When there isn't looks for icon-lg.png and renames it to icon.png.
107
119
  # When neither exists, copies over default icon.
@@ -115,36 +127,6 @@ module JsDuck
115
127
  end
116
128
  end
117
129
 
118
- # Creates table of contents at the top of guide by looking for <h2> elements in HTML.
119
- def add_toc(guide, html)
120
- toc = [
121
- "<div class='toc'>\n",
122
- "<p><strong>Contents</strong></p>\n",
123
- "<ol>\n",
124
- ]
125
- new_html = []
126
- i = 0
127
- html.each_line do |line|
128
- if line =~ /^<h2>(.*)<\/h2>$/
129
- i += 1
130
- text = Util::HTML.strip_tags($1)
131
- toc << "<li><a href='#!/guide/#{guide['name']}-section-#{i}'>#{text}</a></li>\n"
132
- new_html << "<h2 id='#{guide['name']}-section-#{i}'>#{text}</h2>\n"
133
- else
134
- new_html << line
135
- end
136
- end
137
- toc << "</ol>\n"
138
- toc << "</div>\n"
139
- # Inject TOC at below first heading if at least 2 items in TOC
140
- if i >= 2
141
- new_html.insert(1, toc)
142
- new_html.flatten.join
143
- else
144
- html
145
- end
146
- end
147
-
148
130
  # Returns HTML listing of guides
149
131
  def to_html(style="")
150
132
  html = @groups.map do |group|
@@ -16,14 +16,8 @@ module JsDuck
16
16
  @templates = {
17
17
  "html5" => '<video src="%u">%a</video>',
18
18
  "vimeo" => [
19
- '<p><object width="640" height="360">',
20
- '<param name="allowfullscreen" value="true" />',
21
- '<param name="allowscriptaccess" value="always" />',
22
- '<param name="flashvars" value="api=1" />',
23
- '<param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=%u&amp;server=vimeo.com&amp;color=4CC208&amp;fullscreen=1" />',
24
- '<embed src="http://vimeo.com/moogaloop.swf?clip_id=%u&amp;server=vimeo.com&amp;color=4CC208&amp;fullscreen=1" ',
25
- 'type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="640" height="360"></embed>',
26
- '</object></p>',
19
+ '<p><iframe src="http://player.vimeo.com/video/%u" width="640" height="360" frameborder="0" ',
20
+ 'webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></p>'
27
21
  ].join
28
22
  }
29
23
 
@@ -1,6 +1,8 @@
1
- require "jsduck/js/function"
1
+ require "jsduck/js/class"
2
+ require "jsduck/js/method"
3
+ require "jsduck/js/event"
4
+ require "jsduck/js/property"
2
5
  require "jsduck/js/node"
3
- require "jsduck/tag_registry"
4
6
 
5
7
  module JsDuck
6
8
  module Js
@@ -48,313 +50,19 @@ module JsDuck
48
50
  def detect(node)
49
51
  ast = Js::Node.create(node)
50
52
 
51
- exp = ast.expression_statement? ? ast["expression"] : nil
52
- var = ast.variable_declaration? ? ast["declarations"][0] : nil
53
-
54
- # Ext.define("Class", {})
55
- if exp && exp.ext_define?
56
- make_class(exp["arguments"][0].to_value, exp)
57
-
58
- # Ext.override(Class, {})
59
- elsif exp && exp.ext_override?
60
- make_class("", exp)
61
-
62
- # foo = Ext.extend(Parent, {})
63
- elsif exp && exp.assignment_expression? && exp["right"].ext_extend?
64
- make_class(exp["left"].to_s, exp["right"])
65
-
66
- # Foo = ...
67
- elsif exp && exp.assignment_expression? && class_name?(exp["left"].to_s)
68
- make_class(exp["left"].to_s, exp["right"])
69
-
70
- # var foo = Ext.extend(Parent, {})
71
- elsif var && var["init"].ext_extend?
72
- make_class(var["id"].to_s, var["init"])
73
-
74
- # var Foo = ...
75
- elsif var && class_name?(var["id"].to_s)
76
- make_class(var["id"].to_s, var["right"])
77
-
78
- # function Foo() {}
79
- elsif ast.function? && class_name?(ast["id"].to_s || "")
80
- make_class(ast["id"].to_s)
81
-
82
- # { ... }
83
- elsif ast.object_expression?
84
- make_class("", ast)
85
-
86
- # function foo() {}
87
- elsif ast.function?
88
- make_method(ast["id"].to_s || "", ast)
89
-
90
- # foo = function() {}
91
- elsif exp && exp.assignment_expression? && exp["right"].function?
92
- make_method(exp["left"].to_s, exp["right"])
93
-
94
- # var foo = function() {}
95
- elsif var && var["init"] && var["init"].function?
96
- make_method(var["id"].to_s, var["init"])
97
-
98
- # (function() {})
99
- elsif exp && exp.function?
100
- make_method(exp["id"].to_s || "", exp)
101
-
102
- # foo: function() {}
103
- elsif ast.property? && ast["value"].function?
104
- make_method(ast["key"].key_value, ast["value"])
105
-
106
- # this.fireEvent("foo", ...)
107
- elsif exp && exp.fire_event?
108
- make_event(exp["arguments"][0].to_value)
109
-
110
- # foo = ...
111
- elsif exp && exp.assignment_expression?
112
- make_property(exp["left"].to_s, exp["right"])
113
-
114
- # var foo = ...
115
- elsif var
116
- make_property(var["id"].to_s, var["init"])
117
-
118
- # foo: ...
119
- elsif ast.property?
120
- make_property(ast["key"].key_value, ast["value"])
121
-
122
- # foo;
123
- elsif exp && exp.identifier?
124
- make_property(exp.to_s)
125
-
126
- # "foo" (inside some expression)
127
- elsif ast.string?
128
- make_property(ast.to_value)
129
-
130
- # "foo"; (as a statement of it's own)
131
- elsif exp && exp.string?
132
- make_property(exp.to_value)
133
-
53
+ if doc = Js::Class.detect(ast, @docs)
54
+ doc
55
+ elsif doc = Js::Method.detect(ast)
56
+ doc
57
+ elsif doc = Js::Event.detect(ast)
58
+ doc
59
+ elsif doc = Js::Property.detect(ast)
60
+ doc
134
61
  else
135
- make_property()
62
+ Js::Property.make()
136
63
  end
137
64
  end
138
65
 
139
- private
140
-
141
- # Class name begins with upcase char
142
- def class_name?(name)
143
- return name.split(/\./).last =~ /\A[A-Z]/
144
- end
145
-
146
- def make_class(name, ast=nil)
147
- cls = {
148
- :tagname => :class,
149
- :name => name,
150
- }
151
-
152
- # apply information from Ext.extend, Ext.define, or {}
153
- if ast
154
- if ast.ext_define?
155
- detect_ext_define(cls, ast)
156
- elsif ast.ext_extend?
157
- detect_ext_something(:extends, cls, ast)
158
- elsif ast.ext_override?
159
- detect_ext_something(:override, cls, ast)
160
- elsif ast.object_expression?
161
- detect_class_members_from_object(cls, ast)
162
- elsif ast.array_expression?
163
- detect_class_members_from_array(cls, ast)
164
- end
165
- end
166
-
167
- return cls
168
- end
169
-
170
- # Detection of Ext.extend() or Ext.override().
171
- # The type parameter must be correspondingly either :extend or :override.
172
- def detect_ext_something(type, cls, ast)
173
- args = ast["arguments"]
174
- cls[type] = args[0].to_s
175
- if args.length == 2 && args[1].object_expression?
176
- detect_class_members_from_object(cls, args[1])
177
- end
178
- end
179
-
180
- # Inspects Ext.define() and copies detected properties over to the
181
- # given cls Hash
182
- def detect_ext_define(cls, ast)
183
- # defaults
184
- cls.merge!(TagRegistry.ext_define_defaults)
185
- cls[:members] = []
186
- cls[:code_type] = :ext_define
187
-
188
- ast["arguments"][1].each_property do |key, value, pair|
189
- if tag = TagRegistry.get_by_ext_define_pattern(key)
190
- tag.parse_ext_define(cls, value)
191
- else
192
- case key
193
- when "config"
194
- cls[:members] += make_configs(value, {:accessor => true})
195
- when "cachedConfig"
196
- cls[:members] += make_configs(value, {:accessor => true})
197
- when "eventedConfig"
198
- cls[:members] += make_configs(value, {:accessor => true, :evented => true})
199
- when "statics"
200
- cls[:members] += make_statics(value)
201
- when "inheritableStatics"
202
- cls[:members] += make_statics(value, {:inheritable => true})
203
- else
204
- detect_method_or_property(cls, key, value, pair)
205
- end
206
- end
207
- end
208
- end
209
-
210
- # Detects class members from object literal
211
- def detect_class_members_from_object(cls, ast)
212
- cls[:members] = []
213
- ast.each_property do |key, value, pair|
214
- detect_method_or_property(cls, key, value, pair)
215
- end
216
- end
217
-
218
- # Detects class members from array literal
219
- def detect_class_members_from_array(cls, ast)
220
- cls[:members] = []
221
-
222
- # This will most likely be an @enum class, in which case the
223
- # enum will be for documentation purposes only.
224
- cls[:enum] = {:doc_only => true}
225
-
226
- ast["elements"].each do |el|
227
- detect_method_or_property(cls, el.key_value, el, el)
228
- end
229
- end
230
-
231
- # Detects item in object literal either as method or property
232
- def detect_method_or_property(cls, key, value, pair)
233
- if value.function?
234
- m = make_method(key, value)
235
- cls[:members] << m if apply_autodetected(m, pair)
236
- else
237
- p = make_property(key, value)
238
- cls[:members] << p if apply_autodetected(p, pair)
239
- end
240
- end
241
-
242
- def make_configs(ast, defaults={})
243
- configs = []
244
-
245
- ast.each_property do |name, value, pair|
246
- cfg = make_property(name, value, :cfg)
247
- cfg.merge!(defaults)
248
- configs << cfg if apply_autodetected(cfg, pair)
249
- end
250
-
251
- configs
252
- end
253
-
254
- def make_statics(ast, defaults={})
255
- statics = []
256
-
257
- ast.each_property do |name, value, pair|
258
- if value.function?
259
- s = make_method(name, value)
260
- else
261
- s = make_property(name, value)
262
- end
263
-
264
- s[:static] = true
265
- s.merge!(defaults)
266
-
267
- statics << s if apply_autodetected(s, pair, defaults[:inheritable])
268
- end
269
-
270
- statics
271
- end
272
-
273
- # Sets auto-detection related properties :autodetected and
274
- # :inheritdoc on the given member Hash.
275
- #
276
- # When member has a comment, adds code to the related docset and
277
- # returns false.
278
- #
279
- # Otherwise detects the line number of member and returns true.
280
- def apply_autodetected(m, ast, inheritable=true)
281
- docset = find_docset(ast.raw)
282
-
283
- if !docset || docset[:type] != :doc_comment
284
- if inheritable
285
- m[:inheritdoc] = {}
286
- else
287
- m[:private] = true
288
- end
289
- m[:autodetected] = {:tagname => m[:tagname]}
290
- end
291
-
292
- if docset
293
- docset[:code] = m
294
- return false
295
- else
296
- m[:linenr] = ast.linenr
297
- return true
298
- end
299
- end
300
-
301
- # Looks up docset associated with given AST node.
302
- # A dead-stupid and -slow implementation, but works.
303
- #
304
- # The comparison needs to be done between raw AST nodes - multiple
305
- # Node instances can be created to wrap a single raw AST node,
306
- # and they will then not be equal.
307
- def find_docset(raw_ast)
308
- @docs.find do |docset|
309
- docset[:code] == raw_ast
310
- end
311
- end
312
-
313
- def make_method(name, ast)
314
- return {
315
- :tagname => :method,
316
- :name => name,
317
- :params => make_params(ast),
318
- :chainable => chainable?(ast) && name != "constructor",
319
- }
320
- end
321
-
322
- def make_params(ast)
323
- if ast.function? && !ast.ext_empty_fn?
324
- ast["params"].map {|p| {:name => p.to_s} }
325
- else
326
- []
327
- end
328
- end
329
-
330
- def chainable?(ast)
331
- if ast.function? && !ast.ext_empty_fn?
332
- Js::Function.return_types(ast.raw) == [:this]
333
- else
334
- false
335
- end
336
- end
337
-
338
- def make_event(name)
339
- return {
340
- :tagname => :event,
341
- :name => name,
342
- }
343
- end
344
-
345
- def make_property(name=nil, ast=nil, tagname=:property)
346
- return {
347
- :tagname => tagname,
348
- :name => name,
349
- :type => ast && ast.value_type,
350
- :default => ast && make_default(ast),
351
- }
352
- end
353
-
354
- def make_default(ast)
355
- ast.to_value != nil ? ast.to_s : nil
356
- end
357
-
358
66
  end
359
67
 
360
68
  end