jsduck 4.0.beta → 4.0.beta2

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.
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