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
@@ -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