jsduck 4.0.beta → 4.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/jsduck/ast.rb CHANGED
@@ -73,7 +73,7 @@ module JsDuck
73
73
 
74
74
  # Foo = ...
75
75
  elsif exp && assignment?(exp) && class_name?(to_s(exp["left"]))
76
- make_class(to_s(exp["left"]))
76
+ make_class(to_s(exp["left"]), exp["right"])
77
77
 
78
78
  # var foo = Ext.extend("Parent", {})
79
79
  elsif var && var["init"] && ext_extend?(var["init"])
@@ -81,7 +81,7 @@ module JsDuck
81
81
 
82
82
  # var Foo = ...
83
83
  elsif var && class_name?(to_s(var["id"]))
84
- make_class(to_s(var["id"]))
84
+ make_class(to_s(var["id"]), var["right"])
85
85
 
86
86
  # function Foo() {}
87
87
  elsif function?(ast) && class_name?(to_s(ast["id"]))
@@ -193,12 +193,20 @@ module JsDuck
193
193
  :name => name,
194
194
  }
195
195
 
196
- # apply information from Ext.extend or Ext.define
196
+ # apply information from Ext.extend, Ext.define, or {}
197
197
  if ast
198
198
  if ext_extend?(ast)
199
- cls[:extends] = to_s(ast["arguments"][0])
199
+ args = ast["arguments"]
200
+ cls[:extends] = to_s(args[0])
201
+ if args.length == 2 && args[1]["type"] == "ObjectExpression"
202
+ detect_class_members_from_object(cls, args[1])
203
+ end
200
204
  elsif ext_define?(ast)
201
205
  detect_ext_define(cls, ast)
206
+ elsif ast["type"] == "ObjectExpression"
207
+ detect_class_members_from_object(cls, ast)
208
+ elsif ast["type"] == "ArrayExpression"
209
+ detect_class_members_from_array(cls, ast)
202
210
  end
203
211
  end
204
212
 
@@ -217,6 +225,7 @@ module JsDuck
217
225
  cls[:aliases] = []
218
226
  cls[:members] = []
219
227
  cls[:statics] = []
228
+ cls[:code_type] = :ext_define
220
229
 
221
230
  each_pair_in_object_expression(ast["arguments"][1]) do |key, value, pair|
222
231
  case key
@@ -247,17 +256,38 @@ module JsDuck
247
256
  when "inheritableStatics"
248
257
  cls[:statics] += make_statics(value, {:inheritable => true})
249
258
  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
259
+ detect_method_or_property(cls, key, value, pair)
257
260
  end
258
261
  end
259
262
  end
260
263
 
264
+ # Detects class members from object literal
265
+ def detect_class_members_from_object(cls, ast)
266
+ cls[:members] = []
267
+ each_pair_in_object_expression(ast) do |key, value, pair|
268
+ detect_method_or_property(cls, key, value, pair)
269
+ end
270
+ end
271
+
272
+ # Detects class members from array literal
273
+ def detect_class_members_from_array(cls, ast)
274
+ cls[:members] = []
275
+ ast["elements"].each do |el|
276
+ detect_method_or_property(cls, key_value(el), el, el)
277
+ end
278
+ end
279
+
280
+ # Detects item in object literal either as method or property
281
+ def detect_method_or_property(cls, key, value, pair)
282
+ if function?(value)
283
+ m = make_method(key, value)
284
+ cls[:members] << m if apply_autodetected(m, pair)
285
+ else
286
+ p = make_property(key, value)
287
+ cls[:members] << p if apply_autodetected(p, pair)
288
+ end
289
+ end
290
+
261
291
  def make_extends(cfg_value)
262
292
  return nil unless cfg_value
263
293
 
@@ -325,8 +355,8 @@ module JsDuck
325
355
  # returns false.
326
356
  #
327
357
  # Otherwise detects the line number of member and returns true.
328
- def apply_autodetected(m, pair, inheritable=true)
329
- docset = find_docset(pair)
358
+ def apply_autodetected(m, ast, inheritable=true)
359
+ docset = find_docset(ast)
330
360
 
