jekyll-sanity-checker 1.0.0 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c87617b0a35196dbf0935ed56672d5125d96ae6c
4
- data.tar.gz: cfc830023fefb829707cd486de73e6bf64fdb5df
3
+ metadata.gz: 51965e489751bb5c1ec0187035e7a81d34ac1f88
4
+ data.tar.gz: bc471f5d84895af28fa695de6bc1d705e0663d8e
5
5
  SHA512:
6
- metadata.gz: 0ceee07753ade7b5da1ebec8c12c82510a3fade2b670867ce7f2ec13b47072d2ae221f46990e48e0ef3149757e02d7de2377364ec7e82fe842585e9980b93d6d
7
- data.tar.gz: 147e95264502c2073444b6aa18d904905675311518c0aa8b44c8eb90a62cbed573b0927d5f64dd85be0e324fe6806d78af24f143592533524352d161358d3fb8
6
+ metadata.gz: 5869a4095f6d4bffaff7c06440425fca4009641d3b29ca7c8530a1513168546427db0f0a3481bfb3dd25d03ec9d423200b555f08b159dbf24c8d269f317a844a
7
+ data.tar.gz: a9fe702be95c54a4e2e290b4cb811b3ce42f83a68e88e679788cf70a0bdb03e28470246ba71797aedb1ebf7faa778484aedde3e74ac6764ebf5f8407eb7a7c75
@@ -7,6 +7,7 @@ require 'jekyll_sanity_checker/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'jekyll-sanity-checker'
9
9
  spec.version = JekyllSanityChecker::VERSION
10
+ spec.homepage = 'https://github.com/fastly/jekyll-sanity-checker'
10
11
  spec.authors = ['Edmund Huber']
11
12
  spec.email = 'edmund@fastly.com'
12
13
  spec.license = 'MIT'
@@ -23,6 +24,7 @@ Gem::Specification.new do |spec|
23
24
 
24
25
  spec.add_development_dependency 'bundler', '~> 1.6'
25
26
  spec.add_development_dependency 'minitest', '~> 5.0'
27
+ spec.add_development_dependency 'pry'
26
28
  spec.add_development_dependency 'rake'
27
29
  spec.add_development_dependency 'rubocop', '~> 0.25', '>= 0.25.0'
28
30
  end
@@ -2,10 +2,9 @@ require 'addressable/uri'
2
2
  require 'css_parser'
3
3
  require 'nokogiri'
4
4
 
5
- module JekyllSanityChecker
6
- class CheckFailed < StandardError
7
- end
5
+ require 'jekyll_sanity_checker/errors'
8
6
 
7
+ module JekyllSanityChecker
9
8
  def process
10
9
  super
11
10
 
@@ -23,43 +22,60 @@ module JekyllSanityChecker
23
22
  run_success = true
24
23
 
25
24
  Dir.glob("#{dest}/**/*.html") do |html|
26
- run_success = run_success && JekyllSanityChecker.check_html(@config, html)
25
+ run_success = JekyllSanityChecker.check_html(@config, html) && run_success
27
26
  end
28
27
 
29
28
  Dir.glob("#{dest}/**/*.css") do |css|
30
- run_success = run_success && JekyllSanityChecker.check_css(@config, css)
29
+ run_success = JekyllSanityChecker.check_css(@config, css) && run_success
31
30
  end
32
31
 
33
- raise Jekyll::Errors::FatalException, 'checks failed' unless run_success
32
+ # If Jekyll is run with --watch , don't raise FatalException, just warn. Fixes #4 .
33
+ raise Jekyll::Errors::FatalException, 'checks failed' if !config.fetch('watch', false) && !run_success
34
+ end
35
+
36
+ def self.aggregate_errors(page, el = nil)
37
+ yield
38
+ true
39
+ rescue CheckFailed => e
40
+ if el.nil?
41
+ Jekyll.logger.error("problem on #{page}")
42
+ else
43
+ Jekyll.logger.error("problem on #{page} , line #{el.line}")
44
+ Jekyll.logger.error(el.to_s.strip)
45
+ end
46
+ Jekyll.logger.error(e.message)
47
+ false
34
48
  end
35
49
 
36
50
  def check_html(config, page)
37
51
  success = true
