nanoc 4.11.13 → 4.11.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +790 -744
  3. data/lib/nanoc.rb +4 -2
  4. data/lib/nanoc/data_sources/filesystem.rb +12 -6
  5. data/lib/nanoc/extra.rb +1 -0
  6. data/lib/nanoc/extra/core_ext.rb +0 -1
  7. data/lib/nanoc/extra/link_collector.rb +1 -1
  8. data/lib/nanoc/extra/srcset_parser.rb +79 -0
  9. data/lib/nanoc/filters/colorize_syntax/colorizers.rb +1 -1
  10. data/lib/nanoc/filters/erb.rb +1 -5
  11. data/lib/nanoc/filters/relativize_paths.rb +62 -10
  12. data/lib/nanoc/filters/sass/functions.rb +1 -1
  13. data/lib/nanoc/helpers/rendering.rb +5 -4
  14. data/lib/nanoc/orig_cli.rb +0 -5
  15. data/lib/nanoc/rule_dsl.rb +1 -0
  16. data/lib/nanoc/rule_dsl/action_provider.rb +1 -1
  17. data/lib/nanoc/rule_dsl/compiler_dsl.rb +1 -1
  18. data/lib/nanoc/rule_dsl/errors.rb +25 -0
  19. data/lib/nanoc/rule_dsl/rules_loader.rb +1 -1
  20. data/lib/nanoc/version.rb +1 -1
  21. metadata +37 -33
  22. data/lib/nanoc/base.rb +0 -13
  23. data/lib/nanoc/base/changes_stream.rb +0 -53
  24. data/lib/nanoc/base/errors.rb +0 -65
  25. data/lib/nanoc/checking.rb +0 -14
  26. data/lib/nanoc/checking/check.rb +0 -93
  27. data/lib/nanoc/checking/checks.rb +0 -14
  28. data/lib/nanoc/checking/checks/css.rb +0 -16
  29. data/lib/nanoc/checking/checks/external_links.rb +0 -151
  30. data/lib/nanoc/checking/checks/html.rb +0 -16
  31. data/lib/nanoc/checking/checks/internal_links.rb +0 -95
  32. data/lib/nanoc/checking/checks/mixed_content.rb +0 -37
  33. data/lib/nanoc/checking/checks/stale.rb +0 -41
  34. data/lib/nanoc/checking/checks/w3c_validator.rb +0 -31
  35. data/lib/nanoc/checking/dsl.rb +0 -27
  36. data/lib/nanoc/checking/issue.rb +0 -16
  37. data/lib/nanoc/checking/loader.rb +0 -50
  38. data/lib/nanoc/checking/runner.rb +0 -136
  39. data/lib/nanoc/deploying.rb +0 -10
  40. data/lib/nanoc/deploying/deployer.rb +0 -45
  41. data/lib/nanoc/deploying/deployers.rb +0 -11
  42. data/lib/nanoc/deploying/deployers/fog.rb +0 -220
  43. data/lib/nanoc/deploying/deployers/git.rb +0 -112
  44. data/lib/nanoc/deploying/deployers/rsync.rb +0 -68
  45. data/lib/nanoc/extra/core_ext/pathname.rb +0 -27
  46. data/lib/nanoc/orig_cli/commands/check.rb +0 -43
  47. data/lib/nanoc/orig_cli/commands/deploy.rb +0 -126
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- module Nanoc
5
- module Int
6
- end
7
-
8
- module Base
9
- end
10
- end
11
-
12
- require_relative 'base/errors'
13
- require_relative 'base/changes_stream'
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc
4
- class ChangesStream
5
- class ChangesListener
6
- def initialize(y)
7
- @y = y
8
- end
9
-
10
- def unknown
11
- @y << :unknown
12
- end
13
-
14
- def lib
15
- @y << :lib
16
- end
17
-
18
- def to_stop(&block)
19
- if block_given?
20
- @to_stop = block
21
- else
22
- @to_stop
23
- end
24
- end
25
- end
26
-
27
- def initialize(enum: nil)
28
- @enum = enum
29
- @enum ||=
30
- Enumerator.new do |y|
31
- @listener = ChangesListener.new(y)
32
- yield(@listener)
33
- end.lazy
34
- end
35
-
36
- def stop
37
- @listener&.to_stop&.call
38
- end
39
-
40
- def map
41
- self.class.new(enum: @enum.map { |e| yield(e) })
42
- end
43
-
44
- def to_enum
45
- @enum
46
- end
47
-
48
- def each
49
- @enum.each { |e| yield(e) }
50
- nil
51
- end
52
- end
53
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc
4
- module Int
5
- # Module that contains all Nanoc-specific errors.
6
- #
7
- # @api private
8
- module Errors
9
- Generic = ::Nanoc::Core::Error
10
-
11
- NoSuchSnapshot = ::Nanoc::Core::Errors::NoSuchSnapshot
12
- CannotGetCompiledContentOfBinaryItem = ::Nanoc::Core::Errors::CannotGetCompiledContentOfBinaryItem
13
- CannotGetParentOrChildrenOfNonLegacyItem = ::Nanoc::Core::Errors::CannotGetParentOrChildrenOfNonLegacyItem
14
- CannotLayoutBinaryItem = ::Nanoc::Core::Errors::CannotLayoutBinaryItem
15
- UnknownLayout = ::Nanoc::Core::Errors::UnknownLayout
16
- CannotUseBinaryFilter = ::Nanoc::Core::Errors::CannotUseBinaryFilter
17
- CannotUseTextualFilter = ::Nanoc::Core::Errors::CannotUseTextualFilter
18
-
19
- # Error that is raised during site compilation when a layout is compiled
20
- # for which the filter cannot be determined. This is similar to the
21
- # {UnknownFilter} error, but specific for filters for layouts.
22
- class CannotDetermineFilter < Generic
23
- # @param [String] layout_identifier The identifier of the layout for
24
- # which the filter could not be determined
25
- def initialize(layout_identifier)
26
- super("The filter to be used for the “#{layout_identifier}” layout could not be determined. Make sure the layout does have a filter.")
27
- end
28
- end
29
-
30
- # Error that is raised when no rules file can be found in the current
31
- # working directory.
32
- class NoRulesFileFound < Generic
33
- def initialize
34
- super('This site does not have a rules file, which is required for Nanoc sites.')
35
- end
36
- end
37
-
38
- # Error that is raised when no compilation rule that can be applied to the
39
- # current item can be found.
40
- class NoMatchingCompilationRuleFound < Generic
41
- # @param [Nanoc::Core::Item] item The item for which no compilation rule
42
- # could be found
43
- def initialize(item)
44
- super("No compilation rules were found for the “#{item.identifier}” item.")
45
- end
46
- end
47
-
48
- # Error that is raised when no routing rule that can be applied to the
49
- # current item can be found.
50
- class NoMatchingRoutingRuleFound < Generic
51
- # @param [Nanoc::Core::ItemRep] rep The item repiresentation for which no
52
- # routing rule could be found
53
- def initialize(rep)
54
- super("No routing rules were found for the “#{rep.item.identifier}” item (rep “#{rep.name}”).")
55
- end
56
- end
57
-
58
- class AmbiguousMetadataAssociation < Generic
59
- def initialize(content_filenames, meta_filename)
60
- super("There are multiple content files (#{content_filenames.sort.join(', ')}) that could match the file containing metadata (#{meta_filename}).")
61
- end
62
- end
63
- end
64
- end
65
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- module Nanoc::Checking
5
- end
6
-
7
- require_relative 'checking/check'
8
- require_relative 'checking/checks'
9
- require_relative 'checking/dsl'
10
- require_relative 'checking/runner'
11
- require_relative 'checking/loader'
12
- require_relative 'checking/issue'
13
-
14
- Nanoc::Check = Nanoc::Checking::Check
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc::Checking
4
- # @api private
5
- class OutputDirNotFoundError < ::Nanoc::Core::Error
6
- def initialize(directory_path)
7
- super("Unable to run check against output directory at “#{directory_path}”: directory does not exist.")
8
- end
9
- end
10
-
11
- # @api private
12
- class Check < Nanoc::Core::Context
13
- extend DDPlugin::Plugin
14
-
15
- DDMemoize.activate(self)
16
-
17
- attr_reader :issues
18
-
19
- def self.define(ident, &block)
20
- klass = Class.new(::Nanoc::Checking::Check) { identifier(ident) }
21
- klass.send(:define_method, :run) do
22
- instance_exec(&block)
23
- end
24
- end
25
-
26
- def self.create(site)
27
- output_dir = site.config.output_dir
28
- unless File.exist?(output_dir)
29
- raise Nanoc::Checking::OutputDirNotFoundError.new(output_dir)
30
- end
31
-
32
- output_filenames = Dir[output_dir + '/**/*'].select { |f| File.file?(f) }
33
-
34
- # FIXME: ugly
35
- compiler = Nanoc::Core::Compiler.new_for(site)
36
- res = compiler.run_until_reps_built
37
- reps = res.fetch(:reps)
38
- view_context =
39
- Nanoc::Core::ViewContextForShell.new(
40
- items: site.items,
41
- reps: reps,
42
- )
43
-
44
- context = {
45
- items: Nanoc::Core::PostCompileItemCollectionView.new(site.items, view_context),
46
- layouts: Nanoc::Core::LayoutCollectionView.new(site.layouts, view_context),
47
- config: Nanoc::Core::ConfigView.new(site.config, view_context),
48
- output_filenames: output_filenames,
49
- }
50
-
51
- new(context)
52
- end
53
-
54
- def initialize(context)
55
- super(context)
56
-
57
- @issues = Set.new
58
- end
59
-
60
- def run
61
- raise NotImplementedError.new('Nanoc::Checking::Check subclasses must implement #run')
62
- end
63
-
64
- def add_issue(desc, subject: nil)
65
- # Simplify subject
66
- # FIXME: do not depend on working directory
67
- if subject&.start_with?(Dir.getwd)
68
- subject = subject[(Dir.getwd.size + 1)..subject.size]
69
- end
70
-
71
- @issues << Issue.new(desc, subject, self.class)
72
- end
73
-
74
- # @private
75
- def output_filenames
76
- super.reject { |f| excluded_patterns.any? { |pat| pat.match?(f) } }
77
- end
78
-
79
- # @private
80
- memoized def excluded_patterns
81
- @config
82
- .fetch(:checks, {})
83
- .fetch(:all, {})
84
- .fetch(:exclude_files, [])
85
- .map { |pattern| Regexp.new(pattern) }
86
- end
87
-
88
- # @private
89
- def output_html_filenames
90
- output_filenames.select { |f| File.extname(f) =~ /\A\.x?html?\z/ }
91
- end
92
- end
93
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- module Nanoc::Checking::Checks
5
- end
6
-
7
- require_relative 'checks/w3c_validator'
8
-
9
- require_relative 'checks/css'
10
- require_relative 'checks/external_links'
11
- require_relative 'checks/html'
12
- require_relative 'checks/internal_links'
13
- require_relative 'checks/mixed_content'
14
- require_relative 'checks/stale'
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ::Nanoc::Checking::Checks
4
- # @api private
5
- class CSS < ::Nanoc::Checking::Checks::W3CValidator
6
- identifier :css
7
-
8
- def extension
9
- 'css'
10
- end
11
-
12
- def validator_class
13
- ::W3CValidators::CSSValidator
14
- end
15
- end
16
- end
@@ -1,151 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ::Nanoc::Checking::Checks
4
- # A validator that verifies that all external links point to a location that exists.
5
- #
6
- # @api private
7
- class ExternalLinks < ::Nanoc::Checking::Check
8
- identifiers :external_links, :elinks
9
-
10
- def run
11
- # Find all broken external hrefs
12
- # TODO: de-duplicate this (duplicated in internal links check)
13
- filenames = output_html_filenames.reject { |f| excluded_file?(f) }
14
- hrefs_with_filenames = ::Nanoc::Extra::LinkCollector.new(filenames, :external).filenames_per_href
15
- results = select_invalid(hrefs_with_filenames.keys.shuffle)
16
-
17
- # Report them
18
- results.each do |res|
19
- filenames = hrefs_with_filenames[res.href]
20
- filenames.each do |filename|
21
- add_issue(
22
- "broken reference to #{res.href}: #{res.explanation}",
23
- subject: filename,
24
- )
25
- end
26
- end
27
- end
28
-
29
- class Result
30
- attr_reader :href
31
- attr_reader :explanation
32
-
33
- def initialize(href, explanation)
34
- @href = href
35
- @explanation = explanation
36
- end
37
- end
38
-
39
- def select_invalid(hrefs)
40
- ::Parallel.map(hrefs, in_threads: 10) { |href| validate(href) }.compact
41
- end
42
-
43
- def validate(href)
44
- # Parse
45
- url = nil
46
- begin
47
- url = URI.parse(href)
48
- rescue URI::Error
49
- return Result.new(href, 'invalid URI')
50
- end
51
-
52
- # Skip excluded URLs
53
- return nil if excluded?(href)
54
-
55
- # Skip non-HTTP URLs
56
- return nil if url.scheme !~ /^https?$/
57
-
58
- # Get status
59
- res = nil
60
- last_err = nil
61
- timeouts = [3, 5, 10, 30, 60]
62
- 5.times do |i|
63
- begin
64
- Timeout.timeout(timeouts[i]) do
65
- res = request_url_once(url)
66
- end
67
- rescue => e
68
- last_err = e
69
- next
70
- end
71
-
72
- if /^3..$/.match?(res.code)
73
- if i == 4
74
- return Result.new(href, 'too many redirects')
75
- end
76
-
77
- location = extract_location(res, url)
78
- return Result.new(href, 'redirection without a target location') if location.nil?
79
-
80
- if /^30[18]$/.match?(res.code)
81
- return Result.new(href, "link has moved permanently to '#{location}'")
82
- end
83
-
84
- url = URI.parse(location)
85
- elsif res.code == '200'
86
- return nil
87
- else
88
- return Result.new(href, res.code)
89
- end
90
- end
91
- if last_err
92
- return Result.new(href, last_err.message)
93
- else
94
- raise Nanoc::Core::Errors::InternalInconsistency, 'last_err cannot be nil'
95
- end
96
- end
97
-
98
- def extract_location(res, url)
99
- location = res['Location']
100
-
101
- case location
102
- when nil
103
- nil
104
- when /^https?:\/\//
105
- location
106
- else
107
- base_url = url.dup
108
- base_url.path = (/^\//.match?(location) ? '' : '/')
109
- base_url.query = nil
110
- base_url.fragment = nil
111
- base_url.to_s + location
112
- end
113
- end
114
-
115
- def path_for_url(url)
116
- path =
117
- if url.path.nil? || url.path.empty?
118
- '/'
119
- else
120
- url.path
121
- end
122
-
123
- if url.query
124
- path = path + '?' + url.query
125
- end
126
-
127
- path
128
- end
129
-
130
- def request_url_once(url)
131
- req = Net::HTTP::Get.new(path_for_url(url))
132
- req['User-Agent'] = "Mozilla/5.0 Nanoc/#{Nanoc::VERSION} (link rot checker)"
133
- http = Net::HTTP.new(url.host, url.port)
134
- if url.instance_of? URI::HTTPS
135
- http.use_ssl = true
136
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
137
- end
138
- http.request(req)
139
- end
140
-
141
- def excluded?(href)
142
- excludes = @config.fetch(:checks, {}).fetch(:external_links, {}).fetch(:exclude, [])
143
- excludes.any? { |pattern| Regexp.new(pattern).match(href) }
144
- end
145
-
146
- def excluded_file?(file)
147
- excludes = @config.fetch(:checks, {}).fetch(:external_links, {}).fetch(:exclude_files, [])
148
- excludes.any? { |pattern| Regexp.new(pattern).match(file) }
149
- end
150
- end
151
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ::Nanoc::Checking::Checks
4
- # @api private
5
- class HTML < ::Nanoc::Checking::Checks::W3CValidator
6
- identifier :html
7
-
8
- def extension
9
- '{htm,html}'
10
- end
11
-
12
- def validator_class
13
- ::W3CValidators::NuValidator
14
- end
15
- end
16
- end
@@ -1,95 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc::Checking::Checks
4
- # A check that verifies that all internal links point to a location that exists.
5
- #
6
- # @api private
7
- class InternalLinks < ::Nanoc::Checking::Check
8
- identifiers :internal_links, :ilinks
9
-
10
- # Starts the validator. The results will be printed to stdout.
11
- #
12
- # Internal links that match a regexp pattern in `@config[:checks][:internal_links][:exclude]` will
13
- # be skipped.
14
- #
15
- # @return [void]
16
- def run
17
- # TODO: de-duplicate this (duplicated in external links check)
18
- filenames = output_html_filenames
19
- uris = ::Nanoc::Extra::LinkCollector.new(filenames, :internal).filenames_per_href
20
-
21
- uris.each_pair do |href, fns|
22
- fns.each do |filename|
23
- next if valid?(href, filename)
24
-
25
- add_issue(
26
- "broken reference to #{href}",
27
- subject: filename,
28
- )
29
- end
30
- end
31
- end
32
-
33
- protected
34
-
35
- def valid?(href, origin)
36
- # Skip hrefs that point to self
37
- # FIXME: this is ugly and won’t always be correct
38
- return true if href == '.'
39
-
40
- # Turn file: into output_dir-as-root relative paths
41
-
42
- output_dir = @config.output_dir
43
- output_dir += '/' unless output_dir.end_with?('/')
44
- base_uri = URI("file://#{output_dir}")
45
- path = href.sub(/#{base_uri}/, '').sub(/file:\/{1,3}/, '')
46
-
47
- path = "/#{path}" unless path.start_with?('/')
48
-
49
- # Skip hrefs that are specified in the exclude configuration
50
- return true if excluded?(path, origin)
51
-
52
- # Make an absolute path
53
- path = ::File.join(output_dir, path[1..path.length])
54
-
55
- # Remove fragment
56
- path = path.sub(/#.*$/, '')
57
- return true if path.empty?
58
-
59
- # Remove query string
60
- path = path.sub(/\?.*$/, '')
61
- return true if path.empty?
62
-
63
- # Decode URL (e.g. '%20' -> ' ')
64
- path = CGI.unescape(path)
65
-
66
- # Check whether file exists
67
- return true if File.file?(path)
68
-
69
- # Check whether directory with index file exists
70
- return true if File.directory?(path) && @config[:index_filenames].any? { |fn| File.file?(File.join(path, fn)) }
71
-
72
- # Nope :(
73
- false
74
- end
75
-
76
- def excluded?(href, origin)
77
- config = @config.fetch(:checks, {}).fetch(:internal_links, {})
78
- excluded_target?(href, config) || excluded_origin?(origin, config)
79
- end
80
-
81
- def excluded_target?(href, config)
82
- excludes = config.fetch(:exclude_targets, config.fetch(:exclude, []))
83
- excludes.any? { |pattern| Regexp.new(pattern).match(href) }
84
- end
85
-
86
- def excluded_origin?(origin, config)
87
- # FIXME: do not depend on current working directory
88
- origin = File.absolute_path(origin)
89
-
90
- relative_origin = origin[@config.output_dir.size..-1]
91
- excludes = config.fetch(:exclude_origins, [])
92
- excludes.any? { |pattern| Regexp.new(pattern).match(relative_origin) }
93
- end
94
- end
95
- end