jsduck 4.6.1 → 4.6.2
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/.travis.yml +2 -1
- data/README.md +6 -12
- data/Rakefile +1 -1
- data/js-classes/Date.js +19 -18
- data/jsduck.gemspec +3 -2
- data/lib/jsduck/assets.rb +5 -5
- data/lib/jsduck/ast.rb +2 -2
- data/lib/jsduck/batch_formatter.rb +11 -5
- data/lib/jsduck/class_formatter.rb +1 -1
- data/lib/jsduck/doc_formatter.rb +19 -21
- data/lib/jsduck/doc_parser.rb +1 -1
- data/lib/jsduck/guides.rb +17 -8
- data/lib/jsduck/html_stack.rb +46 -32
- data/lib/jsduck/img/dir.rb +94 -0
- data/lib/jsduck/img/dir_set.rb +39 -0
- data/lib/jsduck/img/writer.rb +23 -0
- data/lib/jsduck/inline/auto_link.rb +106 -0
- data/lib/jsduck/inline/img.rb +19 -11
- data/lib/jsduck/inline/link.rb +6 -132
- data/lib/jsduck/inline/link_renderer.rb +69 -0
- data/lib/jsduck/options.rb +5 -3
- metadata +420 -396
- data/lib/jsduck/images.rb +0 -72
data/lib/jsduck/guides.rb
CHANGED
@@ -5,6 +5,7 @@ require 'jsduck/util/null_object'
|
|
5
5
|
require 'jsduck/logger'
|
6
6
|
require 'jsduck/grouped_asset'
|
7
7
|
require 'jsduck/util/html'
|
8
|
+
require 'jsduck/img/dir'
|
8
9
|
require 'fileutils'
|
9
10
|
|
10
11
|
module JsDuck
|
@@ -39,6 +40,7 @@ module JsDuck
|
|
39
40
|
def load_all_guides
|
40
41
|
each_item do |guide|
|
41
42
|
guide["url"] = resolve_url(guide)
|
43
|
+
guide[:filename] = guide["url"] + "/README.md"
|
42
44
|
guide[:html] = load_guide(guide)
|
43
45
|
end
|
44
46
|
end
|
@@ -52,22 +54,29 @@ module JsDuck
|
|
52
54
|
|
53
55
|
def load_guide(guide)
|
54
56
|
return Logger.warn(:guide, "Guide not found", guide["url"]) unless File.exists?(guide["url"])
|
55
|
-
|
56
|
-
guide_file = guide["url"] + "/README.md"
|
57
|
-
|
58
|
-
return Logger.warn(:guide, "Guide not found", guide_file) unless File.exists?(guide_file)
|
57
|
+
return Logger.warn(:guide, "Guide not found", guide[:filename]) unless File.exists?(guide[:filename])
|
59
58
|
|
60
59
|
begin
|
61
|
-
|
62
|
-
@formatter.img_path = "guides/#{guide["name"]}"
|
63
|
-
|
64
|
-
return add_toc(guide, @formatter.format(Util::IO.read(guide_file)))
|
60
|
+
return format_guide(guide)
|
65
61
|
rescue
|
66
62
|
Logger.fatal_backtrace("Error while reading/formatting guide #{guide['url']}", $!)
|
67
63
|
exit(1)
|
68
64
|
end
|
69
65
|
end
|
70
66
|
|
67
|
+
def format_guide(guide)
|
68
|
+
@formatter.doc_context = {:filename => guide[:filename], :linenr => 0}
|
69
|
+
@formatter.images = Img::Dir.new(guide["url"], "guides/#{guide["name"]}")
|
70
|
+
html = add_toc(guide, @formatter.format(Util::IO.read(guide[:filename])))
|
71
|
+
|
72
|
+
# Report unused images (but ignore the icon files)
|
73
|
+
@formatter.images.get("icon.png")
|
74
|
+
@formatter.images.get("icon-lg.png")
|
75
|
+
@formatter.images.report_unused
|
76
|
+
|
77
|
+
return html
|
78
|
+
end
|
79
|
+
|
71
80
|
def write_guide(guide, dir)
|
72
81
|
return unless guide[:html]
|
73
82
|
|
data/lib/jsduck/html_stack.rb
CHANGED
@@ -4,18 +4,6 @@ module JsDuck
|
|
4
4
|
|
5
5
|
# Tracks opening and closing of HTML tags, with the purpose of
|
6
6
|
# closing down the unfinished tags.
|
7
|
-
#
|
8
|
-
# Synopsis:
|
9
|
-
#
|
10
|
-
# tags = HtmlStack.new
|
11
|
-
# # open and close a bunch of tags
|
12
|
-
# tags.open("a")
|
13
|
-
# tags.open("b")
|
14
|
-
# tags.close("b")
|
15
|
-
#
|
16
|
-
# # ask which tags still need to be closed
|
17
|
-
# tags.close_unfinished --> "</a>"
|
18
|
-
#
|
19
7
|
class HtmlStack
|
20
8
|
|
21
9
|
# Initializes the stack with two optional parameters:
|
@@ -28,18 +16,23 @@ module JsDuck
|
|
28
16
|
@open_tags = []
|
29
17
|
end
|
30
18
|
|
31
|
-
#
|
32
|
-
def open(
|
33
|
-
|
34
|
-
tag
|
19
|
+
# Scans an opening tag in HTML using the passed in StringScanner.
|
20
|
+
def open(s)
|
21
|
+
s.scan(/</) + push_tag(s.scan(/\w+/)) + s.scan_until(/>|\Z/)
|
35
22
|
end
|
36
23
|
|
37
|
-
#
|
38
|
-
def close(
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
24
|
+
# Scans a closing tag in HTML using the passed in StringScanner.
|
25
|
+
def close(s)
|
26
|
+
s.scan(/<\//)
|
27
|
+
tag = s.scan(/\w+/)
|
28
|
+
s.scan(/>/)
|
29
|
+
|
30
|
+
pop_tags(tag).map {|t| "</#{t}>" }.join
|
31
|
+
end
|
32
|
+
|
33
|
+
# Registers opening of a tag. Returns the tag.
|
34
|
+
def push_tag(tag)
|
35
|
+
@open_tags.push(tag) unless void?(tag)
|
43
36
|
tag
|
44
37
|
end
|
45
38
|
|
@@ -48,22 +41,43 @@ module JsDuck
|
|
48
41
|
@open_tags.include?(tag)
|
49
42
|
end
|
50
43
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
private
|
45
|
+
|
46
|
+
# Registers closing of a tag. Returns all the tags that need to
|
47
|
+
# be closed at that point.
|
48
|
+
def pop_tags(tag)
|
49
|
+
if !@open_tags.include?(tag)
|
50
|
+
if @ignore_html[tag]
|
51
|
+
return [tag]
|
52
|
+
else
|
53
|
+
warn_unopened(tag)
|
54
|
+
return []
|
55
|
+
end
|
56
|
+
end
|
55
57
|
|
56
|
-
|
58
|
+
popped = []
|
59
|
+
begin
|
60
|
+
popped << t = @open_tags.pop
|
61
|
+
if t != tag
|
62
|
+
warn_unclosed(t)
|
63
|
+
end
|
64
|
+
end until t == tag
|
57
65
|
|
58
|
-
|
66
|
+
popped
|
59
67
|
end
|
60
68
|
|
61
|
-
|
69
|
+
def warn_unopened(*tags)
|
70
|
+
warn("Unopened HTML tag", tags)
|
71
|
+
end
|
72
|
+
|
73
|
+
def warn_unclosed(*tags)
|
74
|
+
warn("Unclosed HTML tag", tags)
|
75
|
+
end
|
62
76
|
|
63
|
-
def
|
77
|
+
def warn(msg, tags)
|
64
78
|
ctx = @doc_context
|
65
|
-
tag_list =
|
66
|
-
Logger.warn(:html, "
|
79
|
+
tag_list = tags.map {|tag| "<#{tag}>" }.join(", ")
|
80
|
+
Logger.warn(:html, "#{msg}: #{tag_list}", ctx[:filename], ctx[:linenr])
|
67
81
|
end
|
68
82
|
|
69
83
|
def void?(tag)
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'dimensions'
|
2
|
+
require 'jsduck/logger'
|
3
|
+
|
4
|
+
module JsDuck
|
5
|
+
module Img
|
6
|
+
|
7
|
+
# Looks up images from a directory.
|
8
|
+
class Dir
|
9
|
+
def initialize(full_path, relative_path)
|
10
|
+
@full_path = full_path
|
11
|
+
@relative_path = relative_path
|
12
|
+
@images = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Retrieves hash of information for a given relative image
|
16
|
+
# filename. It will have the fields:
|
17
|
+
#
|
18
|
+
# - :filename - the same as the parameter of this method
|
19
|
+
# - :full_path - actual path in the filesystem.
|
20
|
+
# - :relative_path - relative path to be used inside <img> tag.
|
21
|
+
# - :width - Image width
|
22
|
+
# - :height - Image height
|
23
|
+
#
|
24
|
+
# When the image is not found, returns nil.
|
25
|
+
def get(filename)
|
26
|
+
img = scan_img(filename)
|
27
|
+
if img
|
28
|
+
@images[filename] = img
|
29
|
+
end
|
30
|
+
img
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns all used images.
|
34
|
+
def all_used
|
35
|
+
@images.values
|
36
|
+
end
|
37
|
+
|
38
|
+
# Print warnings about all unused images.
|
39
|
+
def report_unused
|
40
|
+
scan_for_unused_images.each {|img| warn_unused(img) }
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def scan_img(filename)
|
46
|
+
full_path = File.join(@full_path, filename)
|
47
|
+
if File.exists?(File.join(@full_path, filename))
|
48
|
+
img_record(filename)
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Scans directory for image files, building a hash of image files
|
55
|
+
# found in that directory.
|
56
|
+
def scan_for_unused_images
|
57
|
+
unused = []
|
58
|
+
::Dir[@full_path+"/**/*.{png,jpg,jpeg,gif}"].each do |path|
|
59
|
+
filename = relative_path(@full_path, path)
|
60
|
+
unused << img_record(filename) unless @images[filename]
|
61
|
+
end
|
62
|
+
unused
|
63
|
+
end
|
64
|
+
|
65
|
+
def warn_unused(img)
|
66
|
+
Logger.warn(:image_unused, "Image not used.", img[:full_path])
|
67
|
+
end
|
68
|
+
|
69
|
+
def img_record(filename)
|
70
|
+
full_path = File.join(@full_path, filename)
|
71
|
+
width, height = Dimensions.dimensions(full_path)
|
72
|
+
|
73
|
+
return {
|
74
|
+
:filename => filename,
|
75
|
+
:relative_path => File.join(@relative_path, filename),
|
76
|
+
:full_path => full_path,
|
77
|
+
:width => width,
|
78
|
+
:height => height,
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
# Given a path to directory and a path to file, returns the path
|
83
|
+
# to this file relative to the given dir. For example:
|
84
|
+
#
|
85
|
+
# base_path("/foo/bar", "/foo/bar/baz/img.jpg") --> "baz/img.jpg"
|
86
|
+
#
|
87
|
+
def relative_path(dir_path, file_path)
|
88
|
+
file_path.slice(dir_path.length+1, file_path.length)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "jsduck/img/dir"
|
2
|
+
require "jsduck/logger"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module JsDuck
|
6
|
+
module Img
|
7
|
+
|
8
|
+
# A collection if Img::Dir objects.
|
9
|
+
#
|
10
|
+
# Looks up images from directories specified through --images
|
11
|
+
# option.
|
12
|
+
#
|
13
|
+
# This class provides the same interface as Img::Dir, except that
|
14
|
+
# the constructor takes array of full_paths not just one.
|
15
|
+
class DirSet
|
16
|
+
def initialize(full_paths, relative_path)
|
17
|
+
@dirs = full_paths.map {|path| Img::Dir.new(path, relative_path) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def get(filename)
|
21
|
+
@dirs.each do |dir|
|
22
|
+
if img = dir.get(filename)
|
23
|
+
return img
|
24
|
+
end
|
25
|
+
end
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def all_used
|
30
|
+
@dirs.map {|dir| dir.all_used }.flatten
|
31
|
+
end
|
32
|
+
|
33
|
+
def report_unused
|
34
|
+
@dirs.each {|dir| dir.report_unused }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "jsduck/logger"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module JsDuck
|
5
|
+
module Img
|
6
|
+
|
7
|
+
# Copies images to destination directory.
|
8
|
+
class Writer
|
9
|
+
# Takes an array of image records retrieved from
|
10
|
+
# Img::Dir#all_used or Img::DirSet#all_used and copies all of
|
11
|
+
# them to given output directory.
|
12
|
+
def self.copy(images, output_dir)
|
13
|
+
images.each do |img|
|
14
|
+
dest = File.join(output_dir, img[:filename])
|
15
|
+
Logger.log("Copying image", dest)
|
16
|
+
FileUtils.makedirs(File.dirname(dest))
|
17
|
+
FileUtils.cp(img[:full_path], dest)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'jsduck/logger'
|
2
|
+
|
3
|
+
module JsDuck
|
4
|
+
module Inline
|
5
|
+
|
6
|
+
# Takes care of the auto-detection of links in text.
|
7
|
+
class AutoLink
|
8
|
+
# Sets up instance to work in context of particular class, so it
|
9
|
+
# knows that #blah is in context of SomeClass.
|
10
|
+
attr_accessor :class_context
|
11
|
+
|
12
|
+
# Sets up instance to work in context of particular doc object.
|
13
|
+
# Used for error reporting.
|
14
|
+
attr_accessor :doc_context
|
15
|
+
|
16
|
+
def initialize(link_renderer)
|
17
|
+
@class_context = ""
|
18
|
+
@doc_context = {}
|
19
|
+
@relations = link_renderer.relations
|
20
|
+
@renderer = link_renderer
|
21
|
+
@magic_link_re = magic_link_re
|
22
|
+
end
|
23
|
+
|
24
|
+
# Looks input text for patterns like:
|
25
|
+
#
|
26
|
+
# My.ClassName
|
27
|
+
# MyClass#method
|
28
|
+
# #someProperty
|
29
|
+
#
|
30
|
+
# and converts them to links, as if they were surrounded with
|
31
|
+
# {@link} tag. One notable exception is that Foo is not created to
|
32
|
+
# link, even when Foo class exists, but Foo.Bar is. This is to
|
33
|
+
# avoid turning normal words into links. For example:
|
34
|
+
#
|
35
|
+
# Math involves a lot of numbers. Ext JS is a JavaScript framework.
|
36
|
+
#
|
37
|
+
# In these sentences we don't want to link "Math" and "Ext" to the
|
38
|
+
# corresponding JS classes. And that's why we auto-link only
|
39
|
+
# class names containing a dot "."
|
40
|
+
#
|
41
|
+
def replace(input)
|
42
|
+
input.gsub(@magic_link_re) do
|
43
|
+
cls = $1 || $3
|
44
|
+
member = $2 || $4
|
45
|
+
replace_magic_link(cls, member)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Generates regex for auto-linking class and member names in text.
|
52
|
+
def magic_link_re
|
53
|
+
ident_re = "(?:[A-Za-z_$][A-Za-z0-9_$]*)"
|
54
|
+
cls_re = "(#{ident_re}(?:\\.#{ident_re})*)"
|
55
|
+
ns_cls_re = "(#{ident_re}(?:\\.#{ident_re})+)"
|
56
|
+
member_re = "(?:#(#{ident_re}))"
|
57
|
+
/#{cls_re}#{member_re}|#{ns_cls_re}|#{member_re}/m
|
58
|
+
end
|
59
|
+
|
60
|
+
def replace_magic_link(cls, member)
|
61
|
+
if cls && member
|
62
|
+
if @relations[cls] && @renderer.get_matching_member(cls, {:name => member})
|
63
|
+
return @renderer.link(cls, member, cls+"."+member)
|
64
|
+
else
|
65
|
+
warn_magic_link("#{cls}##{member} links to non-existing " + (@relations[cls] ? "member" : "class"))
|
66
|
+
end
|
67
|
+
elsif cls
|
68
|
+
if @relations[cls]
|
69
|
+
return @renderer.link(cls, nil, cls)
|
70
|
+
else
|
71
|
+
cls2, member2 = split_to_cls_and_member(cls)
|
72
|
+
if @relations[cls2] && @renderer.get_matching_member(cls2, {:name => member2})
|
73
|
+
return @renderer.link(cls2, member2, cls2+"."+member2)
|
74
|
+
elsif cls =~ /\.(js|css|html|php)\Z/
|
75
|
+
# Ignore common filenames
|
76
|
+
else
|
77
|
+
warn_magic_link("#{cls} links to non-existing class")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
else
|
81
|
+
if @renderer.get_matching_member(@class_context, {:name => member})
|
82
|
+
return @renderer.link(@class_context, member, member)
|
83
|
+
elsif member =~ /\A([A-F0-9]{3}|[A-F0-9]{6})\Z/i || member =~ /\A[0-9]/
|
84
|
+
# Ignore HEX color codes and
|
85
|
+
# member names beginning with number
|
86
|
+
else
|
87
|
+
warn_magic_link("##{member} links to non-existing member")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
return "#{cls}#{member ? '#' : ''}#{member}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def split_to_cls_and_member(str)
|
95
|
+
parts = str.split(/\./)
|
96
|
+
return [parts.slice(0, parts.length-1).join("."), parts.last]
|
97
|
+
end
|
98
|
+
|
99
|
+
def warn_magic_link(msg)
|
100
|
+
Logger.warn(:link_auto, msg, @doc_context[:filename], @doc_context[:linenr])
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
data/lib/jsduck/inline/img.rb
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
require 'jsduck/util/html'
|
2
2
|
require 'jsduck/logger'
|
3
|
+
require 'pp'
|
3
4
|
|
4
5
|
module JsDuck
|
5
6
|
module Inline
|
6
7
|
|
7
8
|
# Implementation of inline tag {@img}
|
8
9
|
class Img
|
9
|
-
#
|
10
|
-
#
|
11
|
-
attr_accessor :base_path
|
12
|
-
|
13
|
-
# This will hold list of all image paths gathered from {@img} tags.
|
10
|
+
# Instance of Img::Dir or Img::DirSet that's used for looking up
|
11
|
+
# image information.
|
14
12
|
attr_accessor :images
|
15
13
|
|
14
|
+
# Sets up instance to work in context of particular doc object.
|
15
|
+
# Used for error reporting.
|
16
|
+
attr_accessor :doc_context
|
17
|
+
|
16
18
|
def initialize(opts={})
|
17
|
-
@tpl = opts[:img_tpl] || '<img src="%u" alt="%a"/>'
|
19
|
+
@tpl = opts[:img_tpl] || '<img src="%u" alt="%a" width="%w" height="%h"/>'
|
18
20
|
|
19
21
|
@re = /\{@img\s+(\S*?)(?:\s+(.+?))?\}/m
|
20
|
-
|
21
|
-
@base_path = nil
|
22
|
-
@images = []
|
23
22
|
end
|
24
23
|
|
25
24
|
# Takes StringScanner instance.
|
@@ -37,13 +36,22 @@ module JsDuck
|
|
37
36
|
|
38
37
|
# applies the image template
|
39
38
|
def apply_tpl(url, alt_text)
|
40
|
-
@images
|
39
|
+
img = @images.get(url)
|
40
|
+
if !img
|
41
|
+
Logger.warn(:image, "Image #{url} not found.", @doc_context[:filename], @doc_context[:linenr])
|
42
|
+
img = {}
|
43
|
+
end
|
44
|
+
|
41
45
|
@tpl.gsub(/(%\w)/) do
|
42
46
|
case $1
|
43
47
|
when '%u'
|
44
|
-
|
48
|
+
img[:relative_path]
|
45
49
|
when '%a'
|
46
50
|
Util::HTML.escape(alt_text||"")
|
51
|
+
when '%w'
|
52
|
+
img[:width]
|
53
|
+
when '%h'
|
54
|
+
img[:height]
|
47
55
|
else
|
48
56
|
$1
|
49
57
|
end
|