jsduck 4.0.1 → 4.1.1

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.
Files changed (91) hide show
  1. data/Rakefile +14 -0
  2. data/esprima/esprima.js +210 -85
  3. data/jsduck.gemspec +3 -3
  4. data/lib/jsduck/accessors.rb +10 -8
  5. data/lib/jsduck/aggregator.rb +7 -7
  6. data/lib/jsduck/app.rb +24 -164
  7. data/lib/jsduck/app_data.rb +2 -4
  8. data/lib/jsduck/assets.rb +5 -2
  9. data/lib/jsduck/ast.rb +9 -4
  10. data/lib/jsduck/batch_formatter.rb +54 -0
  11. data/lib/jsduck/batch_parser.rb +106 -0
  12. data/lib/jsduck/categories.rb +5 -6
  13. data/lib/jsduck/class.rb +77 -239
  14. data/lib/jsduck/class_doc_expander.rb +0 -5
  15. data/lib/jsduck/class_formatter.rb +14 -10
  16. data/lib/jsduck/class_name.rb +23 -0
  17. data/lib/jsduck/class_writer.rb +9 -8
  18. data/lib/jsduck/doc_ast.rb +5 -6
  19. data/lib/jsduck/doc_formatter.rb +61 -272
  20. data/lib/jsduck/enum.rb +4 -4
  21. data/lib/jsduck/esprima.rb +10 -4
  22. data/lib/jsduck/examples.rb +5 -5
  23. data/lib/jsduck/export_writer.rb +62 -0
  24. data/lib/jsduck/exporter/app.rb +62 -0
  25. data/lib/jsduck/exporter/examples.rb +58 -0
  26. data/lib/jsduck/exporter/full.rb +60 -0
  27. data/lib/jsduck/file_categories.rb +7 -4
  28. data/lib/jsduck/function_ast.rb +99 -0
  29. data/lib/jsduck/grouped_asset.rb +27 -16
  30. data/lib/jsduck/guide_writer.rb +8 -7
  31. data/lib/jsduck/guides.rb +31 -29
  32. data/lib/jsduck/icons.rb +12 -1
  33. data/lib/jsduck/images.rb +3 -3
  34. data/lib/jsduck/importer.rb +7 -7
  35. data/lib/jsduck/index_html.rb +20 -6
  36. data/lib/jsduck/inherit_doc.rb +9 -12
  37. data/lib/jsduck/inline/example.rb +42 -0
  38. data/lib/jsduck/inline/img.rb +55 -0
  39. data/lib/jsduck/inline/link.rb +227 -0
  40. data/lib/jsduck/inline/video.rb +67 -0
  41. data/lib/jsduck/inline_examples.rb +7 -7
  42. data/lib/jsduck/js_parser.rb +5 -4
  43. data/lib/jsduck/lint.rb +14 -2
  44. data/lib/jsduck/logger.rb +5 -6
  45. data/lib/jsduck/members_index.rb +132 -0
  46. data/lib/jsduck/merger.rb +3 -9
  47. data/lib/jsduck/options.rb +39 -41
  48. data/lib/jsduck/override.rb +3 -7
  49. data/lib/jsduck/relations.rb +9 -9
  50. data/lib/jsduck/renderer.rb +3 -3
  51. data/lib/jsduck/return_values.rb +72 -0
  52. data/lib/jsduck/search_data.rb +16 -20
  53. data/lib/jsduck/shortener.rb +58 -0
  54. data/lib/jsduck/signature_renderer.rb +1 -2
  55. data/lib/jsduck/source/file.rb +98 -0
  56. data/lib/jsduck/source/file_parser.rb +72 -0
  57. data/lib/jsduck/source/writer.rb +89 -0
  58. data/lib/jsduck/tag/aside.rb +1 -1
  59. data/lib/jsduck/tag/since.rb +1 -1
  60. data/lib/jsduck/template_dir.rb +2 -2
  61. data/lib/jsduck/util/html.rb +27 -0
  62. data/lib/jsduck/util/io.rb +32 -0
  63. data/lib/jsduck/util/json.rb +60 -0
  64. data/lib/jsduck/util/null_object.rb +23 -0
  65. data/lib/jsduck/util/os.rb +14 -0
  66. data/lib/jsduck/util/parallel.rb +34 -0
  67. data/lib/jsduck/util/singleton.rb +35 -0
  68. data/lib/jsduck/util/stdout.rb +33 -0
  69. data/lib/jsduck/videos.rb +5 -5
  70. data/lib/jsduck/web_writer.rb +79 -0
  71. data/lib/jsduck/welcome.rb +6 -6
  72. data/spec/class_factory.rb +20 -0
  73. metadata +32 -20
  74. data/lib/jsduck/api_exporter.rb +0 -48
  75. data/lib/jsduck/app_exporter.rb +0 -60
  76. data/lib/jsduck/examples_exporter.rb +0 -56
  77. data/lib/jsduck/full_exporter.rb +0 -35
  78. data/lib/jsduck/html.rb +0 -25
  79. data/lib/jsduck/inline_img.rb +0 -53
  80. data/lib/jsduck/inline_video.rb +0 -58
  81. data/lib/jsduck/io.rb +0 -30
  82. data/lib/jsduck/json_duck.rb +0 -52
  83. data/lib/jsduck/lexer.rb +0 -251
  84. data/lib/jsduck/null_object.rb +0 -19
  85. data/lib/jsduck/os.rb +0 -11
  86. data/lib/jsduck/parallel_wrap.rb +0 -32
  87. data/lib/jsduck/source_file.rb +0 -97
  88. data/lib/jsduck/source_file_parser.rb +0 -70
  89. data/lib/jsduck/source_writer.rb +0 -87
  90. data/lib/jsduck/stats.rb +0 -103
  91. data/lib/jsduck/stdout.rb +0 -31
@@ -25,8 +25,8 @@ module JsDuck
25
25
 
26
26
  # Given an enum class, returns the type infered from its values.
27
27
  def infer_type(cls)
28
- if cls[:members][:property].length > 0
29
- types = cls[:members][:property].map {|p| p[:type] }
28
+ if cls[:members].length > 0
29
+ types = cls[:members].map {|p| p[:type] }
30
30
  types.sort.uniq.join("/")
31
31
  else
32
32
  "Object"
@@ -37,7 +37,7 @@ module JsDuck
37
37
  def expand_default(cls)
38
38
  if cls[:enum][:default] =~ /\A(.*)\.\*\Z/
39
39
  each_alias($1) do |name, owner|
40
- cls[:members][:property] << {
40
+ cls[:members] << {
41
41
  :tagname => :property,
42
42
  :id => 'property-' + name,
43
43
  :name => name,
@@ -64,7 +64,7 @@ module JsDuck
64
64
  # Remove the auto-inserted inheritdoc tag so the auto-detected enum
65
65
  # values default to being public.
66
66
  def strip_inheritdoc(cls)
67
- cls[:members][:property].each do |p|
67
+ cls[:members].each do |p|
68
68
  p[:inheritdoc] = nil if p[:autodetected]
69
69
  end
70
70
  end
@@ -1,6 +1,6 @@
1
1
  require 'v8'
2
- require 'json'
3
- require 'singleton'
2
+ require 'jsduck/util/json'
3
+ require 'jsduck/util/singleton'
4
4
 
5
5
  module JsDuck
6
6
 
@@ -9,12 +9,18 @@ module JsDuck
9
9
  # Initialized as singleton to avoid loading the esprima.js more
10
10
  # than once - otherwise performace will severely suffer.
11
11
  class Esprima
12
- include Singleton
12
+ include Util::Singleton
13
13
 
14
14
  def initialize
15
15
  @v8 = V8::Context.new
16
16
  esprima = File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))+"/esprima/esprima.js";
17
+
18
+ # Esprima attempts to assign to window.esprima, but our v8
19
+ # engine has no global window variable defined. So define our
20
+ # own and then grab esprima out from it again.
21
+ @v8.eval("var window = {};")
17
22
  @v8.load(esprima)
23
+ @v8.eval("var esprima = window.esprima;")
18
24
  end
19
25
 
20
26
  # Parses JavaScript source code using Esprima.js
@@ -23,7 +29,7 @@ module JsDuck
23
29
  def parse(input)
24
30
  @v8['js'] = input
25
31
  json = @v8.eval("JSON.stringify(esprima.parse(js, {comment: true, range: true, raw: true}))")
26
- return JSON.parse(json, :max_nesting => false)
32
+ return Util::Json.parse(json, :max_nesting => false)
27
33
  end
28
34
 
29
35
  end
@@ -1,5 +1,5 @@
1
- require 'jsduck/json_duck'
2
- require 'jsduck/null_object'
1
+ require 'jsduck/util/json'
2
+ require 'jsduck/util/null_object'
3
3
  require 'jsduck/grouped_asset'
4
4
 
5
5
  module JsDuck
@@ -11,16 +11,16 @@ module JsDuck
11
11
  if filename
12
12
  Examples.new(filename, opts)
13
13
  else
14
- NullObject.new(:to_array => [], :[] => nil)
14
+ Util::NullObject.new(:to_array => [], :[] => nil)
15
15
  end
16
16
  end
17
17
 
18
18
  # Parses examples config file
19
19
  def initialize(filename, opts)
20
- @groups = JsonDuck.read(filename)
20
+ @groups = Util::Json.read(filename)
21
21
  @opts = opts
22
22
  fix_examples_data
23
- build_map_by_name("Two examples have the same name", filename)
23
+ build_map_by_name
24
24
  end
25
25
 
26
26
  # Prefix all relative URL-s in examples list with path given in --examples-base-url
@@ -0,0 +1,62 @@
1
+ require 'jsduck/util/stdout'
2
+ require 'jsduck/exporter/full'
3
+ require 'jsduck/exporter/examples'
4
+ require 'jsduck/batch_formatter'
5
+ require 'jsduck/class_writer'
6
+ require 'jsduck/guide_writer'
7
+ require 'fileutils'
8
+
9
+ module JsDuck
10
+
11
+ # Performs the export in one of the export formats.
12
+ class ExportWriter
13
+ def initialize(relations, assets, opts)
14
+ @relations = relations
15
+ @assets = assets
16
+ @opts = opts
17
+ end
18
+
19
+ def write
20
+ format_classes
21
+
22
+ clean_output_dir unless @opts.output_dir == :stdout
23
+
24
+ export_classes
25
+ export_examples_in_guides if @opts.export == :examples
26
+
27
+ Util::Stdout.flush if @opts.output_dir == :stdout
28
+ end
29
+
30
+ private
31
+
32
+ def export_classes
33
+ cw = ClassWriter.new(get_exporter, @relations, @opts)
34
+ cw.write(@opts.output_dir, ".json")
35
+ end
36
+
37
+ def get_exporter
38
+ exporters = {
39
+ :full => Exporter::Full,
40
+ :examples => Exporter::Examples,
41
+ }
42
+ exporters[@opts.export]
43
+ end
44
+
45
+ def export_examples_in_guides
46
+ gw = GuideWriter.new(Exporter::Examples, @assets.guides, @opts)
47
+ gw.write(@opts.output_dir, ".json")
48
+ end
49
+
50
+ # -- util routines --
51
+
52
+ def clean_output_dir
53
+ FileUtils.rm_rf(@opts.output_dir)
54
+ end
55
+
56
+ def format_classes
57
+ BatchFormatter.format_all!(@relations, @assets, @opts)
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,62 @@
1
+ require 'jsduck/renderer'
2
+ require 'jsduck/doc_formatter'
3
+ require 'jsduck/exporter/full'
4
+
5
+ module JsDuck
6
+ module Exporter
7
+
8
+ # Exports data for Docs app.
9
+ class App < Full
10
+ def initialize(relations, opts)
11
+ super(relations, opts)
12
+ @renderer = Renderer.new(opts)
13
+ end
14
+
15
+ # Returns compacted class data hash which contains an additional
16
+ # :html field with full HTML to show on class overview page.
17
+ def export(cls)
18
+ data = super(cls)
19
+ data[:html] = @renderer.render(data)
20
+ return compact(data)
21
+ end
22
+
23
+ private
24
+
25
+ # removes extra data from export
26
+ def compact(cls)
27
+ cls.delete(:doc)
28
+ cls[:members] = compact_members_group(cls[:members])
29
+ cls[:statics] = compact_members_group(cls[:statics])
30
+ cls[:files] = compact_files(cls[:files])
31
+ cls
32
+ end
33
+
34
+ def compact_members_group(group)
35
+ c_group = {}
36
+ group.each_pair do |type, members|
37
+ c_group[type] = members.map {|m| compact_member(m) }
38
+ end
39
+ c_group
40
+ end
41
+
42
+ def compact_member(m)
43
+ m_copy = {}
44
+ [:name, :tagname, :owner, :meta, :id].each do |key|
45
+ m_copy[key] = m[key]
46
+ end
47
+ m_copy
48
+ end
49
+
50
+ # Remove full path from filename for privacy considerations as the
51
+ # path can reveal information about the system where JSDuck was
52
+ # run. The docs app doesn't need to have this information.
53
+ def compact_files(files)
54
+ files.map do |f|
55
+ {:filename => File.basename(f[:filename]), :href => f[:href]}
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,58 @@
1
+ require 'jsduck/inline_examples'
2
+
3
+ module JsDuck
4
+ module Exporter
5
+
6
+ # Exporter for inline examples.
7
+ #
8
+ # It produces the following structure:
9
+ #
10
+ # {
11
+ # :type => :class, # can also be :guide
12
+ # :name => "Panel",
13
+ # :examples => [
14
+ # {:code => "bla bla", :options => {}},
15
+ # {:code => "bla bla", :options => {"raw" => true}},
16
+ # ...
17
+ # ]
18
+ # }
19
+ #
20
+ class Examples
21
+ def initialize(relations, opts)
22
+ # All params ignored, they're present to be compatible with
23
+ # other exporters.
24
+ @inline_examples = InlineExamples.new
25
+ end
26
+
27
+ # Returns hash of class name and inline examples
28
+ def export(cls)
29
+ examples = @inline_examples.extract(cls[:doc])
30
+ if examples.length > 0
31
+ {
32
+ :type => :class,
33
+ :name => cls[:name],
34
+ :examples => examples,
35
+ }
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ # Returns hash of guide name and inline examples
42
+ def export_guide(guide)
43
+ examples = @inline_examples.extract(guide[:html] || "")
44
+ if examples.length > 0
45
+ {
46
+ :type => :guide,
47
+ :name => guide["name"],
48
+ :examples => examples,
49
+ }
50
+ else
51
+ nil
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,60 @@
1
+ require 'jsduck/class'
2
+
3
+ module JsDuck
4
+ module Exporter
5
+
6
+ # Exporter for all the class docs.
7
+ class Full
8
+ def initialize(relations, opts={})
9
+ @relations = relations
10
+ # opts parameter is here just for compatibility with other exporters
11
+ end
12
+
13
+ # Returns all data in Class object as hash.
14
+ def export(cls)
15
+ # Make copy of the internal data structure of a class
16
+ # so our modifications on it will be safe.
17
+ h = cls.internal_doc.clone
18
+
19
+ h[:members] = {}
20
+ h[:statics] = {}
21
+ Class.each_member_type do |tagname|
22
+ h[:members][tagname] = export_members(cls, {:tagname => tagname, :static => false})
23
+ h[:statics][tagname] = export_members(cls, {:tagname => tagname, :static => true})
24
+ end
25
+ h[:component] = cls.inherits_from?("Ext.Component")
26
+ h[:superclasses] = cls.superclasses.collect {|c| c[:name] }
27
+ h[:subclasses] = @relations.subclasses(cls).collect {|c| c[:name] }
28
+ h[:mixedInto] = @relations.mixed_into(cls).collect {|c| c[:name] }
29
+
30
+ h[:mixins] = cls.deps(:mixins).collect {|c| c[:name] }
31
+ h[:parentMixins] = cls.parent_deps(:mixins).collect {|c| c[:name] }
32
+ h[:requires] = cls.deps(:requires).collect {|c| c[:name] }
33
+ h[:uses] = cls.deps(:uses).collect {|c| c[:name] }
34
+
35
+ h
36
+ end
37
+
38
+ private
39
+
40
+ # Looks up members, and sorts them so that constructor method is first
41
+ def export_members(cls, cfg)
42
+ ms = cls.find_members(cfg)
43
+ ms.sort! {|a,b| a[:name] <=> b[:name] }
44
+ cfg[:tagname] == :method ? constructor_first(ms) : ms
45
+ end
46
+
47
+ # If methods list contains constructor, move it into the beginning.
48
+ def constructor_first(ms)
49
+ constr = ms.find {|m| m[:name] == "constructor" }
50
+ if constr
51
+ ms.delete(constr)
52
+ ms.unshift(constr)
53
+ end
54
+ ms
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -1,3 +1,6 @@
1
+ require 'jsduck/logger'
2
+ require 'jsduck/util/json'
3
+
1
4
  module JsDuck
