jsduck 3.1.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
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