jsduck 5.3.4 → 6.0.0beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/bin/jsduck +3 -3
  4. data/lib/jsduck/app.rb +1 -1
  5. data/lib/jsduck/assets.rb +3 -3
  6. data/lib/jsduck/base_type.rb +2 -2
  7. data/lib/jsduck/batch_processor.rb +3 -1
  8. data/lib/jsduck/categories/file.rb +2 -2
  9. data/lib/jsduck/class_doc_expander.rb +1 -1
  10. data/lib/jsduck/css/parser.rb +59 -90
  11. data/lib/jsduck/css/type.rb +55 -0
  12. data/lib/jsduck/doc/parser.rb +1 -1
  13. data/lib/jsduck/doc/scanner.rb +2 -2
  14. data/lib/jsduck/doc/subproperties.rb +1 -1
  15. data/lib/jsduck/export_writer.rb +4 -4
  16. data/lib/jsduck/exporter/app.rb +2 -1
  17. data/lib/jsduck/exporter/full.rb +5 -3
  18. data/lib/jsduck/external_classes.rb +337 -5
  19. data/lib/jsduck/format/class.rb +3 -3
  20. data/lib/jsduck/format/doc.rb +6 -5
  21. data/lib/jsduck/format/subproperties.rb +1 -1
  22. data/lib/jsduck/guide_toc.rb +45 -27
  23. data/lib/jsduck/guide_toc_entry.rb +54 -0
  24. data/lib/jsduck/guides.rb +9 -5
  25. data/lib/jsduck/img/dir.rb +1 -1
  26. data/lib/jsduck/inline/example.rb +3 -1
  27. data/lib/jsduck/inline/img.rb +3 -3
  28. data/lib/jsduck/inline/link.rb +2 -2
  29. data/lib/jsduck/inline/link_renderer.rb +3 -2
  30. data/lib/jsduck/inline/video.rb +2 -1
  31. data/lib/jsduck/js/class.rb +6 -11
  32. data/lib/jsduck/js/ext_define.rb +46 -0
  33. data/lib/jsduck/js/ext_patterns.rb +7 -2
  34. data/lib/jsduck/js/rkelly_adapter.rb +16 -2
  35. data/lib/jsduck/logger.rb +40 -25
  36. data/lib/jsduck/member_registry.rb +41 -0
  37. data/lib/jsduck/news.rb +18 -5
  38. data/lib/jsduck/options/config.rb +35 -0
  39. data/lib/jsduck/options/helpful_parser.rb +111 -0
  40. data/lib/jsduck/options/input_files.rb +60 -0
  41. data/lib/jsduck/options/jsb.rb +25 -0
  42. data/lib/jsduck/{options.rb → options/parser.rb} +436 -484
  43. data/lib/jsduck/options/processor.rb +47 -0
  44. data/lib/jsduck/options/record.rb +51 -0
  45. data/lib/jsduck/output_dir.rb +4 -4
  46. data/lib/jsduck/parser.rb +5 -5
  47. data/lib/jsduck/process/components.rb +19 -0
  48. data/lib/jsduck/process/ext4_events.rb +4 -2
  49. data/lib/jsduck/process/importer.rb +5 -2
  50. data/lib/jsduck/process/inherit_members.rb +2 -0
  51. data/lib/jsduck/process/lint.rb +3 -3
  52. data/lib/jsduck/process/no_doc.rb +1 -1
  53. data/lib/jsduck/process/overrides.rb +4 -3
  54. data/lib/jsduck/process/versions.rb +86 -51
  55. data/lib/jsduck/render/class.rb +3 -2
  56. data/lib/jsduck/render/subproperties.rb +18 -0
  57. data/lib/jsduck/render/tags.rb +13 -1
  58. data/lib/jsduck/source/file.rb +2 -2
  59. data/lib/jsduck/tag/class.rb +6 -0
  60. data/lib/jsduck/tag/component.rb +19 -0
  61. data/lib/jsduck/tag/css_mixin.rb +10 -0
  62. data/lib/jsduck/tag/deprecated.rb +1 -1
  63. data/{template-min/resources/images/class-m.png → lib/jsduck/tag/icons/class-large.png} +0 -0
  64. data/lib/jsduck/tag/icons/class-redirect.png +0 -0
  65. data/lib/jsduck/tag/icons/class.png +0 -0
  66. data/{template-min/resources/images/component-m.png → lib/jsduck/tag/icons/component-large.png} +0 -0
  67. data/lib/jsduck/tag/icons/component-redirect.png +0 -0
  68. data/lib/jsduck/tag/icons/component.png +0 -0
  69. data/{template-min/resources/images/singleton-m.png → lib/jsduck/tag/icons/singleton-large.png} +0 -0
  70. data/lib/jsduck/tag/icons/singleton-redirect.png +0 -0
  71. data/lib/jsduck/tag/icons/singleton.png +0 -0
  72. data/lib/jsduck/tag/inheritdoc.rb +2 -2
  73. data/lib/jsduck/tag/new.rb +13 -0
  74. data/lib/jsduck/tag/since.rb +2 -2
  75. data/lib/jsduck/tag/singleton.rb +13 -0
  76. data/lib/jsduck/tag/tag.rb +19 -0
  77. data/lib/jsduck/tag_registry.rb +20 -81
  78. data/lib/jsduck/util/io.rb +5 -0
  79. data/lib/jsduck/util/json.rb +3 -3
  80. data/lib/jsduck/util/null_object.rb +14 -1
  81. data/lib/jsduck/util/parallel.rb +7 -3
  82. data/lib/jsduck/version.rb +1 -1
  83. data/lib/jsduck/warning/registry.rb +4 -2
  84. data/lib/jsduck/warning/tag.rb +57 -0
  85. data/lib/jsduck/web/class_icons.rb +76 -0
  86. data/lib/jsduck/web/css.rb +12 -1
  87. data/lib/jsduck/web/data.rb +4 -3
  88. data/lib/jsduck/web/index_html.rb +26 -10
  89. data/lib/jsduck/web/member_icons.rb +3 -3
  90. data/lib/jsduck/web/search.rb +4 -4
  91. data/lib/jsduck/web/source.rb +1 -1
  92. data/lib/jsduck/web/template.rb +6 -6
  93. data/lib/jsduck/web/tree.rb +22 -0
  94. data/lib/jsduck/web/writer.rb +11 -9
  95. data/template-min/app-0f524ddd276c4019a11a6128932a9c96.js +1 -0
  96. data/template-min/index-template.html +1 -1
  97. data/template-min/print-template.html +1 -1
  98. data/template-min/resources/css/{app-4689d2a5522dcd3c9e9923ca59c33f27.css → app-de670120f43fdad3091a0cc2c10daadb.css} +1 -1
  99. data/template-min/resources/images/icons.xcf +0 -0
  100. data/template-min/template.html +2 -2
  101. metadata +46 -13
  102. data/lib/jsduck/css/lexer.rb +0 -203
  103. data/lib/jsduck/option_parser.rb +0 -109
  104. data/lib/jsduck/web/icons.rb +0 -31
  105. data/template-min/app-0c945a27f43452df695771ddb60b3d14.js +0 -1
@@ -0,0 +1,41 @@
1
+ require "jsduck/tag_registry"
2
+
3
+ module JsDuck
4
+
5
+ # Access to member tags
6
+ class MemberRegistry
7
+ class << self
8
+ # Same as #definitions, but returns just the names of member types.
9
+ def names
10
+ definitions.map {|mt| mt[:name] }
11
+ end
12
+
13
+ # Returns array of available member type definitions.
14
+ # Sorted in the order defined by :position.
15
+ def definitions
16
+ if !@definitions
17
+ @definitions = TagRegistry.tags.find_all do |tag|
18
+ tag.respond_to?(:member_type) && tag.member_type
19
+ end.map do |tag|
20
+ cfg = tag.member_type
21
+ cfg[:name] = tag.tagname
22
+ cfg
23
+ end
24
+ @definitions.sort! {|a, b| a[:position] <=> b[:position] }
25
+ end
26
+
27
+ @definitions
28
+ end
29
+
30
+ # Regex for matching member type name in member reference.
31
+ #
32
+ # The regex matches strings like: "method-" or "event-". It
33
+ # contains a capture group to capture the actual name of the
34
+ # member, leaving out the dash "-".
35
+ def regex
36
+ @regex ||= Regexp.new("(?:(" + names.join("|") + ")-)")
37
+ end
38
+ end
39
+ end
40
+
41
+ end
@@ -7,7 +7,7 @@ module JsDuck
7
7
  # Creates News object from relations data when --import option
8
8
  # specified.
9
9
  def self.create(relations, doc_formatter, opts)
10
- if opts[:imports].length > 0
10
+ if opts.import.length > 0
11
11
  News.new(relations, doc_formatter)
12
12
  else
13
13
  Util::NullObject.new(:to_html => "")
