jsduck 5.0.0.beta01 → 5.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.travis.yml +1 -0
  2. data/README.md +6 -32
  3. data/Rakefile +5 -5
  4. data/bin/jsduck +0 -1
  5. data/js-classes/String.js +3 -5
  6. data/jsduck.gemspec +3 -2
  7. data/lib/jsduck/aggregator.rb +1 -3
  8. data/lib/jsduck/app.rb +2 -2
  9. data/lib/jsduck/categories/file.rb +0 -6
  10. data/lib/jsduck/class.rb +1 -2
  11. data/lib/jsduck/doc/parser.rb +12 -5
  12. data/lib/jsduck/doc/scanner.rb +6 -0
  13. data/lib/jsduck/doc/standard_tag_parser.rb +10 -5
  14. data/lib/jsduck/doc/subproperties.rb +9 -2
  15. data/lib/jsduck/docs_code_comparer.rb +20 -7
  16. data/lib/jsduck/exporter/app.rb +18 -13
  17. data/lib/jsduck/exporter/full.rb +18 -21
  18. data/lib/jsduck/format/doc.rb +0 -1
  19. data/lib/jsduck/format/html_stack.rb +1 -2
  20. data/lib/jsduck/format/subproperties.rb +2 -2
  21. data/lib/jsduck/inline/auto_link.rb +1 -1
  22. data/lib/jsduck/inline/img.rb +1 -1
  23. data/lib/jsduck/inline/link.rb +4 -6
  24. data/lib/jsduck/inline/video.rb +1 -2
  25. data/lib/jsduck/js/ast.rb +1 -1
  26. data/lib/jsduck/js/esprima.rb +24 -9
  27. data/lib/jsduck/logger.rb +50 -12
  28. data/lib/jsduck/members_index.rb +1 -2
  29. data/lib/jsduck/merger.rb +20 -2
  30. data/lib/jsduck/options.rb +125 -24
  31. data/lib/jsduck/process/accessors.rb +21 -8
  32. data/lib/jsduck/process/enums.rb +2 -3
  33. data/lib/jsduck/process/ext4_events.rb +2 -1
  34. data/lib/jsduck/process/global_members.rb +1 -2
  35. data/lib/jsduck/process/importer.rb +2 -6
  36. data/lib/jsduck/process/inherit_class.rb +58 -0
  37. data/lib/jsduck/process/inherit_doc.rb +6 -175
  38. data/lib/jsduck/process/inherit_members.rb +257 -0
  39. data/lib/jsduck/process/lint.rb +18 -7
  40. data/lib/jsduck/process/overrides.rb +1 -2
  41. data/lib/jsduck/render/class.rb +1 -1
  42. data/lib/jsduck/tag/alias.rb +2 -1
  43. data/lib/jsduck/tag/alternate_class_names.rb +1 -0
  44. data/lib/jsduck/tag/aside.rb +3 -3
  45. data/lib/jsduck/tag/author.rb +18 -3
  46. data/lib/jsduck/tag/autodetected.rb +21 -0
  47. data/lib/jsduck/tag/boolean_tag.rb +1 -1
  48. data/lib/jsduck/tag/cfg.rb +7 -3
  49. data/lib/jsduck/tag/class.rb +1 -1
  50. data/lib/jsduck/tag/class_list_tag.rb +1 -1
  51. data/lib/jsduck/tag/constructor.rb +1 -1
  52. data/lib/jsduck/tag/css_var.rb +1 -1
  53. data/lib/jsduck/tag/default.rb +1 -1
  54. data/lib/jsduck/tag/deprecated_tag.rb +1 -1
  55. data/lib/jsduck/tag/docauthor.rb +2 -0
  56. data/lib/jsduck/tag/enum.rb +2 -2
  57. data/lib/jsduck/tag/event.rb +1 -1
  58. data/lib/jsduck/tag/extends.rb +1 -1
  59. data/lib/jsduck/tag/ftype.rb +2 -1
  60. data/lib/jsduck/tag/inheritdoc.rb +1 -1
  61. data/lib/jsduck/tag/localdoc.rb +33 -0
  62. data/lib/jsduck/tag/markdown.rb +1 -1
  63. data/lib/jsduck/tag/member.rb +1 -1
  64. data/lib/jsduck/tag/method.rb +1 -1
  65. data/lib/jsduck/tag/mixins.rb +1 -0
  66. data/lib/jsduck/tag/override.rb +1 -1
  67. data/lib/jsduck/tag/param.rb +16 -5
  68. data/lib/jsduck/tag/preventable.rb +1 -1
  69. data/lib/jsduck/tag/property.rb +7 -3
  70. data/lib/jsduck/tag/ptype.rb +2 -1
  71. data/lib/jsduck/tag/requires.rb +1 -0
  72. data/lib/jsduck/tag/return.rb +2 -1
  73. data/lib/jsduck/tag/since.rb +1 -5
  74. data/lib/jsduck/tag/tag.rb +21 -12
  75. data/lib/jsduck/tag/throws.rb +2 -1
  76. data/lib/jsduck/tag/type.rb +2 -2
  77. data/lib/jsduck/tag/uses.rb +1 -0
  78. data/lib/jsduck/tag/xtype.rb +2 -1
  79. data/lib/jsduck/tag_loader.rb +26 -15
  80. data/lib/jsduck/tag_registry.rb +20 -11
  81. data/lib/jsduck/web/css.rb +22 -0
  82. data/lib/jsduck/web/data.rb +50 -0
  83. data/lib/jsduck/web/icons.rb +31 -0
  84. data/lib/jsduck/web/index_html.rb +88 -0
  85. data/lib/jsduck/web/search.rb +148 -0
  86. data/lib/jsduck/{source/writer.rb → web/source.rb} +2 -2
  87. data/lib/jsduck/web/template.rb +52 -0
  88. data/lib/jsduck/web/writer.rb +84 -0
  89. metadata +513 -488
  90. data/lib/jsduck/app_data.rb +0 -41
  91. data/lib/jsduck/icons.rb +0 -29
  92. data/lib/jsduck/index_html.rb +0 -84
  93. data/lib/jsduck/search_data.rb +0 -146
  94. data/lib/jsduck/template_dir.rb +0 -50
  95. data/lib/jsduck/web_writer.rb +0 -87
