jsduck 4.6.1 → 4.6.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|