331
361
  if !docset || docset[:type] != :doc_comment
332
362
  if inheritable
@@ -344,7 +374,7 @@ module JsDuck
344
374
  # Get line number from third place at range array.
345
375
  # This third item exists in forked EsprimaJS at
346
376
  # https://github.com/nene/esprima/tree/linenr-in-range
347
- m[:linenr] = pair["range"][2]
377
+ m[:linenr] = ast["range"][2]
348
378
  return true
349
379
  end
350
380
  end
data/lib/jsduck/class.rb CHANGED
@@ -239,7 +239,11 @@ module JsDuck
239
239
  #
240
240
  # Optionally one can also specify type name to differenciate
241
241
  # between different types of members.
242
- def get_members(name, type_name=nil, static=false)
242
+ #
243
+ # Finally static flag can be specified. True to look only at
244
+ # static members, false to look at instance members, and nil to
245
+ # look at both.
246
+ def get_members(name, type_name=nil, static=nil)
243
247
  # build hash of all members
244
248
  unless @members_map
245
249
  @members_map = {}
@@ -254,7 +258,9 @@ module JsDuck
254
258
 
255
259
  ms = @members_map[name] || []
256
260
  ms = ms.find_all {|m| m[:tagname] == type_name } if type_name
257
- ms = ms.find_all {|m| m[:meta][:static] } if static
261
+ # static = true | false | nil
262
+ ms = ms.find_all {|m| m[:meta][:static] } if static == true
263
+ ms = ms.find_all {|m| !m[:meta][:static] } if static == false
258
264
  return ms
259
265
  end
260
266
 
@@ -10,7 +10,6 @@ module JsDuck
10
10
  def initialize(exporter_class, relations, opts)
11
11
  @relations = relations
12
12
  @exporter = exporter_class.new(relations, opts)
13
- @parallel = ParallelWrap.new(:in_processes => opts.processes)
14
13
  end
15
14
 
16
15
  # Writes class data into given directory or STDOUT when dir == :stdout.
@@ -24,13 +23,13 @@ module JsDuck
24
23
  private
25
24
 
26
25
  def write_stdout
27
- json = @parallel.map(@relations.classes) {|cls| @exporter.export(cls) }.compact
26
+ json = ParallelWrap.map(@relations.classes) {|cls| @exporter.export(cls) }.compact
28
27
  Stdout.instance.add(json)
29
28
  end
30
29
 
31
30
  def write_dir(dir, extension)
32
31
  FileUtils.mkdir(dir)
33
- @parallel.each(@relations.classes) do |cls|
32
+ ParallelWrap.each(@relations.classes) do |cls|
34
33
  filename = dir + "/" + cls[:name] + extension
35
34
  Logger.instance.log("Writing docs", filename)
36
35
  json = @exporter.export(cls)
@@ -51,6 +51,7 @@ module JsDuck
51
51
  :singleton => !!doc_map[:singleton],
52
52
  :requires => detect_list(:requires, doc_map),
53
53
  :uses => detect_list(:uses, doc_map),
54
+ :enum => detect_enum(doc_map),
54
55
  }, doc_map)
55
56
  end
56
57
 
@@ -276,6 +277,15 @@ module JsDuck
276
277
  end
277
278
  end
278
279
 
280
+ def detect_enum(doc_map)
281
+ return nil unless extract(doc_map, :class, :enum)
282
+
283
+ return {
284
+ :type => extract(doc_map, :class, :type),
285
+ :default => extract(doc_map, :class, :default),
286
+ }
287
+ end
288
+
279
289
  # Combines :doc-s of most tags
280
290
  # Ignores tags that have doc comment themselves and subproperty tags
281
291
  def detect_doc(docs)
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require 'rubygems'
3
- require 'rdiscount'
4
3
  require 'strscan'
4
+ require 'jsduck/markdown'
5
5
  require 'jsduck/logger'
6
6
  require 'jsduck/inline_img'
