nanoc 4.9.9 → 4.10.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,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c23c8c05886939d4bc9c4e4799112a9ef174381d43c1873cbbbac67c097bc36
4
- data.tar.gz: bc39d8366fc4bab70f88b9ed0e922e6de85d69f45385261e2d2c909f67286e09
3
+ metadata.gz: 046c9869b2f916776a4f0fe741916842cbbc68b584e11b606abac58b854a9f44
4
+ data.tar.gz: aeb902ad7fca66c89b63d46e2e9e8508c08e1cc3cdeb8dcbf0c45698d8c6a905
5
5
  SHA512:
6
- metadata.gz: '05993eb336217ff6e8563b1d5d7f9adbded355ba096f69aed6d1acf9bcde1fbe8dce4452bfd51deb5e9364876a4a9a0e9603047b3fe64c31769e24c67a3d7ea2'
7
- data.tar.gz: 83201d2e62a3439c0824b43490c946c2009b944016344d65d3a2398d5b966d82431dee4a44bc8f5e8dbf281ddf3908e27e3d4c5260465c1690fb3ea6afffd51d
6
+ metadata.gz: 93124b5f9eab590c046853ef39aa1c58516a365f2f721e7f1de73a34c0c4916ea1c3331a6c5db5e218e5921b0ef77abb5b50f5caf832d832eb0cc328133d58a8
7
+ data.tar.gz: f3c544d01d70da7ac4379311208fd57932ba26ca157b039fdda05d4c96f8c7f6fbe93a45376ad4bf4e2cf3c10e5c2e9a6157a8b729dd58006a675b9ffc3a48ce
data/NEWS.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Nanoc news
2
2
 
