jsduck 5.2.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTQ4ZGE0ZGEzMTQxNDY1ZjhiY2FhNTliMmJjNTQ0OGY2NTZkY2VhMA==
4
+ Yjc3MDZjMmZlYWNkOGMzNzQ4MTdlZTA2MGU4M2Y3OWE1MmE5OWQ4MA==
5
5
  data.tar.gz: !binary |-
6
- ZWEzMjZmMGEzOTBmOGM4OGQ4NzZiY2NhMjA5OTZkZGVlMzdhMTc4NQ==
6
+ ZDkwMmFjOWQ1MTRlYzYxYWIzZjRhNThjMDlhNDc2YzBhZDU1ZjA0OQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZjA3N2ZjODkwZjNhNjBiNWExNTNlZjI0YzA4Y2EyZmZjZDk5MWUyMzY1Y2Iy
10
- NTlmYWY0MjJlODhjZjgwZGE3ZmY2YzZjZWI3MWE5OWVhM2ZjN2Q3YWJlODQy
11
- Zjg0OGM1NDZmNTdkNDExNmJlN2ZmZTU4ODkxYWI1YTM3MWFhMjk=
9
+ MjBlMjA4YzMwNTQzOTFmODE4MDcxNmJiZTU1OGMxY2FjOWVjZTNkZjViZDA3
10
+ ZDk3MTZlNGFmODU3Y2NlN2QwMTc5YjI0ODc1Yzc0MGJkMzUyMDI1M2Y3N2U4
11
+ ODViMjRlNDg4NDVhYWU2NDk2YWM5ZjRjMzk2ZDlkMjQ4NDNmY2U=
12
12
  data.tar.gz: !binary |-
