jsduck 4.4.1 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,6 @@ install:
6
6
  - gem install rdiscount
7
7
  - gem install json
8
8
  - gem install parallel
9
- - gem install therubyracer
9
+ - gem install therubyracer -v 0.10.1
10
10
  - gem install rspec
11
11
  - gem install rake
data/README.md CHANGED
@@ -141,6 +141,7 @@ Who's using JSDuck?
141
141
  - Appcelerator [Titanium SDK](http://docs.appcelerator.com/titanium/2.0/index.html)
142
142
  - AT&T [API Platform SDK for HTML5](https://code-api-att.com/SenchaSdk20Drop23Docs/)
143
143
  - Bryntum [Siesta unit testing framework](http://www.bryntum.com/products/siesta/docs/)
144
+ - [CKEditor](http://docs.ckeditor.com)
144
145
  - [GeoExt 2](https://github.com/geoext/geoext2)
145
146
  - Rally Software [Rally App SDK](https://rally1.rallydev.com/apps/2.0p/doc/)
146
147
  - [Sencha](http://docs.sencha.com) - obviously :)
data/Rakefile CHANGED
@@ -104,7 +104,7 @@ def compress
104
104
  dir = "template-min"
105
105
 
106
106
  # Create JSB3 file for Docs app
107
- system("sencha", "create", "jsb", "-a", "http://localhost/docs/", "-p", "#{dir}/app.jsb3")
107
+ system("sencha", "create", "jsb", "-a", "http://localhost/~renesaarsoo/docs/", "-p", "#{dir}/app.jsb3")
108
108
  # Concatenate files listed in JSB3 file
109
109
  system("sencha", "build", "-p", "#{dir}/app.jsb3", "-d", dir)
110
110
 
@@ -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 = '4.4.1'
6
- s.date = '2012-11-20'
5
+ s.version = '4.5.0'
6
+ s.date = '2012-12-06'
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"
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_dependency 'rdiscount'
23
23
  s.add_dependency 'json'
24
24
  s.add_dependency 'parallel'
25
- s.add_dependency 'therubyracer'
25
+ s.add_dependency 'therubyracer', '= 0.10.1'
26
26
 
27
27
  s.add_development_dependency 'rspec'
28
28
  s.add_development_dependency 'rake'
@@ -1,6 +1,7 @@
1
1
  require "jsduck/serializer"
2
2
  require "jsduck/evaluator"
3
3
  require "jsduck/function_ast"
4
+ require "jsduck/ext_patterns"
4
5
 
5
6
  module JsDuck
6
7
 
@@ -10,21 +11,10 @@ module JsDuck
10
11
  def initialize(docs = [], options = {})
11
12
  @serializer = JsDuck::Serializer.new
12
13
  @evaluator = JsDuck::Evaluator.new
13
- @ext_define_patterns = build_ext_define_patterns(options[:ext_namespaces] || ["Ext"])
14
+ @ext_patterns = JsDuck::ExtPatterns.new(options[:ext_namespaces] || ["Ext"])
14
15
  @docs = docs
15
16
  end
16
17
 
17
- # Given Array of alternate Ext namespaces builds list of patterns
18
- # for detecting Ext.define:
19
- #
20
- # ["Ext","Foo"] --> ["Ext.define", "Ext.ClassManager.create", "Foo.define", "Foo.ClassManager.create"]
21
- #
22
- def build_ext_define_patterns(namespaces)
23
- namespaces.map do |ns|
24
- [ns + ".define", ns + ".ClassManager.create"]
25
- end.flatten
26
- end
27
-
28
18
  # Performs the detection of code in all docsets.
29
19
  #
30
20
  # @returns the processed array of docsets. (But it does it
@@ -68,7 +58,11 @@ module JsDuck
68
58
  if exp && ext_define?(exp)
69
59
  make_class(to_value(exp["arguments"][0]), exp)
70
60
 
71
- # foo = Ext.extend("Parent", {})
61
+ # Ext.override(Class, {})
62
+ elsif exp && ext_override?(exp)
63
+ make_class("", exp)
64
+
65
+ # foo = Ext.extend(Parent, {})
72
66
  elsif exp && assignment?(exp) && ext_extend?(exp["right"])
73
67
  make_class(to_s(exp["left"]), exp["right"])
74
68
 
@@ -76,7 +70,7 @@ module JsDuck
76
70
  elsif exp && assignment?(exp) && class_name?(to_s(exp["left"]))
77
71
  make_class(to_s(exp["left"]), exp["right"])
78
72
 
79
- # var foo = Ext.extend("Parent", {})
73
+ # var foo = Ext.extend(Parent, {})
80
74
  elsif var && var["init"] && ext_extend?(var["init"])
81
75
  make_class(to_s(var["id"]), var["init"])
82
76
 
@@ -88,6 +82,10 @@ module JsDuck
88
82
  elsif function?(ast) && class_name?(to_s(ast["id"]))
89
83
  make_class(to_s(ast["id"]))
90
84
 
85
+ # { ... }
86
+ elsif object?(ast)
87
+ make_class("", ast)
88
+
91
89
  # function foo() {}
92
90
  elsif function?(ast)
93
91
  make_method(to_s(ast["id"]), ast)
@@ -108,6 +106,10 @@ module JsDuck
108
106
  elsif property?(ast) && function?(ast["value"])
109
107
  make_method(key_value(ast["key"]), ast["value"])
110
108
 
109
+ # this.fireEvent("foo", ...)
110
+ elsif exp && fire_event?(exp)
111
+ make_event(to_value(exp["arguments"][0]))
112
+
111
113
  # foo = ...
112
114
  elsif exp && assignment?(exp)
113
115
  make_property(to_s(exp["left"]), exp["right"])
@@ -152,11 +154,15 @@ module JsDuck
152
154
  end
153
155
 
154
156
  def ext_define?(ast)
155
- call?(ast) && @ext_define_patterns.include?(to_s(ast["callee"]))
157
+ call?(ast) && ext_pattern?("Ext.define", ast["callee"])
156
158
  end
157
159
 
158
160
  def ext_extend?(ast)
159
- call?(ast) && to_s(ast["callee"]) == "Ext.extend"
161
+ call?(ast) && ext_pattern?("Ext.extend", ast["callee"])
162
+ end
163
+
164
+ def ext_override?(ast)
165
+ call?(ast) && ext_pattern?("Ext.override", ast["callee"])
160
166
  end
161
167
 
162
168
  def function?(ast)
@@ -164,7 +170,15 @@ module JsDuck
164
170
  end
165
171
 
166
172
  def empty_fn?(ast)
167
- ast["type"] == "MemberExpression" && to_s(ast) == "Ext.emptyFn"
173
+ ast["type"] == "MemberExpression" && ext_pattern?("Ext.emptyFn", ast)
174
+ end
175
+
176
+ def ext_pattern?(pattern, ast)
177
+ @ext_patterns.matches?(pattern, to_s(ast))
178
+ end
179
+
180
+ def fire_event?(ast)
181
+ call?(ast) && to_s(ast["callee"]) == "this.fireEvent"
168
182
  end
169
183
 
170
184
  def var?(ast)
@@ -183,6 +197,10 @@ module JsDuck
183
197
  ast["type"] == "Literal" && ast["value"].is_a?(String)
184
198
  end
185
199
 
200
+ def object?(ast)
201
+ ast["type"] == "ObjectExpression"
202
+ end
203
+
186
204
  # Class name begins with upcase char
187
205
  def class_name?(name)
188
206
  return name.split(/\./).last =~ /\A[A-Z]/
@@ -196,15 +214,13 @@ module JsDuck
196
214
 
197
215
  # apply information from Ext.extend, Ext.define, or {}
198
216
  if ast
199
- if ext_extend?(ast)
200
- args = ast["arguments"]
201
- cls[:extends] = to_s(args[0])
202
- if args.length == 2 && args[1]["type"] == "ObjectExpression"
203
- detect_class_members_from_object(cls, args[1])
204
- end
205
- elsif ext_define?(ast)
217
+ if ext_define?(ast)
206
218
  detect_ext_define(cls, ast)
207
- elsif ast["type"] == "ObjectExpression"
219
+ elsif ext_extend?(ast)
220
+ detect_ext_something(:extends, cls, ast)
221
+ elsif ext_override?(ast)
222
+ detect_ext_something(:override, cls, ast)
223
+ elsif object?(ast)
208
224
  detect_class_members_from_object(cls, ast)
209
225
  elsif ast["type"] == "ArrayExpression"
210
226
  detect_class_members_from_array(cls, ast)
@@ -214,6 +230,16 @@ module JsDuck
214
230
  return cls
215
231
  end
216
232
 
233
+ # Detection of Ext.extend() or Ext.override().
234
+ # The type parameter must be correspondingly either :extend or :override.
235
+ def detect_ext_something(type, cls, ast)
236
+ args = ast["arguments"]
237
+ cls[type] = to_s(args[0])
238
+ if args.length == 2 && object?(args[1])
239
+ detect_class_members_from_object(cls, args[1])
240
+ end
241
+ end
242
+
217
243
  # Inspects Ext.define() and copies detected properties over to the
218
244
  # given cls Hash
219
245
  def detect_ext_define(cls, ast)
@@ -419,6 +445,13 @@ module JsDuck
419
445
  end
420
446
  end
421
447
 
448
+ def make_event(name)
449
+ return {
450
+ :tagname => :event,
451
+ :name => name,
452
+ }
453
+ end
454
+
422
455
  def make_property(name=nil, ast=nil, tagname=:property)
423
456
  return {
424
457
  :tagname => tagname,
@@ -461,7 +494,7 @@ module JsDuck
461
494
  # are turned into strings, but values are left as is for further
462
495
  # processing.
463
496
  def each_pair_in_object_expression(ast)
464
- return unless ast && ast["type"] == "ObjectExpression"
497
+ return unless ast && object?(ast)
465
498
 
466
499
  ast["properties"].each do |p|
467
500
  yield(key_value(p["key"]), p["value"], p)
@@ -1,5 +1,6 @@
1
1
  require 'strscan'
2
2
  require 'jsduck/meta_tag_registry'
3
+ require 'jsduck/logger'
3
4
 
4
5
  module JsDuck
5
6
 
@@ -28,7 +29,9 @@ module JsDuck
28
29
  @meta_tags = MetaTagRegistry.instance
29
30
  end
30
31
 
31
- def parse(input)
32
+ def parse(input, filename="", linenr=0)
33
+ @filename = filename
34
+ @linenr = linenr
32
35
  @tags = []
33
36
  @input = StringScanner.new(purify(input))
34
37
  parse_loop
@@ -77,6 +80,11 @@ module JsDuck
77
80
  @tags << @current_tag = {:tagname => tag, :doc => ""}
78
81
  end
79
82
 
83
+ def remove_last_tag
84
+ @tags.pop
85
+ @current_tag = @tags.last
86
+ end
87
+
80
88
  def parse_loop
81
89
  add_tag(:default)
82
90
  while !@input.eos? do
@@ -141,19 +149,56 @@ module JsDuck
141
149
  elsif look(/@evented\b/)
142
150
  boolean_at_tag(/@evented/, :evented)
143
151
  elsif look(/@/)
144
- match(/@/)
145
- tag = @meta_tags[look(/\w+/)]
146
- if tag
147
- meta_at_tag(tag)
148
- else
149
- @current_tag[:doc] += "@"
150
- end
152
+ other_at_tag
151
153
  elsif look(/[^@]/)
152
- @current_tag[:doc] += match(/[^@]+/)
154
+ skip_to_next_at_tag
153
155
  end
154
156
  end
155
157
  end
156
158
 
159
+ # Skips until the beginning of next @tag.
160
+ #
161
+ # There must be space before the next @tag - this ensures that we
162
+ # don't detect tags inside "foo@example.com" or "{@link}".
163
+ #
164
+ # Also check that the @tag is not part of an indented code block -
165
+ # in which case we also ignore the tag.
166
+ def skip_to_next_at_tag
167
+ @current_tag[:doc] += match(/[^@]+/)
168
+
169
+ while look(/@/) && (!prev_char_is_whitespace? || indented_as_code?)
170
+ @current_tag[:doc] += match(/@+[^@]+/)
171
+ end
172
+ end
173
+
174
+ def prev_char_is_whitespace?
175
+ @current_tag[:doc][-1,1] =~ /\s/
176
+ end
177
+
178
+ def indented_as_code?
179
+ @current_tag[:doc] =~ /^ {4,}[^\n]*\Z/
180
+ end
181
+
182
+ # Processes anything else beginning with @-sign.
183
+ #
184
+ # - When @ is not followed by any word chards, do nothing.
185
+ # - When it's one of the meta-tags, process it as such.
186
+ # - When it's something else, print a warning.
187
+ #
188
+ def other_at_tag
189
+ match(/@/)
190
+
191
+ name = look(/\w+/)
192
+ tag = @meta_tags[name]
193
+
194
+ if tag
195
+ meta_at_tag(tag)
196
+ elsif name
197
+ Logger.warn(:tag, "Unsupported tag: @#{name}", @filename, @linenr)
198
+ @current_tag[:doc] += "@"
199
+ end
200
+ end
201
+
157
202
  # Matches the given meta-tag
158
203
  def meta_at_tag(tag)
159
204
  prev_tag = @current_tag
@@ -302,6 +347,14 @@ module JsDuck
302
347
  add_tag(:override)
303
348
  maybe_ident_chain(:class)
304
349
  skip_white
350
+
351
+ # When @override not followed by class name, ignore the tag.
352
+ # That's because the current ext codebase has some methods
353
+ # tagged with @override to denote they override something.
354
+ # But that's not what @override is meant for in JSDuck.
355
+ unless @current_tag[:class]
356
+ remove_last_tag
357
+ end
305
358
  end
306
359
 
307
360
  # matches @type {type} or @type type
@@ -12,7 +12,7 @@ module JsDuck
12
12
  def detect(docs, code)
13
13
  doc_map = build_doc_map(docs)
14
14
 
15
- if doc_map[:class]
15
+ if doc_map[:class] || doc_map[:override]
16
16
  :class
17
17
  elsif doc_map[:event]
18
18
  :event
@@ -57,4 +57,3 @@ module JsDuck
57
57
  end
58
58
 
59
59
  end
60
-
@@ -0,0 +1,58 @@
1
+ module JsDuck
2
+
3
+ # Identifies Ext JS builtins like Ext.define and Ext.extend, taking
4
+ # also into account the possibility of aliasing the Ext namespace.
5
+ #
6
+ # For example when the following command line option is used:
7
+ #
8
+ # --ext-namespaces=Ext,MyApp
9
+ #
10
+ # we need to identify both Ext.define and MyApp.define, but
11
+ # Ext.define is additionally aliased withing ExtJS as
12
+ # Ext.ClassManager.create, so we also need to recognize
13
+ # Ext.ClassManager.create and MyApp.ClassManager.create.
14
+ #
15
+ # The matches? method will take care of identifying all these four
16
+ # cases:
17
+ #
18
+ # ps = ExtPatterns.new(["Ext", "MyApp"])
19
+ # matches?("Ext.define", "MyApp.define") --> true
20
+ #
21
+ class ExtPatterns
22
+ def initialize(namespaces)
23
+ @patterns = {
24
+ "Ext.define" => build_patterns(namespaces, [".define", ".ClassManager.create"]),
25
+ "Ext.extend" => build_patterns(namespaces, [".extend"]),
26
+ "Ext.override" => build_patterns(namespaces, [".override"]),
27
+ "Ext.emptyFn" => build_patterns(namespaces, [".emptyFn"]),
28
+ }
29
+ end
30
+
31
+ # True when string matches the given pattern type.
32
+ #
33
+ # Pattern type is one of: "Ext.define", "Ext.extend",
34
+ # "Ext.override", "Ext.emptyFn"
35
+ def matches?(pattern, string)
36
+ @patterns[pattern].include?(string)
37
+ end
38
+
39
+ private
40
+
41
+ # Given Array of alternate Ext namespaces builds list of patterns
42
+ # for detecting Ext.define or some other construct:
43
+ #
44
+ # build_patterns(["Ext", "Foo"], [".define"]) --> ["Ext.define", "Foo.define"]
45
+ #
46
+ def build_patterns(namespaces, suffixes)
47
+ patterns = []
48
+ namespaces.each do |ns|
49
+ suffixes.each do |suffix|
50
+ patterns << ns + suffix
51
+ end
52
+ end
53
+ patterns
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,30 @@
1
+ module JsDuck
2
+
3
+ # Handles patterns of external classes.
4
+ #
5
+ # A pattern can be a simple classname or a one with a wildcard "*".
6
+ class ExternalClasses
7
+
8
+ def initialize(classnames = [])
9
+ @class_names = {}
10
+ @patterns = []
11
+ classnames.each do |name|
12
+ if name =~ /\*/
13
+ @patterns << make_pattern(name)
14
+ else
15
+ @class_names[name] = true
16
+ end
17
+ end
18
+ end
19
+
20
+ # True if the classname matches an external class pattern.
21
+ def is?(classname)
22
+ @class_names[classname] || @patterns.any? {|p| classname =~ p }
23
+ end
24
+
25
+ def make_pattern(pattern)
26
+ Regexp.new("^" + pattern.split(/\*/, -1).map {|s| Regexp.escape(s) }.join(".*") + "$")
27
+ end
28
+ end
29
+
30
+ end
@@ -138,17 +138,17 @@ module JsDuck
138
138
 
139
139
  # True if range A is less than range B
140
140
  def less(a, b)
141
- return a[1] < b[0]
141
+ return a[1] <= b[0]
142
142
  end
143
143
 
144
144
  # True if range A is greater than range B
145
145
  def greater(a, b)
146
- return a[0] > b[1]
146
+ return a[0] >= b[1]
147
147
  end
148
148
 
149
149
  # True if range A is within range B
150
150
  def within(a, b)
151
- return b[0] < a[0] && a[1] < b[1]
151
+ return b[0] <= a[0] && a[1] <= b[1]
152
152
  end
153
153
 
154
154