jsduck 3.0.1 → 3.1.0

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.
@@ -0,0 +1,34 @@
1
+ require 'jsduck/json_duck'
2
+ require 'jsduck/icons'
3
+ require 'jsduck/search_data'
4
+ require 'jsduck/stats'
5
+
6
+ module JsDuck
7
+
8
+ # Creates big JS file with data for Docs app.
9
+ class AppData
10
+ attr_accessor :guides
11
+ attr_accessor :videos
12
+ attr_accessor :examples
13
+
14
+ def initialize(relations, opts)
15
+ @relations = relations
16
+ @opts = opts
17
+ end
18
+
19
+ # Writes classes, guides, videos, and search data to one big .js file
20
+ def write(filename)
21
+ js = "Docs.data = " + JsonDuck.generate({
22
+ :classes => Icons.new.create(@relations.classes),
23
+ :guides => @guides.to_array,
24
+ :videos => @videos.to_array,
25
+ :examples => @examples.to_array,
26
+ :search => SearchData.new.create(@relations.classes),
27
+ :stats => @opts.stats ? Stats.new.create(@relations.classes) : [],
28
+ }) + ";\n"
29
+ File.open(filename, 'w') {|f| f.write(js) }
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -1,29 +1,31 @@
1
+ require 'jsduck/full_exporter'
2
+ require 'jsduck/renderer'
3
+ require 'jsduck/doc_formatter'
4
+
1
5
  module JsDuck
2
6
 
3
- # Export class data as hash with :cfg being replace with :cfgs and
4
- # including all the inherited members too. Same for :properties,
5
- # :methods, and :events.
6
- class Exporter
7
- def initialize(relations)
8
- @relations = relations
7
+ # Exports data for Docs app.
8
+ class AppExporter < FullExporter
9
+ def initialize(relations, opts)
10
+ super(relations, opts)
11
+
12
+ @renderer = Renderer.new
13
+ # Inject formatter to all meta-tags.
14
+ doc_formatter = DocFormatter.new(relations, opts)
15
+ opts.meta_tags.each {|tag| tag.formatter = doc_formatter }
16
+ @renderer.meta_tags = opts.meta_tags
9
17
  end
10
18
 
11
- # Returns all data in Class object as hash.
19
+ # Returns compacted class data hash which contains an additional
20
+ # :html field with full HTML to show on class overview page.
12
21
  def export(cls)
13
- h = cls.to_hash
14
- h[:members] = {}
15
- Class.default_members_hash.each_key do |key|
16
- h[:members][key] = cls.members(key)
17
- h[:statics][key] = cls.members(key, :statics)
18
- end
19
- h[:component] = cls.inherits_from?("Ext.Component")
20
- h[:superclasses] = cls.superclasses.collect {|c| c.full_name }
21
- h[:subclasses] = @relations.subclasses(cls).collect {|c| c.full_name }
22
- h[:mixedInto] = @relations.mixed_into(cls).collect {|c| c.full_name }
23
- h[:allMixins] = cls.all_mixins.collect {|c| c.full_name }
24
- h
22
+ data = super(cls)
23
+ data[:html] = @renderer.render(data)
24
+ return compact(data)
25
25
  end
26
26
 
27
+ private
28
+
27
29
  # removes extra data from export
28
30
  def compact(cls)
29
31
  cls.delete(:doc)
@@ -1,81 +1,32 @@
1
1
  require 'jsduck/logger'
2
2
  require 'jsduck/json_duck'
3
+ require 'jsduck/file_categories'
3
4
  require 'jsduck/auto_categories'
4
5
 
5
6
  module JsDuck
6
7
 
7
8
  # Reads in categories and outputs them as HTML div
8
9
  class Categories
