jsduck 5.2.0 → 5.3.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.
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
  "",