@@ -63,10 +63,11 @@ module JsDuck
63
63
  new_items
64
64
  end
65
65
 
66
+ # Returns all members of a class that have been marked as new, or
67
+ # have parameters marked as new.
66
68
  def filter_new_members(cls)
67
- members = []
68
- cls.all_local_members.each do |m|
69
- members << m if m[:new] && visible?(m)
69
+ members = cls.all_local_members.find_all do |m|
70
+ visible?(m) && (m[:new] || new_params?(m))
70
71
  end
71
72
  members = discard_accessors(members)
72
73
  members.sort! {|a, b| a[:name] <=> b[:name] }
@@ -76,6 +77,10 @@ module JsDuck
76
77
  !member[:private] && !member[:hide]
77
78
  end
78
79
 
80
+ def new_params?(member)
81
+ Array(member[:params]).any? {|p| p[:new] }
82
+ end
83
+
79
84
  def discard_accessors(members)
80
85
  accessors = {}
81
86
  members.find_all {|m| m[:accessor] }.each do |cfg|
@@ -127,7 +132,15 @@ module JsDuck
127
132
  if m[:tagname] == :class
128
133
  @doc_formatter.link(m[:name], nil, m[:name])
129
134
  else
130
- @doc_formatter.link(m[:owner], m[:name], m[:name], m[:tagname], m[:static])
135
+ @doc_formatter.link(m[:owner], m[:name], m[:name], m[:tagname], m[:static]) + params_note(m)
136
+ end
137
+ end
138
+
139
+ def params_note(m)
140
+ if !m[:new] && new_params?(m)
141
+ " <small>+parameters</small>"
142
+ else
143
+ ""
131
144
  end
132
145
  end
133
146
 
