jsduck 3.0.pre2 → 3.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/README.md +14 -7
  2. data/Rakefile +277 -24
  3. data/bin/compare +163 -0
  4. data/bin/stats +92 -0
  5. data/jsduck.gemspec +3 -3
  6. data/lib/jsduck/accessors.rb +64 -8
  7. data/lib/jsduck/aggregator.rb +10 -6
  8. data/lib/jsduck/aliases.rb +3 -2
  9. data/lib/jsduck/app.rb +51 -44
  10. data/lib/jsduck/author_tag.rb +11 -0
  11. data/lib/jsduck/categories.rb +14 -8
  12. data/lib/jsduck/class.rb +2 -1
  13. data/lib/jsduck/class_formatter.rb +5 -4
  14. data/lib/jsduck/doc_author_tag.rb +11 -0
  15. data/lib/jsduck/doc_formatter.rb +17 -37
  16. data/lib/jsduck/doc_parser.rb +39 -11
  17. data/lib/jsduck/exporter.rb +11 -0
  18. data/lib/jsduck/guides.rb +3 -3
  19. data/lib/jsduck/images.rb +72 -0
  20. data/lib/jsduck/js_parser.rb +20 -4
  21. data/lib/jsduck/lexer.rb +2 -8
  22. data/lib/jsduck/lint.rb +3 -2
  23. data/lib/jsduck/logger.rb +24 -5
  24. data/lib/jsduck/merger.rb +38 -8
  25. data/lib/jsduck/meta_tag.rb +49 -0
  26. data/lib/jsduck/meta_tag_loader.rb +48 -0
  27. data/lib/jsduck/options.rb +37 -25
  28. data/lib/jsduck/os.rb +11 -0
  29. data/lib/jsduck/renderer.rb +37 -34
  30. data/lib/jsduck/search_data.rb +1 -1
  31. data/lib/jsduck/source_file.rb +13 -8
  32. data/template-min/app.js +1 -1
  33. data/template-min/{egIframe.html → extIframe.html} +3 -4
  34. data/template-min/extjs/ext-all-debug.js +3107 -2026
  35. data/template-min/extjs/ext-all.js +1 -1
  36. data/template-min/extjs/resources/css/ext-all.css +1 -1
  37. data/template-min/resources/css/app.css +1 -1
  38. data/template-min/resources/images/down-arr.png +0 -0
  39. data/template-min/resources/images/gettingstarted.jpg +0 -0
  40. data/template-min/resources/images/ipad-l.jpg +0 -0
  41. data/template-min/resources/images/ipad-p.jpg +0 -0
  42. data/template-min/resources/images/iphone-l.jpg +0 -0
  43. data/template-min/resources/images/iphone-p.jpg +0 -0
  44. data/template-min/resources/images/iphone-small-l.jpg +0 -0
  45. data/template-min/resources/images/iphone-small-p.jpg +0 -0
  46. data/template-min/resources/images/link-arrow-next.png +0 -0
  47. data/template-min/template.html +5 -1
  48. data/template-min/touch-welcome.html +122 -0
  49. data/template-min/touchIframe.html +85 -0
  50. data/template-min/welcome.html +2 -0
  51. metadata +25 -8
  52. data/lib/jsduck/page.rb +0 -118
  53. data/lib/jsduck/timer.rb +0 -44
@@ -29,9 +29,12 @@ module JsDuck
29
29
  # Default value: '<img src="%u" alt="%a"/>'
30
30
  attr_accessor :img_tpl
31
31
 
32
- # Assign to this a function that retrieves the example code when
33
- # passed in a filename
34
- attr_accessor :get_example
32
+ # This will hold list of all image paths gathered from {@img} tags.
33
+ attr_accessor :images
34
+
35
+ # Base path to prefix images from {@img} tags.
36
+ # Defaults to no prefix.
37
+ attr_accessor :img_path
35
38
 
36
39
  # Sets up instance to work in context of particular class, so
37
40
  # that when {@link #blah} is encountered it knows that
@@ -57,13 +60,12 @@ module JsDuck
57
60
  @doc_context = {}
58
61
  @max_length = 120
59
62
  @relations = {}
63
+ @images = []
60
64
  @link_tpl = '<a href="%c%#%m">%a</a>'