7
7
  require 'jsduck/inline_video'
@@ -133,12 +133,12 @@ module JsDuck
133
133
  text = $2
134
134
  if target =~ /^(.*)#(static-)?(?:(cfg|property|method|event|css_var|css_mixin)-)?(.*)$/
135
135
  cls = $1.empty? ? @class_context : $1
136
- static = !!$2
136
+ static = $2 ? true : nil
137
137
  type = $3 ? $3.intern : nil
138
138
  member = $4
139
139
  else
140
140
  cls = target
141
- static = false
141
+ static = nil
142
142
  type = false
143
143
  member = false
144
144
  end
@@ -257,7 +257,7 @@ module JsDuck
257
257
  end
258
258
 
259
259
  # applies the link template
260
- def link(cls, member, anchor_text, type=nil, static=false)
260
+ def link(cls, member, anchor_text, type=nil, static=nil)
261
261
  # Use the canonical class name for link (not some alternateClassName)
262
262
  cls = @relations[cls].full_name
263
263
  # prepend type name to member name
@@ -281,7 +281,7 @@ module JsDuck
281
281
  end
282
282
  end
283
283
 
284
- def get_matching_member(cls, member, type=nil, static=false)
284
+ def get_matching_member(cls, member, type=nil, static=nil)
285
285
  ms = get_members(cls, member, type, static).find_all {|m| !m[:private] }
286
286
  if ms.length > 1
287
287
  instance_ms = ms.find_all {|m| !m[:meta][:static] }
@@ -291,7 +291,7 @@ module JsDuck
291
291
  end
292
292
  end
293
293
 
294
- def get_members(cls, member, type=nil, static=false)
294
+ def get_members(cls, member, type=nil, static=nil)
295
295
  @relations[cls] ? @relations[cls].get_members(member, type, static) : []
296
296
  end
297
297
 
@@ -310,7 +310,7 @@ module JsDuck
310
310
  # code-blocks beginning with empty line.
311
311
  input.gsub!(/<pre>(<code>)?\n?/, "<pre>\\1")
312
312
 
313
- replace(RDiscount.new(input).to_html)
313
+ replace(JsDuck::Markdown.to_html(input))
314
314
  end
315
315
 
316
316
  # Shortens text
@@ -130,6 +130,8 @@ module JsDuck
130
130
  at_var
131
131
  elsif look(/@throws\b/)
132
132
  at_throws
133
+ elsif look(/@enum\b/)
134
+ at_enum
133
135
  elsif look(/@inheritable\b/)
134
136
  boolean_at_tag(/@inheritable/, :inheritable)
135
137
  elsif look(/@accessor\b/)
@@ -282,6 +284,16 @@ module JsDuck
282
284
  skip_white
283
285
  end
284
286
 
287
+ # matches @enum {type} name ...
288
+ def at_enum
289
+ match(/@enum/)
290
+ add_tag(:class)
291
+ @current_tag[:enum] = true
292
+ maybe_type
293
+ maybe_name_with_default
294
+ skip_white
295
+ end
296
+
285
297
  # matches @type {type} or @type type
286
298
  #
287
299
  # The presence of @type implies that we are dealing with property.