@@ -0,0 +1,35 @@
1
+ require 'jsduck/util/json'
2
+
3
+ module JsDuck
4
+ module Options
5
+
6
+ # Handles reading JSON config file, specified by --config option.
7
+ class Config
8
+
9
+ # Reads JSON configuration from file and returns an array of
10
+ # config options that can be feeded into optparser.
11
+ def self.read(filename)
12
+ config = []
13
+ json = Util::Json.read(filename)
14
+ json.each_pair do |key, value|
15
+ if key == "--"
16
+ # filenames
17
+ config += Array(value).map(&:to_s)
18
+ elsif value == true
19
+ # simple switch
20
+ config += [key.to_s]
21
+ else
22
+ # An option with value or with multiple values.
23
+ # In the latter case, add the option multiple times.
24
+ Array(value).each do |v|
25
+ config += [key.to_s, v.to_s]
26
+ end
27
+ end
28
+ end
29
+ config
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,111 @@
1
+ require 'optparse'
2
+
3
+ module JsDuck
4
+ module Options
5
+
6
+ # JSDuck version of OptionParser
7
+ #
8
+ # Enhanced with ability to output options help in two ways:
9
+ #
10
+ # - short list of all options (with the built-in #help method)
11
+ # - long description of one option (with the added #help_single method)
12
+ #
13
+ class HelpfulParser < ::OptionParser
14
+ def initialize
15
+ @full_options_index = {}
16
+ super
17
+ end
18
+
19
+ # Override the #on method to do some pre-processing on its
20
+ # parameters before passing them to the original #on method.
21
+ #
22
+ # Options are defined as usual:
23
+ #
24
+ # opts.on("-v", "--version", Type, "First line of description.",
25
+ # "Second line of description.",
26
+ # "Third line of description.")
27
+ #
28
+ # But only the first line of description will be passed to
29
+ # original #on method - meaning that #help method will also only
30
+ # list this first line.
31
+ #
32
+ # The remaining lines are saved to a separate place and can be
33
+ # retrieved through asking for full docs for an option with
34
+ # #help_single method.
35
+ #
36
+ def on(*opts, &block)
37
+ core = []
38
+ keys = []
39
+ desc = []
40
+
41
+ desc_started = false
42
+ opts.each do |o|
43
+ if desc_started
44
+ desc << o
45
+ elsif String === o
46
+ if o =~ /^-/
47
+ core << o
48
+ keys << o
49
+ else
50
+ core << o
51
+ desc << o
52
+ desc_started = true
53
+ end
54
+ else
55
+ core << o
56
+ end
57
+ end
58
+
59
+ full = {:keys => keys, :desc => desc}
60
+
61
+ keys.each do |op|
62
+ each_index_key(op) {|k| @full_options_index[k] = full }
63
+ end
64
+
65
+ super(*core, &block)
66
+ end
67
+
68
+ # Helper that turns option name like --images=PATH into list of
69
+ # keys by which we index the options:
70
+ #
71
+ # "--images=PATH" --> ["--images", "images"]
72
+ #
73
+ # For options containing "[no-]" all the alternative forms are expanded:
74
+ #
75
+ # "--[no-]seo" --> ["--[no-]seo", "[no-]seo", "--seo", "seo", "--no-seo", "no-seo"]
76
+ #
77
+ def each_index_key(option_name)
78
+ key = option_name.sub(/\[?=.*/, '')
79
+ plain_key = key.sub(/^-*/, '')
80
+ [key, plain_key].each do |k|
81
+ yield k
82
+ if k =~ /\[no-\]/
83
+ yield k.sub(/\[no-\]/, '')
84
+ yield k.sub(/\[no-\]/, 'no-')
85
+ end
86
+ end
87
+ end
88
+
89
+ # Returns long help text for a single option.
90
+ def help_single(option_name)
91
+ o = @full_options_index[option_name] || {:keys => [option_name], :desc => ["No such option. See --help=help"]}
92
+
93
+ r = []
94
+
95
+ r << ""
96
+ r << " " + o[:keys].join(", ")
97
+ r << ""
98
+
99
+ o[:desc].each do |line|
100
+ r << " " + line
101
+ end
102
+
103
+ r << ""
104
+ r << ""
105
+
106
+ return r.join("\n")
107
+ end
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,60 @@
1
+ require 'jsduck/options/jsb'
2
+ require 'jsduck/logger'
3
+
4
+ module JsDuck
5
+ module Options
6
+
7
+ # Finalizes the list of input files.
8
+ class InputFiles
9
+ def initialize(opts)
10
+ @opts = opts
11
+ end
12
+
13
+ # Expands opts.input_files (modifying its contents):
14
+ #
15
+ # - When file is a directory, scans all JS, SCSS files in there.
16
+ # - When file is a .jsb3 file, extracts list of files from it.
17
+ # - Otherwise returns array with this same input filename.
18
+ #
19
+ # Then excludes the files and dirs listed in opts.exclude.
20
+ def expand!
21
+ @opts.input_files = expand_files(@opts.input_files)
22
+ exclude_files!(@opts.input_files, @opts.exclude)
23
+ end
24
+
25
+ private
26
+
27
+ def expand_files(unexpanded_files)
28
+ unexpanded_files.map {|fname| expand_filename(fname) }.flatten
29
+ end
30
+
31
+ def expand_filename(fname)
32
+ files = []
33
+
34
+ if File.exists?(fname)
35
+ if File.directory?(fname)
36
+ Dir[fname+"/**/*.{js,scss}"].each {|f| files << f }
37
+ elsif fname =~ /\.jsb3$/
38
+ Options::Jsb.read(fname).each {|fn| read_filenames(fn) }
39
+ else
40
+ files << fname
41
+ end
42
+ else
43
+ Logger.warn(nil, "File not found", {:filename => fname})
44
+ end
45
+
46
+ files
47
+ end
48
+
49
+ # Removes the files matching exclude_paths from list of files
50
+ def exclude_files!(files, exclude_paths)
51
+ exclude_paths.each do |exclude_path|
52
+ exclude_re = Regexp.new('\A' + Regexp.escape(exclude_path))
53
+ files.reject! {|f| f =~ exclude_re }
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,25 @@
1
+ require 'jsduck/util/json'
2
+
3
+ module JsDuck
4
+ module Options
5
+
6
+ # Handles reading of JSB3 files.
7
+ class Jsb
8
+
9
+ # Extracts files of first build in JSB3 file.
10
+ def self.read(filename)
11
+ json = Util::Json.read(filename)
12
+ basedir = File.dirname(filename)
13
+
14
+ return json["builds"][0]["packages"].map do |package_id|
15
+ package = json["packages"].find {|p| p["id"] == package_id }
16
+ (package ? package["files"] : []).map do |file|
17
+ File.expand_path(basedir + "/" + file["path"] + file["name"])
18
+ end
19
+ end.flatten
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -1,221 +1,95 @@
1
- require 'jsduck/option_parser'
1
+ require 'jsduck/options/helpful_parser'
2
+ require 'jsduck/options/record'
3
+ require 'jsduck/options/config'
4
+ require 'jsduck/warning/parser'
5
+ require 'jsduck/warning/warn_exception'
2
6
  require 'jsduck/logger'
3
- require 'jsduck/util/json'
4
- require 'jsduck/util/os'
5
7
  require 'jsduck/util/io'
6
- require 'jsduck/util/parallel'
7
- require 'jsduck/tag_registry'
8
- require 'jsduck/js/ext_patterns'
9
- require 'jsduck/warning/parser'
10
8
  require 'jsduck/version'
11
9
 
12
10
  module JsDuck
11
+ module Options
13
12
 
14
- # Keeps command line options
15
- class Options
16
- attr_accessor :input_files
17
-
18
- attr_accessor :output_dir
19
- attr_accessor :ignore_global
20
- attr_accessor :external_classes
21
- attr_accessor :ext4_events
22
-
23
- # Customizing output
24
- attr_accessor :title
25
- attr_accessor :header
26
- attr_accessor :footer
27
- attr_accessor :head_html
28
- attr_accessor :body_html
29
- attr_accessor :css
30
- attr_accessor :message
31
- attr_accessor :welcome
32
- attr_accessor :guides
33
- attr_accessor :videos
34
- attr_accessor :examples
35
- attr_accessor :categories_path
36
- attr_accessor :source
37
- attr_accessor :images
38
- attr_accessor :link_tpl
39
- attr_accessor :img_tpl
40
- attr_accessor :export
41
- attr_accessor :seo
42
- attr_accessor :eg_iframe
43
- attr_accessor :examples_base_url
44
- attr_accessor :tests
45
- attr_accessor :comments_url
46
- attr_accessor :comments_domain
47
- attr_accessor :search
48
- attr_accessor :ignore_html
49
-
50
- # Debugging
51
- attr_accessor :warnings_exit_nonzero
52
- attr_accessor :cache
53
- attr_accessor :cache_dir
54
- attr_accessor :template_dir
55
- attr_accessor :template_links
56
- attr_accessor :extjs_path
57
- attr_accessor :local_storage_db
58
- attr_accessor :touch_examples_ui
59
- attr_accessor :ext_namespaces
60
- attr_accessor :imports
61
- attr_accessor :new_since
62
-
63
- def initialize
64
- @input_files = []
65
- @exclude = []
66
-
67
- @output_dir = nil
68
- @ignore_global = false
69
- @external_classes = [
70
- # JavaScript builtins
71
- "Object",
72
- "String",
73
- "Number",
74
- "Boolean",
75
- "RegExp",
76
- "Function",
77
- "Array",
78
- "Arguments",
79
- "Date",
80
- # JavaScript builtin error classes
81
- # https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error
82
- "Error",
83
- "EvalError",
84
- "RangeError",
85
- "ReferenceError",
86
- "SyntaxError",
87
- "TypeError",
88
- "URIError",
89
- # DOM
90
- "HTMLElement",
91
- "XMLElement",
92
- "NodeList",
93
- "TextNode",
94
- "CSSStyleSheet",
95
- "CSSStyleRule",
96
- "Event",
97
- # Other browser-environment classes
98
- "Window",
99
- "XMLHttpRequest",
100
- # Special anything-goes type
101
- "Mixed",
102
- ]
103
- @ext4_events = nil
104
-
105
- # Customizing output
106
- @title = "Documentation - JSDuck"
107
- @header = "<strong>Documentation</strong> JSDuck"
108
- @footer = format_footer("Generated on {DATE} by {JSDUCK} {VERSION}.")
109
- @head_html = ""
110
- @body_html = ""
111
- @css = ""
112
- @message = ""
113
- @welcome = nil
114
- @guides = nil
115
- @videos = nil
116
- @examples = nil
117
- @categories_path = nil
118
- @source = true
119
- @images = []
120
- @custom_tag_paths = []
121
- @link_tpl = '<a href="#!/api/%c%-%m" rel="%c%-%m" class="docClass">%a</a>'
122
- # Note that we wrap image template inside <p> because {@img} often
123
- # appears inline within text, but that just looks ugly in HTML
124
- @img_tpl = '<p><img src="%u" alt="%a" width="%w" height="%h"></p>'
125
- @export = nil
126
- @seo = false
127
- @eg_iframe = nil
128
- @examples_base_url = "extjs-build/examples/"
129
- @tests = false
130
- @comments_url = nil
131
- @comments_domain = nil
132
- @search = {}
133
- @ignore_html = {}
134
-
135
- # Debugging
136
- @warnings_exit_nonzero = false
137
- @cache = false
138
- @cache_dir = nil
139
- @root_dir = File.dirname(File.dirname(File.dirname(__FILE__)))
140
- @template_dir = @root_dir + "/template-min"
141
- @template_links = false
142
- @extjs_path = "extjs/ext-all.js"
143
- @local_storage_db = "docs"
144
- @touch_examples_ui = false
145
- @imports = []
146
- @new_since = nil
147
-
148
- # Turn multiprocessing off by default in Windows
149
- Util::Parallel.in_processes = Util::OS::windows? ? 0 : nil
150
-
151
- # Enable all warnings except the following:
152
- Logger.set_warning(:all, true)
153
- Logger.set_warning(:link_auto, false)
154
- Logger.set_warning(:param_count, false)
155
- Logger.set_warning(:fires, false)
156
- Logger.set_warning(:nodoc, false)
157
-
158
- @optparser = create_option_parser
159
- end
13
+ # Performs parsing of JSDuck options.
14
+ class Parser
160
15
 
161
- # Make options object behave like hash.
162
- # This allows us to substitute it with hash in unit tests.
163
- def [](key)
164
- instance_variable_get("@#{key}")
165
- end
166
- def []=(key, value)
167
- instance_variable_set("@#{key}", value)
168
- end
16
+ def initialize(file_class=File, config_class=Options::Config)
17
+ @file = file_class
18
+ @config = config_class
169
19
 
170
- def parse!(argv)
171
- parse_options(argv)
172
- auto_detect_config_file unless @config_option_specified
173
- exclude_input_files
174
- validate
175
-
176
- if @custom_tag_paths.length > 0
177
- TagRegistry.reconfigure(@custom_tag_paths)
178
- else
179
- # Ensure the TagRegistry get instantiated just once.
180
- # Otherwise the parallel processing causes multiple requests
181
- # to initialize the TagRegistry, resulting in loading the Tag
182
- # definitions multiple times.
183
- TagRegistry.instance
20
+ @opts = Options::Record.new
21
+ @defaults = {}
22
+
23
+ @root_dir = @file.dirname(@file.dirname(@file.dirname(@file.dirname(__FILE__))))
24
+
25
+ @optparser = Options::HelpfulParser.new
26
+ init_parser
27
+ end
28
+
29
+ # Parses array of command line options, returning
30
+ # Options::Record object containing all the options.
31
+ def parse(argv)
32
+ init_defaults
33
+ parse_options(argv)
34
+ auto_detect_config_file unless @opts.config
35
+ @opts
184
36
  end
185
- end
186
37
 
187
- private
188
-
189
- def create_option_parser
190
- return JsDuck::OptionParser.new do | opts |
191
- opts.banner = "Usage: jsduck [options] files/dirs..."
192
- opts.separator ""
193
- opts.separator "For example:"
194
- opts.separator ""
195
- opts.separator " # Documentation for builtin JavaScript classes like Array and String"
196
- opts.separator " jsduck --output output/dir --builtin-classes"
197
- opts.separator ""
198
- opts.separator " # Documentation for your own JavaScript"
199
- opts.separator " jsduck --output output/dir input-file.js some/input/dir"
200
- opts.separator ""
201
- opts.separator "The main options:"
202
- opts.separator ""
203
-
204
- opts.on('-o', '--output=PATH',
38
+ private
39
+
40
+ def init_parser
41
+ banner "Usage: jsduck [options] files/dirs..."
42
+
43
+ separator ""
44
+ separator "For example:"
45
+ separator ""
46
+ separator " # Documentation for builtin JavaScript classes like Array and String"
47
+ separator " jsduck --output output/dir --builtin-classes"
48
+ separator ""
49
+ separator " # Documentation for your own JavaScript"
50
+ separator " jsduck --output output/dir input-file.js some/input/dir"
51
+ separator ""
52
+ separator "The main options:"
53
+ separator ""
54
+
55
+ attribute :input_files, []
56
+ validator :input_files do
57
+ if @opts.input_files.empty? && !@opts.welcome && !@opts.guides && !@opts.videos && !@opts.examples
58
+ "Please specify some input files, otherwise there's nothing I can do :("
59
+ end
60
+ end
61
+
62
+ attribute :output
63
+ option('-o', '--output=PATH',
205
64
  "Directory to write all this documentation.",
206
65
  "",
207
66
  "This option is REQUIRED. When the directory exists,",
208
67
  "it will be overwritten. Give dash '-' as argument",
209
68
  "to write docs to STDOUT (works only with --export).") do |path|
210
69
  if path == "-"
211
- @output_dir = :stdout
70
+ @opts.output = :stdout
212
71
  else
213
- @output_dir = canonical(path)
214
- @cache_dir = @output_dir + "/.cache" unless @cache_dir
72
+ @opts.output = canonical(path)
73
+ @opts.cache_dir = @opts.output + "/.cache" unless @opts.cache_dir
74
+ end
75
+ end
76
+ validator :output do
77
+ if @opts.output == :stdout
78
+ # No output dir needed for export
79
+ if !@opts.export
80
+ "Output to STDOUT only works when using --export option"
81
+ end
82
+ elsif !@opts.output
83
+ "Please specify an output directory, where to write all this amazing documentation"
84
+ elsif @file.exists?(@opts.output) && !@file.directory?(@opts.output)
85
+ "The output directory is not really a directory at all :("
86
+ elsif !@file.exists?(@file.dirname(@opts.output))
87
+ "The parent directory for #{@opts.output} doesn't exist"
215
88
  end
216
89
  end
217
90
 
218
- opts.on('--export=full/examples',
91
+ attribute :export
92
+ option('--export=full/examples',
219
93
  "Exports docs in JSON.",
220
94
  "",
221
95
  "For each JavaScript class a JSON file gets written,",
@@ -223,16 +97,15 @@ module JsDuck
223
97
  "",
224
98
  "- full - docs and metadata for class and its members.",
225
99
  "- examples - inline examples from classes and guides.") do |format|
226
- export_type = format.to_sym
227
- if [:full, :examples].include?(export_type)
228
- @export = export_type
229
- else
230
- Logger.fatal("Unsupported export type: '#{export_type}'")
231
- exit(1)
100
+ @opts.export = format.to_sym
101
+ end
102
+ validator :export do
103
+ if ![nil, :full, :examples].include?(@opts.export)
104
+ "Unknown export format: #{@opts.export}"
232
105
  end
233
106
  end
234
107
 
235
- opts.on('--builtin-classes',
108
+ option('--builtin-classes',
236
109
  "Includes docs for JavaScript builtins.",
237
110
  "",
238
111
  "Docs for the following classes are included:",
@@ -245,18 +118,22 @@ module JsDuck
245
118
  "- Object",
246
119
  "- RegExp",
247
120
  "- String") do
248
- read_filenames(@root_dir + "/js-classes")
121
+ @opts.input_files << @root_dir + "/js-classes"
249
122
  end
250
123
 
251
- opts.on('--seo', "Enables SEO-friendly print version.",
124
+ attribute :seo, false
125
+ option('--[no-]seo', "Enables SEO-friendly print version. (OFF)",
252
126
  "",
253
127
  "Instead of index.html creates index.php that will serve",
254
128
  "the regular docs, print-friendly docs, and search-engine-",
255
- "friendly docs (the latter two are pretty much the same).") do
256
- @seo = true
129
+ "friendly docs (the latter two are pretty much the same).",
130
+ "",
131
+ "Disabled by default.") do |on|
132
+ @opts.seo = on
257
133
  end
258
134
 
259
- opts.on('--config=PATH',
135
+ attribute :config
136
+ option('--config=PATH',
260
137
  "Loads config options from JSON file.",
261
138
  "",
262
139
  "An alternative to listing all options on command line.",
@@ -266,7 +143,7 @@ module JsDuck
266
143
  "",
267
144
  "See also: https://github.com/senchalabs/jsduck/wiki/Config-file") do |path|
268
145
  path = canonical(path)
269
- if File.exists?(path)
146
+ if @file.exists?(path)
270
147
  config = read_json_config(path)
271
148
  else
272
149
  Logger.fatal("The config file #{path} doesn't exist")
@@ -274,30 +151,33 @@ module JsDuck
274
151
  end
275
152
  # treat paths inside JSON config relative to the location of
276
153
  # config file. When done, switch back to current working dir.
277
- @working_dir = File.dirname(path)
154
+ @working_dir = @file.dirname(path)
278
155
  parse_options(config)
279
156
  @working_dir = nil
280
- @config_option_specified = true
157
+ @opts.config = path
281
158
  end
282
159
 
283
- opts.on('--encoding=NAME', "Input encoding (defaults to UTF-8).") do |encoding|
284
- JsDuck::Util::IO.encoding = encoding
160
+ attribute :encoding
161
+ option('--encoding=NAME', "Input encoding (defaults to UTF-8).") do |encoding|
162
+ @opts.encoding = encoding
285
163
  end
286
164
 
287
- opts.on('--exclude=PATH', "Exclude input file or directory.",
165
+ attribute :exclude, []
166
+ option('--exclude=PATH1,PATH2', Array, "Exclude input file or directory.",
288
167
  "",
289
168
  "For example to include all the subdirs of",
290
169
  "/app/js except /app/js/new, run JSDuck with:",
291
170
  "",
292
- " jsduck /app/js --exclude /app/js/new") do |path|
293
- @exclude << path
171
+ " jsduck /app/js --exclude /app/js/new") do |paths|
172
+ @opts.exclude += paths.map {|p| canonical(p) }
294
173
  end
295
174
 
296
- opts.separator ""
297
- opts.separator "Customizing output:"
298
- opts.separator ""
175
+ separator ""
176
+ separator "Customizing output:"
177
+ separator ""
299
178
 
300
- opts.on('--title=TEXT',
179
+ attribute :title, "Documentation - JSDuck"
180
+ option('--title=TEXT',
301
181
  "Custom title text for the documentation.",
302
182
  "",
303
183
  "Defaults to 'Documentation - JSDuck'",
@@ -305,11 +185,11 @@ module JsDuck
305
185
  "The title will be used both inside <title> and in",
306
186
  "the header of the page. Inside page header the left",
307
187
  "part (from ' - ' separator) will be shown in bold.") do |text|
308
- @title = text
309
- @header = text.sub(/^(.*?) +- +/, "<strong>\\1 </strong>")
188
+ @opts.title = text
310
189
  end
311
190
 
312
- opts.on('--footer=TEXT',
191
+ attribute :footer, "Generated on {DATE} by {JSDUCK} {VERSION}."
192
+ option('--footer=TEXT',
313
193
  "Custom footer text for the documentation.",
314
194
  "",
315
195
  "The text can contain various placeholders:",
@@ -319,10 +199,11 @@ module JsDuck
319
199
  " {VERSION} - JSDuck version number.",
320
200
  "",
321
201
  "Defaults to: 'Generated on {DATE} by {JSDUCK} {VERSION}.'") do |text|
322
- @footer = format_footer(text)
202
+ @opts.footer = text
323
203
  end
324
204
 
325
- opts.on('--head-html=HTML',
205
+ attribute :head_html, ""
206
+ option('--head-html=HTML',
326
207
  "HTML for the <head> section of index.html.",
327
208
  "",
328
209
  "Useful for adding extra <style> and other tags.",
@@ -332,10 +213,11 @@ module JsDuck
332
213
  "",
333
214
  "This option can be used repeatedly to append several",
334
215
  "things to the header.") do |html|
335
- @head_html += maybe_file(html)
216
+ @opts.head_html += maybe_file(html)
336
217
  end
337
218
 
338
- opts.on('--body-html=HTML',
219
+ attribute :body_html, ""
220
+ option('--body-html=HTML',
339
221
  "HTML for the <body> section of index.html.",
340
222
  "",
341
223
  "Useful for adding extra markup to the page.",
@@ -345,10 +227,11 @@ module JsDuck
345
227
  "",
346
228
  "This option can be used repeatedly to append several",
347
229
  "things to the body.") do |html|
348
- @body_html += maybe_file(html)
230
+ @opts.body_html += maybe_file(html)
349
231
  end
350
232
 
351
- opts.on('--css=CSS',
233
+ attribute :css, ""
234
+ option('--css=CSS',
352
235
  "Extra CSS rules to include to the page.",
353
236
  "",
354
237
  "Also a name of a CSS file can be passed.",
@@ -356,19 +239,21 @@ module JsDuck
356
239
  "",
357
240
  "This option can be used repeatedly to append multiple",
358
241
  "chunks of CSS.") do |css|
359
- @css += maybe_file(css)
242
+ @opts.css += maybe_file(css)
360
243
  end
361
244
 
362
- opts.on('--message=HTML',
245
+ attribute :message, ""
246
+ option('--message=HTML',
363
247
  "(Warning) message to show prominently.",
364
248
  "",
365
249
  "Useful for warning users that they are viewing an old",
366
250
  "version of the docs, and prividing a link to the new",
367
251
  "version.") do |html|
368
- @message += html
252
+ @opts.message += html
369
253
  end
370
254
 
371
- opts.on('--welcome=PATH',
255
+ attribute :welcome
256
+ option('--welcome=PATH',
372
257
  "File with content for welcome page.",
373
258
  "",
374
259
  "Either HTML or Markdown file with content for welcome page.",
@@ -376,10 +261,11 @@ module JsDuck
376
261
  "Markdown file must have a .md or .markdown extension.",
377
262
  "",
378
263
  "See also: https://github.com/senchalabs/jsduck/wiki/Welcome-page") do |path|
379
- @welcome = canonical(path)
264
+ @opts.welcome = canonical(path)
380
265
  end
381
266
 
382
- opts.on('--guides=PATH',
267
+ attribute :guides
268
+ option('--guides=PATH',
383
269
  "JSON file describing the guides.",
384
270
  "",
385
271
  "The file should be in a dir containing the actual guides.",
@@ -387,55 +273,86 @@ module JsDuck
387
273
  "other images referenced by the README.md file.",
388
274
  "",
389
275
  "See also: https://github.com/senchalabs/jsduck/wiki/Guides") do |path|
390
- @guides = canonical(path)
276
+ @opts.guides = canonical(path)
391
277
  end
392
278
 
393
- opts.on('--videos=PATH',
279
+ attribute :guides_toc_level, 2
280
+ option('--guides-toc-level=LEVEL',
281
+ "Max heading level for guides' tables of contents.",
282
+ "",
283
+ "Values between 1 and 6 define the maximum level of a heading",
284
+ "to be included into guides' Table of Contents:",
285
+ "",
286
+ "1 - Hides the table of contents.",
287
+ "2 - <H2> headings are included.",
288
+ "3 - <H2>,<H3> headings are included.",
289
+ "4 - <H2>,<H3>,<H4> headings are included.",
290
+ "5 - <H2>,<H3>,<H4>,<H5> headings are included.",
291
+ "6 - <H2>,<H3>,<H4>,<H5>,<H6> headings are included.") do |level|
292
+ @opts.guides_toc_level = level.to_i
293
+ end
294
+ validator :guides_toc_level do
295
+ if !(1..6).include?(@opts.guides_toc_level)
296
+ "Unsupported --guides-toc-level: '#{@opts.guides_toc_level}'"
297
+ end
298
+ end
299
+
300
+ attribute :videos
301
+ option('--videos=PATH',
394
302
  "JSON file describing the videos.",
395
303
  "",
396
304
  "See also: https://github.com/senchalabs/jsduck/wiki/Videos") do |path|
397
- @videos = canonical(path)
305
+ @opts.videos = canonical(path)
398
306
  end
399
307
 
400
- opts.on('--examples=PATH',
308
+ attribute :examples
309
+ option('--examples=PATH',
401
310
  "JSON file describing the examples.",
402
311
  "",
403
312
  "See also: https://github.com/senchalabs/jsduck/wiki/Examples") do |path|
404
- @examples = canonical(path)
313
+ @opts.examples = canonical(path)
405
314
  end
406
315
 
407
- opts.on('--categories=PATH',
316
+ attribute :categories
317
+ option('--categories=PATH',
408
318
  "JSON file defining categories for classes.",
409
319
  "",
410
320
  "Without this option the classes will be categorized",
411
321
  "based on how they are namespaced.",
412
322
  "",
413
323
  "See also: https://github.com/senchalabs/jsduck/wiki/Categories") do |path|
414
- @categories_path = canonical(path)
324
+ @opts.categories = canonical(path)
415
325
  end
416
326
 
417
- opts.on('--no-source',
418
- "Turns off the output of source files.") do
419
- @source = false
327
+ attribute :source, true
328
+ option('--[no-]source',
329
+ "Enables output of source files. (ON)",
330
+ "",
331
+ "Enabled by default.") do |on|
332
+ @opts.source = on
420
333
  end
421
334
 
422
- opts.on('--images=PATH',
423
- "Path for images referenced by {@img} tag.",
335
+ attribute :images, []
336
+ option('--images=PATH1,PATH2', Array,
337
+ "Paths for images referenced by {@img} tag.",
424
338
  "",
425
- "Several paths can be specified by using the option",
426
- "multiple times. This option only applies to {@img}",
427
- "tags used in API (classes/members) documentation.",
428
- "Images used in guides must be located inside the",
429
- "directory of the specific guide.") do |path|
430
- @images << canonical(path)
339
+ "This option only applies to {@img} tags used in",
340
+ "API (classes/members) documentation. Images used",
341
+ "in guides must be located inside the directory of",
342
+ "the specific guide.") do |paths|
343
+ @opts.images += paths.map {|p| canonical(p) }
431
344
  end
432
345
 
433
- opts.on('--tests',
434
- "Creates page for testing inline examples.") do
435
- @tests = true
346
+ attribute :tests, false
347
+ option('--[no-]tests',
348
+ "Creates page for testing inline examples. (OFF)",
349
+ "",
350
+ "Disabled by default.") do |on|
351
+ @opts.tests = on
436
352
  end
437
353
 
438
- opts.on('--import=VERSION:PATH',
354
+ attribute :import, []
355
+ option('--import=VERSION:PATH',
439
356
  "Imports docs generating @since & @new.",
440
357
  "",
441
358
  "For example:",
@@ -455,32 +372,35 @@ module JsDuck
455
372
  "",
456
373
  "See also: https://github.com/senchalabs/jsduck/wiki/@since") do |v|
457
374
  if v =~ /\A(.*?):(.*)\z/
458
- @imports << {:version => $1, :path => canonical($2)}
375
+ @opts.import << {:version => $1, :path => canonical($2)}
459
376
  else
460
- @imports << {:version => v}
377
+ @opts.import << {:version => v}
461
378
  end
462
379
  end
463
380
 
464
- opts.on('--new-since=VERSION',
381
+ attribute :new_since
382
+ option('--new-since=VERSION',
465
383
  "Since when to label items with @new tag.",
466
384
  "",
467
385
  "The VERSION must be one of the version names defined",
468
386
  "with --import option.",
469
387
  "",
470
388
  "Defaults to the last version listed by --import.") do |v|
471
- @new_since = v
389
+ @opts.new_since = v
472
390
  end
473
391
 
474
- opts.on('--comments-url=URL',
392
+ attribute :comments_url
393
+ option('--comments-url=URL',
475
394
  "Address of comments server.",
476
395
  "",
477
396
  "For example: http://projects.sencha.com/auth",
478
397
  "",
479
398
  "Must be used together with --comments-domain option.") do |url|
480
- @comments_url = url
399
+ @opts.comments_url = url
481
400
  end
482
401
 
483
- opts.on('--comments-domain=STRING',
402
+ attribute :comments_domain
403
+ option('--comments-domain=STRING',
484
404
  "A name identifying the subset of comments.",
485
405
  "",
486
406
  "A string consisting of <name>/<version>.",
@@ -488,10 +408,11 @@ module JsDuck
488
408
  "For example: ext-js/4",
489
409
  "",
490
410
  "Must be used together with --comments-url option.") do |domain|
491
- @comments_domain = domain
411
+ @opts.comments_domain = domain
492
412
  end
493
413
 
494
- opts.on('--search-url=URL',
414
+ attribute :search, {}
415
+ option('--search-url=URL',
495
416
  "Address of guides search server.",
496
417
  "",
497
418
  "When supplied, the search for guides is performed through this",
@@ -503,10 +424,10 @@ module JsDuck
503
424
  "Must be used together with --search-domain option.",
504
425
  "",
505
426
  "This option is EXPERIMENTAL.") do |url|
506
- @search[:url] = url
427
+ @opts.search[:url] = url
507
428
  end
508
429
 
509
- opts.on('--search-domain=STRING',
430
+ option('--search-domain=STRING',
510
431
  "A name identifying the subset to search from.",
511
432
  "",
512
433
  "A string consisting of <name>/<version>.",
@@ -519,27 +440,27 @@ module JsDuck
519
440
  "Must be used together with --search-url option.",
520
441
  "",
521
442
  "This option is EXPERIMENTAL.") do |domain|
522
- @search[:product], @search[:version] = domain.split(/\//)
443
+ @opts.search[:product], @opts.search[:version] = domain.split(/\//)
523
444
  end
524
445
 
525
- opts.separator ""
526
- opts.separator "Tweaking:"
527
- opts.separator ""
446
+ separator ""
447
+ separator "Tweaking:"
448
+ separator ""
528
449
 
529
- opts.on('--tags=PATH',
530
- "Path to custom tag implementations.",
531
- "",
532
- "Can be a path to single Ruby file or a directory.",
450
+ attribute :tags, []
451
+ option('--tags=PATH1,PATH2', Array,
452
+ "Paths to custom tag implementations.",
533
453
  "",
534
- "This option can be used repeatedly to include multiple",
535
- "tags from different places.",
454
+ "Paths can point to specific Ruby files or to a directory,",
455
+ "in which case all ruby files in that directory are loaded.",
536
456
  "",
537
- "See also: https://github.com/senchalabs/jsduck/wiki/Custom-tags") do |path|
538
- @custom_tag_paths << canonical(path)
457
+ "See also: https://github.com/senchalabs/jsduck/wiki/Custom-tags") do |paths|
458
+ @opts.tags += paths.map {|p| canonical(p) }
539
459
  end
540
460
 
541
- opts.on('--ignore-global',
542
- "Turns off the creation of 'global' class.",
461
+ attribute :ignore_global, false
462
+ option('--[no-]ignore-global',
463
+ "Turns off the creation of 'global' class. (OFF)",
543
464
  "",
544
465
  "The 'global' class gets created when members that",
545
466
  "don't belong to any class are found - all these",
@@ -547,11 +468,36 @@ module JsDuck
547
468
  "",
548
469
  "Using this option won't suppress the warning that's",
549
470
  "given when global members are found. For that you",
550
- "need to additionally use --warnings=-global") do
551
- @ignore_global = true
552
- end
553
-
554
- opts.on('--external=Foo,Bar,Baz', Array,
471
+ "need to additionally use --warnings=-global",
472
+ "",
473
+ "Disabled by default.") do |on|
474
+ @opts.ignore_global = on
475
+ end
476
+
477
+ attribute :external, [
478
+ # JavaScript builtins
479
+ "Object",
480
+ "String",
481
+ "Number",
482
+ "Boolean",
483
+ "RegExp",
484
+ "Function",
485
+ "Array",
486
+ "Arguments",
487
+ "Date",
488
+ # JavaScript builtin error classes
489
+ # https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error
490
+ "Error",
491
+ "EvalError",
492
+ "RangeError",
493
+ "ReferenceError",
494
+ "SyntaxError",
495
+ "TypeError",
496
+ "URIError",
497
+ # Special anything-goes type
498
+ "Mixed",
499
+ ]
500
+ option('--external=Foo,Bar,Baz', Array,
555
501
  "Declares list of external classes.",
556
502
  "",
557
503
  "These classes will then no more generate warnings",
@@ -562,12 +508,30 @@ module JsDuck
562
508
  "",
563
509
  "The wildcard '*' can be used to match a set of classes",
564
510
  "e.g. to ignore all classes of a particular namespace:",
565
- "--external='Foo.*'") do |classes|
566
- @external_classes += classes
511
+ "",
512
+ " --external='Foo.*'",
513
+ "",
514
+ "The special keyword '@browser' includes a slew of standard",
515
+ "browser API classes like HTMLElement and XMLHttpRequest:",
516
+ "",
517
+ " --external='@browser'",
518
+ "",
519
+ "The list of these classes comes from Mozilla docs, not",
520
+ "including experimental, non-standard or obsolete APIs:",
521
+ "https://developer.mozilla.org/en-US/docs/Web/API",
522
+ "",
523
+ "NB! If you only need to reference a few of these classes",
524
+ "in your docs, it's better to define these explicitly, as",
525
+ "using '@browser' will bring along a slew of names like",
526
+ "Node, Attr, Blob, CSS, Range, Element, which you might",
527
+ "mistakenly use in your docs by writing Node instead of",
528
+ "MyNamespace.Node and JSDuck won't warn you about it.") do |classes|
529
+ @opts.external += classes
567
530
  end
568
531
 
569
- opts.on('--[no-]ext4-events',
570
- "Forces Ext4 options param on events.",
532
+ attribute :ext4_events
533
+ option('--[no-]ext4-events',
534
+ "Forces Ext4 options param on events. (AUTO)",
571
535
  "",
572
536
  "In Ext JS 4 and Sencha Touch 2 all event handlers are",
573
537
  "passed an additional options object at the end of the",
@@ -580,18 +544,20 @@ module JsDuck
580
544
  "classes using Ext.define(), and only then append the",
581
545
  "options parameter. This should work most of the time.",
582
546
  "",
583
- "Use this option to override the auto-detection.") do |e|
584
- @ext4_events = e
547
+ "Use this option to override the auto-detection.") do |on|
548
+ @opts.ext4_events = on
585
549
  end
586
550
 
587
- opts.on('--examples-base-url=URL',
551
+ attribute :examples_base_url
552
+ option('--examples-base-url=URL',
588
553
  "Base URL for examples with relative URL-s.",
589
554
  " ",
590
555
  "Defaults to: 'extjs-build/examples/'") do |path|
591
- @examples_base_url = path
556
+ @opts.examples_base_url = path
592
557
  end
593
558
 
594
- opts.on('--link=TPL',
559
+ attribute :link, '<a href="#!/api/%c%-%m" rel="%c%-%m" class="docClass">%a</a>'
560
+ option('--link=TPL',
595
561
  "HTML template for replacing {@link}.",
596
562
  "",
597
563
  "Possible placeholders:",
@@ -604,10 +570,11 @@ module JsDuck
604
570
  "%a - anchor text for link",
605
571
  "",
606
572
  "Defaults to: '<a href=\"#!/api/%c%-%m\" rel=\"%c%-%m\" class=\"docClass\">%a</a>'") do |tpl|
607
- @link_tpl = tpl
573
+ @opts.link = tpl
608
574
  end
609
575
 
610
- opts.on('--img=TPL',
576
+ attribute :img, '<p><img src="%u" alt="%a" width="%w" height="%h"></p>'
577
+ option('--img=TPL',
611
578
  "HTML template for replacing {@img}.",
612
579
  "",
613
580
  "Possible placeholders:",
@@ -618,10 +585,11 @@ module JsDuck
618
585
  "%h - height of image",
619
586
  "",
620
587
  "Defaults to: '<p><img src=\"%u\" alt=\"%a\" width=\"%w\" height=\"%h\"></p>'") do |tpl|
621
- @img_tpl = tpl
588
+ @opts.img = tpl
622
589
  end
623
590
 
624
- opts.on('--eg-iframe=PATH',
591
+ attribute :eg_iframe
592
+ option('--eg-iframe=PATH',
625
593
  "HTML file used to display inline examples.",
626
594
  "",
627
595
  "The file will be used inside <iframe> that renders the",
@@ -630,10 +598,11 @@ module JsDuck
630
598
  "with the example code.",
631
599
  "",
632
600
  "See also: https://github.com/senchalabs/jsduck/wiki/Inline-examples") do |path|
633
- @eg_iframe = canonical(path)
601
+ @opts.eg_iframe = canonical(path)
634
602
  end
635
603
 
636
- opts.on('--ext-namespaces=Ext,Foo', Array,
604
+ attribute :ext_namespaces
605
+ option('--ext-namespaces=Ext,Foo', Array,
637
606
  "Additional Ext JS namespaces to recognize.",
638
607
  "",
639
608
  "Defaults to 'Ext'",
@@ -644,18 +613,22 @@ module JsDuck
644
613
  "and JSDuck will recognize both Ext.define() and",
645
614
  "YourNs.define() plus few other things that depend on",
646
615
  "Ext namespace like Ext.emptyFn.") do |namespaces|
647
- Js::ExtPatterns.set(namespaces)
616
+ @opts.ext_namespaces = namespaces
648
617
  end
649
618
 
650
- opts.on('--touch-examples-ui',
651
- "Turns on phone/tablet UI for examples.",
619
+ attribute :touch_examples_ui, false
620
+ option('--[no-]touch-examples-ui',
621
+ "Turns on phone/tablet UI for examples. (OFF)",
652
622
  "",
653
623
  "This is a very Sencha Touch 2 docs specific option.",
654
- "Effects both normal- and inline-examples.") do
655
- @touch_examples_ui = true
624
+ "Effects both normal- and inline-examples.",
625
+ "",
626
+ "Disabled by default") do |on|
627
+ @opts.touch_examples_ui = on
656
628
  end
657
629
 
658
- opts.on('--ignore-html=TAG1,TAG2', Array,
630
+ attribute :ignore_html, {}
631
+ option('--ignore-html=TAG1,TAG2', Array,
659
632
  "Ignore a particular unclosed HTML tag.",
660
633
  "",
661
634
  "Normally all tags like <foo> that aren't followed at some",
@@ -668,15 +641,16 @@ module JsDuck
668
641
  "<locale> and <debug> which would otherwise be reported",
669
642
  "as unclosed tags.") do |tags|
670
643
  tags.each do |tag|
671
- @ignore_html[tag] = true
644
+ @opts.ignore_html[tag] = true
672
645
  end
673
646
  end
674
647
 
675
- opts.separator ""
676
- opts.separator "Performance:"
677
- opts.separator ""
648
+ separator ""
649
+ separator "Performance:"
650
+ separator ""
678
651
 
679
- opts.on('-p', '--processes=COUNT',
652
+ attribute :processes
653
+ option('-p', '--processes=COUNT',
680
654
  "The number of parallel processes to use.",
681
655
  "",
682
656
  "Defaults to the number of processors/cores.",
@@ -686,13 +660,17 @@ module JsDuck
686
660
  "results.",
687
661
  "",
688
662
  "In Windows this option is disabled.") do |count|
689
- Util::Parallel.in_processes = count.to_i
663
+ @opts.processes = count.to_i
664
+ end
665
+ validator :processes do
666
+ if @opts.processes.to_i < 0
667
+ "Number of processes must be a positive number."
668
+ end
690
669
  end
691
670
 
692
- opts.on('--[no-]cache',
693
- "Turns parser cache on/off (EXPERIMENTAL).",
694
- "",
695
- "Defaults to off.",
671
+ attribute :cache, false
672
+ option('--[no-]cache',
673
+ "Turns parser cache on/off. (OFF)",
696
674
  "",
697
675
  "When enabled, the results of parsing source files is saved",
698
676
  "inside the JSDuck output directory. Next time JSDuck runs,",
@@ -704,11 +682,14 @@ module JsDuck
704
682
  "don't invalidate the cache, so avoid caching when developing",
705
683
  "your custom tags.",
706
684
  "",
707
- "To change the cache directory location, use --cache-dir.") do |enabled|
708
- @cache = enabled
685
+ "To change the cache directory location, use --cache-dir.",
686
+ "",
687
+ "Disabled by default.") do |on|
688
+ @opts.cache = on
709
689
  end
710
690
 
711
- opts.on('--cache-dir=PATH',
691
+ attribute :cache_dir
692
+ option('--cache-dir=PATH',
712
693
  "Directory where to cache the parsed source.",
713
694
  "",
714
695
  "Defaults to: <output-dir>/.cache",
@@ -726,29 +707,34 @@ module JsDuck
726
707
  "won't work.",
727
708
  "",
728
709
  "This option only has an effect when --cache is also used.") do |path|
729
- @cache_dir = path
710
+ @opts.cache_dir = path
730
711
  end
731
712
 
732
- opts.separator ""
733
- opts.separator "Debugging:"
734
- opts.separator ""
713
+ separator ""
714
+ separator "Debugging:"
715
+ separator ""
735
716
 
736
- opts.on('-v', '--verbose',
717
+ attribute :verbose, false
718
+ option('-v', '--verbose',
737
719
  "Turns on excessive logging.",
738
720
  "",
739
721
  "Log messages are written to STDERR.") do
740
- Logger.verbose = true
722
+ @opts.verbose = true
741
723
  end
742
724
 
743
- opts.on('--warnings=+A,-B,+C',
725
+ attribute :warnings, []
726
+ option('--warnings=+A,-B,+C',
744
727
  "Turns warnings selectively on/off.",
745
728
  "",
746
729
  " +all - to turn on all warnings.",
747
730
  " -all - to turn off all warnings.",
748
731
  "",
749
732
  "Additionally a pattern can be specified to only apply the",
750
- "setting for a particular set of files. For example to turn",
751
- "off all warnings related to chart classes:",
733
+ "setting for a particular set of files. (The pattern is just",
734
+ "a string against which the the full path of each filename",
735
+ "gets matched - attempting to use a pattern like '../foo' will",
736
+ "fail.) For example to turn off all warnings related to chart",
737
+ "classes:",
752
738
  "",
753
739
  " -all:extjs/src/chart",
754
740
  "",
@@ -761,68 +747,91 @@ module JsDuck
761
747
  "",
762
748
  *Logger.doc_warnings) do |warnings|
763
749
  begin
764
- Warning::Parser.new(warnings).parse.each do |w|
765
- Logger.set_warning(w[:type], w[:enabled], w[:path], w[:params])
766
- end
750
+ @opts.warnings += Warning::Parser.new(warnings).parse
767
751
  rescue Warning::WarnException => e
768
752
  Logger.warn(nil, e.message)
769
753
  end
770
754
  end
771
755
 
772
- opts.on('--warnings-exit-nonzero',
773
- "Exits with non-zero exit code on warnings.",
756
+ attribute :warnings_exit_nonzero, false
757
+ option('--[no-]warnings-exit-nonzero',
758
+ "Exits with non-zero exit code on warnings. (OFF)",
774
759
  "",
775
760
  "By default JSDuck only exits with non-zero exit code",
776
761
  "when a fatal error is encountered (code 1).",
777
762
  "",
778
763
  "With this option the exit code will be 2 when any warning",
779
- "gets printed.") do
780
- @warnings_exit_nonzero = true
764
+ "gets printed.") do |on|
765
+ @opts.warnings_exit_nonzero = on
781
766
  end
782
767
 
783
- opts.on('--[no-]color',
784
- "Turn on/off colorized terminal output.",
768
+ attribute :color
769
+ option('--[no-]color',
770
+ "Turn on/off colorized terminal output. (AUTO)",
785
771
  "",
786
772
  "By default the colored output is on, but gets disabled",
787
773
  "automatically when output is not an interactive terminal",
788
774
  "(or when running on Windows system).") do |on|
789
- Logger.colors = on
775
+ @opts.color = on
790
776
  end
791
777
 
792
- opts.on('--pretty-json',
793
- "Turns on pretty-printing of JSON.",
778
+ attribute :pretty_json
779
+ option('--[no-]pretty-json',
780
+ "Turns on pretty-printing of JSON. (OFF)",
794
781
  "",
795
782
  "This is useful when studying the JSON files generated",
796
783
  "by --export option. But the option effects any JSON",
797
784
  "that gets generated, so it's also useful when debugging",
798
- "the resource files generated for the docs app.") do
799
- Util::Json.pretty = true
785
+ "the resource files generated for the docs app.") do |on|
786
+ @opts.pretty_json = on
800
787
  end
801
788
 
802
- opts.on('--template=PATH',
789
+ attribute :template, @root_dir + "/template-min"
790
+ option('--template=PATH',
803
791
  "Dir containing the UI template files.",
804
792
  "",
805
793
  "Useful when developing the template files.") do |path|
806
- @template_dir = canonical(path)
794
+ @opts.template = canonical(path)
795
+ end
796
+ validator :template do
797
+ if @opts.export
798
+ # Don't check these things when exporting
799
+ elsif !@file.exists?(@opts.template + "/extjs")
800
+ [
801
+ "Oh noes! The template directory does not contain extjs/ directory :(",
802
+ "Please copy ExtJS over to template/extjs or create symlink.",
803
+ "For example:",
804
+ " $ cp -r /path/to/ext-4.0.0 " + @opts.template + "/extjs",
805
+ ]
806
+ elsif !@file.exists?(@opts.template + "/resources/css")
807
+ [
808
+ "Oh noes! CSS files for custom ExtJS theme missing :(",
809
+ "Please compile SASS files in template/resources/sass with compass.",
810
+ "For example:",
811
+ " $ compass compile " + @opts.template + "/resources/sass",
812
+ ]
813
+ end
807
814
  end
808
815
 
809
- opts.on('--template-links',
810
- "Creates symlinks to UI template files.",
816
+ attribute :template_links, false
817
+ option('--[no-]template-links',
818
+ "Creates symlinks to UI template files. (OFF)",
811
819
  "",
812
820
  "Useful for template files development.",
813
- "Only works on platforms supporting symbolic links.") do
814
- @template_links = true
821
+ "Only works on platforms supporting symbolic links.") do |on|
822
+ @opts.template_links = on
815
823
  end
816
824
 
817
- opts.on('-d', '--debug',
825
+ option('-d', '--debug',
818
826
  "Same as --template=template --template-links.",
819
827
  "",
820
828
  "Useful shorthand during development.") do
821
- @template_dir = canonical("template")
822
- @template_links = true
829
+ @opts.template = canonical("template")
830
+ @opts.template_links = true
823
831
  end
824
832
 
825
- opts.on('--extjs-path=PATH',
833
+ attribute :extjs_path, "extjs/ext-all.js"
834
+ option('--extjs-path=PATH',
826
835
  "Path for main ExtJS JavaScript file.",
827
836
  "",
828
837
  "This is the ExtJS file that's used by the docs app UI.",
@@ -830,17 +839,18 @@ module JsDuck
830
839
  "Defaults to extjs/ext-all.js",
831
840
  "",
832
841
  "Useful for switching to extjs/ext-all-debug.js in development.") do |path|
833
- @extjs_path = path # NB! must be relative path
842
+ @opts.extjs_path = path # NB! must be relative path
834
843
  end
835
844
 
836
- opts.on('--local-storage-db=NAME',
845
+ attribute :local_storage_db, "docs"
846
+ option('--local-storage-db=NAME',
837
847
  "Prefix for LocalStorage database names.",
838
848
  "",
839
849
  "Defaults to 'docs'") do |name|
840
- @local_storage_db = name
850
+ @opts.local_storage_db = name
841
851
  end
842
852
 
843
- opts.on('-h', '--help[=--some-option]',
853
+ option('-h', '--help[=--some-option]',
844
854
  "Use --help=--option for help on option.",
845
855
  "",
846
856
  "For example To get help on --processes option any of the",
@@ -851,159 +861,101 @@ module JsDuck
851
861
  " --help=-p",
852
862
  " --help=p") do |v|
853
863
  if v
854
- puts opts.help_single(v)
864
+ puts @optparser.help_single(v)
855
865
  else
856
- puts opts.help
866
+ puts @optparser.help
857
867
  end
858
868
  exit
859
869
  end
860
870
 
861
- opts.on('--version', "Prints JSDuck version") do
871
+ option('--version', "Prints JSDuck version") do
862
872
  puts "JSDuck " + JsDuck::VERSION + " (Ruby #{RUBY_VERSION})"
863
873
  exit
864
874
  end
865
875
  end
866
- end
867
876
 
868
- # Parses the given command line options
869
- # (could have also been read from config file)
870
- def parse_options(options)
871
- @optparser.parse!(options).each {|fname| read_filenames(canonical(fname)) }
872
- end
877
+ # A little language for describing the options.
878
+ # Simple delegetions to @optparser and @opts.
873
879
 
874
- # Reads jsduck.json file in current directory
875
- def auto_detect_config_file
876
- fname = Dir.pwd + "/jsduck.json"
877
- if File.exists?(fname)
878
- Logger.log("Auto-detected config file", fname)
879
- parse_options(read_json_config(fname))
880
+ def banner(txt)
881
+ @optparser.banner = txt
880
882
  end
881
- end
882
883
 
883
- # Reads JSON configuration from file and returns an array of
884
- # config options that can be feeded into optparser.
885
- def read_json_config(fname)
886
- config = []
887
- json = Util::Json.read(fname)
888
- json.each_pair do |key, value|
889
- if key == "--"
890
- # filenames
891
- config += Array(value).map(&:to_s)
892
- elsif value == true
893
- # simple switch
894
- config += [key.to_s]
895
- else
896
- # An option with value or with multiple values.
897
- # In the latter case, add the option multiple times.
898
- Array(value).each do |v|
899
- config += [key.to_s, v.to_s]
900
- end
901
- end
884
+ def separator(txt)
885
+ @optparser.separator(txt)
902
886
  end
903
- config
904
- end
905
887
 
906
- # scans directory for .js files or simply adds file to input files list
907
- def read_filenames(fname)
908
- if File.exists?(fname)
909
- if File.directory?(fname)
910
- Dir[fname+"/**/*.{js,css,scss}"].each {|f| @input_files << f }
911
- elsif fname =~ /\.jsb3$/
912
- extract_jsb_files(fname).each {|fn| read_filenames(fn) }
913
- else
914
- @input_files << fname
915
- end
916
- else
917
- Logger.warn(nil, "File not found", fname)
888
+ def option(*params, &block)
889
+ @optparser.on(*params, &block)
918
890
  end
919
- end
920
891
 
921
- # When --exclude option used, removes the files matching the
922
- # exclude path from @input_files
923
- def exclude_input_files
924
- @exclude.each do |exclude_path|
925
- exclude_re = Regexp.new('\A' + Regexp.escape(canonical(exclude_path)))
926
- @input_files.reject! {|f| f =~ exclude_re }
892
+ def attribute(name, value=nil)
893
+ @opts.attribute(name, value)
894
+ @defaults[name] = value
927
895
  end
928
- end
929
896
 
930
- # Extracts files of first build in jsb file
931
- def extract_jsb_files(jsb_file)
932
- json = Util::Json.read(jsb_file)
933
- basedir = File.dirname(jsb_file)
897
+ def validator(name, &block)
898
+ @opts.validator(name, &block)
899
+ end
934
900
 
935
- return json["builds"][0]["packages"].map do |package_id|
936
- package = json["packages"].find {|p| p["id"] == package_id }
937
- (package ? package["files"] : []).map do |file|
938
- File.expand_path(basedir + "/" + file["path"] + file["name"])
901
+ # Initializes attributes with default values. This is needed to
902
+ # be able to run the most tests without re-instantiating this
903
+ # class every time.
904
+ def init_defaults
905
+ @defaults.each_pair do |name, value|
906
+ @opts.send(:"#{name}=", clone(value))
939
907
  end
940
- end.flatten
941
- end
908
+ end
942
909
 
943
- # When given string is a file, returns the contents of the file.
944
- # Otherwise returns the string unchanged.
945
- def maybe_file(str)
946
- path = canonical(str)
947
- if File.exists?(path)
948
- Util::IO.read(path)
949
- else
950
- str
910
+ # clones hashes and arrays.
911
+ def clone(obj)
912
+ (obj.is_a?(Array) || obj.is_a?(Hash)) ? obj.clone : obj
951
913
  end
952
- end
953
914
 
954
- # Converts relative path to full path
955
- #
956
- # Especially important for running on Windows where C:\foo\bar
957
- # pathnames are converted to C:/foo/bar which ruby can work on
958
- # more easily.
959
- def canonical(path)
960
- File.expand_path(path, @working_dir)
961
- end
915
+ # Parses the given command line options
916
+ # (could have also been read from config file)
917
+ def parse_options(options)
918
+ @optparser.parse!(options).each do |fname|
919
+ @opts.input_files << canonical(fname)
920
+ end
921
+ end
962
922
 
963
- # Replace special placeholders in footer text
964
- def format_footer(text)
965
- jsduck = "<a href='https://github.com/senchalabs/jsduck'>JSDuck</a>"
966
- date = Time.new.strftime('%a %d %b %Y %H:%M:%S')
967
- text.gsub(/\{VERSION\}/, JsDuck::VERSION).gsub(/\{JSDUCK\}/, jsduck).gsub(/\{DATE\}/, date)
968
- end
923
+ # Reads jsduck.json file in current directory
924
+ def auto_detect_config_file
925
+ fname = Dir.pwd + "/jsduck.json"
926
+ if @file.exists?(fname)
927
+ Logger.log("Auto-detected config file", fname)
928
+ parse_options(read_json_config(fname))
929
+ end
930
+ end
931
+
932
+ # Reads JSON configuration from file and returns an array of
933
+ # config options that can be feeded into optparser.
934
+ def read_json_config(filename)
935
+ @config.read(filename)
936
+ end
969
937
 
970
- # Runs checks on the options
971
- def validate
972
- if @input_files.length == 0 && !@welcome && !@guides && !@videos && !@examples
973
- Logger.fatal("You should specify some input files, otherwise there's nothing I can do :(")
974
- exit(1)
975
- elsif @output_dir == :stdout && !@export
976
- Logger.fatal("Output to STDOUT only works when using --export option")
977
- exit(1)
978
- elsif ![nil, :full, :api, :examples].include?(@export)
979
- Logger.fatal("Unknown export format: #{@export}")
980
- exit(1)
981
- elsif @output_dir != :stdout
982
- if !@output_dir
983
- Logger.fatal("You should also specify an output directory, where I could write all this amazing documentation")
984
- exit(1)
985
- elsif File.exists?(@output_dir) && !File.directory?(@output_dir)
986
- Logger.fatal("The output directory is not really a directory at all :(")
987
- exit(1)
988
- elsif !File.exists?(File.dirname(@output_dir))
989
- Logger.fatal("The parent directory for #{@output_dir} doesn't exist")
990
- exit(1)
991
- elsif !@export && !File.exists?(@template_dir + "/extjs")
992
- Logger.fatal("Oh noes! The template directory does not contain extjs/ directory :(")
993
- Logger.fatal("Please copy ExtJS over to template/extjs or create symlink.")
994
- Logger.fatal("For example:")
995
- Logger.fatal(" $ cp -r /path/to/ext-4.0.0 " + @template_dir + "/extjs")
996
- exit(1)
997
- elsif !@export && !File.exists?(@template_dir + "/resources/css")
998
- Logger.fatal("Oh noes! CSS files for custom ExtJS theme missing :(")
999
- Logger.fatal("Please compile SASS files in template/resources/sass with compass.")
1000
- Logger.fatal("For example:")
1001
- Logger.fatal(" $ compass compile " + @template_dir + "/resources/sass")
1002
- exit(1)
938
+ # When given string is a file, returns the contents of the file.
939
+ # Otherwise returns the string unchanged.
940
+ def maybe_file(str)
941
+ path = canonical(str)
942
+ if @file.exists?(path)
943
+ Util::IO.read(path)
944
+ else
945
+ str
1003
946
  end
1004
947
  end
948
+
949
+ # Converts relative path to full path
950
+ #
951
+ # Especially important for running on Windows where C:\foo\bar
952
+ # pathnames are converted to C:/foo/bar which ruby can work on
953
+ # more easily.
954
+ def canonical(path)
955
+ @file.expand_path(path, @working_dir)
956
+ end
957
+
1005
958
  end
1006
959
 
1007
960
  end
1008
-
1009
961
  end