3
+ ## 4.10.0 (2018-10-14)
4
+
5
+ Features:
6
+
7
+ * Added source map support to the Sass filter (#1365) [Gregory Pakosz]
8
+ * Added a `nanoc()` Sass function (#1365) [Gregory Pakosz]
9
+
10
+ Fixes:
11
+
12
+ * Ensured breadcrumb trail always ends in item itself (#1367)
13
+
14
+ Enhancemens:
15
+
16
+ * Made Nanoc error when meta-file cannot be unambiguously associated (#1370)
17
+
3
18
  ## 4.9.9 (2018-10-06)
4
19
 
5
20
  Fixes:
@@ -236,6 +236,12 @@ module Nanoc::Int
236
236
  end
237
237
  end
238
238
 
239
+ class AmbiguousMetadataAssociation < Generic
240
+ def initialize(content_filenames, meta_filename)
241
+ super("There are multiple content files (#{content_filenames.join(', ')}) that could match the file containing metadata (#{meta_filename}).")
242
+ end
243
+ end
244
+
239
245
  class InternalInconsistency < Generic
240
246
  end
241
247
  end
@@ -93,8 +93,8 @@ end
93
93
 
94
94
  # Tracking issue:
95
95
  # https://github.com/nanoc/features/issues/24
96
- Nanoc::Feature.define('live_cmd', version: '4.9')
96
+ Nanoc::Feature.define('live_cmd', version: '4.10')
97
97
 
98
98
  # Tracking issue:
99
99
  # https://github.com/nanoc/features/issues/40
100
- Nanoc::Feature.define('toml', version: '4.9')
100
+ Nanoc::Feature.define('toml', version: '4.10')
@@ -34,7 +34,7 @@ module Nanoc
34
34
  class << self
35
35
  def define(ident, &block)
36
36
  filter_class = Class.new(::Nanoc::Filter) { identifier(ident) }
37
- filter_class.send(:define_method, :run) do |content, params|
37
+ filter_class.send(:define_method, :run) do |content, params = {}|
38
38
  instance_exec(content, params, &block)
39
39
  end
40
40
  end
@@ -176,30 +176,49 @@ module Nanoc::DataSources
176
176
 
177
177
  return [] if dir_name.nil?
178
178
 
179
- all_split_files_in(dir_name).each do |base_filename, (meta_ext, content_exts)|
180
- content_exts.each do |content_ext|
181
- meta_filename = filename_for(base_filename, meta_ext)
182
- content_filename = filename_for(base_filename, content_ext)
183
-
184
- proto_doc = read_proto_document(content_filename, meta_filename, klass)
185
-
186
- content = content_for(proto_doc, content_filename)
187
- attributes = attributes_for(proto_doc, content_filename, meta_filename)
188
- identifier = identifier_for(content_filename, meta_filename, dir_name)
189
-
190
- res << klass.new(
191
- content,
192
- attributes,
193
- identifier,
194
- content_checksum_data: content_checksum_data_for(proto_doc),
195
- attributes_checksum_data: attributes_checksum_data_for(proto_doc, content_filename, meta_filename),
196
- )
197
- end
179
+ each_content_meta_pair_in(dir_name) do |content_filename, meta_filename|
180
+ proto_doc = read_proto_document(content_filename, meta_filename, klass)
181
+
182
+ content = content_for(proto_doc, content_filename)
183
+ attributes = attributes_for(proto_doc, content_filename, meta_filename)
184
+ identifier = identifier_for(content_filename, meta_filename, dir_name)
185
+
186
+ res << klass.new(
187
+ content,
188
+ attributes,
189
+ identifier,
190
+ content_checksum_data: content_checksum_data_for(proto_doc),
191
+ attributes_checksum_data: attributes_checksum_data_for(proto_doc, content_filename, meta_filename),
192
+ )
198
193
  end
199
194
 
200
195
  res
201
196
  end
202
197
 
198
+ # Enumerates each pair of content file and meta file. If there is ambiguity, it will raise an error.
199
+ def each_content_meta_pair_in(dir_name)
200
+ all_split_files_in(dir_name).each do |base_filename, (meta_ext, content_exts)|
201
+ meta_filename = filename_for(base_filename, meta_ext)
202
+ content_filenames = content_exts.map { |e| filename_for(base_filename, e) }
203
+
204
+ have_possible_ambiguity = meta_filename && content_filenames.size > 1
205
+ if have_possible_ambiguity && content_filenames.count { |fn| !parser.frontmatter?(fn) } != 1
206
+ raise Nanoc::Int::Errors::AmbiguousMetadataAssociation.new(content_filenames, meta_filename)
207
+ end
208
+
209
+ content_filenames.each do |content_filename|
210
+ real_meta_filename =
211
+ if have_possible_ambiguity && parser.frontmatter?(content_filename)
212
+ nil
213
+ else
214
+ meta_filename
215
+ end
216
+
217
+ yield(content_filename, real_meta_filename)
218
+ end
219
+ end
220
+ end
221
+
203
222
  def content_checksum_data_for(proto_doc)
204
223
  data = proto_doc.content_checksum_data
205
224
  data ? Digest::SHA1.digest(data) : nil
@@ -379,8 +398,11 @@ module Nanoc::DataSources
379
398
  end
380
399
  end
381
400
 
401
+ def parser
402
+ @parser ||= Parser.new(config: @config)
403
+ end
404
+
382
405
  def parse(content_filename, meta_filename)
383
- parser = Parser.new(config: @config)
384
406
  parser.call(content_filename, meta_filename)
385
407
  end
386
408
  end
@@ -70,6 +70,11 @@ class Nanoc::DataSources::Filesystem
70
70
  meta
71
71
  end
72
72
 
73
+ def frontmatter?(filename)
74
+ data = Tools.read_file(filename, config: @config)
75
+ /\A#{SEPARATOR}\s*$/.match?(data)
76
+ end
77
+
73
78
  def verify_meta(meta, filename)
74
79
  return if meta.is_a?(Hash)
75
80
 
@@ -1,45 +1,153 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nanoc::Filters
4
- # @api private
5
- class Sass < Nanoc::Filter
6
- identifier :sass
7
-
8
- requires 'sass', 'nanoc/filters/sass/sass_filesystem_importer'
9
-
10
- # Runs the content through [Sass](http://sass-lang.com/).
11
- # Parameters passed to this filter will be passed on to Sass.
12
- #
13
- # @param [String] content The content to filter
14
- #
15
- # @return [String] The filtered content
16
- def run(content, params = {})
4
+ Nanoc::Filter.define(:sass) do |content, params = {}|
5
+ include Sass
6
+ css(self, @item_rep, content, params)
7
+ end
8
+
9
+ Nanoc::Filter.define(:sass_sourcemap) do |content, params = {}|
10
+ include Sass
11
+ sourcemap(self, @item_rep, content, params)
12
+ end
13
+
14
+ require 'sass'
15
+
16
+ module Sass
17
+ def css(filter, rep, content, params)
18
+ css, = render(filter, rep, content, params)
19
+ css
20
+ end
21
+
22
+ def sourcemap(filter, rep, content, params)
23
+ _, sourcemap = render(filter, rep, content, params)
24
+ sourcemap
25
+ end
26
+
27
+ private
28
+
29
+ def render(filter, rep, content, params = {})
30
+ importer = NanocSassImporter.new(filter)
31
+
17
32
  options = params.merge(
18
- nanoc_current_filter: self,
19
- filename: @item&.raw_filename,
33
+ load_paths: [importer, *params[:load_paths]&.reject { |p| p.is_a?(String) && %r{^content/} =~ p }],
34
+ importer: importer,
35
+ filename: rep.item.identifier.to_s,
36
+ cache: false,
20
37
  )
38
+ sourcemap_path = options.delete(:sourcemap_path)
39
+
21
40
  engine = ::Sass::Engine.new(content, options)
22
- engine.render
41
+ css, sourcemap = sourcemap_path ? engine.render_with_sourcemap(sourcemap_path) : engine.render
42
+ [css, sourcemap&.to_json(css_uri: rep.path, type: rep.path.nil? ? :inline : :auto)]
23
43
  end
24
44
 
25
- def self.item_filename_map_for_config(config, items)
26
- @item_filename_map ||= {}
27
- @item_filename_map[config] ||=
28
- {}.tap do |map|
29
- items.each do |item|
30
- if item.raw_filename
31
- path = Pathname.new(item.raw_filename).realpath.to_s
32
- map[path] = item
45
+ # @api private
46
+ class NanocSassImporter < ::Sass::Importers::Filesystem
47
+ attr_reader :filter
48
+
49
+ def initialize(filter)
50
+ @filter = filter
51
+ super('.')
52
+ end
53
+
54
+ def find_relative(name, base_identifier, options)
55
+ base_raw_filename = filter.items[base_identifier].raw_filename
56
+
57
+ # we can't resolve a relative filename from an in-memory item
58
+ return unless base_raw_filename
59
+
60
+ raw_filename, syntax = ::Sass::Util.destructure(find_real_file(File.dirname(base_raw_filename), name, options))
61
+ return unless raw_filename
62
+
63
+ item = raw_filename_to_item(raw_filename)
64
+ # it doesn't make sense to import a file, from Nanoc's content if the corresponding item has been deleted
65
+ raise "unable to map #{raw_filename} to any item" if item.nil?
66
+
67
+ filter.depend_on([item])
68
+
69
+ options[:syntax] = syntax
70
+ options[:filename] = item.identifier.to_s
71
+ options[:importer] = self
72
+ ::Sass::Engine.new(item.raw_content, options)
73
+ end
74
+
75
+ def find(identifier, options)
76
+ items = filter.items.find_all(identifier)
77
+ return if items.empty?
78
+
79
+ content = if items.size == 1
80
+ items.first.compiled_content
81
+ else
82
+ items.map { |item| %(@import "#{item.identifier}";) }.join("\n")
83
+ end
84
+
85
+ options[:syntax] = :scss
86
+ options[:filename] = identifier.to_s
87
+ options[:importer] = self
88
+ ::Sass::Engine.new(content, options)
89
+ end
90
+
91
+ def key(identifier, _options)
92
+ [self.class.name + ':' + root, identifier.to_s]
93
+ end
94
+
95
+ def public_url(identifier, _sourcemap_directory)
96
+ path = filter.items[identifier].path
97
+ return path unless path.nil?
98
+
99
+ raw_filename = filter.items[identifier].raw_filename
100
+ return if raw_filename.nil?
101
+
102
+ ::Sass::Util.file_uri_from_path(raw_filename)
103
+ end
104
+
105
+ def to_s
106
+ 'Nanoc Sass Importer'
107
+ end
108
+
109
+ def self.raw_filename_to_item_map_for_config(config, items)
110
+ @raw_filename_to_item_map ||= {}
111
+ @raw_filename_to_item_map[config.object_id] ||=
112
+ {}.tap do |map|
113
+ items.each do |item|
114
+ if item.raw_filename
115
+ path = Pathname.new(item.raw_filename).realpath.to_s
116
+ map[path] = item
117
+ end
33
118
  end
34
119
  end
35
- end
36
- end
120
+ end
121
+
122
+ def raw_filename_to_item(filename)
123
+ realpath = Pathname.new(filename).realpath.to_s
37
124
 
38
- def imported_filename_to_item(filename)
39
- realpath = Pathname.new(filename).realpath.to_s
125
+ map = self.class.raw_filename_to_item_map_for_config(filter.config, filter.items)
126
+ map[realpath]
127
+ end
128
+ end
129
+ end
40
130
 
41
- map = self.class.item_filename_map_for_config(@config, @items)
42
- map[realpath]
131
+ module ::Sass::Script::Functions
132
+ def nanoc(string, params)
133
+ assert_type string, :String
134
+ assert_type params, :Hash
135
+ result = options[:importer].filter.instance_eval(string.value)
136
+ case result
137
+ when TrueClass, FalseClass
138
+ bool(result)
139
+ when Array
140
+ list(result, :comma)
141
+ when Hash
142
+ map(result)
143
+ when nil
144
+ null
145
+ when Numeric
146
+ number(result)
147
+ else
148
+ params['unquote'] ? unquoted_string(result) : quoted_string(result)
149
+ end
43
150
  end
151
+ declare :nanoc, [:string], var_kwargs: true
44
152
  end
45
153
  end
@@ -33,6 +33,34 @@ module Nanoc::Helpers
33
33
 
34
34
  # @return [Array]
35
35
  def breadcrumbs_trail
36
+ # The design of this function is a little complicated.
37
+ #
38
+ # We can’t use #parent_of from the ChildParent helper, because the
39
+ # breadcrumb trail can have gaps. For example, the breadcrumbs trail for
40
+ # /software/oink.md might be /index.md -> nil -> /software/oink.md if
41
+ # there is no item matching /software.* or /software/index.*.
42
+ #
43
+ # What this function does instead is something more complicated:
44
+ #
45
+ # 1. It creates an ordered prefix list, based on the identifier of the
46
+ # item to create a breadcrumbs trail for. For example,
47
+ # /software/oink.md might have the prefix list
48
+ # ['', '/software', '/software/oink.md'].
49
+ #
50
+ # 2. For each of the elements in that list, it will create a list of
51
+ # patterns could match zero or more items. For example, the element
52
+ # '/software' would correspond to the pattern '/software.*'.
53
+ #
54
+ # 3. For each of the elements in that list, and for each pattern for that
55
+ # element, it will find any matching element. For example, the
56
+ # pattern '/software.*' (coming from the prefix /software) would match
57
+ # the item /software.md.
58
+ #
59
+ # 4. Return the list of items, with the last element replaced by the item
60
+ # for which the breadcrumb is generated for -- while ancestral items
61
+ # in the breadcrumbs trail can have a bit of ambiguity, the item for
62
+ # which to generate the breadcrumbs trail is fixed.
63
+
36
64
  # e.g. ['', '/foo', '/foo/bar']
37
65
  components = item.identifier.components
38
66
  prefixes = components.inject(['']) { |acc, elem| acc + [acc.last + '/' + elem] }
@@ -40,9 +68,9 @@ module Nanoc::Helpers
40
68
  if @item.identifier.legacy?
41
69
  prefixes.map { |pr| @items[Nanoc::Identifier.new('/' + pr, type: :legacy)] }
42
70
  else
43
- prefixes
44
- .reject { |pr| pr =~ /^\/index\./ }
45
- .map do |pr|
71
+ ancestral_prefixes = prefixes.reject { |pr| pr =~ /^\/index\./ }[0..-2]
72
+ ancestral_items =
73
+ ancestral_prefixes.map do |pr|
46
74
  if pr == ''
47
75
  @items['/index.*']
48
76
  else
@@ -50,6 +78,7 @@ module Nanoc::Helpers
50
78
  prefix_patterns.lazy.map { |pat| @items[pat] }.find(&:itself)
51
79
  end
52
80
  end
81
+ ancestral_items + [item]
53
82
  end
54
83
  end
55
84
  end
data/lib/nanoc/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Nanoc
4
4
  # The current Nanoc version.
5
- VERSION = '4.9.9'
5
+ VERSION = '4.10.0'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nanoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.9.9
4
+ version: 4.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Defreyne
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-06 00:00:00.000000000 Z
11
+ date: 2018-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -401,7 +401,6 @@ files:
401
401
  - lib/nanoc/filters/relativize_paths.rb
402
402
  - lib/nanoc/filters/rubypants.rb
403
403
  - lib/nanoc/filters/sass.rb
404
- - lib/nanoc/filters/sass/sass_filesystem_importer.rb
405
404
  - lib/nanoc/filters/slim.rb
406
405
  - lib/nanoc/filters/typogruby.rb
407
406
  - lib/nanoc/filters/uglify_js.rb
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- class ::Sass::Importers::Filesystem
5
- alias _orig_find _find
6
-
7
- def _find(dir, name, options)
8
- # Find filename
9
- full_filename, _syntax = ::Sass::Util.destructure(find_real_file(dir, name, options))
10
- return nil if full_filename.nil?
11
-
12
- # Create dependency
13
- filter = options[:nanoc_current_filter]
14
- if filter
15
- item = filter.imported_filename_to_item(full_filename)
16
- filter.depend_on([item]) unless item.nil?
17
- end
18
-
19
- # Call original _find
20
- _orig_find(dir, name, options)
21
- end
22
- end