@@ -0,0 +1,73 @@
1
+ module JsDuck
2
+
3
+ class Enum
4
+ def initialize(classes)
5
+ @classes = classes
6
+ end
7
+
8
+ # Applies additional processing to all enum-classes.
9
+ def process_all!
10
+ @classes.each_value do |cls|
11
+ if cls[:enum]
12
+ process(cls)
13
+ end
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ # processes single class
20
+ def process(cls)
21
+ expand_default(cls)
22
+ strip_inheritdoc(cls)
23
+ cls[:enum][:type] = infer_type(cls) unless cls[:enum][:type]
24
+ end
25
+
26
+ # Given an enum class, returns the type infered from its values.
27
+ def infer_type(cls)
28
+ if cls[:members][:property].length > 0
29
+ types = cls[:members][:property].map {|p| p[:type] }
30
+ types.sort.uniq.join("/")
31
+ else
32
+ "Object"
33
+ end
34
+ end
35
+
36
+ # Expands default value like widget.* into list of properties
37
+ def expand_default(cls)
38
+ if cls[:enum][:default] =~ /\A(.*)\.\*\Z/
39
+ each_alias($1) do |name, owner|
40
+ cls[:members][:property] << {
41
+ :tagname => :property,
42
+ :id => 'property-' + name,
43
+ :name => name,
44
+ :default => "'" + name + "'",
45
+ :type => "String",
46
+ :meta => {:private => owner[:private]},
47
+ :private => owner[:private],
48
+ :files => cls[:files],
49
+ :owner => cls[:name],
50
+ :doc => "Alias for {@link #{owner[:name]}}.",
51
+ }
52
+ end
53
+ end
54
+ end
55
+
56
+ def each_alias(prefix)
57
+ @classes.each_value do |cls|
58
+ if cls[:aliases] && cls[:aliases][prefix]
59
+ cls[:aliases][prefix].each {|name| yield(name, cls) }
60
+ end
61
+ end
62
+ end
63
+
64
+ # Remove the auto-inserted inheritdoc tag so the auto-detected enum
65
+ # values default to being public.
66
+ def strip_inheritdoc(cls)
67
+ cls[:members][:property].each do |p|
68
+ p[:inheritdoc] = nil if p[:autodetected]
69
+ end
70
+ end
71
+ end
72
+
73
+ end
@@ -10,7 +10,6 @@ module JsDuck
10
10
  def initialize(exporter_class, guides, opts)
11
11
  @guides = guides
12
12
  @exporter = exporter_class.new(guides, opts)
13
- @parallel = ParallelWrap.new(:in_processes => opts.processes)
14
13
  end
15
14
 
16
15
  # Writes guide data into given directory or STDOUT when dir == :stdout.
@@ -24,13 +23,13 @@ module JsDuck
24
23
  private
25
24
 
26
25
  def write_stdout
27
- json = @parallel.map(all_guides) {|guide| @exporter.export_guide(guide) }.compact
26
+ json = ParallelWrap.map(all_guides) {|guide| @exporter.export_guide(guide) }.compact
28
27
  Stdout.instance.add(json)
29
28
  end
30
29
 
31
30
  def write_dir(dir, extension)
32
31
  FileUtils.mkdir(dir) unless File.exists?(dir)
33
- @parallel.each(all_guides) do |guide|
32
+ ParallelWrap.each(all_guides) do |guide|
34
33
  filename = dir + "/" + guide["name"] + extension
35
34
  Logger.instance.log("Writing guide", filename)
36
35
  json = @exporter.export_guide(guide)
data/lib/jsduck/guides.rb CHANGED
@@ -36,15 +36,13 @@ module JsDuck
36
36
  end
37
37
 
38
38
  # Modified each_item that also loads HTML for each guide
39
- def each_item(&block)
40
- unless @loaded
41
- super do |guide|
42
- guide[:html] = load_guide(guide)
43
- end
44
- @loaded = true
39
+ def each_item
40
+ super do |guide|
41
+ # Load the guide if not loaded
42
+ guide[:html] = load_guide(guide) if guide[:html] == nil
43
+ # Pass guide to block if it was successfully loaded.
44
+ yield guide if guide[:html]
45
45
  end
46
-
47
- super(&block)
48
46
  end
49
47
 
50
48
  # Modified to_array that excludes the :html from guide nodes
@@ -60,12 +58,13 @@ module JsDuck
60
58
  def load_guide(guide)
61
59
  in_dir = @path + "/guides/" + guide["name"]
62
60
 
63
- begin
64
- return Logger.instance.warn(:guide, "Guide #{in_dir} not found") unless File.exists?(in_dir)
61
+ return Logger.instance.warn(:guide, "Guide #{in_dir} not found") unless File.exists?(in_dir)
65
62
 
66
- guide_file = in_dir + "/README.md"
67
- return Logger.instance.warn(:guide, "README.md not found in #{in_dir}") unless File.exists?(guide_file)
63
+ guide_file = in_dir + "/README.md"
68
64
 
65
+ return Logger.instance.warn(:guide, "README.md not found in #{in_dir}") unless File.exists?(guide_file)
66
+
67
+ begin
69
68
  @formatter.doc_context = {:filename => guide_file, :linenr => 0}
70
69
  name = File.basename(in_dir)
71
70
  @formatter.img_path = "guides/#{name}"
@@ -0,0 +1,102 @@
1
+ require 'jsduck/json_duck'
2
+ require 'jsduck/null_object'
3
+ require 'jsduck/logger'
4
+ require 'jsduck/parallel_wrap'
5
+
6
+ module JsDuck
7
+
8
+ # Reads in JSDuck exports of different versions of docs.
9
+ module Importer
10
+ module_function
11
+
12
+ # Loads in exported docs and generates @since and @new tags based on that data.
13
+ def import(imports, relations)
14
+ if imports.length > 0
15
+ generate_since_tags(read_all(imports), relations)
16
+ end
17
+ end
18
+
19
+ # Reads in data for all versions, returning array of
20
+ # version/class-data pairs. We don't use a hash to preserve the
21
+ # order of versions (from oldest to newest).
22
+ def read_all(imports)
23
+ imports.map do |ver|
24
+ {
25
+ :version => ver[:version],
26
+ :classes => ver[:path] ? read(ver) : current_version,
27
+ }
28
+ end
29
+ end
30
+
31
+ def current_version
32
+ NullObject.new(:[] => NullObject.new(:[] => true))
33
+ end
34
+
35
+ # Reads in data from all .json files in directory
36
+ def read(ver)
37
+ # Map list of files into pairs of (classname, members-hash)
38
+ pairs = ParallelWrap.map(Dir[ver[:path] + "/*.json"]) do |filename|
39
+ JsDuck::Logger.instance.log("Importing #{ver[:version]}", filename)
40
+ json = JsonDuck.read(filename)
41
+ [json["name"], members_id_index(json)]
42
+ end
43
+
44
+ # Turn key-value pairs array into hash
45
+ return Hash[ pairs ]
46
+ end
47
+
48
+ # creates index of all class members
49
+ def members_id_index(json)
50
+ index = {}
51
+ ["members", "statics"].each do |group_name|
52
+ json[group_name].each_pair do |tagname, members|
53
+ members.each do |m|
54
+ index[m["id"]] = true
55
+ end
56
+ end
57
+ end
58
+ index
59
+ end
60
+
61
+ # Using the imported versions data, adds @since tags to all
62
+ # classes/members.
63
+ def generate_since_tags(versions, relations)
64
+ last_version = versions.last[:version]
65
+
66
+ relations.each do |cls|
67
+ v = cls[:meta][:since] || class_since(versions, cls)
68
+ cls[:meta][:since] = v
69
+ cls[:meta][:new] = true if v == last_version
70
+
71
+ cls.all_local_members.each do |m|
72
+ v = m[:meta][:since] || member_since(versions, cls, m)
73
+ m[:meta][:since] = v
74
+ m[:meta][:new] = true if v == last_version
75
+ end
76
+ end
77
+ end
78
+
79
+ def member_since(versions, cls, m)
80
+ versions.each do |ver|
81
+ c = ver[:classes][cls[:name]]
82
+ return ver[:version] if c && c[m[:id]]
83
+ cls[:alternateClassNames].each do |name|
84
+ c = ver[:classes][name]
85
+ return ver[:version] if c && c[m[:id]]
86
+ end
87
+ end
88
+ end
89
+
90
+ # Returns name of the version since which the class is available
91
+ def class_since(versions, cls)
92
+ versions.each do |ver|
93
+ return ver[:version] if ver[:classes][cls[:name]]
94
+ cls[:alternateClassNames].each do |name|
95
+ return ver[:version] if ver[:classes][name]
96
+ end
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ end