jsduck 3.1.0 → 3.2.1

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 (41) hide show
  1. data/README.md +14 -9
  2. data/Rakefile +31 -230
  3. data/jsduck.gemspec +2 -2
  4. data/lib/jsduck/accessors.rb +14 -6
  5. data/lib/jsduck/aggregator.rb +9 -4
  6. data/lib/jsduck/app.rb +1 -0
  7. data/lib/jsduck/app_data.rb +14 -7
  8. data/lib/jsduck/app_exporter.rb +3 -3
  9. data/lib/jsduck/class.rb +8 -5
  10. data/lib/jsduck/class_formatter.rb +1 -3
  11. data/lib/jsduck/css_parser.rb +1 -1
  12. data/lib/jsduck/doc_formatter.rb +140 -36
  13. data/lib/jsduck/doc_parser.rb +27 -44
  14. data/lib/jsduck/index_html.rb +0 -3
  15. data/lib/jsduck/inherit_doc.rb +20 -4
  16. data/lib/jsduck/js_parser.rb +1 -1
  17. data/lib/jsduck/lint.rb +15 -0
  18. data/lib/jsduck/logger.rb +9 -7
  19. data/lib/jsduck/merger.rb +18 -16
  20. data/lib/jsduck/meta_tag.rb +28 -5
  21. data/lib/jsduck/meta_tag_loader.rb +38 -21
  22. data/lib/jsduck/meta_tag_registry.rb +79 -0
  23. data/lib/jsduck/options.rb +69 -12
  24. data/lib/jsduck/renderer.rb +10 -38
  25. data/lib/jsduck/search_data.rb +53 -3
  26. data/lib/jsduck/tag/abstract.rb +14 -0
  27. data/lib/jsduck/{author_tag.rb → tag/author.rb} +2 -2
  28. data/lib/jsduck/tag/deprecated.rb +33 -0
  29. data/lib/jsduck/{doc_author_tag.rb → tag/docauthor.rb} +2 -2
  30. data/lib/jsduck/tag/markdown.rb +12 -0
  31. data/lib/jsduck/tag/preventable.rb +28 -0
  32. data/lib/jsduck/tag/protected.rb +14 -0
  33. data/lib/jsduck/tag/readonly.rb +14 -0
  34. data/lib/jsduck/tag/required.rb +21 -0
  35. data/lib/jsduck/tag/static.rb +14 -0
  36. data/lib/jsduck/tag/template.rb +23 -0
  37. data/opt/example.js +149 -0
  38. metadata +17 -9
  39. data/opt/extjs-welcome.html +0 -74
  40. data/opt/touch-iframe.html +0 -85
  41. data/opt/touch-welcome.html +0 -122
@@ -34,9 +34,6 @@ module JsDuck
34
34
  "{header}" => @opts.header,
35
35
  "{footer}" => "<div id='footer-content' style='display: none'>#{@opts.footer}</div>",
36
36
  "{extjs_path}" => @opts.extjs_path,
37
- "{local_storage_db}" => @opts.local_storage_db,
38
- "{show_print_button}" => @opts.seo ? "true" : "false",
39
- "{touch_examples_ui}" => @opts.touch_examples_ui ? "true" : "false",
40
37
  "{welcome}" => @welcome.to_html,
41
38
  "{categories}" => @categories.to_html,
42
39
  "{guides}" => @guides.to_html,
@@ -1,4 +1,5 @@
1
1
  require 'jsduck/logger'
2
+ require 'pp'
2
3
 
3
4
  module JsDuck
4
5
 
@@ -41,19 +42,34 @@ module JsDuck
41
42
  warn("@inheritdoc #{inherit[:cls]}##{inherit[:member]} - class not found", context)
42
43
  return m
43
44
  end
44
- parent = parent_cls.get_member(inherit[:member], inherit[:type] || m[:tagname])
45
+ parent = parent_cls.get_members(inherit[:member], inherit[:type] || m[:tagname], inherit[:static] || m[:meta][:static])[0]
45
46
  unless parent
46
47
  warn("@inheritdoc #{inherit[:cls]}##{inherit[:member]} - member not found", context)
47
48
  return m
48
49
  end
49
50
  else
50
51
  parent_cls = @relations[m[:owner]].parent
51
- unless parent_cls
52
+ mixins = @relations[m[:owner]].mixins
53
+ # Warn when no parent or mixins at all
54
+ if !parent_cls && mixins.length == 0
52
55
  warn("@inheritdoc - parent class not found", context)
53
56
  return m
54
57
  end
55
- parent = parent_cls.get_member(m[:name], m[:tagname])
56
- unless parent
58
+ # First check for the member in all mixins, because members
59
+ # from mixins override those from parent class. Looking first
60
+ # from mixins is probably a bit slower, but it's the correct
61
+ # order to do things.
62
+ if mixins.length > 0
63
+ parent = mixins.map do |mix|
64
+ mix.get_members(m[:name], m[:tagname], m[:meta][:static])[0]
65
+ end.compact.first
66
+ end
67
+ # When not found, try looking from parent class
68
+ if !parent && parent_cls
69
+ parent = parent_cls.get_members(m[:name], m[:tagname], m[:meta][:static])[0]
70
+ end
71
+ # Only when both parent and mixins fail, throw warning
72
+ if !parent
57
73
  warn("@inheritdoc - parent member not found", context)
58
74
  return m
59
75
  end
@@ -8,7 +8,7 @@ module JsDuck
8
8
  class JsParser < JsLiteralParser
9
9
  def initialize(input, options = {})
10
10
  super(input)
11
- @doc_parser = DocParser.new(:js, options[:meta_tags])
11
+ @doc_parser = DocParser.new
12
12
  @docs = []
13
13
  @ext_namespaces = options[:ext_namespaces] || ["Ext"]
14
14
  end
data/lib/jsduck/lint.rb CHANGED
@@ -13,6 +13,7 @@ module JsDuck
13
13
  # Runs the linter
14
14
  def run
15
15
  warn_globals
16
+ warn_no_doc
16
17
  warn_unnamed
17
18
  warn_optional_params
18
19
  warn_duplicate_params
@@ -41,6 +42,20 @@ module JsDuck
41
42
  end
42
43
  end
43
44
 
45
+ # print warning for each member or parameter with no name
46
+ def warn_no_doc
47
+ @relations.each do |cls|
48
+ if cls[:doc] == ""
49
+ warn(:no_doc, "No documentation for #{cls[:name]}", cls)
50
+ end
51
+ end
52
+ each_member do |member|
53
+ if member[:doc] == ""
54
+ warn(:no_doc, "No documentation for #{member[:owner]}##{member[:name]}", member)
55
+ end
56
+ end
57
+ end
58
+
44
59
  # print warning for each non-optional parameter that follows an optional parameter
45
60
  def warn_optional_params
46
61
  each_member do |member|
data/lib/jsduck/logger.rb CHANGED
@@ -17,9 +17,13 @@ module JsDuck
17
17
  [:inheritdoc, "@inheritdoc referring to unknown class or member"],
18
18
  [:extend, "@extend or @mixin referring to unknown class"],
19
19
  [:link, "{@link} to unknown class or member"],
20
+ [:link_private, "{@link} to private member"],
21
+ [:link_ambiguous, "{@link} is ambiguous"],
22
+ [:link_auto, "Auto-detected link to unknown class or member"],
20
23
 
21
24
  [:alt_name, "Name used as both classname and alternate classname"],
22
25
  [:name_missing, "Member or parameter has no name"],
26
+ [:no_doc, "Member or class without documentation"],
23
27
  [:dup_param, "Method has two parameters with same name"],
24
28
  [:req_after_opt, "Required parameter comes after optional"],
25
29
  [:subproperty, "@param foo.bar where foo param doesn't exist"],
@@ -34,10 +38,12 @@ module JsDuck
34
38
  [:cat_class_missing, "Class is missing from categories file"],
35
39
  [:guide, "Guide is missing from --guides dir"],
36
40
  ]
37
- # Turn on all warnings by default
41
+ # Turn off all warnings by default.
42
+ # This is good for testing.
43
+ # When running JSDuck app, the Options class enables most warnings.
38
44
  @warnings = {}
