jsduck 4.0.1 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. data/Rakefile +14 -0
  2. data/esprima/esprima.js +210 -85
  3. data/jsduck.gemspec +3 -3
  4. data/lib/jsduck/accessors.rb +10 -8
  5. data/lib/jsduck/aggregator.rb +7 -7
  6. data/lib/jsduck/app.rb +24 -164
  7. data/lib/jsduck/app_data.rb +2 -4
  8. data/lib/jsduck/assets.rb +5 -2
  9. data/lib/jsduck/ast.rb +9 -4
  10. data/lib/jsduck/batch_formatter.rb +54 -0
  11. data/lib/jsduck/batch_parser.rb +106 -0
  12. data/lib/jsduck/categories.rb +5 -6
  13. data/lib/jsduck/class.rb +77 -239
  14. data/lib/jsduck/class_doc_expander.rb +0 -5
  15. data/lib/jsduck/class_formatter.rb +14 -10
  16. data/lib/jsduck/class_name.rb +23 -0
  17. data/lib/jsduck/class_writer.rb +9 -8
  18. data/lib/jsduck/doc_ast.rb +5 -6
  19. data/lib/jsduck/doc_formatter.rb +61 -272
  20. data/lib/jsduck/enum.rb +4 -4
  21. data/lib/jsduck/esprima.rb +10 -4
  22. data/lib/jsduck/examples.rb +5 -5
  23. data/lib/jsduck/export_writer.rb +62 -0
  24. data/lib/jsduck/exporter/app.rb +62 -0
  25. data/lib/jsduck/exporter/examples.rb +58 -0
  26. data/lib/jsduck/exporter/full.rb +60 -0
  27. data/lib/jsduck/file_categories.rb +7 -4
  28. data/lib/jsduck/function_ast.rb +99 -0
  29. data/lib/jsduck/grouped_asset.rb +27 -16
  30. data/lib/jsduck/guide_writer.rb +8 -7
  31. data/lib/jsduck/guides.rb +31 -29
  32. data/lib/jsduck/icons.rb +12 -1
  33. data/lib/jsduck/images.rb +3 -3
  34. data/lib/jsduck/importer.rb +7 -7
  35. data/lib/jsduck/index_html.rb +20 -6
  36. data/lib/jsduck/inherit_doc.rb +9 -12
  37. data/lib/jsduck/inline/example.rb +42 -0
  38. data/lib/jsduck/inline/img.rb +55 -0
  39. data/lib/jsduck/inline/link.rb +227 -0
  40. data/lib/jsduck/inline/video.rb +67 -0
  41. data/lib/jsduck/inline_examples.rb +7 -7
  42. data/lib/jsduck/js_parser.rb +5 -4
  43. data/lib/jsduck/lint.rb +14 -2
  44. data/lib/jsduck/logger.rb +5 -6
  45. data/lib/jsduck/members_index.rb +132 -0
  46. data/lib/jsduck/merger.rb +3 -9
  47. data/lib/jsduck/options.rb +39 -41
  48. data/lib/jsduck/override.rb +3 -7
  49. data/lib/jsduck/relations.rb +9 -9
  50. data/lib/jsduck/renderer.rb +3 -3
  51. data/lib/jsduck/return_values.rb +72 -0
  52. data/lib/jsduck/search_data.rb +16 -20
  53. data/lib/jsduck/shortener.rb +58 -0
  54. data/lib/jsduck/signature_renderer.rb +1 -2
  55. data/lib/jsduck/source/file.rb +98 -0
  56. data/lib/jsduck/source/file_parser.rb +72 -0
  57. data/lib/jsduck/source/writer.rb +89 -0
  58. data/lib/jsduck/tag/aside.rb +1 -1
  59. data/lib/jsduck/tag/since.rb +1 -1
  60. data/lib/jsduck/template_dir.rb +2 -2
  61. data/lib/jsduck/util/html.rb +27 -0
  62. data/lib/jsduck/util/io.rb +32 -0
  63. data/lib/jsduck/util/json.rb +60 -0
  64. data/lib/jsduck/util/null_object.rb +23 -0
  65. data/lib/jsduck/util/os.rb +14 -0
  66. data/lib/jsduck/util/parallel.rb +34 -0
  67. data/lib/jsduck/util/singleton.rb +35 -0
  68. data/lib/jsduck/util/stdout.rb +33 -0
  69. data/lib/jsduck/videos.rb +5 -5
  70. data/lib/jsduck/web_writer.rb +79 -0
  71. data/lib/jsduck/welcome.rb +6 -6
  72. data/spec/class_factory.rb +20 -0
  73. metadata +32 -20
  74. data/lib/jsduck/api_exporter.rb +0 -48
  75. data/lib/jsduck/app_exporter.rb +0 -60
  76. data/lib/jsduck/examples_exporter.rb +0 -56
  77. data/lib/jsduck/full_exporter.rb +0 -35
  78. data/lib/jsduck/html.rb +0 -25
  79. data/lib/jsduck/inline_img.rb +0 -53
  80. data/lib/jsduck/inline_video.rb +0 -58
  81. data/lib/jsduck/io.rb +0 -30
  82. data/lib/jsduck/json_duck.rb +0 -52
  83. data/lib/jsduck/lexer.rb +0 -251
  84. data/lib/jsduck/null_object.rb +0 -19
  85. data/lib/jsduck/os.rb +0 -11
  86. data/lib/jsduck/parallel_wrap.rb +0 -32
  87. data/lib/jsduck/source_file.rb +0 -97
  88. data/lib/jsduck/source_file_parser.rb +0 -70
  89. data/lib/jsduck/source_writer.rb +0 -87
  90. data/lib/jsduck/stats.rb +0 -103
  91. data/lib/jsduck/stdout.rb +0 -31