@@ -6,40 +6,37 @@ module JsDuck
6
6
  # Exporter for all the class docs.
7
7
  class Full
8
8
  def initialize(relations, opts={})
9
- @relations = relations
10
- # opts parameter is here just for compatibility with other exporters
9
+ # parameters are just for compatibility with other exporters
11
10
  end
12
11
 
13
- # Returns all data in Class object as hash.
12
+ # Returns a hash of class data, with :members field expanded
13
+ # into list of all members (including those inherited from
14
+ # parents and mixins).
14
15
  def export(cls)
15
16
  # Make copy of the internal data structure of a class
16
17
  # so our modifications on it will be safe.
17
18
  h = cls.internal_doc.clone
18
19
 
19
- h[:members] = {}
20
- h[:statics] = {}
21
- TagRegistry.member_type_names.each do |tagname|
22
- h[:members][tagname] = export_members(cls, {:tagname => tagname, :static => false})
23
- h[:statics][tagname] = export_members(cls, {:tagname => tagname, :static => true})
24
- end
25
- h[:component] = cls.inherits_from?("Ext.Component")
26
- h[:superclasses] = cls.superclasses.collect {|c| c[:name] }
27
- h[:subclasses] = @relations.subclasses(cls).collect {|c| c[:name] }.sort
28
- h[:mixedInto] = @relations.mixed_into(cls).collect {|c| c[:name] }.sort
29
- h[:alternateClassNames] = cls[:alternateClassNames].sort if cls[:alternateClassNames]
30
-
31
- h[:mixins] = cls.deps(:mixins).collect {|c| c[:name] }.sort
32
- h[:parentMixins] = cls.parent_deps(:mixins).collect {|c| c[:name] }.sort
33
- h[:requires] = cls.deps(:requires).collect {|c| c[:name] }.sort
34
- h[:uses] = cls.deps(:uses).collect {|c| c[:name] }.sort
20
+ h[:members] = export_members(cls)
35
21
 
36
22
  h
37
23
  end
38
24
 
39
25
  private
40
26
 
41
- # Looks up members, and sorts them so that constructor method is first
42
- def export_members(cls, cfg)
27
+ # Generates flat list of all members
28
+ def export_members(cls)
29
+ groups = []
30
+ TagRegistry.member_type_names.each do |tagname|
31
+ groups << export_members_group(cls, {:tagname => tagname, :static => false})
32
+ groups << export_members_group(cls, {:tagname => tagname, :static => true})
33
+ end
34
+ groups.flatten
35
+ end
36
+
37
+ # Looks up members of given type, and sorts them so that
38
+ # constructor method is first
39
+ def export_members_group(cls, cfg)
43
40
  ms = cls.find_members(cfg)
44
41
  ms.sort! {|a,b| a[:name] <=> b[:name] }
45
42
  cfg[:tagname] == :method ? constructor_first(ms) : ms
