jsduck 3.0.pre → 3.0.pre2

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/jsduck.gemspec CHANGED
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.required_rubygems_version = ">= 1.3.7"
3
3
 
4
4
  s.name = 'jsduck'
5
- s.version = '3.0.pre'
6
- s.date = '2011-09-08'
5
+ s.version = '3.0.pre2'
6
+ s.date = '2011-09-20'
7
7
  s.summary = "Simple JavaScript Duckumentation generator"
8
8
  s.description = "Documentation generator for ExtJS 4"
9
9
  s.homepage = "https://github.com/senchalabs/jsduck"
@@ -0,0 +1,71 @@
1
+ require 'jsduck/logger'
2
+
3
+ module JsDuck
4
+
5
+ class Accessors
6
+ # Given a class, generates accessor methods to configs with
7
+ # @accessor tag. Modifies the class by adding these methods.
8
+ # When class already contains a getter or setter, the method is
9
+ # not added.
10
+ def create(cls)
11
+ # Grab all configs tagged as @accessor
12
+ accessors = cls[:members][:cfg].find_all {|cfg| cfg[:accessor] }
13
+
14
+ # Build lookup table of method names
15
+ methods = {}
16
+ cls[:members][:method].each do |m|
17
+ methods[m[:name]] = m;
18
+ end
19
+
20
+ accessors.each do |cfg|
21
+ # add getter if no method with same name exists
22
+ get = create_getter(cfg)
23
+ if !methods[get[:name]]
24
+ cls[:members][:method] << get
25
+ end
26
+ # add setter if no method with same name exists
27
+ set = create_setter(cfg)
28
+ if !methods[set[:name]]
29
+ cls[:members][:method] << set
30
+ end
31
+ end
32
+ end
33
+
34
+ def create_getter(cfg)
35
+ return {
36
+ :tagname => :method,
37
+ :name => "get" + upcase_first(cfg[:name]),
38
+ :doc => "Returns the value of {@link #cfg-#{cfg[:name]}}.",
39
+ :params => [],
40
+ :return => {
41
+ :type => cfg[:type],
42
+ :doc => "",
43
+ },
44
+ :owner => cfg[:owner],
45
+ }
46
+ end
47
+
48
+ def create_setter(cfg)
49
+ return {
50
+ :tagname => :method,
51
+ :name => "set" + upcase_first(cfg[:name]),
52
+ :doc => "Sets the value of {@link #cfg-#{cfg[:name]}}.",
53
+ :params => [{
54
+ :type => cfg[:type],
55
+ :name => cfg[:name],
56
+ :doc => "",
57
+ }],
58
+ :return => {
59
+ :type => "undefined",
60
+ :doc => "",
61
+ },
62
+ :owner => cfg[:owner],
63
+ }
64
+ end
65
+
66
+ def upcase_first(str)
67
+ str[0,1].upcase + str[1..-1]
68
+ end
69
+ end
70
+
71
+ end
@@ -1,4 +1,5 @@
1
1
  require 'jsduck/class'
2
+ require 'jsduck/accessors'
2
3
 
3
4
  module JsDuck
4
5
 
@@ -50,12 +51,15 @@ module JsDuck
50
51
 
51
52
  # Merges new class-doc into old one.
52
53
  def merge_classes(old, new)
53
- [:extends, :xtype, :singleton, :private, :protected].each do |tag|
54
+ [:extends, :singleton, :private, :protected].each do |tag|
54
55
  old[tag] = old[tag] || new[tag]
55
56
  end
56
- [:mixins, :alternateClassNames, :xtypes].each do |tag|
57
+ [:mixins, :alternateClassNames].each do |tag|
57
58
  old[tag] = old[tag] + new[tag]
58
59
  end
60
+ new[:xtypes].each_pair do |key, xtypes|
61
+ old[:xtypes][key] = (old[:xtypes][key] || []) + xtypes
62
+ end
59
63
  old[:doc] = old[:doc].length > 0 ? old[:doc] : new[:doc]
