jsduck 4.0.1 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
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