@@ -1,4 +1,3 @@
1
- require 'rubygems'
2
1
  require 'strscan'
3
2
  require 'rdiscount'
4
3
  require 'jsduck/format/html_stack'
@@ -78,9 +78,8 @@ module JsDuck
78
78
  end
79
79
 
80
80
  def warn(msg, tags)
81
- ctx = @doc_context
82
81
  tag_list = tags.map {|tag| "<#{tag}>" }.join(", ")
83
- Logger.warn(:html, "#{msg}: #{tag_list}", ctx[:filename], ctx[:linenr])
82
+ Logger.warn(:html, "#{msg}: #{tag_list}", @doc_context)
84
83
  end
85
84
 
86
85
  def void?(tag)
@@ -50,9 +50,9 @@ module JsDuck
50
50
  else
51
51
  context = @formatter.doc_context
52
52
  if tp.error == :syntax
53
- Logger.warn(:type_syntax, "Incorrect type syntax #{type}", context[:filename], context[:linenr])
53
+ Logger.warn(:type_syntax, "Incorrect type syntax #{type}", context)
54
54
  else
55
- Logger.warn(:type_name, "Unknown type #{type}", context[:filename], context[:linenr])
55
+ Logger.warn(:type_name, "Unknown type #{type}", context)
56
56
  end
57
57
  Util::HTML.escape(type)
58
58
  end
@@ -97,7 +97,7 @@ module JsDuck
97
97
  end
98
98
 
99
99
  def warn_magic_link(msg)
100
- Logger.warn(:link_auto, msg, @doc_context[:filename], @doc_context[:linenr])
100
+ Logger.warn(:link_auto, msg, @doc_context)
101
101
  end
102
102
 
103
103
  end
@@ -38,7 +38,7 @@ module JsDuck
38
38
  def apply_tpl(url, alt_text)
39
39
  img = @images.get(url)
40
40
  if !img
41
- Logger.warn(:image, "Image #{url} not found.", @doc_context[:filename], @doc_context[:linenr])
41
+ Logger.warn(:image, "Image #{url} not found.", @doc_context)
42
42
  img = {}
43
43
  end
44
44
 
@@ -62,15 +62,13 @@ module JsDuck
62
62
  text = cls
63
63
  end
64
64
 
65
- file = @doc_context[:filename]
66
- line = @doc_context[:linenr]
67
65
  if !@relations[cls]
68
- Logger.warn(:link, "#{full_link} links to non-existing class", file, line)
66
+ Logger.warn(:link, "#{full_link} links to non-existing class", @doc_context)
69
67
  return text
70
68
  elsif member
71
69
  ms = @renderer.find_members(cls, {:name => member, :tagname => type, :static => static})
72
70
  if ms.length == 0
73
- Logger.warn(:link, "#{full_link} links to non-existing member", file, line)
71
+ Logger.warn(:link, "#{full_link} links to non-existing member", @doc_context)
74
72
  return text
75
73
  end
76
74
 
@@ -82,11 +80,11 @@ module JsDuck
82
80
  instance_ms = ms.find_all {|m| !m[:static] }
83
81
  if instance_ms.length > 1
84
82
  alternatives = instance_ms.map {|m| "#{m[:tagname]} in #{m[:owner]}" }.join(", ")
85
- Logger.warn(:link_ambiguous, "#{full_link} is ambiguous: "+alternatives, file, line)
83
+ Logger.warn(:link_ambiguous, "#{full_link} is ambiguous: "+alternatives, @doc_context)
86
84
  elsif instance_ms.length == 0
87
85
  static_ms = ms.find_all {|m| m[:static] }
88
86
  alternatives = static_ms.map {|m| "static " + m[:tagname].to_s }.join(", ")
89
- Logger.warn(:link_ambiguous, "#{full_link} is ambiguous: "+alternatives, file, line)
87
+ Logger.warn(:link_ambiguous, "#{full_link} is ambiguous: "+alternatives, @doc_context)
90
88
  end
91
89
  end
92
90
 
@@ -46,8 +46,7 @@ module JsDuck
46
46
  # applies the video template of the specified type
47
47
  def apply_tpl(type, url, alt_text)
48
48
  unless @templates.has_key?(type)
49
- ctx = @doc_context
50
- Logger.warn(nil, "Unknown video type #{type}", ctx[:filename], ctx[:linenr])
49
+ Logger.warn(nil, "Unknown video type #{type}", @doc_context)
51
50
  end
52
51
 
53
52
  @templates[type].gsub(/(%\w)/) do
data/lib/jsduck/js/ast.rb CHANGED
@@ -286,7 +286,7 @@ module JsDuck
286
286
  else