13
- NTc0ZDQ1NDMxYTU5MDhiMTYyMTFlN2RiODNlYTczYThkZjUzMDFkNzk4NTU3
14
- NzY5YzVkNDMyYzA0NWI1MjM5NWQ4M2VkYzI2ZDcxNzQ0MGI0Y2Y5OWVkNDYz
15
- MjE0MjFiZjRjNWIxNWYzMjcxMWU2YjNkMjY3M2VmNTZmMzg2ODY=
13
+ M2U1ZTRiZDk0NjNiODUwZjNmMTRmYjIwNTA3NDg4ZTlkMGQ0MzZhYzY4YzA0
14
+ NmNiOWIwYmUwNTI0YjhkMDMzMzA3NTRjZWU0ZWE5YjlkNmM4Mzk1YzFmNTAy
15
+ NTU4ZDg2ZjBiMzU5ZGFjNzI4OTZjZTM3NTFlYzE2MDFiNjVlZGI=
data/README.md CHANGED
@@ -98,6 +98,8 @@ Who's using JSDuck?
98
98
  - [CKEditor](http://docs.ckeditor.com)
99
99
  - [GeoExt 2](https://github.com/geoext/geoext2)
100
100
  - Rally Software [Rally App SDK](https://prod.help.rallydev.com/apps/2.0rc1/doc/)
101
+ - Wikimedia Foundation [Mediawiki](https://doc.wikimedia.org/mediawiki-core/master/js/)
102
+ and [VisualEditor](https://doc.wikimedia.org/VisualEditor/master/)
101
103
  - [Sencha](http://docs.sencha.com) - obviously :)
102
104
 
103
105
  These are some that we know of. Want your project listed here? Drop us a line.
@@ -3,6 +3,7 @@ require 'jsduck/util/io'
3
3
  require 'jsduck/parser'
4
4
  require 'jsduck/source/file'
5
5
  require 'jsduck/logger'
6
+ require 'jsduck/cache'
6
7
 
7
8
  module JsDuck
8
9
 
@@ -11,17 +12,33 @@ module JsDuck
11
12
  class BatchParser
12
13
 
13
14
  def self.parse(opts)
14
- Util::Parallel.map(opts.input_files) do |fname|
15
+ cache = Cache.create(opts)
16
+
17
+ results = Util::Parallel.map(opts.input_files) do |fname|
15
18
  Logger.log("Parsing", fname)
19
+
16
20
  begin
17
21
  source = Util::IO.read(fname)
18
- docs = Parser.new.parse(source, fname, opts)
19
- Source::File.new(source, docs, fname)
22
+ docs = nil
23
+
24
+ unless docs = cache.read(fname, source)
25
+ docs = Parser.new.parse(source, fname, opts)
26
+ cache.write(fname, source, docs)
27
+ end
28
+
29
+ {
30
+ :file => Source::File.new(source, docs, fname),
31
+ :cache => cache.previous_entry,
32
+ }
20
33
  rescue
21
34
  Logger.fatal_backtrace("Error while parsing #{fname}", $!)
22
35
  exit(1)
23
36
  end
24
37
  end
38
+
39
+ cache.cleanup( results.map {|r| r[:cache] }.compact )
40
+
41
+ return results.map {|r| r[:file] }
25
42
  end
26
43
 
27
44
  end
@@ -0,0 +1,137 @@
1
+ require 'digest/md5'
2
+ require 'fileutils'
3
+ require 'jsduck/util/null_object'
4
+ require 'set'
5
+
6
+ module JsDuck
7
+
8
+ # Reads/writes parsed files in cache.
9
+ #
10
+ # When writing to cache:
11
+ #
12
+ # - makes MD5 hash of <file name> + <file contents>
13
+ # - Dumps the the parsed data structure using Marshal into <md5>.dat
14
+ #
15
+ # When reading from cache:
16
+ #
17
+ # - makes MD5 hash of <file name> + <file contents>
18
+ # - Reads the parsed data structure using Marshal from <md5>.dat
19
+ #
20
+ # Additionally a manifest.txt file is saved into the cache
21
+ # directory, the contents of which is a string like the following:
22
+ #
23
+ # Ruby: 1.9.3, JSDuck: 5.2.0
24
+ #
25
+ # This file is consulted before all other cache operations. When
26
+ # the version numbers in there don't match with current Ruby and
27
+ # JSDuck versions, the whole cache gets invalidated - all cached
28
+ # files get deleted. This is to avoid problems with the Marshal
29
+ # file format changes between Ruby versions and parsed data
30
+ # structure changes between JSDuck versions.
31
+ #
32
+ # After all files have been checked into cache, the files that
33
+ # weren't touched get deleted (using the #cleanup method). This
34
+ # ensures that the number of files in cache only grows when more
35
+ # files are added to the documentation.
36
+ #
37
+ class Cache
38
+
39
+ # Factory method to produce a cache object. When caching is
40
+ # disabled, returns a NullObject which emulates a cache that's
41
+ # always empty.
42
+ def self.create(opts)
43
+ # Check also for cache_dir, which will be nil when output_dir is :stdout
44
+ if opts.cache && opts.cache_dir
45
+ Cache.new(opts)
46
+ else
47
+ Util::NullObject.new(
48
+ :read => nil,
49
+ :write => nil,
50
+ :previous_entry => nil,
51
+ :cleanup => nil
52
+ )
53
+ end
54
+ end
55
+
56
+ # The name of the cache file that was previously read or written.
57
+ # When the #read call failed to find the file, it will be nil.
58
+ # But it will always be available after the #write call.
59
+ attr_reader :previous_entry
60
+
61
+ def initialize(opts)
62
+ @jsduck_version = opts.version
63
+ @cache_dir = opts.cache_dir
64
+ @manifest_file = @cache_dir + "/manifest.txt"
65
+ @previous_entry = nil
66
+
67
+ FileUtils.mkdir_p(@cache_dir) unless File.exists?(@cache_dir)
68
+
69
+ # Invalidate the whole cache when it was generated with a
70
+ # different Ruby and/or JSDuck version.
71
+ invalidate_all! unless valid_manifest?
72
+ end
73
+
74
+ # Given the name and contents of a source file, reads the already
75
+ # parsed data structure from cache. Returns nil when not found.
76
+ def read(file_name, file_contents)
77
+ fname = cache_file_name(file_name, file_contents)
78
+ if File.exists?(fname)
79
+ @previous_entry = fname
80
+ File.open(fname, "rb") {|file| Marshal::load(file) }
81
+ else
82
+ @previous_entry = nil
83
+ nil
84
+ end
85
+ end
86
+
87
+ # Writes parse data into cache under a name generated from the
88
+ # name and contents of a source file.
89
+ def write(file_name, file_contents, data)
90
+ fname = cache_file_name(file_name, file_contents)
91
+ @previous_entry = fname
92
+ File.open(fname, "wb") {|file| Marshal::dump(data, file) }
93
+ end
94
+
95
+ # Given listing of used cache files (those that were either read
96
+ # or written during this jsduck run) removes rest of the files
97
+ # from cache directory that were unused.
98
+ def cleanup(used_cache_entries)
99
+ used = Set.new(used_cache_entries)
100
+
101
+ Dir[@cache_dir + "/*.dat"].each do |file|
102
+ FileUtils.rm_rf(file) unless used.include?(file)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def cache_file_name(file_name, file_contents)
109
+ @cache_dir + "/" + md5(file_name + file_contents) + ".dat"
110
+ end
111
+
112
+ def md5(string)
113
+ Digest::MD5.hexdigest(string)
114
+ end
115
+
116
+ def valid_manifest?
117
+ manifest = File.exists?(@manifest_file) ? Util::IO.read(@manifest_file) : ""
118
+ return manifest == current_manifest
119
+ end
120
+
121
+ def invalidate_all!
122
+ FileUtils.rm_rf(@cache_dir)
123
+ FileUtils.mkdir(@cache_dir)
124
+ save_manifest
125
+ end
126
+
127
+ def save_manifest
128
+ File.open(@manifest_file, "w") {|f| f.write(current_manifest) }
129
+ end
130
+
131
+ def current_manifest
132
+ "Ruby: #{RUBY_VERSION}, JSDuck: #{@jsduck_version}\n"
133
+ end
134
+
135
+ end
136
+
137
+ end
@@ -29,7 +29,8 @@ module JsDuck
29
29
  end
30
30
 
31
31
  def write_dir(dir, extension)
32
- FileUtils.mkdir(dir)
32
+ FileUtils.mkdir(dir) unless File.exists?(dir)
33
+
33
34
  Util::Parallel.each(@relations.classes) do |cls|
34
35
  filename = dir + "/" + cls[:name] + extension
35
36
  Logger.log("Writing docs", filename)
@@ -4,6 +4,7 @@ require 'jsduck/exporter/examples'
4
4
  require 'jsduck/format/batch'
5
5
  require 'jsduck/class_writer'
6
6
  require 'jsduck/guide_writer'
7
+ require 'jsduck/output_dir'
7
8
  require 'fileutils'
8
9
 
9
10
  module JsDuck
@@ -50,7 +51,7 @@ module JsDuck
50
51
  # -- util routines --
51
52
 
52
53
  def clean_output_dir
53
- FileUtils.rm_rf(@opts.output_dir)
54
+ OutputDir.clean(@opts)
54
55
  end
55
56
 
56
57
  def format_classes
@@ -122,7 +122,7 @@ module JsDuck
122
122
  when "inheritableStatics"
123
123
  cls[:members] += make_statics(value, {:inheritable => true})
124
124
  else
125
- detect_method_or_property(cls, key, value, pair)
125
+ detect_method_or_property(cls, key, value, pair) if pair.raw["kind"] == "init"
126
126
  end
127
127
  end
128
128
  end
@@ -132,7 +132,7 @@ module JsDuck
132
132
  def detect_class_members_from_object(cls, ast)
133
133
  cls[:members] = []
134
134
  ast.each_property do |key, value, pair|
135
- detect_method_or_property(cls, key, value, pair)
135
+ detect_method_or_property(cls, key, value, pair) if pair.raw["kind"] == "init"
136
136
  end
137
137
  end
138
138
 
@@ -34,7 +34,7 @@ module JsDuck
34
34
  make(exp["id"].to_s || "", exp)
35
35
 
36
36
  # foo: function() {}
37
- elsif ast.property? && ast["value"].function?
37
+ elsif ast.property? && ast.raw["kind"] == "init" && ast["value"].function?
38
38
  make(ast["key"].key_value, ast["value"])
39
39
 
40
40
  # Object.defineProperty(obj, "prop", {value: function() {} })
@@ -41,9 +41,18 @@ module JsDuck
41
41
  # Object.defineProperty(obj, "prop", {value: x})
42
42
  elsif exp && exp.define_property?
43
43
  name = exp["arguments"][1].to_value
44
- writable = exp.object_descriptor("writable")
45
- readonly = writable ? !writable.to_value : true
46
- make(name, exp.object_descriptor("value"), readonly)
44
+
45
+ if exp.object_descriptor("set")
46
+ # Object with a setter is not readonly
47
+ make(name)
48
+ elsif exp.object_descriptor("get")
49
+ # Object with a getter and no setter is readonly
50
+ make(name, nil, true)
51
+ else
52
+ writable = exp.object_descriptor("writable")
53
+ readonly = writable ? !writable.to_value : true
54
+ make(name, exp.object_descriptor("value"), readonly)
55
+ end
47
56
 
48
57
  else
49
58
  nil
@@ -216,32 +216,24 @@ module JsDuck
216
216
  when RKelly::Nodes::PropertyNode == node.class
217
217
  make(node, {
218
218
  "type" => "Property",
219
- "key" =>
220
- if node.name.is_a?(Numeric)
221
- {
222
- "type" => "Literal",
223
- "value" => node.name,
224
- "raw" => node.name.to_s,
225
- "range" => offset_range(node, :name),
226
- }
227
- elsif node.name =~ /['"]/
228
- {
229
- "type" => "Literal",
230
- "value" => string_value(node.name),
231
- "raw" => node.name,
232
- "range" => offset_range(node, :name),
233
- }
234
- else
235
- {
236
- "type" => "Identifier",
237
- "name" => node.name,
238
- "range" => offset_range(node, :name),
239
- }
240
- end,
219
+ "key" => property_key(node),
241
220
  "value" => adapt_node(node.value),
242
221
  "kind" => "init",
243
222
  })
244
-
223
+ when RKelly::Nodes::GetterPropertyNode == node.class
224
+ make(node, {
225
+ "type" => "Property",
226
+ "key" => property_key(node),
227
+ "value" => adapt_node(node.value),
228
+ "kind" => "get",
229
+ })
230
+ when RKelly::Nodes::SetterPropertyNode == node.class
231
+ make(node, {
232
+ "type" => "Property",
233
+ "key" => property_key(node),
234
+ "value" => adapt_node(node.value),
235
+ "kind" => "set",
236
+ })
245
237
  # Statements
246
238
  when RKelly::Nodes::ExpressionStatementNode == node.class
247
239
  make(node, {
@@ -458,6 +450,30 @@ module JsDuck
458
450
  }
459
451
  end
460
452
 
453
+ def property_key(node)
454
+ if node.name.is_a?(Numeric)
455
+ {
456
+ "type" => "Literal",
457
+ "value" => node.name,
458
+ "raw" => node.name.to_s,
459
+ "range" => offset_range(node, :name),
460
+ }
461
+ elsif node.name =~ /['"]/
462
+ {
463
+ "type" => "Literal",
464
+ "value" => string_value(node.name),
465
+ "raw" => node.name,
466
+ "range" => offset_range(node, :name),
467
+ }
468
+ else
469
+ {
470
+ "type" => "Identifier",
471
+ "name" => node.name,
472
+ "range" => offset_range(node, :name),
473
+ }
474
+ end
475
+ end
476
+
461
477
  # Evaluates the actual value of a JavaScript string.
462
478
  # Importantly we avoid using Ruby's eval().
463
479
  def string_value(string)
@@ -18,6 +18,7 @@ module JsDuck
18
18
  attr_accessor :ignore_global
19
19
  attr_accessor :external_classes
20
20
  attr_accessor :ext4_events
21
+ attr_accessor :version
21
22
 
22
23
  # Customizing output
23
24
  attr_accessor :title
@@ -48,6 +49,8 @@ module JsDuck
48
49
 
49
50
  # Debugging
50
51
  attr_accessor :warnings_exit_nonzero
52
+ attr_accessor :cache
53
+ attr_accessor :cache_dir
51
54
  attr_accessor :template_dir
52
55
  attr_accessor :template_links
53
56
  attr_accessor :extjs_path
@@ -99,7 +102,7 @@ module JsDuck
99
102
  ]
100
103
  @ext4_events = nil
101
104
 
102
- @version = "5.2.0"
105
+ @version = "5.3.0"
103
106
  # Customizing output
104
107
  @title = "Documentation - JSDuck"
105
108
  @header = "<strong>Documentation</strong> JSDuck"
@@ -132,6 +135,8 @@ module JsDuck
132
135
 
133
136
  # Debugging
134
137
  @warnings_exit_nonzero = false
138
+ @cache = false
139
+ @cache_dir = nil
135
140
  @root_dir = File.dirname(File.dirname(File.dirname(__FILE__)))
136
141
  @template_dir = @root_dir + "/template-min"
137
142
  @template_links = false
@@ -203,16 +208,22 @@ module JsDuck
203
208
  "This option is REQUIRED. When the directory exists,",
204
209
  "it will be overwritten. Give dash '-' as argument",
205
210
  "to write docs to STDOUT (works only with --export).") do |path|
206
- @output_dir = path == "-" ? :stdout : canonical(path)
211
+ if path == "-"
212
+ @output_dir = :stdout
213
+ else
214
+ @output_dir = canonical(path)
215
+ @cache_dir = @output_dir + "/.cache" unless @cache_dir
216
+ end
207
217
  end
208
218
 
209
- opts.on('--export=TYPE',
219
+ opts.on('--export=full/examples',
210
220
  "Exports docs in JSON.",
211
221
  "",
212
- "TYPE is one of:",
222
+ "For each JavaScript class a JSON file gets written,",
223
+ "the contents of which are as follows:",
213
224
  "",
214
- "- full - full class docs.",
215
- "- examples - extracts inline examples from classes.") do |format|
225
+ "- full - docs and metadata for class and its members.",
226
+ "- examples - inline examples from classes and guides.") do |format|
216
227
  export_type = format.to_sym
217
228
  if [:full, :examples].include?(export_type)
218
229
  @export = export_type
@@ -662,6 +673,63 @@ module JsDuck
662
673
  end
663
674
  end
664
675
 
676
+ opts.separator ""
677
+ opts.separator "Performance:"
678
+ opts.separator ""
679
+
680
+ opts.on('-p', '--processes=COUNT',
681
+ "The number of parallel processes to use.",
682
+ "",
683
+ "Defaults to the number of processors/cores.",
684
+ "",
685
+ "Set to 0 to disable parallel processing completely.",
686
+ "This is often useful in debugging to get deterministic",
687
+ "results.",
688
+ "",
689
+ "In Windows this option is disabled.") do |count|
690
+ Util::Parallel.in_processes = count.to_i
691
+ end
692
+
693
+ opts.on('--[no-]cache',
694
+ "Turns parser cache on/off (EXPERIMENTAL).",
695
+ "",
696
+ "Defaults to off.",
697
+ "",
698
+ "When enabled, the results of parsing source files is saved",
699
+ "inside the JSDuck output directory. Next time JSDuck runs,",
700
+ "only the files that have changed are parsed again, others",
701
+ "are read from the cache.",
702
+ "",
703
+ "Note that switching between Ruby and/or JSDuck versions",
704
+ "invalidates the whole cache. But changes in custom tags",
705
+ "don't invalidate the cache, so avoid caching when developing",
706
+ "your custom tags.",
707
+ "",
708
+ "To change the cache directory location, use --cache-dir.") do |enabled|
709
+ @cache = enabled
710
+ end
711
+
712
+ opts.on('--cache-dir=PATH',
713
+ "Directory where to cache the parsed source.",
714
+ "",
715
+ "Defaults to: <output-dir>/.cache",
716
+ "",
717
+ "Each project needs to have a separate cache directory.",
718
+ "Instead of writing the cache into the output directory,",
719
+ "one might consider keeping it together with the source",
720
+ "files.",
721
+ "",
722
+ "Note that JSDuck ensures that the <output-dir>/.cache",
723
+ "dir is preserved when the rest of the <output-dir> gets",
724
+ "wiped clean during the docs generation. If you specify",
725
+ "cache dir like <output-dir>/.mycache, then this will also",
726
+ "be cleaned up during docs generation, and the caching",
727
+ "won't work.",
728
+ "",
729
+ "This option only has an effect when --cache is also used.",) do |path|
730
+ @cache_dir = path
731
+ end
732
+
665
733
  opts.separator ""
666
734
  opts.separator "Debugging:"
667
735
  opts.separator ""
@@ -726,19 +794,6 @@ module JsDuck
726
794
  Logger.colors = on
727
795
  end
728
796
 
729
- opts.on('-p', '--processes=COUNT',
730
- "The number of parallel processes to use.",
731
- "",
732
- "Defaults to the number of processors/cores.",
733
- "",
734
- "Set to 0 to disable parallel processing completely.",
735
- "This is often useful in debugging to get deterministic",
736
- "results.",
737
- "",
738
- "In Windows this option is disabled.") do |count|
739
- Util::Parallel.in_processes = count.to_i
740
- end
741
-
742
797
  opts.on('--pretty-json',
743
798
  "Turns on pretty-printing of JSON.",
744
799
  "",