38
52
  doc = Nokogiri::HTML::Document.parse File.read(page)
53
+
39
54
  doc.css('a').each do |link|
40
- begin
55
+ success = aggregate_errors(page, link) do
41
56
  # Does the link have an href atttribute?
42
- raise CheckFailed, 'no href on <a>' unless link.keys.include? 'href'
57
+ raise LinkNoHref unless link.keys.include? 'href'
43
58
 
44
59
  # If it's a hash-only URL then that's fine.
45
60
  next if link['href'].start_with? '#'
46
61
 
47
- check_resource(config, link['href'], %w(mailto), true)
48
- rescue CheckFailed => e
49
- Jekyll.logger.error("on #{page}, line #{link.line}, problem with '#{link}': #{e.message}")
50
- success = false
51
- end
62
+ # What's the URL of this page when it's deployed?
63
+ absolute_destination = File.expand_path(config['destination'])
64
+ from_url = File.expand_path(page)
65
+ raise 'terrible problem' unless from_url.slice!(absolute_destination) == absolute_destination
66
+
67
+ check_resource(config, link['href'], %w(mailto), true, from_url)
68
+ end && success
52
69
  end
70
+
53
71
  doc.css('img').each do |img|
54
- begin
72
+ success = aggregate_errors(page, img) do
55
73
  # Does the img have a src attribute?
56
- raise CheckFailed, 'no src on <img>' unless img.keys.include? 'src'
74
+ raise ImgNoSrc unless img.keys.include? 'src'
57
75
  check_resource(config, img['src'], %w(data))
58
- rescue CheckFailed => e
59
- Jekyll.logger.error("on #{page}, line #{img.line}, problem with '#{img}': #{e.message}")
60
- success = false
61
- end
76
+ end && success
62
77
  end
78
+
63
79
  success
64
80
  end
65
81
  module_function :check_html
@@ -72,12 +88,9 @@ module JekyllSanityChecker
72
88
  rule_set.each_declaration do |_, value, _|
73
89
  match = /url\((.*?)\)/.match value
74
90
  next if match.nil?
75
- begin
91
+ success = aggregate_errors(stylesheet) do
76
92
  check_resource(config, match[1].tr('\'"', ''), %w(data))
77
- rescue CheckFailed => e
78
- Jekyll.logger.error("in #{stylesheet}, problem with selector '#{rule_set.selectors.join ' '}': #{e.message}")
79
- success = false
80
- end
93
+ end && success
81
94
  end
82
95
  end
83
96
  success
@@ -101,23 +114,23 @@ module JekyllSanityChecker
101
114
  end
102
115
  module_function :load_redirects
103
116
 
104
- def self.check_resource(config, src, shortcircuit_valid_schemes = [], loose_html = false)
117
+ def self.check_resource(config, src, shortcircuit_valid_schemes = [], loose_html = false, from_url = nil)
105
118
  # Parse the URL,
106
119
  begin
107
120
  uri = Addressable::URI.parse(src)
108
121
  rescue Addressable::URI::InvalidURIError
109
- raise CheckFailed, 'not a URI'
122
+ raise InvalidURL, src
110
123
  end
111
124
 
112
125
  # Maybe the scheme tells us that the URI is okay.
113
126
  return if shortcircuit_valid_schemes.include? uri.scheme
114
127
 
115
128
  # Otherwise the scheme should be http or https.
116
- raise CheckFailed, 'not a valid scheme' unless [nil, 'http', 'https'].include? uri.scheme
129
+ raise InvalidScheme, uri.scheme unless [nil, 'http', 'https'].include? uri.scheme
117
130
 
118
131
  # Only absolute URLs please.
119
132
  this_host = config['sanity_check']['host']
120
- raise CheckFailed, "fully-qualified URL back to #{this_host}" if uri.host == this_host
133
+ raise FullyQualifiedURL, uri if uri.host == this_host
121
134
 
122
135
  # Resolve redirects..
123
136
  if @redirects.include? uri.path
@@ -134,25 +147,32 @@ module JekyllSanityChecker
134
147
  # And if it's a local resource,
135
148
  if uri.host.nil?
136
149
  # Check that it's not a relative url.
137
- raise CheckFailed, 'relative URL' unless uri.path.start_with? '/'
150
+ raise RelativeURL.new(uri, from_url) unless uri.path.start_with? '/'
138
151
 
