jsduck 4.0.1 → 4.1.1

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