287
287
  m[:private] = true
288
288
  end
289
- m[:autodetected] = true
289
+ m[:autodetected] = {:tagname => m[:tagname]}
290
290
  end
291
291
 
292
292
  if docset
@@ -1,11 +1,15 @@
1
- require 'v8'
1
+ require 'execjs'
2
2
  require 'jsduck/util/json'
3
3
  require 'jsduck/util/singleton'
4
4
 
5
5
  module JsDuck
6
6
  module Js
7
7
 
8
- # Runs Esprima.js through V8.
8
+ # Runs Esprima.js through JavaScript runtime selected by ExecJS.
9
+ # Normally this will be V8 engine within therubyracer gem, but when
10
+ # JSDuck is installed through some other means than rubygems, then
11
+ # one could use any of the runtimes supported by ExecJS. (NodeJS
12
+ # for example.)
9
13
  #
10
14
  # Initialized as singleton to avoid loading the esprima.js more
11
15
  # than once - otherwise performace will severely suffer.
@@ -13,23 +17,34 @@ module JsDuck
13
17
  include Util::Singleton
14
18
 
15
19
  def initialize
16
- @v8 = V8::Context.new
17
- esprima = File.dirname(File.expand_path(__FILE__))+"/esprima/esprima.js";
20
+ esprima_path = File.dirname(File.expand_path(__FILE__)) + "/esprima/esprima.js"
21
+ esprima = IO.read(esprima_path)
18
22
 
19
23
  # Esprima attempts to assign to window.esprima, but our v8
20
24
  # engine has no global window variable defined. So define our
21
25
  # own and then grab esprima out from it again.
22
- @v8.eval("var window = {};")
23
- @v8.load(esprima)
24
- @v8.eval("var esprima = window.esprima;")
26
+ source = <<-EOJS
27
+ if (typeof window === "undefined") {
28
+ var window = {};
29
+ }
30
+
31
+ #{esprima}
32
+
33
+ var esprima = window.esprima;
34
+
35
+ function runEsprima(js) {
36
+ return JSON.stringify(esprima.parse(js, {comment: true, range: true, raw: true}));
37
+ }
38
+ EOJS
39
+
40
+ @context = ExecJS.compile(source)
25
41
  end
26
42
 
27
43
  # Parses JavaScript source code using Esprima.js
28
44
  #
29
45
  # Returns the resulting AST
30
46
  def parse(input)
31
- @v8['js'] = input
32
- json = @v8.eval("JSON.stringify(esprima.parse(js, {comment: true, range: true, raw: true}))")
47
+ json = @context.call("runEsprima", input)
33
48
  return Util::Json.parse(json, :max_nesting => false)
34
49
  end
35
50
 
data/lib/jsduck/logger.rb CHANGED
@@ -17,6 +17,8 @@ module JsDuck
17
17
  [:inheritdoc, "@inheritdoc referring to unknown class or member"],
18
18
  [:extend, "@extend/mixin/requires/uses referring to unknown class"],
19
19
  [:tag, "Use of unsupported @tag"],
20
+ [:tag_repeated, "An @tag used multiple times, but only once allowed"],
21
+ [:tag_syntax, "@tag syntax error"],
20
22
  [:link, "{@link} to unknown class or member"],
21
23
  [:link_ambiguous, "{@link} is ambiguous"],
22
24
  [:link_auto, "Auto-detected link to unknown class or member"],
@@ -24,7 +26,7 @@ module JsDuck
24
26
 
25
27
  [:alt_name, "Name used as both classname and alternate classname"],
26
28
  [:name_missing, "Member or parameter has no name"],
27
- [:no_doc, "Member or class without documentation"],
29
+ [:no_doc, "Public class or member without documentation"],
28
30
  [:dup_param, "Method has two parameters with the same name"],
29
31
  [:dup_member, "Class has two members with the same name"],
30
32
  [:req_after_opt, "Required parameter comes after optional"],
@@ -33,7 +35,7 @@ module JsDuck
33
35
  [:sing_static, "Singleton class member marked as @static"],
34
36
  [:type_syntax, "Syntax error in {type definition}"],
35
37
  [:type_name, "Unknown type referenced in {type definition}"],
36
- [:enum, "Enum with invalid values of no values at all"],
38
+ [:enum, "Enum with invalid values or no values at all"],
37
39
 
38
40
  [:image, "{@img} referring to missing file"],
39
41
  [:image_unused, "An image exists in --images dir that's not used"],
@@ -50,7 +52,7 @@ module JsDuck
50
52
  # When running JSDuck app, the Options class enables most warnings.