39
45
  @warning_docs.each do |w|
40
- @warnings[w[0]] = true
46
+ @warnings[w[0]] = false
41
47
  end
42
48
 
43
49
  @shown_warnings = {}
@@ -66,11 +72,7 @@ module JsDuck
66
72
 
67
73
  # get documentation for all warnings
68
74
  def doc_warnings
69
- @warning_docs.map {|w| "+#{w[0]} - #{w[1]}" } + [
70
- " ",
71
- "+all - to turn on all warnings (default)",
72
- " ",
73
- ]
75
+ @warning_docs.map {|w| " #{@warnings[w[0]] ? '+' : '-'}#{w[0]} - #{w[1]}" } + [" "]
74
76
  end
75
77
 
76
78
  # Prints warning message.
data/lib/jsduck/merger.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'jsduck/logger'
2
+ require 'jsduck/meta_tag_registry'
2
3
 
3
4
  module JsDuck
4
5
 
@@ -15,6 +16,7 @@ module JsDuck
15
16
  def initialize
16
17
  @filename = ""
17
18
  @linenr = 0
19
+ @meta_tags = MetaTagRegistry.instance
18
20
  end
19
21
 
20
22
  def merge(docs, code)
@@ -64,6 +66,8 @@ module JsDuck
64
66
  :method
65
67
  elsif code[:type] == :assignment && code[:right] && code[:right][:type] == :function
66
68
  :method
69
+ elsif doc_map[:return] || doc_map[:param]
70
+ :method
67
71
  else
68
72
  :property
69
73
  end
@@ -153,7 +157,6 @@ module JsDuck
153
157
  :doc => detect_doc(docs),
154
158
  :params => detect_params(docs, code),
155
159
  :return => detect_return(doc_map, name == "constructor" ? "Object" : "undefined"),
156
- :template => !!doc_map[:template],
157
160
  }, doc_map)
158
161
  end
159
162
 
@@ -176,7 +179,6 @@ module JsDuck
176
179
  :owner => detect_owner(doc_map) || owner,
177
180
  :type => detect_type(:cfg, doc_map, code),
178
181
  :doc => detect_doc(docs),
179
- :required => detect_required(:cfg, doc_map),
180
182
  :default => detect_default(:cfg, doc_map, code),
181
183
  :properties => detect_subproperties(docs, :cfg),
182
184
  :accessor => !!doc_map[:accessor],
@@ -192,6 +194,7 @@ module JsDuck
192
194
  :owner => detect_owner(doc_map),
193
195
  :type => detect_type(:property, doc_map, code),
194
196
  :doc => detect_doc(docs),
197
+ :default => detect_default(:property, doc_map, code),
195
198
  :properties => detect_subproperties(docs, :property),
196
199
  }, doc_map)
197
200
  end
@@ -222,10 +225,7 @@ module JsDuck
222
225
  def add_shared(hash, doc_map)
223
226
  hash.merge!({
224
227
  :private => !!doc_map[:private],
225
- :protected => !!doc_map[:protected],
226
- :static => !!doc_map[:static],
227
228
  :inheritable => !!doc_map[:inheritable],
228
- :deprecated => detect_deprecated(doc_map),
229
229
  :inheritdoc => doc_map[:inheritdoc] ? doc_map[:inheritdoc].first : nil,
230
230
  :meta => detect_meta(doc_map),
231
231
  })
@@ -236,7 +236,7 @@ module JsDuck
236
236
  def create_member_id(m)
237
237
  # Sanitize $ in member names with something safer
238
238
  name = m[:name].gsub(/\$/, 'S-')
239
- "#{m[:static] ? 'static-' : ''}#{m[:tagname]}-#{name}"
239
+ "#{m[:meta][:static] ? 'static-' : ''}#{m[:tagname]}-#{name}"
240
240
  end
241
241
 
242
242
  def detect_name(tagname, doc_map, code, name_type = :last_name)
@@ -314,11 +314,6 @@ module JsDuck
314
314
  return explicit_name == "" || explicit_name == implicit_name
315
315
  end
316
316
 