61
65
  @img_tpl = '<img src="%u" alt="%a"/>'
62
- @example_tpl = '<pre class="inline-example"><code>%a</code></pre>'
63
66
  @link_re = /\{@link\s+(\S*?)(?:\s+(.+?))?\}/m
64
67
  @img_re = /\{@img\s+(\S*?)(?:\s+(.+?))?\}/m
65
- @example_re = /\{@example\s+(\S*?)\s*\}/m
66
- @example_annotation_re = /<pre><code>@example( +[^\n]*)?\s+/m
68
+ @example_annotation_re = /<pre><code>\s*@example( +[^\n]*)?\s+/m
67
69
  end
68
70
 
69
71
  # Replaces {@link} and {@img} tags, auto-generates links for
@@ -74,8 +76,6 @@ module JsDuck
74
76
  #
75
77
  # Replaces {@img path/to/image.jpg Alt text} with HTML from @img_tpl.
76
78
  #
77
- # Replaces {@example path/to/example.js} with source from that file.
78
- #
79
79
  # Adds 'inline-example' class to code examples beginning with @example.
80
80
  #
81
81
  # Additionally replaces strings recognized as ClassNames with
@@ -89,11 +89,12 @@ module JsDuck
89
89
  out += replace_link_tag(s.scan(@link_re))
90
90
  elsif s.check(@img_re)
91
91
  out += replace_img_tag(s.scan(@img_re))
92
- elsif s.check(@example_re)
93
- out += replace_example_tag(s.scan(@example_re))
94
92
  elsif s.check(@example_annotation_re)
95
- s.scan(@example_annotation_re)
96
- out += '<pre class="inline-example"><code>'
93
+ # Match possible classnames following @example and add them
94
+ # as CSS classes inside <pre> element.
95
+ s.scan(@example_annotation_re) =~ @example_annotation_re
96
+ css_classes = ($1 || "").strip
97
+ out += "<pre class='inline-example #{css_classes}'><code>"
97
98
  elsif s.check(/[{<]/)
98
99
  out += s.scan(/[{<]/)
99
100
  else
@@ -129,10 +130,10 @@ module JsDuck
129
130
  file = @doc_context[:filename]
130
131
  line = @doc_context[:linenr]
131
132
  if !@relations[cls]
132
- Logger.instance.warn("#{file} line #{line} #{input} links to non-existing class.")
133
+ Logger.instance.warn("#{input} links to non-existing class", file, line)
133
134
  text
134
135
  elsif member && !get_member(cls, member, type)
135
- Logger.instance.warn("#{file} line #{line} #{input} links to non-existing member.")
136
+ Logger.instance.warn("#{input} links to non-existing member", file, line)
136
137
  text
137
138
  else
138
139
  link(cls, member, text, type)
@@ -144,10 +145,6 @@ module JsDuck
144
145
  input.sub(@img_re) { img($1, $2) }
145
146
  end
146
147
 
147
- def replace_example_tag(input)
148
- input.sub(@example_re) { example($1) }
149
- end
150
-
151
148
  def replace_class_names(input)
152
149
  input.gsub(/(\A|\s)([A-Z][A-Za-z0-9.]*[A-Za-z0-9])(?:(#)([A-Za-z0-9]+))?([.,]?(?:\s|\Z))/m) do
153
150
  before = $1
@@ -167,10 +164,11 @@ module JsDuck
167
164
 
168
165
  # applies the image template
169
166
  def img(url, alt_text)
167
+ @images << url
170
168
  @img_tpl.gsub(/(%\w)/) do
171
169
  case $1
172
170
  when '%u'
173
- url
171
+ @img_path ? (@img_path + "/" + url) : url
174
172
  when '%a'
175
173
  CGI.escapeHTML(alt_text||"")
176
174
  else
@@ -179,24 +177,6 @@ module JsDuck
179
177
  end
180
178
  end
181
179
 
182
- # Replaces example template with example read from file
183
- def example(path)
184
- @example_tpl.gsub(/(%\w)/) do
185
- case $1
186
- when '%a'
187
- if @get_example
188
- CGI.escapeHTML(@get_example.call(path))
189
- else
190
- file = @doc_context[:filename]
191
- line = @doc_context[:linenr]
192
- Logger.instance.warn("--examples not specified, but {@example} found in #{file} line #{line}.")
193
- end
194
- else
195
- $1
196
- end
197
- end
198
- end
199
-
200
180
  # applies the link template
201
181
  def link(cls, member, anchor_text, type=nil)
202
182
  # Use the canonical class name for link (not some alternateClassName)
@@ -30,7 +30,7 @@ module JsDuck
30
30
 
31
31
  @meta_tags_map = {}
32
32
  (meta_tags || []).each do |tag|
33
- @meta_tags_map[tag[:name]] = true
33
+ @meta_tags_map[tag.name] = tag
34
34
  end
35
35
  end
36
36
 
@@ -136,6 +136,8 @@ module JsDuck
136
136
  boolean_at_tag(/@protected/, :protected)
137
137
  elsif look(/@accessor\b/)
138
138
  boolean_at_tag(/@accessor/, :accessor)
139
+ elsif look(/@evented\b/)
140
+ boolean_at_tag(/@evented/, :evented)
139
141
  elsif look(/@template\b/)
140
142
  boolean_at_tag(/@template/, :template)
141
143
  elsif look(/@markdown\b/)
@@ -146,12 +148,9 @@ module JsDuck
146
148
  boolean_at_tag(/@abstract/, :abstract)
147
149
  elsif look(/@/)
148
150
  @input.scan(/@/)
149
- if @meta_tags_map[look(/\w+/)]
150
- add_tag(:meta)
151
- @current_tag[:name] = match(/\w+/)
152
- skip_horiz_white
153
- @current_tag[:content] = @input.scan(/.*$/)
154
- skip_white
151
+ tag = @meta_tags_map[look(/\w+/)]
152
+ if tag
153
+ meta_at_tag(tag)
155
154
  else
156
155
  @current_tag[:doc] += "@"
157
156
  end
@@ -161,6 +160,24 @@ module JsDuck
161
160
  end
162
161
  end
163
162
 
163
+ # Matches the given meta-tag
164
+ def meta_at_tag(tag)
165
+ prev_tag = @current_tag
166
+
167
+ add_tag(:meta)
168
+ @current_tag[:name] = match(/\w+/)
169
+ skip_horiz_white
170
+
171
+ # Fors singleline tags, scan to the end of line and finish the
172
+ # tag. For multiline tags we leave the tag open for :doc
173
+ # addition just like with built-in multiline tags.
174
+ unless tag.multiline
175
+ @current_tag[:doc] = @input.scan(/.*$/).strip
176
+ skip_white
177
+ @current_tag = prev_tag
178
+ end
179
+ end
180
+
164
181
  # matches @class name ...
165
182
  def at_class
166
183
  match(/@class/)
@@ -354,7 +371,7 @@ module JsDuck
354
371
  end
355
372
  end
356
373
 
357
- # matches: <ident-chain> | "[" <ident-chain> [ "=" <literal> ] "]"
374
+ # matches: <ident-chain> | "[" <ident-chain> [ "=" <default-value> ] "]"
358
375
  def maybe_name_with_default
359
376
  skip_horiz_white
360
377
  if look(/\[/)
@@ -364,7 +381,7 @@ module JsDuck
364
381
  if look(/=/)
365
382
  match(/=/)
366
383
  skip_horiz_white
367
- @current_tag[:default] = literal
384
+ @current_tag[:default] = default_value
368
385
  end
369
386
  skip_horiz_white
370
387
  match(/\]/)
@@ -408,9 +425,20 @@ module JsDuck
408
425
  end
409
426
  end
410
427
 
411
- def literal
428
+ # attempts to match javascript literal,
429
+ # when it fails grabs anything up to closing "]"
430
+ def default_value
431
+ start_pos = @input.pos
412
432
  lit = JsLiteralParser.new(@input).literal
413
- lit ? JsLiteralBuilder.new.to_s(lit) : nil
433
+ if lit && look(/ *\]/)
434
+ # When lital matched and there's nothing after it up to the closing "]"
435
+ JsLiteralBuilder.new.to_s(lit)
436
+ else
437
+ # Otherwise reset parsing position to where we started
438
+ # and rescan up to "]" using simple regex.
439
+ @input.pos = start_pos
440
+ match(/[^\]]*/)
441
+ end
414
442
  end
415
443
 
416
444
  # matches {...} and returns text inside brackets
@@ -29,6 +29,7 @@ module JsDuck
29
29
  cls.delete(:doc)
30
30
  cls[:members] = compact_members_group(cls[:members])
31
31
  cls[:statics] = compact_members_group(cls[:statics])
32
+ cls[:files] = compact_files(cls[:files])
32
33
  cls
33
34
  end
34
35
 
@@ -47,6 +48,16 @@ module JsDuck
47
48
  end
48
49
  m_copy
49
50
  end
51
+
52
+ # Remove full path from filename for privacy considerations as the
53
+ # path can reveal information about the system where JSDuck was
54
+ # run. The docs app doesn't need to have this information.
55
+ def compact_files(files)
56
+ files.map do |f|
57
+ {:filename => File.basename(f[:filename]), :href => f[:href]}
58
+ end
59
+ end
60
+
50
61
  end
51
62
 
52
63
  end
data/lib/jsduck/guides.rb CHANGED
@@ -44,14 +44,14 @@ module JsDuck
44
44
  guide_file = in_dir + "/README.md"
45
45
  return Logger.instance.warn("README.md not found in #{in_dir}") unless File.exists?(guide_file)
46
46
 
47
- Logger.instance.log("Writing guide #{out_dir} ...")
47
+ Logger.instance.log("Writing guide", out_dir)
48
48
  # Copy the whole guide dir over
49
49
  FileUtils.cp_r(in_dir, out_dir)
50
50
 
51
51
  @formatter.doc_context = {:filename => guide_file, :linenr => 0}
52
- html = @formatter.format(IO.read(guide_file))
53
52
  name = File.basename(in_dir)
54
- html.gsub!(/<img src="/, "<img src=\"guides/#{name}/")
53
+ @formatter.img_path = "guides/#{name}"
54
+ html = @formatter.format(IO.read(guide_file))
55
55
 
56
56
  JsonDuck.write_jsonp(out_dir+"/README.js", name, {:guide => html, :title => guide["title"]})
57
57
  end
@@ -0,0 +1,72 @@
1
+ require "jsduck/logger"
2
+ require "fileutils"
3
+
4
+ module JsDuck
5
+
6
+ # Looks up images from directories specified through --images option.
7
+ class Images
8
+ def initialize(paths)
9
+ @paths = scan_for_images(paths)
10
+ @images = {}
11
+ end
12
+
13
+ # Scans each path for image files, building a hash of paths where
14
+ # each path points to a hash of image files found in that path.
15
+ def scan_for_images(paths)
16
+ map = {}
17
+ paths.each do |path|
18
+ # Scans directory for image files
19
+ map[path] = {}
20
+ Dir[path+"/**/*.{png,jpg,jpeg,gif}"].each do |img|
21
+ map[path][img] = false
22
+ end
23
+ end
24
+ map
25
+ end
26
+
27
+ # Adds relative image path of an image
28
+ def add(filename)
29
+ unless @images[filename]
30
+ @images[filename] = true
31
+ end
32
+ end
33
+
34
+ # Copys over images to given output dir
35
+ def copy(output_dir)
36
+ @images.each_key do |img|
37
+ unless copy_img(img, output_dir)
38
+ Logger.instance.warn("Image not found.", img)
39
+ end
40
+ end
41
+ report_unused
42
+ end
43
+
44
+ # Attempts to copy one image, returns true on success
45
+ def copy_img(img, output_dir)
46
+ @paths.each_pair do |path, map|
47
+ filename = path + "/" + img
48
+ if map.has_key?(filename)
49
+ dest = output_dir + "/" + img
50
+ Logger.instance.log("Copying image", dest)
51
+ FileUtils.makedirs(File.dirname(dest))
52
+ FileUtils.cp(filename, dest)
53
+ # mark file as used.
54
+ map[filename] = true
55
+ return true
56
+ end
57
+ end
58
+ return false
59
+ end
60
+
61
+ # Report unused images
62
+ def report_unused
63
+ @paths.each_pair do |path, map|
64
+ map.each_pair do |img, used|
65
+ Logger.instance.warn("Image not used.", img) unless used
66
+ end
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -234,7 +234,7 @@ module JsDuck
234
234
  end
235
235
 
236
236
  # <ext-define-cfg> := "{" ( <extend> | <mixins> | <alternate-class-name> | <alias> |
237
- # <requires> | <uses> | <singleton> | <?> )*
237
+ # <xtype> | <requires> | <uses> | <singleton> | <?> )*
238
238
  def ext_define_cfg
239
239
  match("{")
240
240
  cfg = {}
@@ -249,6 +249,8 @@ module JsDuck
249
249
  cfg[:alternateClassNames] = found
250
250
  elsif found = ext_define_alias
251
251
  cfg[:alias] = found
252
+ elsif found = ext_define_xtype
253
+ cfg[:xtype] = found
252
254
  elsif found = ext_define_requires
253
255
  cfg[:requires] = found
254
256
  elsif found = ext_define_uses
@@ -270,12 +272,18 @@ module JsDuck
270
272
  end
271
273
  end
272
274
 
273
- # <mixins> := "mixins" ":" <object-literal>
275
+ # <mixins> := "mixins" ":" [ <object-literal> | <array-literal> ]
274
276
  def ext_define_mixins
275
- if look("mixins", ":", "{")
277
+ if look("mixins", ":")
276
278
  match("mixins", ":")
277
279
  lit = literal
278
- lit && lit[:value].map {|x| x[:value][:value] }
280
+ if lit && lit[:type] == :object
281
+ lit[:value].map {|x| x[:value][:value] }
282
+ elsif lit && lit[:type] == :array
283
+ lit[:value].map {|x| x[:value] }
284
+ else
285
+ nil
286
+ end
279
287
  end
280
288
  end
281
289
 
@@ -295,6 +303,14 @@ module JsDuck
295
303
  end
296
304
  end
297
305
 
306
+ # <xtype> := "xtype" ":" <string-or-list>
307
+ def ext_define_xtype
308
+ if look("xtype", ":")
309
+ match("xtype", ":")
310
+ string_or_list
311
+ end
312
+ end
313
+
298
314
  # <requires> := "requires" ":" <string-or-list>
299
315
  def ext_define_requires
300
316
  if look("requires", ":")
data/lib/jsduck/lexer.rb CHANGED
@@ -118,8 +118,8 @@ module JsDuck
118
118
  :type => :operator,
119
119
  :value => @input.scan(/./)
120
120
  }
121
- elsif @input.check(/[a-zA-Z_]/)
122
- value = @input.scan(/\w+/)
121
+ elsif @input.check(/[a-zA-Z_$]/)
122
+ value = @input.scan(/[$\w]+/)
123
123
  return {
124
124
  :type => KEYWORDS[value] ? :keyword : :ident,
125
125
  :value => value
@@ -167,12 +167,6 @@ module JsDuck
167
167
  :type => :number,
168
168
  :value => nr
169
169
  }
170
- elsif @input.check(/\$/)
171
- value = @input.scan(/\$\w*/)
172
- return {
173
- :type => :ident,
174
- :value => value
175
- }
176
170
  elsif @input.check(/./)
177
171
  return {
178
172
  :type => :operator,
data/lib/jsduck/lint.rb CHANGED
@@ -75,8 +75,9 @@ module JsDuck
75
75
  end
76
76
 
77
77
  # Prints warning + filename and linenumber from doc-context
78
- def warn(msg, context)
79
- Logger.instance.warn(msg + " in #{context[:filename]} line #{context[:linenr]}")
78
+ def warn(msg, member)
79
+ context = member[:files][0]
80
+ Logger.instance.warn(msg, context[:filename], context[:linenr])
80
81
  end
81
82
 
82
83
  end
data/lib/jsduck/logger.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'singleton'
2
+ require 'jsduck/os'
2
3
 
3
4
  module JsDuck
4
5
 
@@ -15,9 +16,11 @@ module JsDuck
15
16
  @shown_warnings = {}
16
17
  end
17
18
 
18
- # Prints log message
19
- def log(msg)
20
- puts msg if @verbose
19
+ # Prints log message with optional filename appended
20
+ def log(msg, filename=nil)
21
+ if @verbose
22
+ puts msg + " " + format(filename) + "..."
23
+ end
21
24
  end
22
25
 
23
26
  # Prints warning message.
@@ -25,12 +28,28 @@ module JsDuck
25
28
  # Ignores duplicate warnings - only prints the first one.
26
29
  # Works best when --processes=0, but it reduces the amount of
27
30
  # warnings greatly also when run multiple processes.
28
- def warn(msg)
31
+ #
32
+ # Optionally filename and line number will be inserted to message.
33
+ def warn(msg, filename=nil, line=nil)
34
+ msg = "Warning: " + format(filename, line) + " " + msg
35
+
29
36
  if @warnings && !@shown_warnings[msg]
30
- $stderr.puts "Warning: " + msg
37
+ $stderr.puts msg
31
38
  @shown_warnings[msg] = true
32
39
  end
33
40
  end
41
+
42
+ # Formats filename and line number for output
43
+ def format(filename=nil, line=nil)
44
+ out = ""
45
+ if filename
46
+ out = OS::windows? ? filename.gsub('/', '\\') : filename
47
+ if line
48
+ out += ":#{line}:"
49
+ end
50
+ end
51
+ out
52
+ end
34
53
  end
35
54
 
36
55
  end
data/lib/jsduck/merger.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'jsduck/logger'
2
+
1
3
  module JsDuck
2
4
 
3
5
  # Takes data from doc-comment and code that follows it and combines
@@ -6,6 +8,15 @@ module JsDuck
6
8
  #
7
9
  # The main method merge() produces a hash as a result.
8
10
  class Merger
11
+ # Allow passing in filename and line for error reporting
12
+ attr_accessor :filename
13
+ attr_accessor :linenr
14
+
15
+ def initialize
16
+ @filename = ""
17
+ @linenr = 0
18
+ end
19
+
9
20
  def merge(docs, code)
10
21
  case detect_doc_type(docs, code)
11
22
  when :class
@@ -170,6 +181,7 @@ module JsDuck
170
181
  :default => detect_default(:cfg, doc_map, code),
171
182
  :properties => detect_subproperties(docs, :cfg),
172
183
  :accessor => !!doc_map[:accessor],
184
+ :evented => !!doc_map[:evented],
173
185
  }, doc_map)
174
186
  end
175
187
 
@@ -222,7 +234,9 @@ module JsDuck
222
234
  end
223
235
 
224
236
  def create_member_id(m)
225
- "#{m[:static] ? 'static-' : ''}#{m[:tagname]}-#{m[:name]}"
237
+ # Sanitize $ in member names with something safer
238
+ name = m[:name].gsub(/\$/, 'S-')
239
+ "#{m[:static] ? 'static-' : ''}#{m[:tagname]}-#{name}"
226
240
  end
227
241
 
228
242
  def detect_name(tagname, doc_map, code, name_type = :last_name)
@@ -319,10 +333,17 @@ module JsDuck
319
333
  def detect_xtypes(doc_map, code)
320
334
  if doc_map[:xtype]
321
335
  {"widget" => doc_map[:xtype].map {|tag| tag[:name] } }
322
- elsif code[:alias]
336
+ elsif code[:xtype] || code[:alias]
323
337
  xtypes = {}
324
- code[:alias].each do |a|
325
- if a =~ /^(\w+)\.(\w+)$/
338
+ (code[:xtype] || []).each do |a|
339
+ if xtypes["widget"]
340
+ xtypes["widget"] << a
341
+ else
342
+ xtypes["widget"] = [a]
343
+ end
344
+ end
345
+ (code[:alias] || []).each do |a|
346
+ if a =~ /^([\w.]+)\.(\w+)$/
326
347
  if xtypes[$1]
327
348
  xtypes[$1] << $2
328
349
  else
@@ -337,7 +358,12 @@ module JsDuck
337
358
  end
338
359
 
339
360
  def detect_meta(doc_map)
340
- doc_map[:meta] ? doc_map[:meta].map {|tag| {:name => tag[:name], :content => tag[:content]} } : []
361
+ meta = {}
362
+ (doc_map[:meta] || []).map do |tag|
363
+ meta[tag[:name]] = [] unless meta[tag[:name]]
364
+ meta[tag[:name]] << tag[:doc]
365
+ end
366
+ meta
341
367
  end
342
368
 
343
369
  def detect_deprecated(doc_map)
@@ -407,8 +433,12 @@ module JsDuck
407
433
  if it[:name] =~ /^(.+)\.([^.]+)$/
408
434
  it[:name] = $2
409
435
  parent = index[$1]
410
- parent[:properties] = [] unless parent[:properties]
411
- parent[:properties] << it
436
+ if parent
437
+ parent[:properties] = [] unless parent[:properties]
438
+ parent[:properties] << it
439
+ else
440
+ Logger.instance.warn("Ignoring subproperty #{$1}.#{$2}, no parent found with name '#{$1}'.", @filename, @linenr)
441
+ end
412
442
  else
413
443
  items << it
414
444
  end
@@ -429,7 +459,7 @@ module JsDuck
429
459
  # Combines :doc-s of most tags
430
460
  # Ignores tags that have doc comment themselves and subproperty tags
431
461
  def detect_doc(docs)
432
- ignore_tags = [:param, :return]
462
+ ignore_tags = [:param, :return, :meta]
433
463
  doc_tags = docs.find_all { |tag| !ignore_tags.include?(tag[:tagname]) && !subproperty?(tag) }
434
464
  doc_tags.map { |tag| tag[:doc] }.compact.join(" ")
435
465
  end
@@ -0,0 +1,49 @@
1
+ module JsDuck
2
+
3
+ # Abstract base class for all meta tag implementations.
4
+ #
5
+ # Child classes must define value for @name attribute. They can
6
+ # also provide @multiline, and override #to_html method.
7
+ class MetaTag
8
+ # Name of the tag (required)
9
+ attr_reader :name
10
+
11
+ # True to include all lines up to next @tag as part of this meta-tag
12
+ attr_reader :multiline
13
+
14
+ # Override this to transform the content of meta-tag to HTML to be
15
+ # included into documentation.
16
+ #
17
+ # It gets passed an array of contents gathered from all meta-tags
18
+ # of given type. It should return an HTML string to inject into
19
+ # document. For help in that it can use the #format method to
20
+ # easily support Markdown and {@link/img} tags inside the contents
21
+ # of meta-tag.
22
+ #
23
+ # By default the method returns nil, which means the tag will not
24
+ # be rendered at all.
25
+ def to_html(contents)
26
+ end
27
+
28
+ # This is used to inject the formatter object for #markdown method
29
+ attr_accessor :formatter
30
+
31
+ # Helper method to format the text in standard JsDuck way.
32
+ # This means running it through Markdown engine and expanding
33
+ # {@link} and {@img} tags.
34
+ def format(text)
35
+ @formatter.format(text)
36
+ end
37
+
38
+ # Returns all descendants of MetaTag class.
39
+ def self.descendants
40
+ result = []
41
+ ObjectSpace.each_object(::Class) do |cls|
42
+ result << cls if cls < self
43
+ end
44
+ result
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,48 @@
1
+ require "jsduck/meta_tag"
2
+ require 'jsduck/author_tag'
3
+ require 'jsduck/doc_author_tag'
4
+
5
+ module JsDuck
6
+
7
+ # Loads user-defined meta-tags
8
+ class MetaTagLoader
9
+ # instatiates builtin meta tags
10
+ def initialize
11
+ @classes = MetaTag.descendants
12
+ @meta_tags = @classes.map {|cls| cls.new }
13
+ end
14
+
15
+ # Loads user-defined meta-tags from given paths.
16
+ # Returns list of meta-tag instances.
17
+ def load(paths)
18
+ paths.each do |path|
19
+ if File.directory?(path)
20
+ Dir[path+"/**/*.rb"].each do |file|
21
+ require(file)
22
+ init_remaining
23
+ end
24
+ else
25
+ require(path)
26
+ init_remaining
27
+ end
28
+ end
29
+ @meta_tags
30
+ end
31
+
32
+ # Instantiates meta tag classes that haven't been instantiated
33
+ # already. This is called after each meta-tags file is loaded so
34
+ # that the list of meta-tags will be in order specified from
35
+ # command line.
36
+ def init_remaining
37
+ MetaTag.descendants.each do |cls|
38
+ if !@classes.include?(cls)
39
+ @classes << cls
40
+ newtag = cls.new
41
+ @meta_tags = @meta_tags.find_all {|t| t.name != newtag.name }
42
+ @meta_tags << newtag
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ end