jsduck 4.4.1 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/README.md +1 -0
- data/Rakefile +1 -1
- data/jsduck.gemspec +3 -3
- data/lib/jsduck/ast.rb +59 -26
- data/lib/jsduck/doc_parser.rb +62 -9
- data/lib/jsduck/doc_type.rb +1 -2
- data/lib/jsduck/ext_patterns.rb +58 -0
- data/lib/jsduck/external_classes.rb +30 -0
- data/lib/jsduck/js_parser.rb +3 -3
- data/lib/jsduck/logger.rb +1 -0
- data/lib/jsduck/options.rb +9 -1
- data/lib/jsduck/override.rb +12 -4
- data/lib/jsduck/relations.rb +4 -3
- data/lib/jsduck/source/file_parser.rb +1 -1
- data/lib/jsduck/type_parser.rb +3 -3
- metadata +406 -400
data/.travis.yml
CHANGED
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
|
|
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 = '4.
|
6
|
-
s.date = '2012-
|
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'
|
data/lib/jsduck/ast.rb
CHANGED
@@ -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
|
-
@
|
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
|
-
#
|
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(
|
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) &&
|
157
|
+
call?(ast) && ext_pattern?("Ext.define", ast["callee"])
|
156
158
|
end
|
157
159
|
|
158
160
|
def ext_extend?(ast)
|
159
|
-
call?(ast) &&
|
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" &&
|
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
|
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
|
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
|
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)
|
data/lib/jsduck/doc_parser.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/jsduck/doc_type.rb
CHANGED
@@ -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
|
data/lib/jsduck/js_parser.rb
CHANGED
@@ -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]
|
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]
|
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]
|
151
|
+
return b[0] <= a[0] && a[1] <= b[1]
|
152
152
|
end
|
153
153
|
|
154
154
|
|