9
- def initialize(doc_formatter, relations={})
10
- @doc_formatter = doc_formatter
11
- @relations = relations
12
- @categories = []
13
- end
14
-
15
- # Automatically divides all available classes into categories
16
- def auto_generate
17
- @categories = AutoCategories.new(@relations).generate
18
- end
19
-
20
- # Parses categories in JSON file
21
- def parse(path)
22
- @categories = JsonDuck.read(path)
23
-
24
- # Don't crash if old syntax is used.
25
- if @categories.is_a?(Hash) && @categories["categories"]
26
- Logger.instance.warn('Update categories file to contain just the array inside {"categories": [...]}')
27
- @categories = @categories["categories"]
28
- end
29
-
30
- # Perform expansion on all class names containing * wildcard
31
- @categories.each do |cat|
32
- cat["groups"].each do |group|
33
- group["classes"] = group["classes"].map do |name|
34
- expand(name) # name =~ /\*/ ? expand(name) : name
35
- end.flatten
36
- end
37
- end
38
-
39
- validate
40
- end
41
-
42
- # Expands class name like 'Foo.*' into multiple class names.
43
- def expand(name)
44
- re = Regexp.new("^" + name.split(/\*/, -1).map {|part| Regexp.escape(part) }.join('.*') + "$")
45
- classes = @relations.to_a.find_all {|cls| re =~ cls[:name] && !cls[:private] }.map {|cls| cls[:name] }.sort
46
- if classes.length == 0
47
- Logger.instance.warn("No class found matching a pattern '#{name}' in categories file.")
10
+ def self.create(filename, doc_formatter, relations)
11
+ if filename
12
+ categories = FileCategories.new(filename, relations)
13
+ else
14
+ categories = AutoCategories.new(relations)
48
15
  end
49
- classes
16
+ Categories.new(categories.generate, doc_formatter, relations)
50
17
  end
51
18
 
52
- # Prints warnings for missing classes in categories file
53
- def validate
54
- # Build a map of all classes listed in categories
55
- listed_classes = {}
56
- @categories.each do |cat|
57
- cat["groups"].each do |group|
58
- group["classes"].each do |cls_name|
59
- listed_classes[cls_name] = true
60
- end
61
- end
62
- end
63
-
64
- # Check that each existing non-private class is listed
65
- @relations.each do |cls|
66
- unless listed_classes[cls[:name]] || cls[:private]
67
- Logger.instance.warn("Class '#{cls[:name]}' not found in categories file")
68
- end
69
- end
19
+ def initialize(categories, doc_formatter, relations={})
20
+ @categories = categories
21
+ @doc_formatter = doc_formatter
22
+ @relations = relations
70
23
  end
71
24
 
72
25
  # Returns HTML listing of classes divided into categories
73
26
  def to_html
74
- return "" if @categories.length == 0
75
-
76
27
  html = @categories.map do |category|
