jsduck 4.0.beta2 → 4.0.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.
@@ -949,7 +949,7 @@
949
949
  *
950
950
  * The following example displays the string "sencha":
951
951
  *
952
- * var upperText="sencha";
952
+ * var upperText="SENCHA";
953
953
  * document.write(upperText.toLocaleLowerCase());
954
954
  *
955
955
  * @return {String} Returns value of the string in lowercase.
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.required_rubygems_version = ">= 1.3.5"
3
3
 
4
4
  s.name = 'jsduck'
5
- s.version = '4.0.beta2'
6
- s.date = '2012-08-01'
5
+ s.version = '4.0.0'
6
+ s.date = '2012-08-09'
7
7
  s.summary = "Simple JavaScript Duckumentation generator"
8
8
  s.description = "Documentation generator for Sencha JS frameworks"
9
9
  s.homepage = "https://github.com/senchalabs/jsduck"
@@ -16,15 +16,13 @@ Gem::Specification.new do |s|
16
16
  end
17
17
  # Add files not in git
18
18
  s.files += Dir['template-min/**/*']
19
- # Add Esprima
20
- s.files += Dir['esprima/esprima.js']
21
19
 
22
20
  s.executables = ["jsduck"]
23
21
 
24
22
  s.add_dependency 'rdiscount'
25
23
  s.add_dependency 'json'
26
24
  s.add_dependency 'parallel'
27
- s.add_dependency 'execjs'
25
+ s.add_dependency 'therubyracer'
28
26
 
29
27
  s.add_development_dependency 'rspec'
30
28
  s.add_development_dependency 'rake'
@@ -2,6 +2,7 @@ require 'jsduck/class'
2
2
  require 'jsduck/accessors'
3
3
  require 'jsduck/logger'
4
4
  require 'jsduck/enum'
5
+ require 'jsduck/override'
5
6
 
6
7
  module JsDuck
7
8
 
@@ -206,10 +207,7 @@ module JsDuck
206
207
  end
207
208
 
208
209
  # Appends Ext4 options parameter to each event parameter list.
209
- # But only when we are dealing with Ext4 codebase.
210
210
  def append_ext4_event_options
