jsduck 3.11.2 → 4.0.beta

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.
data/.gitignore CHANGED
@@ -3,4 +3,5 @@ template/extjs
3
3
  template/resources/css
4
4
  template/resources/sass/.sass-cache
5
5
  template-min/
6
+ esprima/
6
7
  sdk-vars.rb
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
data/Gemfile.lock ADDED
@@ -0,0 +1,45 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jsduck (3.11.0)
5
+ execjs
6
+ json
7
+ parallel
8
+ rdiscount
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ chunky_png (1.2.5)
14
+ compass (0.12.2)
15
+ chunky_png (~> 1.2)
16
+ fssm (>= 0.2.7)
17
+ sass (~> 3.1)
18
+ diff-lcs (1.1.3)
19
+ execjs (1.4.0)
20
+ multi_json (~> 1.0)
21
+ fssm (0.2.9)
22
+ json (1.7.3)
23
+ multi_json (1.3.6)
24
+ parallel (0.5.17)
25
+ rake (0.9.2.2)
26
+ rdiscount (1.6.8)
27
+ rspec (2.10.0)
28
+ rspec-core (~> 2.10.0)
29
+ rspec-expectations (~> 2.10.0)
30
+ rspec-mocks (~> 2.10.0)
31
+ rspec-core (2.10.1)
32
+ rspec-expectations (2.10.0)
33
+ diff-lcs (~> 1.1.3)
34
+ rspec-mocks (2.10.1)
35
+ sass (3.1.19)
36
+
37
+ PLATFORMS
38
+ ruby
39
+ x86-mingw32
40
+
41
+ DEPENDENCIES
42
+ compass
43
+ jsduck!
44
+ rake
45
+ rspec
data/README.md CHANGED
@@ -41,10 +41,7 @@ For **Windows** users out there, you can download the binary version,
41
41
  which includes Ruby interpreter and all dependencies bundled in a
42
42
  single .exe file. Grab it from the [download page][].
43
43
 
44
- If you are brave enough: [try out JSDuck 4.0 beta.][beta]
45
-
46
44
  [download page]: https://github.com/senchalabs/jsduck/downloads
47
- [beta]: https://github.com/senchalabs/jsduck/wiki/4.0-beta
48
45
 
49
46
  Usage
50
47
  -----
data/Rakefile CHANGED
@@ -220,6 +220,7 @@ task :ext4 => :sass do
220
220
  runner = JsDuckRunner.new
221
221
  runner.add_ext4
222
222
  runner.add_debug
223
+ runner.add_options("--tests")
223
224
  runner.run
224
225
 
225
226
  system("cp -r #{EXT_BUILD} #{OUT_DIR}/extjs-build")
@@ -232,7 +233,8 @@ task :sdk => :sass do
232
233
  "--output", OUT_DIR,
233
234
  "--config", "#{SDK_DIR}/extjs/docs/config.json",
234
235
  "--examples-base-url", "extjs-build/examples/",
235
- "--seo"
236
+ "--seo",
237
+ "--tests"
236
238
  )
237
239
  runner.add_debug
238
240
  runner.add_comments('ext-js', '4')
@@ -248,7 +250,8 @@ task :touch2 => :sass do
248
250
  "--output", OUT_DIR,
249
251
  "--config", "#{SDK_DIR}/touch/docs/config.json",
250
252
  "--examples-base-url", "touch-build/examples/production/",
251
- "--seo"
253
+ "--seo",
254
+ "--tests"
252
255
  )
253
256
 
254
257
  runner.add_debug
data/js-classes/String.js CHANGED
@@ -949,7 +949,7 @@
949
949
  *
950
950
  * The following example displays the string "sencha":
951
951
  *
952
- * var upperText="SENCHA";
952
+ * var upperText="sencha";
953
953
  * document.write(upperText.toLocaleLowerCase());
954
954
  *
955
955
  * @return {String} Returns value of the string in lowercase.
data/jsduck.gemspec CHANGED
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.required_rubygems_version = ">= 1.3.5"
3
3
 
4
4
  s.name = 'jsduck'
5
- s.version = '3.11.2'
6
- s.date = '2012-08-07'
5
+ s.version = '4.0.beta'
6
+ s.date = '2012-06-27'
7
7
  s.summary = "Simple JavaScript Duckumentation generator"
8
8
  s.description = "Documentation generator for Sencha JS frameworks"
9
9
  s.homepage = "https://github.com/senchalabs/jsduck"
@@ -16,12 +16,15 @@ Gem::Specification.new do |s|
16
16
  end
17
17
  # Add files not in git
18
18
  s.files += Dir['template-min/**/*']
19
+ # Add Esprima
20
+ s.files += Dir['esprima/esprima.js']
19
21
 
20
22
  s.executables = ["jsduck"]
21
23
 
22
24
  s.add_dependency 'rdiscount'
23
25
  s.add_dependency 'json'
24
26
  s.add_dependency 'parallel'
27
+ s.add_dependency 'execjs'
25
28
 
26
29
  s.add_development_dependency 'rspec'
27
30
  s.add_development_dependency 'rake'
@@ -110,6 +110,7 @@ module JsDuck
110
110
  :owner => cfg[:owner],
111
111
  :files => cfg[:files],
112
112
  :private => cfg[:private],
113
+ :autodetected => cfg[:autodetected],
113
114
  :meta => clone_meta(cfg),
114
115
  })
115
116
  end
@@ -205,7 +205,10 @@ module JsDuck
205
205
  end
206
206
 
207
207
  # Appends Ext4 options parameter to each event parameter list.
208
+ # But only when we are dealing with Ext4 codebase.
208
209
  def append_ext4_event_options
210
+ return unless ext4?
211
+
209
212
  options = {
210
213
  :tagname => :param,
211
214
  :name => "eOpts",
data/lib/jsduck/app.rb CHANGED
@@ -124,9 +124,7 @@ module JsDuck
124
124
  agr.create_global_class
125
125
  agr.remove_ignored_classes
126
126
  agr.create_accessors
127
- if @opts.ext4_events == true || (@opts.ext4_events == nil && agr.ext4?)
128
- agr.append_ext4_event_options
129
- end
127
+ agr.append_ext4_event_options
130
128
  agr.result
131
129
  end
132
130
 
@@ -165,15 +163,14 @@ module JsDuck
165
163
  class_formatter.include_types = !@opts.export
166
164
  # Format all doc-objects in parallel
167
165
  formatted_classes = @parallel.map(@relations.classes) do |cls|
168
- files = cls[:files].map {|f| f[:filename] }.join(" ")
169
- Logger.instance.log("Markdown formatting #{cls[:name]}", files)
166
+ Logger.instance.log("Markdown formatting #{cls[:name]}")
170
167
  begin
171
168
  {
172
169
  :doc => class_formatter.format(cls.internal_doc),
173
170
  :images => doc_formatter.images
174
171
  }
175
172
  rescue
176
- Logger.instance.fatal("Error while formatting #{cls[:name]} #{files}", $!)
173
+ Logger.instance.fatal("Error while formatting #{cls[:name]}", $!)
177
174
  exit(1)
178
175
  end
179
176
  end
data/lib/jsduck/ast.rb ADDED
@@ -0,0 +1,446 @@
1
+ require "jsduck/serializer"
2
+ require "jsduck/evaluator"
3
+
4
+ module JsDuck
5
+
6
+ # Analyzes the AST produced by EsprimaParser.
7
+ class Ast
8
+ # Should be initialized with EsprimaParser#parse result.
9
+ def initialize(docs = [], options = {})
10
+ @serializer = JsDuck::Serializer.new
11
+ @evaluator = JsDuck::Evaluator.new
12
+ @ext_define_patterns = build_ext_define_patterns(options[:ext_namespaces] || ["Ext"])
13
+ @docs = docs
14
+ end
15
+
16
+ # Given Array of alternate Ext namespaces builds list of patterns
17
+ # for detecting Ext.define:
18
+ #
19
+ # ["Ext","Foo"] --> ["Ext.define", "Ext.ClassManager.create", "Foo.define", "Foo.ClassManager.create"]
20
+ #
21
+ def build_ext_define_patterns(namespaces)
22
+ namespaces.map do |ns|
23
+ [ns + ".define", ns + ".ClassManager.create"]
24
+ end.flatten
25
+ end
26
+
27
+ # Performs the detection of code in all docsets.
28
+ #
29
+ # @returns the processed array of docsets. (But it does it
30
+ # destructively by modifying the passed-in docsets.)
31
+ #
32
+ def detect_all!
33
+ # First deal only with doc-comments
34
+ doc_comments = @docs.find_all {|d| d[:type] == :doc_comment }
35
+
36
+ # Detect code in each docset. Sometimes a docset has already
37
+ # been detected as part of detecting some previous docset (like
38
+ # Class detecting all of its configs) - in such case, skip.
39
+ doc_comments.each do |docset|
40
+ code = docset[:code]
41
+ docset[:code] = detect(code) unless code && code[:tagname]
42
+ end
43
+
44
+ # Return all doc-comments + other comments for which related
45
+ # code was detected.
46
+ @docs.find_all {|d| d[:type] == :doc_comment || d[:code] && d[:code][:tagname] }
47
+ end
48
+
49
+ # Given Esprima-produced syntax tree, detects documentation data.
50
+ #
51
+ # This method is exposed for testing purposes only, JSDuck itself
52
+ # only calls the above #detect_all method.
53
+ #
54
+ # @param ast :code from Result of EsprimaParser
55
+ # @returns Hash consisting of the detected :tagname, :name, and
56
+ # other properties relative to the tag. Like so:
57
+ #
58
+ # { :tagname => :method, :name => "foo", ... }
59
+ #
60
+ def detect(ast)
61
+ ast = ast || {}
62
+
63
+ exp = expression?(ast) ? ast["expression"] : nil
64
+ var = var?(ast) ? ast["declarations"][0] : nil
65
+
66
+ # Ext.define("Class", {})
67
+ if exp && ext_define?(exp)
68
+ make_class(to_value(exp["arguments"][0]), exp)
69
+
70
+ # foo = Ext.extend("Parent", {})
71
+ elsif exp && assignment?(exp) && ext_extend?(exp["right"])
72
+ make_class(to_s(exp["left"]), exp["right"])
73
+
74
+ # Foo = ...
75
+ elsif exp && assignment?(exp) && class_name?(to_s(exp["left"]))
76
+ make_class(to_s(exp["left"]))
77
+
78
+ # var foo = Ext.extend("Parent", {})
79
+ elsif var && var["init"] && ext_extend?(var["init"])
80
+ make_class(to_s(var["id"]), var["init"])
81
+
82
+ # var Foo = ...
83
+ elsif var && class_name?(to_s(var["id"]))
84
+ make_class(to_s(var["id"]))
85
+
86
+ # function Foo() {}
87
+ elsif function?(ast) && class_name?(to_s(ast["id"]))
88
+ make_class(to_s(ast["id"]))
89
+
90
+ # function foo() {}
91
+ elsif function?(ast)
92
+ make_method(to_s(ast["id"]), ast)
93
+
94
+ # foo = function() {}
95
+ elsif exp && assignment?(exp) && function?(exp["right"])
96
+ make_method(to_s(exp["left"]), exp["right"])
97
+
98
+ # var foo = function() {}
99
+ elsif var && var["init"] && function?(var["init"])
100
+ make_method(to_s(var["id"]), var["init"])
101
+
102
+ # (function() {})
103
+ elsif exp && function?(exp)
104
+ make_method(exp["id"] ? to_s(exp["id"]) : "", exp)
105
+
106
+ # foo: function() {}
107
+ elsif property?(ast) && function?(ast["value"])
108
+ make_method(key_value(ast["key"]), ast["value"])
109
+
110
+ # foo = ...
111
+ elsif exp && assignment?(exp)
112
+ make_property(to_s(exp["left"]), exp["right"])
113
+
114
+ # var foo = ...
115
+ elsif var
116
+ make_property(to_s(var["id"]), var["init"])
117
+
118
+ # foo: ...
119
+ elsif property?(ast)
120
+ make_property(key_value(ast["key"]), ast["value"])
121
+
122
+ # foo;
123
+ elsif exp && ident?(exp)
124
+ make_property(to_s(exp))
125
+
126
+ # "foo" (inside some expression)
127
+ elsif string?(ast)
128
+ make_property(to_value(ast))
129
+
130
+ # "foo"; (as a statement of it's own)
131
+ elsif exp && string?(exp)
132
+ make_property(to_value(exp))
133
+
134
+ else
135
+ make_property()
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def expression?(ast)
142
+ ast["type"] == "ExpressionStatement"
143
+ end
144
+
145
+ def call?(ast)
146
+ ast["type"] == "CallExpression"
147
+ end
148
+
149
+ def assignment?(ast)
150
+ ast["type"] == "AssignmentExpression"
151
+ end
152
+
153
+ def ext_define?(ast)
154
+ call?(ast) && @ext_define_patterns.include?(to_s(ast["callee"]))
155
+ end
156
+
157
+ def ext_extend?(ast)
158
+ call?(ast) && to_s(ast["callee"]) == "Ext.extend"
159
+ end
160
+
161
+ def function?(ast)
162
+ ast["type"] == "FunctionDeclaration" || ast["type"] == "FunctionExpression" || empty_fn?(ast)
163
+ end
164
+
165
+ def empty_fn?(ast)
166
+ ast["type"] == "MemberExpression" && to_s(ast) == "Ext.emptyFn"
167
+ end
168
+
169
+ def var?(ast)
170
+ ast["type"] == "VariableDeclaration"
171
+ end
172
+
173
+ def property?(ast)
174
+ ast["type"] == "Property"
175
+ end
176
+
177
+ def ident?(ast)
178
+ ast["type"] == "Identifier"
179
+ end
180
+
181
+ def string?(ast)
182
+ ast["type"] == "Literal" && ast["value"].is_a?(String)
183
+ end
184
+
185
+ # Class name begins with upcase char
186
+ def class_name?(name)
187
+ return name.split(/\./).last =~ /\A[A-Z]/
188
+ end
189
+
190
+ def make_class(name, ast=nil)
191
+ cls = {
192
+ :tagname => :class,
193
+ :name => name,
194
+ }
195
+
196
+ # apply information from Ext.extend or Ext.define
197
+ if ast
198
+ if ext_extend?(ast)
199
+ cls[:extends] = to_s(ast["arguments"][0])
200
+ elsif ext_define?(ast)
201
+ detect_ext_define(cls, ast)
202
+ end
203
+ end
204
+
205
+ return cls
206
+ end
207
+
208
+ # Inspects Ext.define() and copies detected properties over to the
209
+ # given cls Hash
210
+ def detect_ext_define(cls, ast)
211
+ # defaults
212
+ cls[:extends] = "Ext.Base"
213
+ cls[:requires] = []
214
+ cls[:uses] = []
215
+ cls[:alternateClassNames] = []
216
+ cls[:mixins] = []
217
+ cls[:aliases] = []
218
+ cls[:members] = []
219
+ cls[:statics] = []
220
+
221
+ each_pair_in_object_expression(ast["arguments"][1]) do |key, value, pair|
222
+ case key
223
+ when "extend"
224
+ cls[:extends] = make_extends(value)
225
+ when "requires"
226
+ cls[:requires] = make_string_list(value)
227
+ when "uses"
228
+ cls[:uses] = make_string_list(value)
229
+ when "alternateClassName"
230
+ cls[:alternateClassNames] = make_string_list(value)
231
+ when "mixins"
232
+ cls[:mixins] = make_mixins(value)
233
+ when "singleton"
234
+ cls[:singleton] = make_singleton(value)
235
+ when "alias"
236
+ cls[:aliases] += make_string_list(value)
237
+ when "xtype"
238
+ cls[:aliases] += make_string_list(value).map {|xtype| "widget."+xtype }
239
+ when "config"
240
+ cls[:members] += make_configs(value, {:accessor => true})
241
+ when "cachedConfig"
242
+ cls[:members] += make_configs(value, {:accessor => true})
243
+ when "eventedConfig"
244
+ cls[:members] += make_configs(value, {:accessor => true, :evented => true})
245
+ when "statics"
246
+ cls[:statics] += make_statics(value)
247
+ when "inheritableStatics"
248
+ cls[:statics] += make_statics(value, {:inheritable => true})
249
+ else
250
+ if function?(value)
251
+ m = make_method(key, value)
252
+ cls[:members] << m if apply_autodetected(m, pair)
253
+ else
254
+ p = make_property(key, value)
255
+ cls[:members] << p if apply_autodetected(p, pair)
256
+ end
257
+ end
258
+ end
259
+ end
260
+
261
+ def make_extends(cfg_value)
262
+ return nil unless cfg_value
263
+
264
+ parent = to_value(cfg_value)
265
+
266
+ return parent.is_a?(String) ? parent : nil
267
+ end
268
+
269
+ def make_string_list(cfg_value)
270
+ return [] unless cfg_value
271
+
272
+ classes = Array(to_value(cfg_value))
273
+
274
+ return classes.all? {|c| c.is_a? String } ? classes : []
275
+ end
276
+
277
+ def make_mixins(cfg_value)
278
+ return [] unless cfg_value
279
+
280
+ v = to_value(cfg_value)
281
+ classes = v.is_a?(Hash) ? v.values : Array(v)
282
+
283
+ return classes.all? {|c| c.is_a? String } ? classes : []
284
+ end
285
+
286
+ def make_singleton(cfg_value)
287
+ cfg_value && to_value(cfg_value) == true
288
+ end
289
+
290
+ def make_configs(ast, defaults={})
291
+ configs = []
292
+
293
+ each_pair_in_object_expression(ast) do |name, value, pair|
294
+ cfg = make_property(name, value, :cfg)
295
+ cfg.merge!(defaults)
296
+ configs << cfg if apply_autodetected(cfg, pair)
297
+ end
298
+
299
+ configs
300
+ end
301
+
302
+ def make_statics(ast, defaults={})
303
+ statics = []
304
+
305
+ each_pair_in_object_expression(ast) do |name, value, pair|
306
+ if function?(value)
307
+ s = make_method(name, value)
308
+ else
309
+ s = make_property(name, value)
310
+ end
311
+
312
+ s[:meta] = {:static => true}
313
+ s.merge!(defaults)
314
+
315
+ statics << s if apply_autodetected(s, pair, defaults[:inheritable])
316
+ end
317
+
318
+ statics
319
+ end
320
+
321
+ # Sets auto-detection related properties :autodetected and
322
+ # :inheritdoc on the given member Hash.
323
+ #
324
+ # When member has a comment, adds code to the related docset and
325
+ # returns false.
326
+ #
327
+ # Otherwise detects the line number of member and returns true.
328
+ def apply_autodetected(m, pair, inheritable=true)
329
+ docset = find_docset(pair)
330
+
331
+ if !docset || docset[:type] != :doc_comment
332
+ if inheritable
333
+ m[:inheritdoc] = {}
334
+ else
335
+ m[:private] = true
336
+ end
337
+ m[:autodetected] = true
338
+ end
339
+
340
+ if docset
341
+ docset[:code] = m
342
+ return false
343
+ else
344
+ # Get line number from third place at range array.
345
+ # This third item exists in forked EsprimaJS at
346
+ # https://github.com/nene/esprima/tree/linenr-in-range
347
+ m[:linenr] = pair["range"][2]
348
+ return true
349
+ end
350
+ end
351
+
352
+ # Looks up docset associated with given AST node.
353
+ # A dead-stupid and -slow implementation, but works.
354
+ def find_docset(ast)
355
+ @docs.find do |docset|
356
+ docset[:code] == ast
357
+ end
358
+ end
359
+
360
+ def make_method(name, ast=nil)
361
+ return {
362
+ :tagname => :method,
363
+ :name => name,
364
+ :params => make_params(ast)
365
+ }
366
+ end
367
+
368
+ def make_params(ast)
369
+ if ast && !empty_fn?(ast)
370
+ ast["params"].map {|p| {:name => to_s(p)} }
371
+ else
372
+ []
373
+ end
374
+ end
375
+
376
+ def make_property(name=nil, ast=nil, tagname=:property)
377
+ return {
378
+ :tagname => tagname,
379
+ :name => name,
380
+ :type => make_value_type(ast),
381
+ :default => make_default(ast),
382
+ }
383
+ end
384
+
385
+ def make_default(ast)
386
+ ast && to_value(ast) != nil ? to_s(ast) : nil
387
+ end
388
+
389
+ def make_value_type(ast)
390
+ if ast
391
+ v = to_value(ast)
392
+ if v.is_a?(String)
393
+ "String"
394
+ elsif v.is_a?(Numeric)
395
+ "Number"
396
+ elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
397
+ "Boolean"
398
+ elsif v.is_a?(Array)
399
+ "Array"
400
+ elsif v.is_a?(Hash)
401
+ "Object"
402
+ elsif v == :regexp
403
+ "RegExp"
404
+ else
405
+ nil
406
+ end
407
+ else
408
+ nil
409
+ end
410
+ end
411
+
412
+ # -- various helper methods --
413
+
414
+ # Iterates over keys and values in ObjectExpression. The keys
415
+ # are turned into strings, but values are left as is for further
416
+ # processing.
417
+ def each_pair_in_object_expression(ast)
418
+ return unless ast && ast["type"] == "ObjectExpression"
419
+
420
+ ast["properties"].each do |p|
421
+ yield(key_value(p["key"]), p["value"], p)
422
+ end
423
+ end
424
+
425
+ # Converts object expression property key to string value
426
+ def key_value(key)
427
+ @evaluator.key_value(key)
428
+ end
429
+
430
+ # Fully serializes the node
431
+ def to_s(ast)
432
+ @serializer.to_s(ast)
433
+ end
434
+
435
+ # Converts AST node into a value.
436
+ def to_value(ast)
437
+ begin
438
+ @evaluator.to_value(ast)
439
+ rescue
440
+ nil
441
+ end
442
+ end
443
+ end
444
+
445
+ end
446
+