nanoc 4.9.9 → 4.10.0

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