2
5
 
3
6
  # Reads categories info from config file
@@ -9,11 +12,11 @@ module JsDuck
9
12
 
10
13
  # Parses categories in JSON file
11
14
  def generate
12
- @categories = JsonDuck.read(@filename)
15
+ @categories = Util::Json.read(@filename)
13
16
 
14
17
  # Don't crash if old syntax is used.
15
18
  if @categories.is_a?(Hash) && @categories["categories"]
16
- Logger.instance.warn(nil, 'Update categories file to contain just the array inside {"categories": [...]}', @filename)
19
+ Logger.warn(nil, 'Update categories file to contain just the array inside {"categories": [...]}', @filename)
17
20
  @categories = @categories["categories"]
18
21
  end
19
22
 
@@ -36,7 +39,7 @@ module JsDuck
36
39
  re = Regexp.new("^" + name.split(/\*/, -1).map {|part| Regexp.escape(part) }.join('.*') + "$")
37
40
  classes = @relations.to_a.find_all {|cls| re =~ cls[:name] && !cls[:private] }.map {|cls| cls[:name] }.sort
38
41
  if classes.length == 0
39
- Logger.instance.warn(:cat_no_match, "No class found matching a pattern '#{name} in categories file'", @filename)
42
+ Logger.warn(:cat_no_match, "No class found matching a pattern '#{name} in categories file'", @filename)
40
43
  end