317
- def detect_required(tagname, doc_map)
318
- main_tag = doc_map[tagname] ? doc_map[tagname].first : {}
319
- return main_tag[:optional] == false
320
- end
321
-
322
317
  # for detecting mixins and alternateClassNames
323
318
  def detect_list(type, doc_map, code)
324
319
  if doc_map[type]
@@ -350,7 +345,7 @@ module JsDuck
350
345
  # it instead of creating a new hash.
351
346
  def build_aliases_hash(aliases, hash={})
352
347
  aliases.each do |a|
353
- if a =~ /^([\w.]+)\.(\w+)$/
348
+ if a =~ /^(.+)\.([^.]+)$/
354
349
  if hash[$1]
355
350
  hash[$1] << $2
356
351
  else
@@ -367,17 +362,24 @@ module JsDuck
367
362
  meta[tag[:name]] = [] unless meta[tag[:name]]
368
363
  meta[tag[:name]] << tag[:doc]
369
364
  end
370
- meta
371
- end
372
365
 
373
- def detect_deprecated(doc_map)
374
- doc_map[:deprecated] ? doc_map[:deprecated].first : nil
366
+ meta.each_pair do |key, value|
367
+ tag = @meta_tags[key]
368
+ meta[key] = tag.to_value(tag.boolean ? true : value)
369
+ end
370
+
371
+ meta[:required] = true if detect_required(doc_map)
372
+ meta
375
373
  end
376
374
 
377
375
  def detect_singleton(doc_map, code)
378
376
  !!(doc_map[:singleton] || code[:type] == :ext_define && code[:singleton])
379
377
  end
380
378
 
379
+ def detect_required(doc_map)
380
+ doc_map[:cfg] && doc_map[:cfg].first[:optional] == false
381
+ end
382
+
381
383
  def detect_params(docs, code)
382
384
  implicit = detect_implicit_params(code)
383
385
  explicit = detect_explicit_params(docs)
@@ -8,17 +8,40 @@ module JsDuck
8
8
  # Name of the tag (required)
9
9
  attr_reader :name
10
10
 
11
+ # The key under which to store this tag. Must be a symbol. When
12
+ # not provided then :name is converted to symbol and used as key.
13
+ attr_accessor :key
14
+
15
+ # The text to display in member signature. Must be a hash
16
+ # defining the short and long versions of the signature text:
17
+ #
18
+ # {:long => "something", :short => "SOM"}
19
+ #
20
+ attr_reader :signature
21
+
11
22
  # True to include all lines up to next @tag as part of this meta-tag
12
23
  attr_reader :multiline
13
24
 
25
+ # True to ignore any text after the @tag, just record the
26
+ # existance of the tag.
27
+ attr_reader :boolean
28
+
29
+ # It gets passed an array of contents gathered from all meta-tags
30
+ # of given type. It should return the value to be stored for this
31
+ # meta-tag at :key. The returned value is also passed to #to_html
32
+ # method. Returning nil will cause the tag to be ignored. By
33
+ # default the contents are returned unchanged.
34
+ def to_value(contents)
35
+ contents
36
+ end
37
+
14
38
  # Override this to transform the content of meta-tag to HTML to be
15
39
  # included into documentation.
16
40
  #
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.
41
+ # It gets passed the value returned by #to_value method. It should
42
+ # return an HTML string to inject into document. For help in that
43
+ # it can use the #format method to easily support Markdown and
44
+ # {@link/img} tags inside the contents of meta-tag.
22
45
  #
23
46
  # By default the method returns nil, which means the tag will not
24
47
  # be rendered at all.
@@ -1,32 +1,41 @@
1
1
  require "jsduck/meta_tag"
2
- require 'jsduck/author_tag'
3
- require 'jsduck/doc_author_tag'
4
2
 
5
3
  module JsDuck
6
4
 
7
- # Loads user-defined meta-tags
5
+ # Loader for built-in and user-defined meta-tags.
8
6
  class MetaTagLoader
9
- # instatiates builtin meta tags
7
+ attr_reader :meta_tags
8
+
10
9
  def initialize
11
- @classes = MetaTag.descendants
12
- @meta_tags = @classes.map {|cls| cls.new }
10
+ @classes = []
11
+ @meta_tags = []
13
12
  end
14
13
 
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
14
+ # Loads user-defined meta-tags from given path.
15
+ #
16
+ # * If path is a directory, loads all *.rb files in it.
17
+ # * If path is the symbol :builtins, loads the builtin
18
+ # tags from ./tag dir.
19
+ # * Otherwise loads tags from the single file.
20
+ def load(path)
21
+ if path == :builtins
22
+ load(File.dirname(__FILE__) + "/tag")
23
+ elsif File.directory?(path)
24
+ # Sort paths, so they are always loaded in the same order.
25
+ # This is important for signatures to always be rendered in
26
+ # the same order.
27
+ Dir[path+"/**/*.rb"].sort.each {|file| load_file(file) }
28
+ else
29
+ load_file(path)
28
30
  end
29
- @meta_tags
31
+ end
32
+
33
+ private
34
+
35
+ # Loads just one file.
36
+ def load_file(file)
37
+ require(file)
38
+ init_remaining
30
39
  end
31
40
 
32
41
  # Instantiates meta tag classes that haven't been instantiated
@@ -37,12 +46,20 @@ module JsDuck
37
46
  MetaTag.descendants.each do |cls|
38
47
  if !@classes.include?(cls)
39
48
  @classes << cls
40
- newtag = cls.new
49
+ newtag = create_tag(cls)
41
50
  @meta_tags = @meta_tags.find_all {|t| t.name != newtag.name }
42
51
  @meta_tags << newtag
43
52
  end
44
53
  end
45
54
  end
55
+
56
+ # Instanciates tag class.
57
+ # When .key is missing, creates it from .name
58
+ def create_tag(cls)
59
+ tag = cls.new
60
+ tag.key = tag.name.to_sym unless tag.key
61
+ tag
62
+ end
46
63
  end
47
64
 
48
65
  end
@@ -0,0 +1,79 @@
1
+ require 'singleton'
2
+ require "jsduck/meta_tag_loader"
3
+
4
+ module JsDuck
5
+
6
+ # Access to meta-tags
7
+ class MetaTagRegistry
8
+ include Singleton
9
+
10
+ def initialize
11
+ @tags = []
12
+ @map = {}
13
+ end
14
+
15
+ # Loads meta-tags from the given paths. See MetaTagLoader#load
16
+ # for details.
17
+ #
18
+ # This should only be called once. Calling it twice will override
19
+ # the previously loaded tags.
20
+ def load(paths)
21
+ loader = MetaTagLoader.new
22
+ paths.each {|p| loader.load(p) }
23
+ register(loader.meta_tags)
24
+ end
25
+
26
+ # Registers MetaTag instances.
27
+ #
28
+ # NB! This is for testing purposes only, elsewhere always use #load.
29
+ def register(tags)
30
+ @tags = tags
31
+ register_keys
32
+ end
33
+
34
+ # Returns array of all available tag instances
35
+ def tags
36
+ @tags
37
+ end
38
+
39
+ # Accesses tag by key or name
40
+ def [](name)
41
+ @map[name]
42
+ end
43
+
44
+ # Returns the formatter assigned to tags
45
+ def formatter
46
+ @formatter
47
+ end
48
+
49
+ # Sets the doc-formatter for all tags
50
+ def formatter=(doc_formatter)
51
+ @formatter = doc_formatter
52
+ @tags.each {|tag| tag.formatter = doc_formatter }
53
+ end
54
+
55
+ # Returns array of attributes to be shown in member signatures
56
+ # (and in order they should be shown in).
57
+ def signatures
58
+ if !@signatures
59
+ @signatures = @tags.find_all(&:signature).map do |tag|
60
+ s = tag.signature
61
+ s[:key] = tag.key
62
+ s
63
+ end
64
+ end
65
+ @signatures
66
+ end
67
+
68
+ private
69
+
70
+ def register_keys
71
+ @map = {}
72
+ @tags.each do |tag|
73
+ @map[tag.key] = tag
74
+ @map[tag.name] = tag
75
+ end
76
+ end
77
+ end
78
+
79
+ end