139
152
  parts = URI.unescape(uri.path).split('/').reject { |s| s.empty? }
140
153
  path = ([File.expand_path(config['destination'])] + parts).join(File::SEPARATOR)
141
154
 
142
155
  if loose_html
143
- if File.directory?(path)
156
+ if self.file?(path) && File.directory?(path)
144
157
  # We treat links to directories special: if there is a directory/index.html, it is ok.
145
- raise CheckFailed, 'directory reference without an index.html' unless File.file? "#{path}/index.html"
158
+ raise NoIndexHTML, path unless self.file? "#{path}/index.html"
146
159
  else
147
160
  # A bla.html can be referenced as bla.
148
- raise CheckFailed, "file '#{path}' does not exist" unless File.file?(path) || File.file?("#{path}.html")
161
+ raise FileDoesNotExist, path unless self.file?(path) || self.file?("#{path}.html")
149
162
  end
150
163
  else
151
164
  # Check that the file exists.
152
- raise CheckFailed, "file '#{path}' does not exist" unless File.file? path
165
+ raise FileDoesNotExist, path unless self.file? path
153
166
  end
154
167
  end
155
168
  end
169
+
170
+ def self.file?(fn)
171
+ # This relies on a not-obvious property of File.{dirname,basename}: for
172
+ # files, those functions behave as you would expect. For directories,
173
+ # File.dirname('lib') returns '.' and File.basename('lib') returns 'lib'.
174
+ Dir.entries(File.dirname(fn)).include? File.basename(fn)
175
+ end
156
176
  end
157
177
 
158
178
  module Jekyll
@@ -0,0 +1,84 @@
1
+ module JekyllSanityChecker
2
+ class CheckFailed < StandardError
3
+ def message
4
+ end
5
+ end
6
+
7
+ class LinkNoHref < CheckFailed
8
+ def message
9
+ "A link (<a>) should always contain an 'href' attribute, even if it's only 'href=\"#\"'."
10
+ end
11
+ end
12
+
13
+ class ImgNoSrc < CheckFailed
14
+ def message
15
+ "Every <img> should have a 'src' attribute."
16
+ end
17
+ end
18
+
19
+ class InvalidURL < CheckFailed
20
+ def initialize(url)
21
+ @url = url
22
+ end
23
+
24
+ def message
25
+ "'#{@url}' is not a valid URL."
26
+ end
27
+ end
28
+
29
+ class InvalidScheme < CheckFailed
30
+ def initialize(scheme)
31
+ @scheme = scheme
32
+ end
33
+
34
+ def message
35
+ "'#{@scheme}' is not an appropriate scheme in this context."
36
+ end
37
+ end
38
+
39
+ class FullyQualifiedURL < CheckFailed
40
+ def initialize(uri)
41
+ @uri = uri
42
+ end
43
+
44
+ def message
45
+ "'#{@uri}' is unnecessarily fully-qualified. It may as well be written '#{@uri.path}'."
46
+ end
47
+ end
48
+
49
+ class NoIndexHTML < CheckFailed
50
+ def initialize(path)
51
+ @path = path
52
+ end
53
+
54
+ def message
55
+ "There is no '#{@path}/index.html', so this link won't work."
56
+ end
57
+ end
58
+
59
+ class RelativeURL < CheckFailed
60
+ def initialize(uri, from_url)
61
+ @uri = uri
62
+ @from_url = from_url
63
+ end
64
+
65
+ def message
66
+ if @from_url
67
+ did_you_mean_url = Addressable::URI.join(@from_url, @uri)
68
+ "'#{@uri}' is a relative URL, which are not recommended. Did you mean '#{did_you_mean_url}' ?"
69
+ else
70
+ "'#{@uri}' is a relative URL, which are not recommended."
71
+ end
72
+ end
73
+ end
74
+
75
+ class FileDoesNotExist < CheckFailed
76
+ def initialize(path)
77
+ @path = path
78
+ end
79
+
80
+ def message
81
+ "The file '#{@path}' does not exist."
82
+ end
83
+ end
84
+ end
@@ -1,3 +1,3 @@
1
1
  module JekyllSanityChecker
2
- VERSION = '1.0.0'
2
+ VERSION = '1.0.2'
3
3
  end
@@ -0,0 +1 @@
1
+ <a></a>
@@ -0,0 +1 @@
1
+ <a href="/test%20with%20spaces.html"></a>
@@ -0,0 +1 @@
1
+ <a href="/OTHER.html"></a>
@@ -0,0 +1 @@
1
+ <a href="b/other.html"></a>
@@ -23,7 +23,7 @@ $capture_error = CaptureError.new # rubocop:disable Style/GlobalVars
23
23
  Jekyll.logger = $capture_error # rubocop:disable Style/GlobalVars
24
24
  Jekyll.logger.log_level = :error
25
25
 
26
- def refute_sane(page, error)
26
+ def configure_jekyll(page)
27
27
  config_hash = {
28
28
  'source' => "spec/fixtures/#{page}",
29
29
  'destination' => 'spec/output',
@@ -31,28 +31,38 @@ def refute_sane(page, error)
31
31
  'host' => 'www.computers.com'
32
32
  }
33
33
  }
34
- site = Jekyll::Site.new(Jekyll.configuration(config_hash))
34
+ Jekyll::Site.new(Jekyll.configuration(config_hash))
35
+ end
36
+
37
+ def refute_sane(page, error)
38
+ site = configure_jekyll(page)
35
39
  assert_raises Jekyll::Errors::FatalException do
36
40
  site.process
37
41
  end
38
42
  assert_match error, $capture_error.captured # rubocop:disable Style/GlobalVars
39
43
  end
40
44
 
45
+ def assert_sane(page)
46
+ site = configure_jekyll(page)
47
+ site.process
48
+ end
49
+
41
50
  describe 'Jekyll sanity checker' do
42
51
  it 'warns about fully-qualified URLs' do
43
- refute_sane 'fully-qualified-url', /fully-qualified URL back to/
52
+ refute_sane 'fully-qualified-url', /is unnecessarily fully-qualified/
44
53
  end
45
54
 
46
55
  it 'warns about unrecognizable schemes' do
47
- refute_sane 'unrecognizable-scheme', /not a valid scheme/
56
+ refute_sane 'unrecognizable-scheme', /not an appropriate scheme/
48
57
  end
49
58
 
50
59
  it 'warns about relative URLs' do
51
- refute_sane 'relative-url', /relative URL/
60
+ # Jekyll logger appends a space. Don't know why.
61
+ refute_sane 'relative-url', "'b/other.html' is a relative URL, which are not recommended. Did you mean '/a/b/other.html' ? "
52
62
  end
53
63
 
54
64
  it 'warns about missing index.html' do
55
- refute_sane 'no-index-html', /without an index\.html/
65
+ refute_sane 'no-index-html', /There is no.*index\.html/
56
66
  end
57
67
 
58
68
  it 'warns about missing files' do
@@ -60,11 +70,11 @@ describe 'Jekyll sanity checker' do
60
70
  end
61
71
 
62
72
  it 'warns about <img> without src' do
63
- refute_sane 'img-missing-src', /no src on <img>/
73
+ refute_sane 'img-missing-src', /should have a 'src'/
64
74
  end
65
75
 
66
76
  it 'warns about <img> with fully-qualified URL src' do
67
- refute_sane 'img-fully-qualified-url', /fully-qualified URL back to/
77
+ refute_sane 'img-fully-qualified-url', /is unnecessarily fully-qualified/
68
78
  end
69
79
 
70
80
  it 'warns about <img> with relative URL src' do
@@ -80,6 +90,22 @@ describe 'Jekyll sanity checker' do
80
90
  end
81
91
 
82
92
  it 'warns about malformed URLs in <a>' do
83
- refute_sane 'a-invalid-uri', /not a URI/
93
+ refute_sane 'a-invalid-uri', /not a valid URL/
94
+ end
95
+
96
+ it 'warns about <a> missing href attribute' do
97
+ refute_sane 'a-missing-href', /should always contain/
98
+ end
99
+
100
+ # Because this test could only fail on case-insensitive filesystems, e.g. OSX
101
+ # with HFS+, if you're concerned that you might have broken related code, or
102
+ # you want to do a full test run, you should also run this test on one such
103
+ # vulnerable system.
104
+ it 'warns about missing files in spite of a case-insensitive filesystem' do
105
+ refute_sane 'missing-file-with-case-sensitivity', /does not exist/
106
+ end
107
+
108
+ it 'can handle spaces in links' do
109
+ assert_sane 'fixed-bug-spaces-in-urls'
84
110
  end