41
44
  classes
42
45
  end
@@ -56,7 +59,7 @@ module JsDuck
56
59
  # Check that each existing non-private class is listed
57
60
  @relations.each do |cls|
58
61
  unless listed_classes[cls[:name]] || cls[:private]
59
- Logger.instance.warn(:cat_class_missing, "Class '#{cls[:name]}' not found in categories file", @filename)
62
+ Logger.warn(:cat_class_missing, "Class '#{cls[:name]}' not found in categories file", @filename)
60
63
  end
61
64
  end
62
65
  end
@@ -0,0 +1,99 @@
1
+ require "jsduck/util/singleton"
2
+ require "jsduck/serializer"
3
+ require "jsduck/evaluator"
4
+
5
+ module JsDuck
6
+
7
+ # Analyzes the AST of a FunctionDeclaration or FunctionExpression.
8
+ class FunctionAst
9
+ include Util::Singleton
10
+
11
+ # True when function always finishes by returning this. False
12
+ # doesn't neccessarily mean that the function doesn't return this
13
+ # - rather it means our static analyzes wasn't able to determine
14
+ # what the function returns.
15
+ def chainable?(ast)
16
+ if ast && function?(ast)
17
+ body_returns(ast["body"]["body"])
18
+ else
19
+ false
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def function?(ast)
26
+ ast["type"] == "FunctionDeclaration" || ast["type"] == "FunctionExpression"
27
+ end
28
+
29
+ def body_returns(body)
30
+ body = skip_returnless_statements(body)
31
+
32
+ return body.length > 0 && return_this?(body[0])
33
+ end
34
+
35
+ def return_this?(ast)
36
+ return?(ast) && !!ast["argument"] && this?(ast["argument"])
37
+ end
38
+
39
+ def return?(ast)
40
+ ast["type"] == "ReturnStatement"
41
+ end
42
+
43
+ def this?(ast)
44
+ ast["type"] == "ThisExpression"
45
+ end
46
+
47
+ def skip_returnless_statements(statements)
48
+ i = statements.find_index {|s| contains_return?(s) }
49
+ if i
50
+ statements.slice(i, statements.length)
51
+ else
52
+ []
53
+ end
54
+ end
55
+
56
+ def contains_return?(ast)
57
+ if return?(ast)
58
+ true
59
+ elsif control_flow?(ast)
60
+ extract_body(ast).any? {|s| contains_return?(s) }
61
+ else
62
+ false
63
+ end
64
+ end
65
+
66
+ def control_flow?(ast)
67
+ CONTROL_FLOW[ast["type"]]
68
+ end
69
+
70
+ def extract_body(ast)
71
+ body = []
72
+ CONTROL_FLOW[ast["type"]].each do |name|
73
+ statements = ast[name]
74
+ if statements.is_a?(Hash)
75
+ body << statements
76
+ else
77
+ body += Array(statements)
78
+ end
79
+ end
80
+ body
81
+ end
82
+
83
+ CONTROL_FLOW = {
84
+ "IfStatement" => ["consequent", "alternate"],
85
+ "SwitchStatement" => ["cases"],
86
+ "SwitchCase" => ["consequent"],
87
+ "ForStatement" => ["body"],
88
+ "ForInStatement" => ["body"],
89
+ "WhileStatement" => ["body"],
90
+ "DoWhileStatement" => ["body"],
91
+ "TryStatement" => ["block", "handlers", "finalizer"],
92
+ "CatchClause" => ["body"],
93
+ "WithStatement" => ["body"],
94
+ "LabeledStatement" => ["body"],
95
+ "BlockStatement" => ["body"],
96
+ }
97
+ end
98
+
99
+ end