@@ -0,0 +1,106 @@
1
+ require 'jsduck/util/parallel'
2
+ require 'jsduck/util/io'
3
+ require 'jsduck/source/file'
4
+ require 'jsduck/aggregator'
5
+ require 'jsduck/class'
6
+ require 'jsduck/relations'
7
+ require 'jsduck/logger'
8
+ require 'jsduck/inherit_doc'
9
+ require 'jsduck/importer'
10
+ require 'jsduck/return_values'
11
+ require 'jsduck/lint'
12
+
13
+ module JsDuck
14
+
15
+ # Performs the parsing of all input files. Input files are read
16
+ # from options object (originating from command line).
17
+ class BatchParser
18
+ def initialize(opts)
19
+ @opts = opts
20
+ end
21
+
22
+ # Array of Source::File objects.
23
+ # Available after calling the #run method.
24
+ attr_reader :parsed_files
25
+
26
+ # Parses the files and returns instance of Relations class.
27
+ def run
28
+ @parsed_files = parallel_parse(@opts.input_files)
29
+ result = aggregate(@parsed_files)
30
+ @relations = filter_classes(result)
31
+ apply_extra_processing
32
+ return @relations
33
+ end
34
+
35
+ private
36
+
37
+ # Parses the files in parallel using as many processes as available CPU-s
38
+ def parallel_parse(filenames)
39
+ Util::Parallel.map(filenames) do |fname|
40
+ Logger.log("Parsing", fname)
41
+ begin
42
+ Source::File.new(Util::IO.read(fname), fname, @opts)
43
+ rescue
44
+ Logger.fatal_backtrace("Error while parsing #{fname}", $!)
45
+ exit(1)
46
+ end
47
+ end
48
+ end
49
+
50
+ # Aggregates parsing results sequencially
51
+ def aggregate(parsed_files)
52
+ agr = Aggregator.new
53
+ parsed_files.each do |file|
54
+ Logger.log("Aggregating", file.filename)
55
+ agr.aggregate(file)
56
+ end
57
+ agr.classify_orphans
58
+ agr.create_global_class
59
+ agr.remove_ignored_classes
60
+ agr.create_accessors
61
+ if @opts.ext4_events == true || (@opts.ext4_events == nil && agr.ext4?)
62
+ agr.append_ext4_event_options
63
+ end
64
+ agr.process_enums
65
+ # Ignore override classes after applying them to actual classes
66
+ @opts.external_classes += agr.process_overrides.map {|o| o[:name] }
67
+ agr.result
68
+ end
69
+
70
+ # Turns all aggregated data into Class objects.
71
+ # Depending on --ignore-global either keeps or discards the global class.
72
+ # Warnings for global members are printed regardless of that setting,
73
+ # but of course can be turned off using --warnings=-global
74
+ def filter_classes(docs)
75
+ classes = []
76
+ docs.each do |d|
77
+ cls = Class.new(d)
78
+ if d[:name] != "global"
79
+ classes << cls
80
+ else
81
+ # add global class only if --ignore-global not specified
82
+ classes << cls unless @opts.ignore_global
83
+
84
+ # Print warning for each global member
85
+ cls.all_local_members.each do |m|
86
+ type = m[:tagname].to_s
87
+ name = m[:name]
88
+ file = m[:files][0]
89
+ Logger.warn(:global, "Global #{type}: #{name}", file[:filename], file[:linenr])
90
+ end
91
+ end
92
+ end
93
+ Relations.new(classes, @opts.external_classes)
94
+ end
95
+
96
+ # Do all kinds of post-processing on relations.
97
+ def apply_extra_processing
98
+ InheritDoc.new(@relations).resolve_all
99
+ Importer.import(@opts.imports, @relations, @opts.new_since)
100
+ ReturnValues.auto_detect(@relations)
101
+ Lint.new(@relations).run
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -1,5 +1,4 @@
1
1
  require 'jsduck/logger'