85
111
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-sanity-checker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edmund Huber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-09 00:00:00.000000000 Z
11
+ date: 2014-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: css_parser
@@ -92,6 +92,20 @@ dependencies:
92
92
  - - ~>
93
93
  - !ruby/object:Gem::Version
94
94
  version: '5.0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: pry
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
95
109
  - !ruby/object:Gem::Dependency
96
110
  name: rake
97
111
  requirement: !ruby/object:Gem::Requirement
@@ -139,22 +153,28 @@ files:
139
153
  - Rakefile
140
154
  - jekyll-sanity-checker.gemspec
141
155
  - lib/jekyll_sanity_checker.rb
156
+ - lib/jekyll_sanity_checker/errors.rb
142
157
  - lib/jekyll_sanity_checker/version.rb
143
158
  - spec/fixtures/a-invalid-uri/test.html
159
+ - spec/fixtures/a-missing-href/test.html
144
160
  - spec/fixtures/css-url-missing-file/test.css
161
+ - spec/fixtures/fixed-bug-spaces-in-urls/test with spaces.html
162
+ - spec/fixtures/fixed-bug-spaces-in-urls/test.html
145
163
  - spec/fixtures/fully-qualified-url/test.html
146
164
  - spec/fixtures/img-fully-qualified-url/test.html
147
165
  - spec/fixtures/img-missing-file/test.html
148
166
  - spec/fixtures/img-missing-src/test.html
149
167
  - spec/fixtures/img-relative-url/test.html
150
168
  - spec/fixtures/img-relative-url/thing
169
+ - spec/fixtures/missing-file-with-case-sensitivity/other.html
170
+ - spec/fixtures/missing-file-with-case-sensitivity/test.html
151
171
  - spec/fixtures/missing-file/test.html
152
172
  - spec/fixtures/no-index-html/no-index/crumb
153
173
  - spec/fixtures/no-index-html/test.html
154
- - spec/fixtures/relative-url/test.html
174
+ - spec/fixtures/relative-url/a/test.html
155
175
  - spec/fixtures/unrecognizable-scheme/test.html
156
176
  - spec/lib/sanity_checker.rb
157
- homepage:
177
+ homepage: https://github.com/fastly/jekyll-sanity-checker
158
178
  licenses:
159
179
  - MIT
160
180
  metadata: {}
@@ -181,17 +201,22 @@ summary: A Jekyll plugin that checks the generated HTML and CSS for common mista
181
201
  test_files:
182
202
  - jekyll-sanity-checker.gemspec
183
203
  - spec/fixtures/a-invalid-uri/test.html
204
+ - spec/fixtures/a-missing-href/test.html
184
205
  - spec/fixtures/css-url-missing-file/test.css
206
+ - spec/fixtures/fixed-bug-spaces-in-urls/test with spaces.html
207
+ - spec/fixtures/fixed-bug-spaces-in-urls/test.html
185
208
  - spec/fixtures/fully-qualified-url/test.html
186
209
  - spec/fixtures/img-fully-qualified-url/test.html
187
210
  - spec/fixtures/img-missing-file/test.html
188
211
  - spec/fixtures/img-missing-src/test.html
189
212
  - spec/fixtures/img-relative-url/test.html
190
213
  - spec/fixtures/img-relative-url/thing
214
+ - spec/fixtures/missing-file-with-case-sensitivity/other.html
215
+ - spec/fixtures/missing-file-with-case-sensitivity/test.html
191
216
  - spec/fixtures/missing-file/test.html
192
217
  - spec/fixtures/no-index-html/no-index/crumb
193
218
  - spec/fixtures/no-index-html/test.html
194
- - spec/fixtures/relative-url/test.html
219
+ - spec/fixtures/relative-url/a/test.html
195
220
  - spec/fixtures/unrecognizable-scheme/test.html
196
221
  - spec/lib/sanity_checker.rb
197
222
  has_rdoc:
@@ -1 +0,0 @@
1
- <a href="relative"></a>