60
64
  # Additionally the doc-comment can contain configs and constructor
61
65
  old[:members][:cfg] += new[:members][:cfg]
@@ -162,6 +166,14 @@ module JsDuck
162
166
  end
163
167
  end
164
168
 
169
+ # Creates accessor method for configs marked with @accessor
170
+ def create_accessors
171
+ accessors = Accessors.new
172
+ @classes.each_value do |cls|
173
+ accessors.create(cls)
174
+ end
175
+ end
176
+
165
177
  # Are we dealing with ExtJS 4?
166
178
  # True if any of the classes is defined with Ext.define()
167
179
  def ext4?
data/lib/jsduck/app.rb CHANGED
@@ -82,8 +82,6 @@ module JsDuck
82
82
  @timer.time(:generating) { puts JsonDuck.generate(@relations.classes) }
83
83
  elsif @opts.export == :json
84
84
  FileUtils.mkdir(@opts.output_dir)
85
- init_output_dirs
86
- @timer.time(:generating) { write_src(parsed_files) }
87
85
  @timer.time(:generating) { format_classes }
88
86
  @timer.time(:generating) { write_classes }
89
87
  else
@@ -114,7 +112,7 @@ module JsDuck
114
112
  def parallel_parse(filenames)
115
113
  @parallel.map(filenames) do |fname|
116
114
  Logger.instance.log("Parsing #{fname} ...")
117
- SourceFile.new(IO.read(fname), fname)
115
+ SourceFile.new(IO.read(fname), fname, @opts)
118
116
  end
119
117
  end
120
118
 
@@ -127,6 +125,7 @@ module JsDuck
127
125
  end
128
126
  agr.classify_orphans
129
127
  agr.create_global_class unless @opts.ignore_global
128
+ agr.create_accessors
130
129
  agr.append_ext4_event_options
131
130
  agr.result
132
131
  end
@@ -179,9 +178,10 @@ module JsDuck
179
178
  # Writes JSON export or JsonP file for each class
180
179
  def write_classes
181
180
  exporter = Exporter.new(@relations)
182
- renderer = Renderer.new
181
+ renderer = Renderer.new(@opts)
182
+ dir = @opts.output_dir + (@opts.export ? "" : "/output")
183
183
  @parallel.each(@relations.classes) do |cls|
184
- filename = @opts.output_dir+"/output/" + cls[:name] + (@opts.export ? ".json" : ".js")
184
+ filename = dir + "/" + cls[:name] + (@opts.export ? ".json" : ".js")
185
185
  Logger.instance.log("Writing to #{filename} ...")
186
186
  data = exporter.export(cls)
187
187
  if @opts.export
@@ -251,6 +251,7 @@ module JsDuck
251
251
  "{footer}" => "<div id='footer-content' style='display: none'>#{@opts.footer}</div>",
252
252
  "{extjs_path}" => @opts.extjs_path,
253
253
  "{local_storage_db}" => @opts.local_storage_db,
254
+ "{show_print_button}" => @opts.seo ? "true" : "false",
254
255
  "{welcome}" => @welcome.to_html,
255
256
  "{categories}" => @categories.to_html,
256
257
  "{guides}" => @guides.to_html,
@@ -39,21 +39,25 @@ module JsDuck
39
39
  if expandable?(m) || @formatter.too_long?(m[:doc])
40
40
  m[:shortDoc] = @formatter.shorten(m[:doc])
41
41
  end
42
- m[:html_type] = format_type(m[:type]) if m[:type] && @include_types
43
- m[:params] = m[:params].map {|p| format_item(p) } if m[:params]
44
- m[:return] = format_item(m[:return]) if m[:return]
45
- m[:properties] = m[:properties].map {|b| format_item(b) } if m[:properties]
42
+
43
+ # We don't validate and format CSS var and mixin type definitions
44
+ is_css_tag = m[:tagname] == :css_var || m[:tagname] == :css_mixin
45
+
46
+ m[:html_type] = (@include_types && !is_css_tag) ? format_type(m[:type]) : m[:type] if m[:type]
47
+ m[:params] = m[:params].map {|p| format_item(p, is_css_tag) } if m[:params]
48
+ m[:return] = format_item(m[:return], is_css_tag) if m[:return]
49
+ m[:properties] = m[:properties].map {|b| format_item(b, is_css_tag) } if m[:properties]
46
50
  m
47
51
  end
48
52
 
49
53
  def expandable?(m)
50
- m[:params] || (m[:properties] && m[:properties].length > 0) || m[:default] || m[:deprecated]
54
+ m[:params] || (m[:properties] && m[:properties].length > 0) || m[:default] || m[:deprecated] || m[:template]
51
55
  end
52
56
 
53
- def format_item(it)
57
+ def format_item(it, is_css_tag)
54
58
  it[:doc] = @formatter.format(it[:doc]) if it[:doc]
55
- it[:html_type] = format_type(it[:type]) if it[:type] && @include_types
56
- it[:properties] = it[:properties].map {|s| format_item(s) } if it[:properties]
59
+ it[:html_type] = (@include_types && !is_css_tag) ? format_type(it[:type]) : it[:type] if it[:type]
60
+ it[:properties] = it[:properties].map {|s| format_item(s, is_css_tag) } if it[:properties]
57
61
  it
58
62
  end
59
63
 
@@ -4,9 +4,9 @@ require 'jsduck/doc_parser'
4
4
  module JsDuck
5
5
 
6
6
  class CssParser
7
- def initialize(input)
7
+ def initialize(input, options = {})
8
8
  @lex = Lexer.new(input)
9
- @doc_parser = DocParser.new(:css)
9
+ @doc_parser = DocParser.new(:css, options[:meta_tags])
10
10
  @docs = []
11
11
  end
12
12
 
@@ -202,14 +202,14 @@ module JsDuck
202
202
  # Use the canonical class name for link (not some alternateClassName)
203
203
  cls = @relations[cls].full_name
204
204
  # prepend type name to member name
205
- member = member && (get_member(cls, member, type)[:tagname].to_s + "-" + member)
205
+ member = member && get_member(cls, member, type)
206
206
 