2
- require 'jsduck/json_duck'
3
2
  require 'jsduck/file_categories'
4
3
  require 'jsduck/auto_categories'
5
4
 
@@ -23,7 +22,7 @@ module JsDuck
23
22
  end
24
23
 
25
24
  # Returns HTML listing of classes divided into categories
26
- def to_html
25
+ def to_html(style="")
27
26
  html = @categories.map do |category|
28
27
  [
29
28
  "<div class='section'>",
@@ -35,7 +34,7 @@ module JsDuck
35
34
  end.flatten.join("\n")
36
35
 
37
36
  return <<-EOHTML
38
- <div id='categories-content' style='display:none'>
37
+ <div id='categories-content' style='#{style}'>
39
38
  #{html}
40
39
  </div>
41
40
  EOHTML
@@ -58,9 +57,9 @@ module JsDuck
58
57
  return groups.map do |g|
59
58
  [
60
59
  "<h3>#{g['name']}</h3>",
61
- "<div class='links'>",
62
- g["classes"].map {|cls| @relations[cls] ? @doc_formatter.link(cls, nil, cls) : cls },
63
- "</div>",
60
+ "<ul class='links'>",
61
+ g["classes"].map {|cls| "<li>" + (@relations[cls] ? @doc_formatter.link(cls, nil, cls) : cls) + "</li>" },
62
+ "</ul>",
64
63
  ]
65
64
  end
66
65
  end
@@ -1,4 +1,5 @@
1
1
  require 'jsduck/logger'
2
+ require 'jsduck/members_index'
2
3
 
3
4
  module JsDuck
4
5
 
@@ -8,6 +9,10 @@ module JsDuck
8
9
  class Class
9
10
  attr_accessor :relations
10
11
 
12
+ # Used only by MembersIndex class itself to access the
13
+ # MembersIndex instances of parents and mixins.
14
+ attr_accessor :members_index
15
+
11
16
  # Creates JSDuck class.
12
17
  #
13
18
  # Pass true as second parameter to create a placeholder class.
@@ -18,19 +23,37 @@ module JsDuck
18
23
  # differenciating between existing and missing classes.
19
24
  @doc[:name] = ClassNameString.new(@doc[:name], class_exists)
20
25
 
21
- @doc[:members] = Class.default_members_hash if !@doc[:members]
22
- @doc[:statics] = Class.default_members_hash if !@doc[:statics]
26
+ @doc[:members] = [] if !@doc[:members]
27
+
28
+ @members_index = MembersIndex.new(self)
29
+
23
30
  @relations = nil
24
31
  end
25
32
 
26
- # Accessors for internal doc object. These are used to run
27
- # ClassFormatter on the internal doc object and then assign it
28
- # back.
33
+ # Returns the internal doc object.
29
34
  def internal_doc
30
35
  @doc
31
36
  end
37
+
38
+ # Sets the internal doc object.
39
+ #
40
+ # The doc object is processed in parallel and then assigned back
41
+ # through this method. But because of parallel processing the
42
+ # assigned doc object will not be just a modified old @doc but a
43
+ # completely new. If we were to just assign to @doc the
44
+ # #find_members caches would still point to old @doc members
45
+ # resulting in mysterious errors further along...
32
46
  def internal_doc=(doc)
33
- @doc = doc
47
+ @doc.merge!(doc) do |key, oldval, newval|
48
+ if key == :members
49
+ oldval.zip(newval) do |ms|
50
+ ms[0].merge!(ms[1])
51
+ end
52
+ oldval
53
+ else
54
+ newval
55
+ end
56
+ end
34
57
  end
35
58
 
36
59
  # Accessor to internal hash
@@ -93,272 +116,87 @@ module JsDuck
93
116
  Class.new({:name => classname}, false)
94
117
  else
95
118
  context = @doc[:files][0]
96
- Logger.instance.warn(:extend, "Class #{classname} not found", context[:filename], context[:linenr])
119
+ Logger.warn(:extend, "Class #{classname} not found", context[:filename], context[:linenr])
97
120
  # Create placeholder class
98
121
  Class.new({:name => classname}, false)
99
122
  end
100
123
  end
101
124
 
102
- # Returns copy of @doc hash
103
- def to_hash
104
- @doc.clone
105
- end
106
-
107
- def to_json(*a)
108
- to_hash.to_json(*a)
109
- end
110
-
111
125
  # Returns true when this class inherits from the specified class.
112
126
  # Also returns true when the class itself is the one we are asking about.
113
127
  def inherits_from?(class_name)
114
- return full_name == class_name || (parent ? parent.inherits_from?(class_name) : false)
128
+ return @doc[:name] == class_name || (parent ? parent.inherits_from?(class_name) : false)
115
129
  end
116
130
 
117
- # Returns array of all public members of particular type in a class,
118
- # sorted by name.
131
+ # Returns list of members filtered by a query.
132
+ # Searches both local and inherited members.
119
133
  #
120
- # For methods the the constructor is listed first.
134
+ # The query hash can contain the following fields:
121
135
  #
122
- # See members_hash for details.
123
- def members(type, context=:members)
124
- ms = members_hash(type, context).values #.find_all {|m| !m[:private] }
125
- ms.sort! {|a,b| a[:name] <=> b[:name] }
126
- type == :method ? constructor_first(ms) : ms
127
- end
128
-
129
- # If methods list contains constructor, rename it with class name
130
- # and move into beginning of methods list.
131
- def constructor_first(ms)
132
- constr = ms.find {|m| m[:name] == "constructor" }
133
- if constr
134
- ms.delete(constr)
135
- ms.unshift(constr)
136
- end
137
- ms
138
- end
139
-
140
- # Returns hash of all members in class (and of parent classes
141
- # and mixin classes). Members are methods, properties, cfgs,
142
- # events (member type is specified through 'type' parameter).
136
+ # - :name : String - the name of the member to find.
143
137
  #
144
- # When parent and child have members with same name,
145
- # member from child overrides tha parent member.
146
- def members_hash(type, context=:members)
147
- # Singletons have no static members
148
- if @doc[:singleton] && context == :statics
149
- # Warn if singleton has static members
150
- @doc[context][type].each do |m|
151
- ctx = m[:files][0]
152
- msg = "Singleton class #{@doc[:name]} can't have static members, remove the @static tag."
153
- Logger.instance.warn(:sing_static, msg, ctx[:filename], ctx[:linenr])
154
- end
155
- return {}
156
- end
157
-
158
- ms = parent ? parent.members_hash(type, context) : {}
159
-
160
- mixins.each do |mix|
161
- merge!(ms, mix.members_hash(type, context))
162
- end
163
-
164
- # For static members, exclude everything not explicitly marked as inheritable
165
- if context == :statics
166
- ms.delete_if {|key, member| !member[:inheritable] }
167
- end
168
-
169
- merge!(ms, local_members_hash(type, context))
170
-
171
- # If singleton has static members, include them as if they were
172
- # instance members. Otherwise they will be completely excluded
173
- # from the docs, as the static members block is not created for
174
- # singletons.
175
- if @doc[:singleton] && @doc[:statics][type].length > 0
176
- merge!(ms, local_members_hash(type, :statics))
177
- end
178
-
179
- ms
180
- end
181
-
182
- # merges second members hash into first one
183
- def merge!(hash1, hash2, skip_overrides=false)
184
- hash2.each_pair do |name, m|
185
- if m[:meta] && m[:meta][:hide]
186
- if hash1[name]
187
- hash1.delete(name)
188
- else
189
- ctx = m[:files][0]
190
- msg = "@hide used but #{m[:tagname]} #{m[:name]} not found in parent class"
191
- Logger.instance.warn(:hide, msg, ctx[:filename], ctx[:linenr])
192
- end
193
- else
194
- if hash1[name]
195
- store_overrides(hash1[name], m)
196
- end
197
- hash1[name] = m
198
- end
199
- end
200
- end
201
-
202
- # Invoked when merge! finds two members with the same name.
203
- # New member always overrides the old, but inside new we keep
204
- # a list of members it overrides. Normally one member will
205
- # override one other member, but a member from mixin can override
206
- # multiple members - although there's not a single such case in
207
- # ExtJS, we have to handle it.
138
+ # - :tagname : Symbol - the member type to look for.
208
139
  #
209
- # Every overridden member is listed just once.
210
- def store_overrides(old, new)
211
- # Sometimes a class is included multiple times (like Ext.Base)
212
- # resulting in its members overriding themselves. Because of
213
- # this, ignore overriding itself.
214
- if new[:owner] != old[:owner]
215
- new[:overrides] = [] unless new[:overrides]
216
- unless new[:overrides].any? {|m| m[:owner] == old[:owner] }
217
- # Make a copy of the important properties for us. We can't
218
- # just push the actual `old` member itself, because there
219
- # can be circular overrides (notably with Ext.Base), which
220
- # will result in infinite loop when we try to convert our
221
- # class into JSON.
222
- new[:overrides] << {
223
- :name => old[:name],
224
- :owner => old[:owner],
225
- :id => old[:id],
226
- }
227
- end
140
+ # - :static : Boolean - true to only return static members,
141
+ # false to only return instance members.
142
+ # When nil or unspecified, both static
143
+ # and instance members are returned.
144
+ #
145
+ # - :local : Boolean - true to only return non-inherited members.
146
+ #
147
+ # When called without arguments all members are returned.
148
+ #
149
+ # When nothing found, an empty array is returned.
150
+ def find_members(query={})
151
+ if query[:name]
152
+ ms = @members_index.global_by_name[query[:name]] || []
153
+ ms = ms.find_all {|m| m[:owner] == @doc[:name]} if query[:local]
154
+ elsif query[:local]
155
+ ms = @members_index.local_by_id.values
156
+ else
157
+ ms = @members_index.global_by_id.values
228
158
  end
229
- end
230
159
 
231
- # Helper method to get the direct members of this class
232
- def local_members_hash(type, context)
233
- local_members = {}
234
- (@doc[context][type] || []).each do |m|
235
- local_members[m[:name]] = m
160
+ if query[:tagname]
161
+ ms = ms.find_all {|m| m[:tagname] == query[:tagname] }
236
162
  end
237
- local_members
238
- end
239
163
 
240
- # Returns members by name. An array of one or more members, or
241
- # empty array when nothing matches.
242
- #
243
- # Optionally one can also specify type name to differenciate
244
- # between different types of members.
245
- #
246
- # Finally static flag can be specified. True to look only at
247
- # static members, false to look at instance members, and nil to
248
- # look at both.
249
- def get_members(name, type_name=nil, static=nil)
250
- # build hash of all members
251
- unless @members_map
252
- @members_map = {}
253
- [:members, :statics].each do |group|
254
- @doc[group].each_key do |type|
255
- members_hash(type, group).each_pair do |key, member|
256
- @members_map[key] = (@members_map[key] || []) + [member]
257
- end
258
- end
259
- end
164
+ if query[:static] == true
165
+ ms = ms.find_all {|m| m[:meta] && m[:meta][:static] }
166
+ elsif query[:static] == false
167
+ ms = ms.reject {|m| m[:meta] && m[:meta][:static] }
260
168
  end
261
169
 
262
- ms = @members_map[name] || []
263
- ms = ms.find_all {|m| m[:tagname] == type_name } if type_name
264
- # static = true | false | nil
265
- ms = ms.find_all {|m| m[:meta][:static] } if static == true
266
- ms = ms.find_all {|m| !m[:meta][:static] } if static == false
267
- return ms
268
- end
269
-
270
- # Call this when renaming or moving members inside class.
271
- def reset_members_lookup!
272
- @members_map = nil
170
+ ms
273
171
  end
274
172
 
275
- # Returns all members of class, including the inherited and mixed in ones
276
- def all_members
277
- all = []
278
- [:members, :statics].each do |group|
279
- @doc[group].each_key do |type|
280
- all += members(type, group)
281
- end
173
+ # This must be called whenever member hashes are changed.
174
+ # It updates the :id fields of members and clears the caches.
175
+ def update_members!(members)
176
+ members.each do |m|
177
+ m[:id] = Class.member_id(m)
282
178
  end
283
- all
179
+ @members_index.invalidate!
284
180
  end
285
181
 
286
182
  # Returns all local members of class
287
183
  def all_local_members
288
- all = []
289
- [:members, :statics].each do |group|
290
- @doc[group].each_value do |ms|
291
- all += ms
292
- end
293
- end
294
- all
295
- end
296
-
297
- # A way to access full class name with similar syntax to
298
- # package_name and short_name
299
- def full_name
300
- @doc[:name]
301
- end
302
-
303
- # Returns package name of the class.
304
- #
305
- # That is the namespace part of full class name.
306
- #
307
- # For example "My.package" is package_name of "My.package.Class"
308
- def package_name
309
- Class.package_name(@doc[:name])
310
- end
311
-
312
- # Returns last part of full class name
313
- #
314
- # For example for "My.package.Class" it is "Class"
315
- def short_name
316
- Class.short_name(@doc[:name])
317
- end
318
-
319
- # Returns CSS icons class for the class
320
- def icon
321
- if @doc[:singleton]
322
- "icon-singleton"
323
- elsif inherits_from?("Ext.Component")
324
- "icon-component"
325
- else
326
- "icon-class"
327
- end
184
+ @doc[:members]
328
185
  end
329
186
 
330
187
  # Static methods
331
188
 
332
- # Utility method that given a package or class name finds the name
333
- # of its parent package.
334
- def self.package_name(name)
335
- name.slice(0, name.length - self.short_name(name).length - 1) || ""
336
- end
337
-
338
- # Utility method that given full package or class name extracts
339
- # the "class"-part of the name.
340
- #
341
- # Because we try to emulate ext-doc, it's not as simple as just
342
- # taking the last part. See class_spec.rb for details.
343
- def self.short_name(name)
344
- parts = name.split(/\./)
345
- short = parts.pop
346
- while parts.length > 1 && parts.last =~ /^[A-Z]/
347
- short = parts.pop + "." + short
348
- end
349
- short
189
+ # Generates member :id from member hash
190
+ def self.member_id(m)
191
+ # Sanitize $ in member names with something safer
192
+ name = m[:name].gsub(/\$/, 'S-')
193
+ "#{m[:meta][:static] ? 'static-' : ''}#{m[:tagname]}-#{name}"
350
194
  end
351
195
 
352
- # Returns default hash that has empty array for each member type
353
- def self.default_members_hash
354
- return {
355
- :cfg => [],
356
- :property => [],
357
- :method => [],
358
- :event => [],
359
- :css_var => [],
360
- :css_mixin => [],
361
- }
196
+ # Loops through all available member types,
197
+ # passing the tagname of the member to the block.
198
+ def self.each_member_type(&block)
199
+ [:cfg, :property, :method, :event, :css_var, :css_mixin].each(&block)
362
200
  end
363
201
  end
364
202