51
53
  @warnings = {}
52
54
  @warning_docs.each do |w|
53
- @warnings[w[0]] = false
55
+ @warnings[w[0]] = {:enabled => false, :patterns => []}
54
56
  end
55
57
 
56
58
  @shown_warnings = {}
@@ -63,15 +65,26 @@ module JsDuck
63
65
  end
64
66
  end
65
67
 
66
- # Enabled or disables a particular warning
67
- # or all warnings when type == :all
68
- def set_warning(type, enabled)
68
+ # Enables or disables a particular warning
69
+ # or all warnings when type == :all.
70
+ # Additionally a filename pattern can be specified.
71
+ def set_warning(type, enabled, pattern=nil)
69
72
  if type == :all
73
+ # When used with a pattern, only add the pattern to the rules
74
+ # where it can have an effect - otherwise we get a warning.
70
75
  @warnings.each_key do |key|
71
- @warnings[key] = enabled
76
+ set_warning(key, enabled, pattern) unless pattern && @warnings[key][:enabled] == enabled
72
77
  end
73
78
  elsif @warnings.has_key?(type)
74
- @warnings[type] = enabled
79
+ if pattern
80
+ if @warnings[type][:enabled] == enabled
81
+ warn(nil, "Warning rule '#{enabled ? '+' : '-'}#{type}:#{pattern}' has no effect")
82
+ else
83
+ @warnings[type][:patterns] << Regexp.new(Regexp.escape(pattern))
84
+ end
85
+ else
86
+ @warnings[type] = {:enabled => enabled, :patterns => []}
87
+ end
75
88
  else
76
89
  warn(nil, "Warning of type '#{type}' doesn't exist")
77
90
  end
@@ -79,7 +92,7 @@ module JsDuck
79
92
 
80
93
  # get documentation for all warnings
81
94
  def doc_warnings
82
- @warning_docs.map {|w| " #{@warnings[w[0]] ? '+' : '-'}#{w[0]} - #{w[1]}" }
95
+ @warning_docs.map {|w| " #{@warnings[w[0]][:enabled] ? '+' : '-'}#{w[0]} - #{w[1]}" }
83
96
  end
84
97
 
85
98
  # Prints warning message.
@@ -93,21 +106,46 @@ module JsDuck
93
106
  # warnings greatly also when run multiple processes.
94
107
  #
95
108
  # Optionally filename and line number will be inserted to message.
109
+ # These two last arguments can also be supplied as one hash of:
110
+ #
111
+ # {:filename => "foo.js", :linenr => 17}
112
+ #
96
113
  def warn(type, msg, filename=nil, line=nil)
114
+ if filename.is_a?(Hash)
115
+ line = filename[:linenr]
116
+ filename = filename[:filename]
117
+ end
118
+
97
119
  msg = paint(:yellow, "Warning: ") + format(filename, line) + " " + msg
98
120
 
99
- if type == nil || @warnings[type]
121
+ if warning_enabled?(type, filename)
100
122
  if !@shown_warnings[msg]
101
123
  $stderr.puts msg
102
124
  @shown_warnings[msg] = true
103
125
  end
104
- elsif !@warnings.has_key?(type)
105
- warn(nil, "Unknown warning type #{type}")
106
126
  end
107
127
 
108
128
  return false
109
129
  end
110
130
 
131
+ # True when the warning is enabled for the given type and filename
132
+ # combination.
133
+ def warning_enabled?(type, filename)
134
+ if type == nil
135
+ true
136
+ elsif !@warnings.has_key?(type)
137
+ warn(nil, "Unknown warning type #{type}")
138
+ false
139
+ else
140
+ rule = @warnings[type]
141
+ if rule[:patterns].any? {|re| filename =~ re }
142
+ !rule[:enabled]
143
+ else
144
+ rule[:enabled]
145
+ end
146
+ end
147
+ end
148
+
111
149
  # Formats filename and line number for output
112
150
  def format(filename=nil, line=nil)
113
151
  out = ""
@@ -97,9 +97,8 @@ module JsDuck
97
97
  if hash1[name]
98
98
  hash1.delete(name)
99
99
  else
100
- ctx = m[:files][0]
101
100
  msg = "@hide used but #{m[:tagname]} #{m[:name]} not found in parent class"
102
- Logger.warn(:hide, msg, ctx[:filename], ctx[:linenr])
101
+ Logger.warn(:hide, msg, m[:files][0])
103
102
  end
104
103
  else
105
104
  if hash1[name]
data/lib/jsduck/merger.rb CHANGED
@@ -45,14 +45,32 @@ module JsDuck
45
45
  def general_merge(h, docs, code)
46
46
  # Merge in all items in docs that don't occour already in result.
47
47
  docs.each_pair do |key, value|
48
- h[key] = value unless h.has_key?(key)
48
+ h[key] = value unless h.has_key?(key) || Merger::explicit?(key)
49
49
  end
50
50
  # Then add all in the items from code not already in result.
51
51
  code.each_pair do |key, value|
52
- h[key] = value unless h.has_key?(key)
52
+ h[key] = value unless h.has_key?(key) || Merger::explicit?(key)
53
53
  end
54
54
  end
55
55
 
56
+ # True when given key gets merged explicitly and should therefore
57
+ # be skipped when auto-merging.
58
+ def self.explicit?(key)
59
+ @explicit = explictly_merged_fields unless @explicit
60
+ @explicit[key]
61
+ end
62
+
63
+ # Generates a lookup-hash of tagnames which are explicitly merged.
64
+ def self.explictly_merged_fields
65
+ mergers = {}
66
+ member_types = TagRegistry.member_type_names + [:class]
67
+ tags = member_types.map {|type| TagRegistry.mergers(type) }.flatten.uniq
68
+ tags.each do |tag|
69
+ mergers[tag.tagname] = true
70
+ end
71
+ mergers
72
+ end
73
+
56
74
  end
57
75
 
58
76
  end
@@ -24,6 +24,8 @@ module JsDuck
24
24
  attr_accessor :footer
25
25
  attr_accessor :head_html
26
26
  attr_accessor :body_html
27
+ attr_accessor :css
28
+ attr_accessor :message
27
29
  attr_accessor :welcome
28
30
  attr_accessor :guides
29
31
  attr_accessor :videos
@@ -46,7 +48,6 @@ module JsDuck
46
48
  attr_accessor :template_dir
47
49
  attr_accessor :template_links
48
50
  attr_accessor :extjs_path
49
- attr_accessor :data_path
50
51
  attr_accessor :local_storage_db
51
52
  attr_accessor :touch_examples_ui
52
53
  attr_accessor :ext_namespaces
@@ -91,14 +92,16 @@ module JsDuck
91
92
  ]
92
93
  @ext4_events = nil
93
94
 
94
- @version = "5.0.0.beta01"
95
+ @version = "5.0.0.beta2"
95
96
 
96
97
  # Customizing output
97
98
  @title = "Documentation - JSDuck"
98
99
  @header = "<strong>Documentation</strong> JSDuck"
99
- @footer = "Generated with <a href='https://github.com/senchalabs/jsduck'>JSDuck</a> #{@version}."
100
+ @footer = format_footer("Generated on {DATE} by {JSDUCK} {VERSION}.")
100
101
  @head_html = ""
101
102
  @body_html = ""
103
+ @css = ""
104
+ @message = ""
102
105
  @welcome = nil
103
106
  @guides = nil
104
107
  @videos = nil
@@ -125,7 +128,6 @@ module JsDuck
125
128
  @template_dir = @root_dir + "/template-min"
126
129
  @template_links = false
127
130
  @extjs_path = "extjs/ext-all.js"
128
- @data_path = nil # This gets assigned in JsDuck::WebWriter after writing the data file.
129
131
  @local_storage_db = "docs"
130
132
  @touch_examples_ui = false
131
133
  @imports = []
@@ -138,6 +140,8 @@ module JsDuck
138
140
  Logger.set_warning(:all, true)
139
141
  Logger.set_warning(:link_auto, false)
140
142
  Logger.set_warning(:param_count, false)
143
+
144
+ @optparser = create_option_parser
141
145
  end
142
146
 
143
147
  # Make options object behave like hash.
@@ -150,16 +154,25 @@ module JsDuck
150
154
  end
151
155
 
152
156
  def parse!(argv)
153
- create_option_parser.parse!(argv).each do |fname|
154
- read_filenames(canonical(fname))
155
- end
157
+ parse_options(argv)
158
+ auto_detect_config_file
156
159
  validate
157
160
 
158
- @custom_tag_paths.each {|path| TagRegistry.load_from(path) }
161
+ if @custom_tag_paths.length > 0
162
+ TagRegistry.reconfigure(@custom_tag_paths)
163
+ else
164
+ # Ensure the TagRegistry get instantiated just once.
165
+ # Otherwise the parallel processing causes multiple requests
166
+ # to initialize the TagRegistry, resulting in loading the Tag
167
+ # definitions multiple times.
168
+ TagRegistry.instance
169
+ end
159
170
  end
160
171
 
172
+ private
173
+
161
174
  def create_option_parser