207
207
  @link_tpl.gsub(/(%[\w#-])/) do
208
208
  case $1
209
209
  when '%c'
210
210
  cls
211
211
  when '%m'
212
- member ? member : ""
212
+ member ? member[:id] : ""
213
213
  when '%#'
214
214
  member ? "#" : ""
215
215
  when '%-'
@@ -24,9 +24,14 @@ module JsDuck
24
24
  #
25
25
  class DocParser
26
26
  # Pass in :css to be able to parse CSS doc-comments
27
- def initialize(mode = :js)
27
+ def initialize(mode = :js, meta_tags = nil)
28
28
  @ident_pattern = (mode == :css) ? /\$?[\w-]+/ : /[$\w]\w*/
29
29
  @ident_chain_pattern = (mode == :css) ? /\$?[\w-]+(\.[\w-]+)*/ : /[$\w]\w*(\.\w+)*/
30
+
31
+ @meta_tags_map = {}
32
+ (meta_tags || []).each do |tag|
33
+ @meta_tags_map[tag[:name]] = true
34
+ end
30
35
  end
31
36
 
32
37
  def parse(input)
@@ -54,12 +59,23 @@ module JsDuck
54
59
  # Now we are left with only two types of lines:
55
60
  # - those beginning with *
56
61
  # - and those without it
62
+ indent = nil
57
63
  input.each_line do |line|
58
64
  line.chomp!
59
65
  if line =~ /\A\s*\*\s?(.*)\Z/
66
+ # When comment contains *-lines, switch indent-trimming off
67
+ indent = 0
60
68
  result << $1
61
- else
69
+ elsif line =~ /\A\s*\Z/
70
+ # pass-through empty lines
62
71
  result << line
72
+ elsif indent == nil && line =~ /\A(\s*)(.*?\Z)/
73
+ # When indent not measured, measure it and remember
74
+ indent = $1.length
75
+ result << $2
76
+ else
77
+ # Trim away indent if available
78
+ result << line.sub(/\A\s{0,#{indent||0}}/, "")
63
79
  end
64
80
  end
65
81
  return result.join("\n")
@@ -106,10 +122,6 @@ module JsDuck
106
122
  at_member
107
123
  elsif look(/@alias\b/)
108
124
  at_alias
109
- elsif look(/@author\b/)
110
- at_author
111
- elsif look(/@docauthor\b/)
112
- at_docauthor
113
125
  elsif look(/@deprecated\b/)
114
126
  at_deprecated
115
127
  elsif look(/@var\b/)
@@ -122,6 +134,10 @@ module JsDuck
122
134
  boolean_at_tag(/@(private|ignore|hide)/, :private)
123
135
  elsif look(/@protected\b/)
124
136
  boolean_at_tag(/@protected/, :protected)
137
+ elsif look(/@accessor\b/)
138
+ boolean_at_tag(/@accessor/, :accessor)
139
+ elsif look(/@template\b/)
140
+ boolean_at_tag(/@template/, :template)
125
141
  elsif look(/@markdown\b/)
126
142
  # this is detected just to be ignored
127
143
  boolean_at_tag(/@markdown/, :markdown)
@@ -129,7 +145,16 @@ module JsDuck
129
145
  # this is detected just to be ignored
130
146
  boolean_at_tag(/@abstract/, :abstract)
131
147
  elsif look(/@/)
132
- @current_tag[:doc] += @input.scan(/@/)
148
+ @input.scan(/@/)
149
+ if @meta_tags_map[look(/\w+/)]
150
+ add_tag(:meta)
151
+ @current_tag[:name] = match(/\w+/)
152
+ skip_horiz_white
153
+ @current_tag[:content] = @input.scan(/.*$/)
154
+ skip_white
155
+ else
156
+ @current_tag[:doc] += "@"
157
+ end
133
158
  elsif look(/[^@]/)
134
159
  @current_tag[:doc] += @input.scan(/[^@]+/)
135
160
  end
@@ -303,24 +328,6 @@ module JsDuck
303
328
  skip_white
304
329
  end
305
330
 
306
- # matches @author some name ... newline
307
- def at_author
308
- match(/@author/)
309
- add_tag(:author)
310
- skip_horiz_white
311
- @current_tag[:name] = @input.scan(/.*$/)
312
- skip_white
313
- end
314
-
315
- # matches @docauthor some name ... newline
316
- def at_docauthor
317
- match(/@docauthor/)
318
- add_tag(:docauthor)
319
- skip_horiz_white
320
- @current_tag[:name] = @input.scan(/.*$/)
321
- skip_white
322
- end
323
-
324
331
  # matches @deprecated <version> some text ... newline
325
332
  def at_deprecated
326
333
  match(/@deprecated/)
@@ -42,7 +42,7 @@ module JsDuck
42
42
 
43
43
  def compact_member(m)
44
44
  m_copy = {}
45
- [:name, :tagname, :owner, :protected, :static, :deprecated, :required].each do |key|
45
+ [:name, :tagname, :owner, :protected, :static, :deprecated, :required, :template, :id].each do |key|
46
46
  m_copy[key] = m[key]
47
47
  end
48
48
  m_copy
data/lib/jsduck/guides.rb CHANGED
@@ -29,9 +29,18 @@ module JsDuck
29
29
  end
30
30
 
31
31
  def write_guide(guide, dir)
32
- in_dir = @path + "/" + guide["name"]
32
+ guide_dir = @path + "/guides/" + guide["name"]
33
+ tutorial_dir = @path + "/tutorials/" + guide["name"]
33
34
  out_dir = dir + "/" + guide["name"]
34
- return Logger.instance.warn("Guide #{in_dir} not found") unless File.exists?(in_dir)
35
+
36
+ if File.exists?(guide_dir)
37
+ in_dir = guide_dir
38
+ elsif File.exists?(tutorial_dir)
39
+ in_dir = tutorial_dir
40
+ else
41
+ return Logger.instance.warn("Guide #{guide_dir} / #{tutorial_dir} not found")
42
+ end
43
+
35
44
  guide_file = in_dir + "/README.md"
36
45
  return Logger.instance.warn("README.md not found in #{in_dir}") unless File.exists?(guide_file)
37
46
 
@@ -6,10 +6,11 @@ require 'jsduck/js_literal_builder'
6
6
  module JsDuck
7
7
 
8
8
  class JsParser < JsLiteralParser
9
- def initialize(input)
9
+ def initialize(input, options = {})
10
10
  super(input)
11
- @doc_parser = DocParser.new
11
+ @doc_parser = DocParser.new(:js, options[:meta_tags])
12
12
  @docs = []
13
+ @ext_namespaces = options[:ext_namespaces] || ["Ext"]
13
14
  end
14
15
 
15
16
  # Parses the whole JavaScript block and returns array where for
@@ -76,9 +77,9 @@ module JsDuck
76
77
  function
77
78
  elsif look("var")
78
79
  var_declaration
79
- elsif look("Ext", ".", "define", "(", :string)
80
+ elsif ext_look(:ns, ".", "define", "(", :string)
80
81
  ext_define
81
- elsif look("Ext", ".", "ClassManager", ".", "create", "(", :string)
82
+ elsif ext_look(:ns, ".", "ClassManager", ".", "create", "(", :string)
82
83
  ext_define
83
84
  elsif look(:ident, ":") || look(:string, ":")
84
85
  property_literal
@@ -155,12 +156,14 @@ module JsDuck
155
156
  return chain
156
157
  end
157
158
 
158
- # <expression> := <function> | <ext-extend> | <literal>
159
+ # <expression> := <function> | <ext-extend> | <ext-base-css-prefix> | <literal>
159
160
  def expression
160
161
  if look("function")
161
162
  function
162
- elsif look("Ext", ".", "extend")
163
+ elsif ext_look(:ns, ".", "extend")
163
164
  ext_extend
165
+ elsif ext_look(:ns, ".", "baseCSSPrefix", "+", :string)
166
+ ext_base_css_prefix
164
167
  else
165
168
  my_literal
166
169
  end
@@ -194,16 +197,26 @@ module JsDuck
194
197
 
195
198
  # <ext-extend> := "Ext" "." "extend" "(" <ident-chain> "," ...
196
199
  def ext_extend
197
- match("Ext", ".", "extend", "(")
200
+ match(:ident, ".", "extend", "(")
198
201
  return {
199
202
  :type => :ext_extend,
200
203
  :extend => ident_chain,
201
204
  }
202
205
  end
203
206
 
207
+ # <ext-base-css-prefix> := "Ext" "." "baseCSSPrefix" "+" <string>
208
+ def ext_base_css_prefix
209
+ match(:ident, ".", "baseCSSPrefix", "+")
210
+ return {
211
+ :type => :literal,
212
+ :class => "String",
213
+ :value => '"x-' + match(:string)[:value] + '"',
214
+ }
215
+ end
216
+
204
217
  # <ext-define> := "Ext" "." ["define" | "ClassManager" "." "create" ] "(" <string> "," <ext-define-cfg>
205
218
  def ext_define
206
- match("Ext", ".");
219
+ match(:ident, ".");
207
220
  look("define") ? match("define") : match("ClassManager", ".", "create");
208
221
  name = match("(", :string)[:value]
209
222
 
@@ -338,6 +351,15 @@ module JsDuck
338
351
  }
339
352
  end
340
353
 
354
+ # Like look() but tries to match as the first argument all the
355
+ # names listed in @ext_namespaces
356
+ def ext_look(placeholder, *args)
357
+ @ext_namespaces.each do |ns|
358
+ return true if look(ns, *args)
359
+ end
360
+ return false
361
+ end
362
+
341
363
  end
342
364
 
343
365
  end