jsduck 5.0.0.beta2 → 5.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +14 -4
- data/bin/jsduck +3 -1
- data/jsduck.gemspec +2 -2
- data/lib/jsduck/app.rb +8 -0
- data/lib/jsduck/assets.rb +3 -0
- data/lib/jsduck/batch_processor.rb +2 -0
- data/lib/jsduck/categories/class_name.rb +2 -26
- data/lib/jsduck/categories/factory.rb +5 -43
- data/lib/jsduck/columns.rb +56 -0
- data/lib/jsduck/doc/delimited_parser.rb +105 -0
- data/lib/jsduck/doc/scanner.rb +2 -1
- data/lib/jsduck/doc/standard_tag_parser.rb +37 -71
- data/lib/jsduck/guide_anchors.rb +32 -0
- data/lib/jsduck/guide_toc.rb +49 -0
- data/lib/jsduck/guides.rb +14 -32
- data/lib/jsduck/inline/video.rb +2 -8
- data/lib/jsduck/js/ast.rb +13 -305
- data/lib/jsduck/js/class.rb +245 -0
- data/lib/jsduck/js/event.rb +34 -0
- data/lib/jsduck/js/fires.rb +42 -0
- data/lib/jsduck/js/method.rb +94 -0
- data/lib/jsduck/js/method_calls.rb +40 -0
- data/lib/jsduck/js/node.rb +29 -0
- data/lib/jsduck/js/property.rb +64 -0
- data/lib/jsduck/js/{function.rb → returns.rb} +8 -3
- data/lib/jsduck/js/scoped_traverser.rb +42 -0
- data/lib/jsduck/logger.rb +13 -1
- data/lib/jsduck/merger.rb +34 -27
- data/lib/jsduck/news.rb +128 -0
- data/lib/jsduck/options.rb +59 -2
- data/lib/jsduck/params_merger.rb +47 -0
- data/lib/jsduck/process/accessors.rb +8 -2
- data/lib/jsduck/process/fires.rb +71 -0
- data/lib/jsduck/process/importer.rb +19 -1
- data/lib/jsduck/render/class.rb +11 -4
- data/lib/jsduck/render/signature_util.rb +14 -0
- data/lib/jsduck/tag/alias.rb +0 -20
- data/lib/jsduck/tag/alternate_class_names.rb +0 -5
- data/lib/jsduck/tag/cfg.rb +30 -5
- data/lib/jsduck/tag/class.rb +45 -2
- data/lib/jsduck/tag/css_mixin.rb +8 -4
- data/lib/jsduck/tag/css_var.rb +26 -5
- data/lib/jsduck/tag/default.rb +2 -8
- data/lib/jsduck/tag/enum.rb +7 -10
- data/lib/jsduck/tag/event.rb +12 -4
- data/lib/jsduck/tag/extends.rb +0 -6
- data/lib/jsduck/tag/fires.rb +53 -0
- data/lib/jsduck/tag/icons/cfg.png +0 -0
- data/lib/jsduck/tag/icons/css_mixin.png +0 -0
- data/lib/jsduck/tag/icons/css_var.png +0 -0
- data/lib/jsduck/tag/icons/event.png +0 -0
- data/lib/jsduck/tag/icons/method.png +0 -0
- data/lib/jsduck/tag/icons/property.png +0 -0
- data/lib/jsduck/tag/member_tag.rb +130 -0
- data/lib/jsduck/tag/method.rb +44 -4
- data/lib/jsduck/tag/param.rb +8 -60
- data/lib/jsduck/tag/property.rb +28 -5
- data/lib/jsduck/tag/tag.rb +3 -75
- data/lib/jsduck/tag/type.rb +1 -11
- data/lib/jsduck/tag_registry.rb +6 -48
- data/lib/jsduck/web/css.rb +8 -1
- data/lib/jsduck/web/data.rb +2 -1
- data/lib/jsduck/web/index_html.rb +1 -0
- data/lib/jsduck/web/member_icons.rb +43 -0
- data/lib/jsduck/web/search.rb +3 -2
- data/lib/jsduck/web/writer.rb +8 -0
- metadata +31 -27
- data/lib/jsduck/docs_code_comparer.rb +0 -44
- data/lib/jsduck/render/signature.rb +0 -94
- data/lib/jsduck/tag/autodetected.rb +0 -21
- data/lib/jsduck/tag/name.rb +0 -36
@@ -0,0 +1,245 @@
|
|
1
|
+
require "jsduck/util/singleton"
|
2
|
+
require "jsduck/tag_registry"
|
3
|
+
require "jsduck/js/method"
|
4
|
+
require "jsduck/js/property"
|
5
|
+
|
6
|
+
module JsDuck
|
7
|
+
module Js
|
8
|
+
|
9
|
+
# Auto-detection of classes.
|
10
|
+
class Class
|
11
|
+
include Util::Singleton
|
12
|
+
|
13
|
+
# Checks if AST node is a class, and if so, returns doc-hash
|
14
|
+
# with clas name and various auto-detected attributes.
|
15
|
+
# When not a class returns nil.
|
16
|
+
def detect(ast, docs)
|
17
|
+
@docs = docs
|
18
|
+
|
19
|
+
exp = ast.expression_statement? ? ast["expression"] : nil
|
20
|
+
var = ast.variable_declaration? ? ast["declarations"][0] : nil
|
21
|
+
|
22
|
+
# Ext.define("Class", {})
|
23
|
+
if exp && exp.ext_define?
|
24
|
+
make(exp["arguments"][0].to_value, exp)
|
25
|
+
|
26
|
+
# Ext.override(Class, {})
|
27
|
+
elsif exp && exp.ext_override?
|
28
|
+
make("", exp)
|
29
|
+
|
30
|
+
# foo = Ext.extend(Parent, {})
|
31
|
+
elsif exp && exp.assignment_expression? && exp["right"].ext_extend?
|
32
|
+
make(exp["left"].to_s, exp["right"])
|
33
|
+
|
34
|
+
# Foo = ...
|
35
|
+
elsif exp && exp.assignment_expression? && class_name?(exp["left"].to_s)
|
36
|
+
make(exp["left"].to_s, exp["right"])
|
37
|
+
|
38
|
+
# var foo = Ext.extend(Parent, {})
|
39
|
+
elsif var && var["init"].ext_extend?
|
40
|
+
make(var["id"].to_s, var["init"])
|
41
|
+
|
42
|
+
# var Foo = ...
|
43
|
+
elsif var && class_name?(var["id"].to_s)
|
44
|
+
make(var["id"].to_s, var["right"])
|
45
|
+
|
46
|
+
# function Foo() {}
|
47
|
+
elsif ast.function? && class_name?(ast["id"].to_s || "")
|
48
|
+
make(ast["id"].to_s)
|
49
|
+
|
50
|
+
# { ... }
|
51
|
+
elsif ast.object_expression?
|
52
|
+
make("", ast)
|
53
|
+
|
54
|
+
else
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Produces a doc-hash for a class.
|
60
|
+
def make(name, ast=nil)
|
61
|
+
cls = {
|
62
|
+
:tagname => :class,
|
63
|
+
:name => name,
|
64
|
+
}
|
65
|
+
|
66
|
+
# apply information from Ext.extend, Ext.define, or {}
|
67
|
+
if ast
|
68
|
+
if ast.ext_define?
|
69
|
+
detect_ext_define(cls, ast)
|
70
|
+
elsif ast.ext_extend?
|
71
|
+
detect_ext_something(:extends, cls, ast)
|
72
|
+
elsif ast.ext_override?
|
73
|
+
detect_ext_something(:override, cls, ast)
|
74
|
+
elsif ast.object_expression?
|
75
|
+
detect_class_members_from_object(cls, ast)
|
76
|
+
elsif ast.array_expression?
|
77
|
+
detect_class_members_from_array(cls, ast)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
return cls
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# Class name begins with upcase char
|
87
|
+
def class_name?(name)
|
88
|
+
return name.split(/\./).last =~ /\A[A-Z]/
|
89
|
+
end
|
90
|
+
|
91
|
+
# Detection of Ext.extend() or Ext.override().
|
92
|
+
# The type parameter must be correspondingly either :extend or :override.
|
93
|
+
def detect_ext_something(type, cls, ast)
|
94
|
+
args = ast["arguments"]
|
95
|
+
cls[type] = args[0].to_s
|
96
|
+
if args.length == 2 && args[1].object_expression?
|
97
|
+
detect_class_members_from_object(cls, args[1])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Inspects Ext.define() and copies detected properties over to the
|
102
|
+
# given cls Hash
|
103
|
+
def detect_ext_define(cls, ast)
|
104
|
+
# defaults
|
105
|
+
cls.merge!(TagRegistry.ext_define_defaults)
|
106
|
+
cls[:members] = []
|
107
|
+
cls[:code_type] = :ext_define
|
108
|
+
|
109
|
+
ast["arguments"][1].each_property do |key, value, pair|
|
110
|
+
if tag = TagRegistry.get_by_ext_define_pattern(key)
|
111
|
+
tag.parse_ext_define(cls, value)
|
112
|
+
else
|
113
|
+
case key
|
114
|
+
when "config"
|
115
|
+
cls[:members] += make_configs(value, {:accessor => true})
|
116
|
+
when "cachedConfig"
|
117
|
+
cls[:members] += make_configs(value, {:accessor => true})
|
118
|
+
when "eventedConfig"
|
119
|
+
cls[:members] += make_configs(value, {:accessor => true, :evented => true})
|
120
|
+
when "statics"
|
121
|
+
cls[:members] += make_statics(value)
|
122
|
+
when "inheritableStatics"
|
123
|
+
cls[:members] += make_statics(value, {:inheritable => true})
|
124
|
+
else
|
125
|
+
detect_method_or_property(cls, key, value, pair)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Detects class members from object literal
|
132
|
+
def detect_class_members_from_object(cls, ast)
|
133
|
+
cls[:members] = []
|
134
|
+
ast.each_property do |key, value, pair|
|
135
|
+
detect_method_or_property(cls, key, value, pair)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Detects class members from array literal
|
140
|
+
def detect_class_members_from_array(cls, ast)
|
141
|
+
cls[:members] = []
|
142
|
+
|
143
|
+
# This will most likely be an @enum class, in which case the
|
144
|
+
# enum will be for documentation purposes only.
|
145
|
+
cls[:enum] = {:doc_only => true}
|
146
|
+
|
147
|
+
ast["elements"].each do |el|
|
148
|
+
detect_method_or_property(cls, el.key_value, el, el)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Detects item in object literal either as method or property
|
153
|
+
def detect_method_or_property(cls, key, value, pair)
|
154
|
+
if value.function?
|
155
|
+
m = make_method(key, value)
|
156
|
+
cls[:members] << m if apply_autodetected(m, pair)
|
157
|
+
else
|
158
|
+
p = make_property(key, value)
|
159
|
+
cls[:members] << p if apply_autodetected(p, pair)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def make_configs(ast, defaults={})
|
164
|
+
configs = []
|
165
|
+
|
166
|
+
ast.each_property do |name, value, pair|
|
167
|
+
cfg = make_property(name, value)
|
168
|
+
cfg[:tagname] = :cfg
|
169
|
+
cfg.merge!(defaults)
|
170
|
+
configs << cfg if apply_autodetected(cfg, pair)
|
171
|
+
end
|
172
|
+
|
173
|
+
configs
|
174
|
+
end
|
175
|
+
|
176
|
+
def make_statics(ast, defaults={})
|
177
|
+
statics = []
|
178
|
+
|
179
|
+
ast.each_property do |name, value, pair|
|
180
|
+
if value.function?
|
181
|
+
s = make_method(name, value)
|
182
|
+
else
|
183
|
+
s = make_property(name, value)
|
184
|
+
end
|
185
|
+
|
186
|
+
s[:static] = true
|
187
|
+
s.merge!(defaults)
|
188
|
+
|
189
|
+
statics << s if apply_autodetected(s, pair, defaults[:inheritable])
|
190
|
+
end
|
191
|
+
|
192
|
+
statics
|
193
|
+
end
|
194
|
+
|
195
|
+
# Sets auto-detection related properties :autodetected and
|
196
|
+
# :inheritdoc on the given member Hash.
|
197
|
+
#
|
198
|
+
# When member has a comment, adds code to the related docset and
|
199
|
+
# returns false.
|
200
|
+
#
|
201
|
+
# Otherwise detects the line number of member and returns true.
|
202
|
+
def apply_autodetected(m, ast, inheritable=true)
|
203
|
+
docset = find_docset(ast.raw)
|
204
|
+
|
205
|
+
if !docset || docset[:type] != :doc_comment
|
206
|
+
if inheritable
|
207
|
+
m[:inheritdoc] = {}
|
208
|
+
else
|
209
|
+
m[:private] = true
|
210
|
+
end
|
211
|
+
m[:autodetected] = {:tagname => m[:tagname]}
|
212
|
+
end
|
213
|
+
|
214
|
+
if docset
|
215
|
+
docset[:code] = m
|
216
|
+
return false
|
217
|
+
else
|
218
|
+
m[:linenr] = ast.linenr
|
219
|
+
return true
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Looks up docset associated with given AST node.
|
224
|
+
# A dead-stupid and -slow implementation, but works.
|
225
|
+
#
|
226
|
+
# The comparison needs to be done between raw AST nodes - multiple
|
227
|
+
# Node instances can be created to wrap a single raw AST node,
|
228
|
+
# and they will then not be equal.
|
229
|
+
def find_docset(raw_ast)
|
230
|
+
@docs.find do |docset|
|
231
|
+
docset[:code] == raw_ast
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def make_method(name, ast)
|
236
|
+
Js::Method.make(name, ast)
|
237
|
+
end
|
238
|
+
|
239
|
+
def make_property(name=nil, ast=nil)
|
240
|
+
Js::Property.make(name, ast)
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "jsduck/util/singleton"
|
2
|
+
|
3
|
+
module JsDuck
|
4
|
+
module Js
|
5
|
+
|
6
|
+
# Auto-detection of events.
|
7
|
+
class Event
|
8
|
+
include Util::Singleton
|
9
|
+
|
10
|
+
# Checks if AST node is an event, and if so, returns doc-hash
|
11
|
+
# with event name and various auto-detected properties.
|
12
|
+
# When not an event returns nil.
|
13
|
+
def detect(ast)
|
14
|
+
exp = ast.expression_statement? ? ast["expression"] : nil
|
15
|
+
|
16
|
+
# this.fireEvent("foo", ...)
|
17
|
+
if exp && exp.fire_event?
|
18
|
+
make(exp["arguments"][0].to_value)
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Produces a doc-hash for an event.
|
25
|
+
def make(name)
|
26
|
+
return {
|
27
|
+
:tagname => :event,
|
28
|
+
:name => name,
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "jsduck/util/singleton"
|
2
|
+
require "jsduck/js/scoped_traverser"
|
3
|
+
|
4
|
+
module JsDuck
|
5
|
+
module Js
|
6
|
+
|
7
|
+
# Looks the AST of a FunctionDeclaration or FunctionExpression for
|
8
|
+
# uses of this.fireEvent().
|
9
|
+
class Fires
|
10
|
+
include Util::Singleton
|
11
|
+
|
12
|
+
# Returns array of event names fired by the given function.
|
13
|
+
# When no events fired, empty array is returned.
|
14
|
+
def detect(function_node)
|
15
|
+
@traverser = Js::ScopedTraverser.new
|
16
|
+
|
17
|
+
events = []
|
18
|
+
@traverser.traverse(function_node["body"]) do |node|
|
19
|
+
if fire_event?(node)
|
20
|
+
events << node["arguments"][0].to_value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
events.sort.uniq
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# True when node is this.fireEvent("name") call.
|
30
|
+
# Also true for me.fireEvent() when me == this.
|
31
|
+
def fire_event?(node)
|
32
|
+
node.call_expression? &&
|
33
|
+
node["callee"].member_expression? &&
|
34
|
+
@traverser.this?(node["callee"]["object"].to_s) &&
|
35
|
+
node["callee"]["property"].to_s == "fireEvent" &&
|
36
|
+
node["arguments"].length > 0 &&
|
37
|
+
node["arguments"][0].value_type == "String"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "jsduck/util/singleton"
|
2
|
+
require "jsduck/js/returns"
|
3
|
+
require "jsduck/js/fires"
|
4
|
+
require "jsduck/js/method_calls"
|
5
|
+
|
6
|
+
module JsDuck
|
7
|
+
module Js
|
8
|
+
|
9
|
+
# Auto-detection of methods.
|
10
|
+
class Method
|
11
|
+
include Util::Singleton
|
12
|
+
|
13
|
+
# Checks if AST node is a method, and if so, returns doc-hash
|
14
|
+
# with method name and various auto-detected properties.
|
15
|
+
# When not a method returns nil.
|
16
|
+
def detect(ast)
|
17
|
+
exp = ast.expression_statement? ? ast["expression"] : nil
|
18
|
+
var = ast.variable_declaration? ? ast["declarations"][0] : nil
|
19
|
+
|
20
|
+
# function foo() {}
|
21
|
+
if ast.function?
|
22
|
+
make(ast["id"].to_s || "", ast)
|
23
|
+
|
24
|
+
# foo = function() {}
|
25
|
+
elsif exp && exp.assignment_expression? && exp["right"].function?
|
26
|
+
make(exp["left"].to_s, exp["right"])
|
27
|
+
|
28
|
+
# var foo = function() {}
|
29
|
+
elsif var && var["init"] && var["init"].function?
|
30
|
+
make(var["id"].to_s, var["init"])
|
31
|
+
|
32
|
+
# (function() {})
|
33
|
+
elsif exp && exp.function?
|
34
|
+
make(exp["id"].to_s || "", exp)
|
35
|
+
|
36
|
+
# foo: function() {}
|
37
|
+
elsif ast.property? && ast["value"].function?
|
38
|
+
make(ast["key"].key_value, ast["value"])
|
39
|
+
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Performs the auto-detection on function AST node and produces
|
46
|
+
# a doc-hash.
|
47
|
+
def make(name, ast)
|
48
|
+
if proper_function?(ast)
|
49
|
+
return {
|
50
|
+
:tagname => :method,
|
51
|
+
:name => name,
|
52
|
+
:params => arr_to_nil(params(ast)),
|
53
|
+
:chainable => chainable?(ast) && name != "constructor",
|
54
|
+
:fires => arr_to_nil(fires(ast)),
|
55
|
+
:method_calls => arr_to_nil(method_calls(ast)),
|
56
|
+
}
|
57
|
+
else
|
58
|
+
return {
|
59
|
+
:tagname => :method,
|
60
|
+
:name => name,
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def proper_function?(ast)
|
68
|
+
ast.function? && !ast.ext_empty_fn?
|
69
|
+
end
|
70
|
+
|
71
|
+
# replaces empty array with nil
|
72
|
+
def arr_to_nil(arr)
|
73
|
+
arr.length == 0 ? nil : arr
|
74
|
+
end
|
75
|
+
|
76
|
+
def params(ast)
|
77
|
+
ast["params"].map {|p| {:name => p.to_s} }
|
78
|
+
end
|
79
|
+
|
80
|
+
def chainable?(ast)
|
81
|
+
Js::Returns.chainable?(ast.raw)
|
82
|
+
end
|
83
|
+
|
84
|
+
def fires(ast)
|
85
|
+
Js::Fires.detect(ast)
|
86
|
+
end
|
87
|
+
|
88
|
+
def method_calls(ast)
|
89
|
+
Js::MethodCalls.detect(ast)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "jsduck/util/singleton"
|
2
|
+
require "jsduck/js/scoped_traverser"
|
3
|
+
|
4
|
+
module JsDuck
|
5
|
+
module Js
|
6
|
+
|
7
|
+
# Looks the AST of a FunctionDeclaration or FunctionExpression for
|
8
|
+
# calls to methods of the owner class.
|
9
|
+
class MethodCalls
|
10
|
+
include Util::Singleton
|
11
|
+
|
12
|
+
# Returns array of method names called by the given function.
|
13
|
+
# When no methods called, empty array is returned.
|
14
|
+
def detect(function_node)
|
15
|
+
@traverser = Js::ScopedTraverser.new
|
16
|
+
|
17
|
+
methods = []
|
18
|
+
@traverser.traverse(function_node["body"]) do |node|
|
19
|
+
if method_call?(node)
|
20
|
+
methods << node["callee"]["property"].to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
methods.sort.uniq
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# True when node is this.someMethod() call.
|
30
|
+
# Also true for me.someMethod() when me == this.
|
31
|
+
def method_call?(node)
|
32
|
+
node.call_expression? &&
|
33
|
+
node["callee"].member_expression? &&
|
34
|
+
node["callee"].raw["computed"] == false &&
|
35
|
+
@traverser.this?(node["callee"]["object"].to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|