211
- return unless ext4?
212
-
213
211
  options = {
214
212
  :tagname => :param,
215
213
  :name => "eOpts",
@@ -234,6 +232,11 @@ module JsDuck
234
232
  Enum.new(@classes).process_all!
235
233
  end
236
234
 
235
+ # Processes all overrides
236
+ def process_overrides
237
+ Override.new(@classes, @documentation).process_all!
238
+ end
239
+
237
240
  # Are we dealing with ExtJS 4?
238
241
  # True if any of the classes is defined with Ext.define()
239
242
  def ext4?
@@ -47,7 +47,7 @@ module JsDuck
47
47
  result = aggregate(parsed_files)
48
48
  @relations = filter_classes(result)
49
49
  InheritDoc.new(@relations).resolve_all
50
- Importer.import(@opts.imports, @relations)
50
+ Importer.import(@opts.imports, @relations, @opts.new_since)
51
51
  Lint.new(@relations).run
52
52
 
53
53
  # Initialize guides, videos, examples, ...
@@ -109,7 +109,7 @@ module JsDuck
109
109
  begin
110
110
  SourceFile.new(JsDuck::IO.read(fname), fname, @opts)
111
111
  rescue
112
- Logger.instance.fatal("Error while parsing #{fname}", $!)
112
+ Logger.instance.fatal_backtrace("Error while parsing #{fname}", $!)
113
113
  exit(1)
114
114
  end
115
115
  end
@@ -126,8 +126,11 @@ module JsDuck
126
126
  agr.create_global_class
127
127
  agr.remove_ignored_classes
128
128
  agr.create_accessors
129
- agr.append_ext4_event_options
129
+ if @opts.ext4_events == true || (@opts.ext4_events == nil && agr.ext4?)
130
+ agr.append_ext4_event_options
131
+ end
130
132
  agr.process_enums
133
+ agr.process_overrides
131
134
  agr.result
132
135
  end
133
136
 
@@ -166,14 +169,15 @@ module JsDuck
166
169
  class_formatter.include_types = !@opts.export
167
170
  # Format all doc-objects in parallel
168
171
  formatted_classes = ParallelWrap.map(@relations.classes) do |cls|
169
- Logger.instance.log("Markdown formatting #{cls[:name]}")
172
+ files = cls[:files].map {|f| f[:filename] }.join(" ")
173
+ Logger.instance.log("Markdown formatting #{cls[:name]}", files)
170
174
  begin
171
175
  {
172
176
  :doc => class_formatter.format(cls.internal_doc),
173
177
  :images => doc_formatter.images
174
178
  }
175
179
  rescue
176
- Logger.instance.fatal("Error while formatting #{cls[:name]}", $!)
180
+ Logger.instance.fatal_backtrace("Error while formatting #{cls[:name]} #{files}", $!)
177
181
  exit(1)
178
182
  end
179
183
  end
@@ -230,7 +230,9 @@ module JsDuck
230
230
  each_pair_in_object_expression(ast["arguments"][1]) do |key, value, pair|
231
231
  case key
232
232
  when "extend"
233
- cls[:extends] = make_extends(value)
233
+ cls[:extends] = make_string(value)
234
+ when "override"
235
+ cls[:override] = make_string(value)
234
236
  when "requires"
235
237
  cls[:requires] = make_string_list(value)
236
238
  when "uses"
@@ -288,7 +290,7 @@ module JsDuck
288
290
  end
289
291
  end
290
292
 
291
- def make_extends(cfg_value)
293
+ def make_string(cfg_value)
292
294
  return nil unless cfg_value
293
295
 
294
296
  parent = to_value(cfg_value)
@@ -147,8 +147,10 @@ module JsDuck
147
147
  # Singletons have no static members
148
148
  if @doc[:singleton] && context == :statics
149
149
  # Warn if singleton has static members
150
- if @doc[context][type].length > 0
151
- Logger.instance.warn(:sing_static, "Singleton class #{@doc[:name]} can't have static members, remove the @static tag.")
150
+ @doc[context][type].each do |m|
151
+ ctx = m[:files][0]
152
+ msg = "Singleton class #{@doc[:name]} can't have static members, remove the @static tag."
153
+ Logger.instance.warn(:sing_static, msg, ctx[:filename], ctx[:linenr])
152
154
  end
153
155
  return {}
154
156
  end
@@ -185,7 +187,8 @@ module JsDuck
185
187
  hash1.delete(name)
186
188
  else
187
189
  ctx = m[:files][0]
188
- Logger.instance.warn(:hide, "@hide used but #{m[:tagname]} #{m[:name]} not found in parent class", ctx[:filename], ctx[:linenr])
190
+ msg = "@hide used but #{m[:tagname]} #{m[:name]} not found in parent class"
191
+ Logger.instance.warn(:hide, msg, ctx[:filename], ctx[:linenr])
189
192
  end
190
193
  else
191
194
  if hash1[name]
@@ -264,6 +267,11 @@ module JsDuck
264
267
  return ms
265
268
  end
266
269
 
270
+ # Call this when renaming or moving members inside class.
271
+ def reset_members_lookup!
272
+ @members_map = nil
273
+ end
274
+
267
275
  # Returns all members of class, including the inherited and mixed in ones
268
276
  def all_members
269
277
  all = []
@@ -27,14 +27,15 @@ module JsDuck
27
27
  @docs
28
28
  end
29
29
 
30
- # <code-block> := <mixin-declaration> | <var-declaration> | <nop>
30
+ # <code-block> := <mixin-declaration> | <var-declaration> | <property>
31
31
  def code_block
32
32
  if look("@mixin")
33
33
  mixin_declaration
34
34
  elsif look(:var, ":")
35
35
  var_declaration
36
36
  else
37
- {:tagname => :nop}
37
+ # Default to property like in JsParser.
38
+ {:tagname => :property}
38
39
  end
39
40
  end
40
41
 
@@ -52,6 +52,7 @@ module JsDuck
52
52
  :requires => detect_list(:requires, doc_map),
53
53
  :uses => detect_list(:uses, doc_map),
54
54
  :enum => detect_enum(doc_map),
55
+ :override => extract(doc_map, :override, :class),
55
56
  }, doc_map)
56
57
  end
57
58
 
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require 'rubygems'
3
3
  require 'strscan'
4
- require 'jsduck/markdown'
4
+ require 'rdiscount'
5
5
  require 'jsduck/logger'
6
6
  require 'jsduck/inline_img'
7
7
  require 'jsduck/inline_video'
@@ -95,7 +95,7 @@ module JsDuck
95
95
  out += replace_link_tag(s.scan(@link_re))
96
96
  elsif substitute = @inline_img.replace(s)
97
97
  out += substitute
98
- elsif substitute = @inline_video.replace(s)
98
+ elsif substitute = @inline_video.replace(s, @doc_context)
99
99
  out += substitute
100
100
  elsif s.check(/[{]/)
101
101
  # There might still be "{" that doesn't begin {@link} or {@img} - ignore it
@@ -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(JsDuck::Markdown.to_html(input))
313
+ replace(RDiscount.new(input).to_html)
314
314
  end
315
315
 
316
316
  # Shortens text
@@ -132,6 +132,8 @@ module JsDuck
132
132
  at_throws
133
133
  elsif look(/@enum\b/)
134
134
  at_enum
135
+ elsif look(/@override\b/)
136
+ at_override
135
137
  elsif look(/@inheritable\b/)
136
138
  boolean_at_tag(/@inheritable/, :inheritable)
137
139
  elsif look(/@accessor\b/)
@@ -294,6 +296,14 @@ module JsDuck
294
296
  skip_white
295
297
  end
296
298
 
299
+ # matches @override name ...
300
+ def at_override
301
+ match(/@override/)
302
+ add_tag(:override)
303
+ maybe_ident_chain(:class)
304
+ skip_white
305
+ end
306
+
297
307
  # matches @type {type} or @type type
298
308
  #
299
309
  # The presence of @type implies that we are dealing with property.
@@ -1,12 +1,10 @@
1
- require 'execjs'
1
+ require 'v8'
2
2
  require 'json'
3
3
  require 'singleton'
4
4
 
5
5
  module JsDuck
6
6
 
7
- # Runs Esprima.js through execjs (this will select any available
8
- # JavaScript runtime - preferably therubyracer on MRI and JScript
9
- # on Windows).
7
+ # Runs Esprima.js through V8.
10
8
  #
11
9
  # Initialized as singleton to avoid loading the esprima.js more
12
10
  # than once - otherwise performace will severely suffer.
@@ -14,17 +12,17 @@ module JsDuck
14
12
  include Singleton
15
13
 
16
14
  def initialize
17
- esprima_path = File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))+"/esprima/esprima.js";
18
- esprima = IO.read(esprima_path)
19
- helper = "function runEsprima(js) { return JSON.stringify(esprima.parse(js, {comment: true, range: true, raw: true})); }"
20
- @context = ExecJS.compile(esprima + "\n\n" + helper)
15
+ @v8 = V8::Context.new
16
+ esprima = File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))+"/esprima/esprima.js";
17
+ @v8.load(esprima)
21
18
  end
22
19
 
23
20
  # Parses JavaScript source code using Esprima.js
24
21
  #
25
22
  # Returns the resulting AST
26
23
  def parse(input)
27
- json = @context.call("runEsprima", input)
24
+ @v8['js'] = input
25
+ json = @v8.eval("JSON.stringify(esprima.parse(js, {comment: true, range: true, raw: true}))")
28
26
  return JSON.parse(json, :max_nesting => false)
29
27
  end
30
28
 
@@ -20,7 +20,7 @@ module JsDuck
20
20
  @groups = JsonDuck.read(filename)
21
21
  @opts = opts
22
22
  fix_examples_data
23
- build_map_by_name("Two examples have the same name")
23
+ build_map_by_name("Two examples have the same name", filename)
24
24
  end
25
25
 
26
26
  # Prefix all relative URL-s in examples list with path given in --examples-base-url
@@ -13,7 +13,7 @@ module JsDuck
13
13
 
14
14
  # Don't crash if old syntax is used.
15
15
  if @categories.is_a?(Hash) && @categories["categories"]
16
- Logger.instance.warn(nil, 'Update categories file to contain just the array inside {"categories": [...]}')
16
+ Logger.instance.warn(nil, 'Update categories file to contain just the array inside {"categories": [...]}', @filename)
17
17
  @categories = @categories["categories"]
18
18
  end
19
19
 
@@ -36,7 +36,7 @@ module JsDuck
36
36
  re = Regexp.new("^" + name.split(/\*/, -1).map {|part| Regexp.escape(part) }.join('.*') + "$")
37
37
  classes = @relations.to_a.find_all {|cls| re =~ cls[:name] && !cls[:private] }.map {|cls| cls[:name] }.sort
38
38
  if classes.length == 0
39
- Logger.instance.warn(:cat_no_match, "No class found matching a pattern '#{name}' in categories file.")
39
+ Logger.instance.warn(:cat_no_match, "No class found matching a pattern '#{name} in categories file'", @filename)
40
40
  end
41
41
  classes
42
42
  end
@@ -56,7 +56,7 @@ module JsDuck
56
56
  # Check that each existing non-private class is listed
57
57
  @relations.each do |cls|
58
58
  unless listed_classes[cls[:name]] || cls[:private]
59
- Logger.instance.warn(:cat_class_missing, "Class '#{cls[:name]}' not found in categories file")
59
+ Logger.instance.warn(:cat_class_missing, "Class '#{cls[:name]}' not found in categories file", @filename)
60
60
  end
61
61
  end
62
62
  end
@@ -13,13 +13,13 @@ module JsDuck
13
13
  #
14
14
  # Prints warning when there is a duplicate item within a group.
15
15
  # The warning message should say something like "duplicate <asset type>"
16
- def build_map_by_name(warning_msg)
16
+ def build_map_by_name(warning_msg, filename)
17
17
  @map_by_name = {}
18
18
  @groups.each do |group|
19
19
  group_map = {}
20
20
  group["items"].each do |item|
21
21
  if group_map[item["name"]]
22
- Logger.instance.warn(:dup_asset, "#{warning_msg} '#{item['name']}'")
22
+ Logger.instance.warn(:dup_asset, "#{warning_msg} '#{item['name']}'", filename)
23
23
  end
24
24
  @map_by_name[item["name"]] = item
25
25
  group_map[item["name"]] = item
@@ -24,7 +24,7 @@ module JsDuck
24
24
  def initialize(filename, formatter, opts)
25
25
  @path = File.dirname(filename)
26
26
  @groups = JsonDuck.read(filename)
27
- build_map_by_name("Two guides have the same name")
27
+ build_map_by_name("Two guides have the same name", filename)
28
28
  @formatter = formatter
29
29
  @opts = opts
30
30
  end
@@ -58,11 +58,11 @@ module JsDuck
58
58
  def load_guide(guide)
59
59
  in_dir = @path + "/guides/" + guide["name"]
60
60
 
61
- return Logger.instance.warn(:guide, "Guide #{in_dir} not found") unless File.exists?(in_dir)
61
+ return Logger.instance.warn(:guide, "Guide not found", in_dir) unless File.exists?(in_dir)
62
62
 
63
63
  guide_file = in_dir + "/README.md"
64
64
 
65
- return Logger.instance.warn(:guide, "README.md not found in #{in_dir}") unless File.exists?(guide_file)
65
+ return Logger.instance.warn(:guide, "Guide not found", guide_file) unless File.exists?(guide_file)
66
66
 
67
67
  begin
68
68
  @formatter.doc_context = {:filename => guide_file, :linenr => 0}
@@ -71,7 +71,7 @@ module JsDuck
71
71
 
72
72
  return add_toc(guide, @formatter.format(JsDuck::IO.read(guide_file)))
73
73
  rescue
74
- Logger.instance.fatal("Error while reading/formatting guide #{in_dir}", $!)
74
+ Logger.instance.fatal_backtrace("Error while reading/formatting guide #{in_dir}", $!)
75
75
  exit(1)
76
76
  end
77
77
  end
@@ -10,9 +10,9 @@ module JsDuck
10
10
  module_function
11
11
 
12
12
  # Loads in exported docs and generates @since and @new tags based on that data.
13
- def import(imports, relations)
13
+ def import(imports, relations, new_since=nil)
14
14
  if imports.length > 0
15
- generate_since_tags(read_all(imports), relations)
15
+ generate_since_tags(read_all(imports), relations, new_since)
16
16
  end
17
17
  end
18
18
 
@@ -60,22 +60,41 @@ module JsDuck
60
60
 
61
61
  # Using the imported versions data, adds @since tags to all
62
62
  # classes/members.
63
- def generate_since_tags(versions, relations)
64
- last_version = versions.last[:version]
63
+ def generate_since_tags(versions, relations, new_since=nil)
64
+ new_versions = build_new_versions_map(versions, new_since)
65
65
 
66
66
  relations.each do |cls|
67
67
  v = cls[:meta][:since] || class_since(versions, cls)
68
68
  cls[:meta][:since] = v
69
- cls[:meta][:new] = true if v == last_version
69
+ cls[:meta][:new] = true if new_versions[v]
70
70
 
71
71
  cls.all_local_members.each do |m|
72
72
  v = m[:meta][:since] || member_since(versions, cls, m)
73
73
  m[:meta][:since] = v
74
- m[:meta][:new] = true if v == last_version
74
+ m[:meta][:new] = true if new_versions[v]
75
75
  end
76
76
  end
77
77
  end
78
78
 
79
+ # Generates a lookup table of versions that we are going to label
80
+ # with @new tags. By default we use the latest version, otherwise
81
+ # use all versions since the latest.
82
+ def build_new_versions_map(versions, new_since=nil)
83
+ new_versions = {}
84
+
85
+ if new_since
86
+ versions.map {|v| v[:version] }.each do |v|
87
+ if v == new_since || !new_versions.empty?
88
+ new_versions[v] = true
89
+ end
90
+ end
91
+ else
92
+ new_versions[versions.last[:version]] = true
93
+ end
94
+
95
+ new_versions
96
+ end
97
+
79
98
  def member_since(versions, cls, m)
80
99
  versions.each do |ver|
81
100
  c = ver[:classes][cls[:name]]
@@ -55,6 +55,9 @@ module JsDuck
55
55
  cls[:members][:property].delete(m)
56
56
  cls[:members][:cfg] << m
57
57
  end
58
+ # The members lookup table inside class is no more valid, so
59
+ # reset it.
60
+ cls.reset_members_lookup!
58
61
  end
59
62
 
60
63
  # For auto-detected members/classes (which have @private == :inherit)
@@ -28,18 +28,18 @@ module JsDuck
28
28
  # Looks for inline tag at the current scan pointer position, when
29
29
  # found, moves scan pointer forward and performs the apporpriate
30
30
  # replacement.
31
- def replace(input)
31
+ def replace(input, doc_context)
32
32
  if input.check(@re)
33
- input.scan(@re).sub(@re) { apply_tpl($1, $2, $3) }
33
+ input.scan(@re).sub(@re) { apply_tpl($1, $2, $3, doc_context) }
34
34
  else
35
35
  false
36
36
  end
37
37
  end
38
38
 
39
39
  # applies the video template of the specified type
40
- def apply_tpl(type, url, alt_text)
40
+ def apply_tpl(type, url, alt_text, ctx)
41
41
  unless @templates.has_key?(type)
42
- Logger.instance.warn(nil, "Unknown video type #{type}")
42
+ Logger.instance.warn(nil, "Unknown video type #{type}", ctx[:filename], ctx[:linenr])
43
43
  end
44
44
 
45
45
  @templates[type].gsub(/(%\w)/) do