77
28
  [
78
- "<div class='section classes'>",
29
+ "<div class='section'>",
79
30
  "<h1>#{category['name']}</h1>",
80
31
  render_columns(category['groups']),
81
32
  "<div style='clear:both'></div>",
@@ -91,7 +42,7 @@ module JsDuck
91
42
  end
92
43
 
93
44
  def render_columns(groups)
94
- align = ["lft", "mid", "rgt"]
45
+ align = ["left-column", "middle-column", "right-column"]
95
46
  i = -1
96
47
  return split(groups, 3).map do |col|
97
48
  i += 1
data/lib/jsduck/class.rb CHANGED
@@ -62,7 +62,7 @@ module JsDuck
62
62
  @relations[classname]
63
63
  elsif !@relations.ignore?(classname)
64
64
  context = @doc[:files][0]
65
- Logger.instance.warn("Class #{classname} not found", context[:filename], context[:linenr])
65
+ Logger.instance.warn(:extend, "Class #{classname} not found", context[:filename], context[:linenr])
66
66
  nil
67
67
  end
68
68
  end
@@ -89,7 +89,8 @@ module JsDuck
89
89
  #
90
90
  # See members_hash for details.
91
91
  def members(type, context=:members)
92
- ms = members_hash(type, context).values.sort {|a,b| a[:name] <=> b[:name] }
92
+ ms = members_hash(type, context).values.find_all {|m| !m[:private] }
93
+ ms.sort! {|a,b| a[:name] <=> b[:name] }
93
94
  type == :method ? constructor_first(ms) : ms
94
95
  end
95
96
 
@@ -104,7 +105,7 @@ module JsDuck
104
105
  ms
105
106
  end
106
107
 
107
- # Returns hash of public members of class (and of parent classes
108
+ # Returns hash of all members in class (and of parent classes
108
109
  # and mixin classes). Members are methods, properties, cfgs,
109
110
  # events (member type is specified through 'type' parameter).
110
111
  #
@@ -115,7 +116,7 @@ module JsDuck
115
116
  if @doc[:singleton] && context == :statics
116
117
  # Warn if singleton has static members
117
118
  if @doc[context][type].length > 0
118
- Logger.instance.warn("Singleton class #{@doc[:name]} can't have static members, remove the @static tag.")
119
+ Logger.instance.warn(:sing_static, "Singleton class #{@doc[:name]} can't have static members, remove the @static tag.")
119
120
  end
120
121
  return {}
121
122
  end
@@ -148,7 +149,7 @@ module JsDuck
148
149
  def local_members_hash(type, context)
149
150
  local_members = {}
150
151
  (@doc[context][type] || []).each do |m|
151
- local_members[m[:name]] = m if !m[:private]
152
+ local_members[m[:name]] = m
152
153
  end
153
154
  local_members
154
155
  end
@@ -174,13 +175,26 @@ module JsDuck
174
175
  @members_map[type_name ? "#{type_name}-#{name}" : name]
175
176
  end
176
177
 
177
- # Loops through each member of the class, invoking block with each of them
178
- def each_member(&block)
178
+ # Returns all public members of class, including the inherited and mixed in ones
179
+ def all_members
180
+ all = []
179
181
  [:members, :statics].each do |group|
180
- @doc[group].each_value do |members|
181
- members.each(&block)
182
+ @doc[group].each_key do |type|
183
+ all += members(type, group)
182
184
  end
183
185
  end
186
+ all
187
+ end
188
+
189
+ # Returns all local public members of class
190
+ def all_local_members
191
+ all = []
192
+ [:members, :statics].each do |group|
193
+ @doc[group].each_value do |ms|
194
+ all += ms.find_all {|m| !m[:private] }
195
+ end
196
+ end
197
+ all
184
198
  end
185
199
 
186
200
  # A way to access full class name with similar syntax to
@@ -69,9 +69,9 @@ module JsDuck
69
69
  else
70
70
  context = @formatter.doc_context
71
71
  if tp.error == :syntax
72
- Logger.instance.warn("Incorrect type syntax #{type}", context[:filename], context[:linenr])
72
+ Logger.instance.warn(:type_syntax, "Incorrect type syntax #{type}", context[:filename], context[:linenr])
73
73
  else
74
- Logger.instance.warn("Unknown type #{type}", context[:filename], context[:linenr])
74
+ Logger.instance.warn(:type_name, "Unknown type #{type}", context[:filename], context[:linenr])
75
75
  end
76
76
  type
77
77
  end
@@ -0,0 +1,49 @@
1
+ require 'jsduck/parallel_wrap'
2
+ require 'jsduck/logger'
3
+ require 'jsduck/json_duck'
4
+ require 'fileutils'
5
+
6
+ module JsDuck
7
+
8
+ # Writes class data into files in JSON or JSONP format or to STDOUT.
9
+ class ClassWriter
10
+ def initialize(exporter_class, relations, opts)
11
+ @relations = relations
12
+ @exporter = exporter_class.new(relations, opts)
13
+ @parallel = ParallelWrap.new(:in_processes => opts.processes)
14
+ end
15
+
16
+ # Writes class data into given directory or STDOUT when dir == :stdout.
17
+ #
18
+ # Extension is either ".json" for normal JSON output
19
+ # or ".js" for JsonP output.
20
+ def write(dir, extension)
21
+ dir == :stdout ? write_stdout : write_dir(dir, extension)
22
+ end
23
+
24
+ private
25
+
26
+ def write_stdout
27
+ json = @parallel.map(@relations.classes) {|cls| @exporter.export(cls) }
28
+ puts JsonDuck.generate(json)
29
+ end
30
+
31
+ def write_dir(dir, extension)
32
+ FileUtils.mkdir(dir)
33
+ @parallel.each(@relations.classes) do |cls|
34
+ filename = dir + "/" + cls[:name] + extension
35
+ Logger.instance.log("Writing docs", filename)
36
+ json = @exporter.export(cls)
37
+ if extension == ".json"
38
+ JsonDuck.write_json(filename, json)
39
+ elsif extension == ".js"
40
+ JsonDuck.write_jsonp(filename, cls[:name].gsub(/\./, "_"), json)
41
+ else
42
+ throw "Unexpected file extension: #{extension}"
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -55,14 +55,14 @@ module JsDuck
55
55
  # name actually exists.
56
56
  attr_accessor :relations
57
57
 
58
- def initialize
58
+ def initialize(relations={}, opts={})
59
59
  @class_context = ""
60
60
  @doc_context = {}
61
61
  @max_length = 120
62
- @relations = {}
62
+ @relations = relations
63
63
  @images = []
64
- @link_tpl = '<a href="%c%#%m">%a</a>'
65
- @img_tpl = '<img src="%u" alt="%a"/>'
64
+ @link_tpl = opts[:link_tpl] || '<a href="%c%#%m">%a</a>'
65
+ @img_tpl = opts[:img_tpl] || '<img src="%u" alt="%a"/>'
66
66
  @link_re = /\{@link\s+(\S*?)(?:\s+(.+?))?\}/m
67
67
  @img_re = /\{@img\s+(\S*?)(?:\s+(.+?))?\}/m
68
68
  @example_annotation_re = /<pre><code>\s*@example( +[^\n]*)?\s+/m
@@ -130,10 +130,13 @@ module JsDuck
130
130
  file = @doc_context[:filename]
131
131
  line = @doc_context[:linenr]
132
132
  if !@relations[cls]
133
- Logger.instance.warn("#{input} links to non-existing class", file, line)
133
+ Logger.instance.warn(:link, "#{input} links to non-existing class", file, line)
134
134
  text
135
135
  elsif member && !get_member(cls, member, type)
136
- Logger.instance.warn("#{input} links to non-existing member", file, line)
136
+ Logger.instance.warn(:link, "#{input} links to non-existing member", file, line)
137
+ text
138
+ elsif member && !public_member?(cls, member, type)
139
+ Logger.instance.warn(:link, "#{input} links to private member", file, line)
137
140
  text
138
141
  else
139
142
  link(cls, member, text, type)
@@ -153,7 +156,7 @@ module JsDuck
153
156
  member = $4
154
157
  after = $5
155
158
 
156
- if @relations[cls] && (member ? get_member(cls, member) : cls =~ /\./)
159
+ if @relations[cls] && (member ? public_member?(cls, member) : cls =~ /\./)
157
160
  label = member ? cls+"."+member : cls
158
161
  before + link(cls, member, label) + after
159
162
  else
@@ -202,8 +205,13 @@ module JsDuck
202
205
  end
203
206
  end
204
207
 
208
+ def public_member?(cls, member, type=nil)
209
+ m = get_member(cls, member, type)
210
+ return m && !m[:private]
211
+ end
212
+
205
213
  def get_member(cls, member, type=nil)
206
- @relations[cls] && @relations[cls].get_member(member, type)
214
+ return @relations[cls] && @relations[cls].get_member(member, type)
207
215
  end
208
216
 
209
217
  # Formats doc-comment for placement into HTML.
@@ -93,9 +93,13 @@ module JsDuck
93
93
  elsif look(/@extends?\b/)
94
94
  at_extends
95
95
  elsif look(/@mixins?\b/)
96
- at_mixins
96
+ class_list_at_tag(/@mixins?/, :mixins)
97
97
  elsif look(/@alternateClassNames?\b/)
98
- at_alternateClassName
98
+ class_list_at_tag(/@alternateClassNames?/, :alternateClassNames)
99
+ elsif look(/@uses\b/)
100
+ class_list_at_tag(/@uses/, :uses)
101
+ elsif look(/@requires\b/)
102
+ class_list_at_tag(/@requires/, :requires)
99
103
  elsif look(/@singleton\b/)
100
104
  boolean_at_tag(/@singleton/, :singleton)
101
105
  elsif look(/@event\b/)
@@ -115,12 +119,20 @@ module JsDuck
115
119
  elsif look(/@type\b/)
116
120
  at_type
117
121
  elsif look(/@xtype\b/)
118
- at_xtype
122
+ at_xtype(/@xtype/, "widget")
119
123
  elsif look(/@ftype\b/)
120
- at_ftype
124
+ at_xtype(/@ftype/, "feature")
125
+ elsif look(/@ptype\b/)
126
+ at_xtype(/@ptype/, "plugin")
121
127
  elsif look(/@member\b/)
122
128
  at_member
123
- elsif look(/@alias\b/)
129
+ elsif look(/@inherit[dD]oc\b/)
130
+ at_inheritdoc
131
+ elsif look(/@alias\s+[\w.]+#\w+/)
132
+ # For backwards compatibility.
133
+ # @alias tag was used as @inheritdoc before
134
+ at_inheritdoc
135
+ elsif look(/@alias/)
124
136
  at_alias
125
137
  elsif look(/@deprecated\b/)
126
138
  at_deprecated
@@ -194,21 +206,12 @@ module JsDuck
194
206
  skip_white
195
207
  end
196
208
 
197
- # matches @mixins name1 name2 ...
198
- def at_mixins
199
- match(/@mixins?/)
200
- add_tag(:mixins)
201
- skip_horiz_white
202
- @current_tag[:mixins] = class_list
203
- skip_white
204
- end
205
-
206
- # matches @alternateClassName name1 name2 ...
207
- def at_alternateClassName
208
- match(/@alternateClassNames?/)
209
- add_tag(:alternateClassNames)
209
+ # matches @<tagname> classname1 classname2 ...
210
+ def class_list_at_tag(regex, tagname)
211
+ match(regex)
212
+ add_tag(tagname)
210
213
  skip_horiz_white
211
- @current_tag[:alternateClassNames] = class_list
214
+ @current_tag[tagname] = class_list
212
215
  skip_white
213
216
  end
214
217
 
@@ -302,22 +305,6 @@ module JsDuck
302
305
  skip_white
303
306
  end
304
307
 
305
- # matches @xtype name
306
- def at_xtype
307
- match(/@xtype/)
308
- add_tag(:xtype)
309
- maybe_ident_chain(:name)
310
- skip_white
311
- end
312
-
313
- # matches @ftype name
314
- def at_ftype
315
- match(/@ftype/)
316
- add_tag(:ftype)
317
- maybe_ident_chain(:name)
318
- skip_white
319
- end
320
-
321
308
  # matches @member name ...
322
309
  def at_member
323
310
  match(/@member/)
@@ -326,11 +313,30 @@ module JsDuck
326
313
  skip_white
327
314
  end
328
315
 
329
- # matches @alias class.name#type-member
316
+ # matches @xtype/ptype/ftype/... name
317
+ def at_xtype(tag, namespace)
318
+ match(tag)
319
+ add_tag(:alias)
320
+ skip_horiz_white
321
+ @current_tag[:name] = namespace + "." + (ident_chain || "")
322
+ skip_white
323
+ end
324
+
325
+ # matches @alias <ident-chain>
330
326
  def at_alias
331
327
  match(/@alias/)
332
328
  add_tag(:alias)
333
329
  skip_horiz_white
330
+ @current_tag[:name] = ident_chain
331
+ skip_white
332
+ end
333
+
334
+ # matches @inheritdoc class.name#type-member
335
+ def at_inheritdoc
336
+ match(/@inherit[dD]oc|@alias/)
337
+
338
+ add_tag(:inheritdoc)
339
+ skip_horiz_white
334
340
  if look(@ident_chain_pattern)
335
341
  @current_tag[:cls] = ident_chain
336
342
  if look(/#\w/)