162
- optparser = JsDuck::OptionParser.new do | opts |
175
+ return JsDuck::OptionParser.new do | opts |
163
176
  opts.banner = "Usage: jsduck [options] files/dirs..."
164
177
  opts.separator ""
165
178
  opts.separator "For example:"
@@ -227,6 +240,9 @@ module JsDuck
227
240
  "",
228
241
  "An alternative to listing all options on command line.",
229
242
  "",
243
+ "When the current directory contains jsduck.json file",
244
+ "then options are automatically read from there.",
245
+ "",
230
246
  "See also: https://github.com/senchalabs/jsduck/wiki/Config-file") do |path|
231
247
  path = canonical(path)
232
248
  if File.exists?(path)
@@ -238,7 +254,7 @@ module JsDuck
238
254
  # treat paths inside JSON config relative to the location of
239
255
  # config file. When done, switch back to current working dir.
240
256
  @working_dir = File.dirname(path)
241
- optparser.parse!(config).each {|fname| read_filenames(canonical(fname)) }
257
+ parse_options(config)
242
258
  @working_dir = nil
243
259
  end
244
260
 
@@ -265,11 +281,14 @@ module JsDuck
265
281
  opts.on('--footer=TEXT',
266
282
  "Custom footer text for the documentation.",
267
283
  "",
268
- "Defaults to: 'Generated with JSDuck {VERSION}.'",
284
+ "The text can contain various placeholders:",
285
+ "",
286
+ " {DATE} - current date and time.",
287
+ " {JSDUCK} - link to JSDuck homepage.",
288
+ " {VERSION} - JSDuck version number.",
269
289
  "",
270
- "'{VERSION}' is a placeholder that will get substituted",
271
- "with the current version of JSDuck. See --version.") do |text|
272
- @footer = text.gsub(/\{VERSION\}/, @version)
290
+ "Defaults to: 'Generated on {DATE} by {JSDUCK} {VERSION}.'") do |text|
291
+ @footer = format_footer(text)
273
292
  end
274
293
 
275
294
  opts.on('--head-html=HTML',
@@ -277,9 +296,12 @@ module JsDuck
277
296
  "",
278
297
  "Useful for adding extra <style> and other tags.",
279
298
  "",
299
+ "Also a name of an HTML file can be passed.",
300
+ "Then the contents of that file will be read in.",
301
+ "",
280
302
  "This option can be used repeatedly to append several",
281
303
  "things to the header.") do |html|
282
- @head_html += html
304
+ @head_html += maybe_file(html)
283
305
  end
284
306
 
285
307
  opts.on('--body-html=HTML',
@@ -287,9 +309,32 @@ module JsDuck
287
309
  "",
288
310
  "Useful for adding extra markup to the page.",
289
311
  "",
312
+ "Also a name of an HTML file can be passed.",
313
+ "Then the contents of that file will be read in.",
314
+ "",
290
315
  "This option can be used repeatedly to append several",
291
316
  "things to the body.") do |html|
292
- @body_html += html
317
+ @body_html += maybe_file(html)
318
+ end
319
+
320
+ opts.on('--css=CSS',
321
+ "Extra CSS rules to include to the page.",
322
+ "",
323
+ "Also a name of a CSS file can be passed.",
324
+ "Then the contents of that file will be read in.",
325
+ "",
326
+ "This option can be used repeatedly to append multiple",
327
+ "chunks of CSS.") do |css|
328
+ @css += maybe_file(css)
329
+ end
330
+
331
+ opts.on('--message=HTML',
332
+ "(Warning) message to show prominently.",
333
+ "",
334
+ "Useful for warning users that they are viewing an old",
335
+ "version of the docs, and prividing a link to the new",
336
+ "version.") do |html|
337
+ @message += html
293
338
  end
294
339
 
295
340
  opts.on('--welcome=PATH',
@@ -546,7 +591,7 @@ module JsDuck
546
591
  @touch_examples_ui = true
547
592
  end
548
593
 
549
- opts.on('--ignore-html=TAG',
594
+ opts.on('--ignore-html=TAG1,TAG2', Array,
550
595
  "Ignore a particular unclosed HTML tag.",
551
596
  "",
552
597
  "Normally all tags like <foo> that aren't followed at some",
@@ -557,8 +602,10 @@ module JsDuck
557
602
  "",
558
603
  "Useful for ignoring the ExtJS preprocessor directives",
559
604
  "<locale> and <debug> which would otherwise be reported",
560
- "as unclosed tags.") do |tag|
561
- @ignore_html[tag] = true
605
+ "as unclosed tags.") do |tags|
606
+ tags.each do |tag|
607
+ @ignore_html[tag] = true
608
+ end
562
609
  end
563
610
 
564
611
  opts.separator ""
@@ -568,24 +615,40 @@ module JsDuck
568
615
  opts.on('-v', '--verbose',
569
616
  "Turns on excessive logging.",
570
617
  "",
571
- "Log messages are writted to STDERR.") do
618
+ "Log messages are written to STDERR.") do
572
619
  Logger.verbose = true
573
620
  end
574
621
 
575
622
  opts.on('--warnings=+A,-B,+C', Array,
576
623
  "Turns warnings selectively on/off.",
577
624
  "",
578
- " +all - to turn on all warnings",
625
+ " +all - to turn on all warnings.",
626
+ " -all - to turn off all warnings.",
627
+ "",
628
+ "Additionally a pattern can be specified to only apply the",
629
+ "setting for a particular set of files. For example to turn",
630
+ "off all warnings related to chart classes:",
631
+ "",
632
+ " -all:extjs/src/chart",
633
+ "",
634
+ "Note, that the order of the rules matters. When you first",
635
+ "say +link and then -all, the result will be that all warnings",
636
+ "get disabled.",
637
+ "",
638
+ "Currently one can't mix disabling and enabling file patterns.",
639
+ "For example --warnings=-link,+link:/src,-link:/src/ux will",
640
+ "ignore the last rule about /src/ux.",
579
641
  "",
580
642
  "List of all available warning types:",
581
643
  "(Those with '+' in front of them default to on)",
582
644
  "",
583
645
  *Logger.doc_warnings) do |warnings|
584
646
  warnings.each do |op|
585
- if op =~ /^([-+]?)(.*)$/
647
+ if op =~ /^([-+]?)(\w+)(?::(.*))?$/
586
648
  enable = !($1 == "-")
587
649
  name = $2.to_sym
588
- Logger.set_warning(name, enable)
650
+ path = $3
651
+ Logger.set_warning(name, enable, path)
589
652
  end
590
653
  end
591
654
  end
@@ -628,6 +691,14 @@ module JsDuck
628
691
  @template_links = true
629
692
  end
630
693
 
694
+ opts.on('-d', '--debug',
695
+ "Same as --template=template --template-links.",
696
+ "",
697
+ "Useful shorthand during development.") do
698
+ @template_dir = canonical("template")
699
+ @template_links = true
700
+ end
701
+
631
702
  opts.on('--extjs-path=PATH',
632
703
  "Path for main ExtJS JavaScript file.",
633
704
  "",
@@ -669,8 +740,20 @@ module JsDuck
669
740
  exit
670
741
  end
671
742
  end
743
+ end
744
+
745
+ # Parses the given command line options
746
+ # (could have also been read from config file)
747
+ def parse_options(options)
748
+ @optparser.parse!(options).each {|fname| read_filenames(canonical(fname)) }
749
+ end
672
750
 
673
- return optparser
751
+ # Reads jsduck.json file in current directory
752
+ def auto_detect_config_file
753
+ fname = Dir.pwd + "/jsduck.json"
754
+ if File.exists?(fname)
755
+ parse_options(read_json_config(fname))
756
+ end
674
757
  end
675
758
 
676
759
  # Reads JSON configuration from file and returns an array of
@@ -724,6 +807,17 @@ module JsDuck
724
807
  end.flatten
725
808
  end
726
809
 
810
+ # When given string is a file, returns the contents of the file.
811
+ # Otherwise returns the string unchanged.
812
+ def maybe_file(str)
813
+ path = canonical(str)
814
+ if File.exists?(path)
815
+ Util::IO.read(path)
816
+ else
817
+ str
818
+ end
819
+ end
820
+
727
821
  # Converts relative path to full path
728
822
  #
729
823
  # Especially important for running on Windows where C:\foo\bar
@@ -733,6 +827,13 @@ module JsDuck
733
827
  File.expand_path(path, @working_dir)
734
828
  end
735
829
 
830
+ # Replace special placeholders in footer text
831
+ def format_footer(text)
832
+ jsduck = "<a href='https://github.com/senchalabs/jsduck'>JSDuck</a>"
833
+ date = Time.new.strftime('%a %d %b %Y %H:%M:%S')
834
+ text.gsub(/\{VERSION\}/, @version).gsub(/\{JSDUCK\}/, jsduck).gsub(/\{DATE\}/, date)
835
+ end
836
+
736
837
  # Runs checks on the options
737
838
  def validate
738
839
  if @input_files.length == 0 && !@welcome && !@guides && !@videos && !@examples