esvg 4.1.0 → 4.1.1

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: 1529e25e48e1086a9ac6756093c81e4e96192400
4
- data.tar.gz: d36d81e3e2fec5665acf2320a658d41c1579a36d
3
+ metadata.gz: 27d93663b8b9b36654390502132bf232efe68c66
4
+ data.tar.gz: 43344a22127df181e71d73602dbe0764e51d13e5
5
5
  SHA512:
6
- metadata.gz: 976ec5f1418b143edb27698ccfedc89eb48a8eda4bfa741725d9a73412bc9b7a55ce42b7dafee6b9e9636ff41d5c6a924624d2a2ded858c5e7f2fcbd8658f651
7
- data.tar.gz: 8ca64f5d17cc615f18e3202e0b2e79a5abe5c58ab80fce208fa757ce72661c6b1a053256424772e9778dc57500fbdcd461f1849e1126aa908ba6f857382ea6e6
6
+ metadata.gz: cc486ddf04d4b6692407740dc3d1c808181587ce8d92c0e3a78b33c35c4631d1913b961d649b2179e4e9378d4c230e2d8c53cf8e513568246511686387e3c7b2
7
+ data.tar.gz: 1d1e3f0476e4c2dee4eb68d2814712407b0e84eb8faf1e5bd49b9b5729bfa95181c106ce5ddd98e6053bb54245907702d8bfe07992dcb2c337d573f7eb65eb20
data/lib/esvg/helpers.rb CHANGED
@@ -10,10 +10,10 @@ module Esvg::Helpers
10
10
 
11
11
 
12
12
  def embed_svgs(*keys)
13
- if Rails.env.production?
14
- esvg.build_paths(keys).each do |path|
13
+ if !Rails.env.production?
14
+ esvg.build_paths(keys).map do |path|
15
15
  javascript_include_tag(path)
16
- end.join("\n")
16
+ end.join("\n").html_safe
17
17
  else
18
18
  esvg.embed_script(keys).html_safe
19
19
  end
data/lib/esvg/svg.rb CHANGED
@@ -22,7 +22,7 @@ module Esvg
22
22
  CONFIG_RAILS = {
23
23
  source: "app/assets/svgs",
24
24
  assets: "app/assets/javascripts",
25
- build: "public/assets",
25
+ build: "public/javascripts",
26
26
  temp: "tmp"
27
27
  }
28
28
 
@@ -77,7 +77,6 @@ module Esvg
77
77
  # Get a list of svg files and modification times
78
78
  #
79
79
  find_files
80
- write_cache
81
80
 
82
81
  @last_read = Time.now.to_i
83
82
 
@@ -90,6 +89,7 @@ module Esvg
90
89
 
91
90
  def find_files
92
91
  files = Dir[File.join(config[:source], '**/*.svg')].uniq.sort
92
+ @svg_symbols = {}
93
93
 
94
94
  # Remove deleted files from svg cache
95
95
  (svgs.keys - file_keys(files)).each { |f| svgs.delete(f) }
@@ -97,16 +97,18 @@ module Esvg
97
97
  dirs = {}
98
98
 
99
99
  files.each do |path|
100
-
101
100
  mtime = File.mtime(path).to_i
102
101
  key = file_key path
103
102
  dkey = dir_key path
104
103
 
105
104
  # Use cache if possible
106
105
  if svgs[key].nil? || svgs[key][:last_modified] != mtime
107
- svgs[key] = process_file(path, mtime, key)
106
+ svgs[key] = process_file(path, mtime)
108
107
  end
109
108
 
109
+ # Name may have changed due to flatten config
110
+ svgs[key][:name] = file_name(path)
111
+
110
112
  dirs[dkey] ||= {}
111
113
  (dirs[dkey][:files] ||= []) << key
112
114
 
@@ -117,31 +119,22 @@ module Esvg
117
119
 
118
120
  dirs = sort(dirs)
119
121
 
120
- # Remove deleted directories from svg_symbols cache
121
- (svg_symbols.keys - dirs.keys).each {|dir| svg_symbols.delete(dir) }
122
-
123
122
  dirs.each do |dir, data|
124
123
 
125
124
  # overwrite cache if
126
125
  if svg_symbols[dir].nil? || # No cache for this dir yet
127
126
  svg_symbols[dir][:last_modified] != data[:last_modified] || # New or updated file
128
- svg_symbols[dir][:optimized] != optimize? || # Cache is unoptimized
129
127
  svg_symbols[dir][:files] != data[:files] # Changed files
130
128
 
131
- symbols = data[:files].map { |f| svgs[f][:content] }.join
132
129
  attributes = data[:files].map { |f| svgs[f][:attr] }
130
+ mtimes = data[:files].map { |f| svgs[f][:last_modified] }.join
133
131
 
134
132
  svg_symbols[dir] = data.merge({
135
133
  name: dir,
136
- symbols: symbols,
137
- optimized: optimize?,
138
- version: config[:version] || Digest::MD5.hexdigest(symbols),
139
- asset: File.basename(dir).start_with?('_')
134
+ asset: File.basename(dir).start_with?('_'),
135
+ version: config[:version] || Digest::MD5.hexdigest(mtimes)
140
136
  })
141
137
 
142
- end
143
-
144
- svg_symbols.keys.each do |dir|
145
138
  svg_symbols[dir][:path] = write_path(dir)
146
139
  end
147
140
  end
@@ -152,15 +145,12 @@ module Esvg
152
145
 
153
146
  def read_cache
154
147
  @svgs = YAML.load(read_tmp '.svgs') || {}
155
- @svg_symbols = YAML.load(read_tmp '.svg_symbols') || {}
156
148
  end
157
149
 
158
150
  def write_cache
159
151
  return if production?
160
152
 
161
153
  write_tmp '.svgs', sort(@svgs).to_yaml
162
- write_tmp '.svg_symbols', sort(@svg_symbols).to_yaml
163
-
164
154
  end
165
155
 
166
156
  def sort(hash)
@@ -195,26 +185,22 @@ module Esvg
195
185
  end.map { |k| svg_symbols[k] }
196
186
  end
197
187
 
198
- def process_file(file, mtime, name)
199
- content = File.read(file)
200
- id = id(name)
188
+ def process_file(path, mtime)
189
+ content = File.read(path)
190
+ id = id(file_key(path))
201
191
  size_attr = dimensions(content)
202
192
 
203
- svg = {
204
- name: name,
193
+ {
194
+ path: path,
205
195
  use: %Q{<use xlink:href="##{id}"/>},
206
196
  last_modified: mtime,
207
- attr: { id: id }.merge(size_attr)
197
+ attr: { id: id }.merge(size_attr),
198
+ content: content
208
199
  }
209
- # Add attributes
210
- svg[:content] = prep_svg(content, svg[:attr])
211
-
212
- svg
213
200
  end
214
201
 
215
202
  def use(file, options={})
216
- if name = exist?(file, options[:fallback])
217
- svg = svgs[name]
203
+ if svg = find_svg(file, options[:fallback])
218
204
 
219
205
  if options[:color]
220
206
  options[:style] ||= ''
@@ -225,7 +211,7 @@ module Esvg
225
211
  fill: options[:fill],
226
212
  style: options[:style],
227
213
  viewBox: svg[:attr][:viewBox],
228
- class: [config[:class], svg[:attr][:id], options[:class]].compact.join(' ')
214
+ class: [config[:class], id(svg[:name]), options[:class]].compact.join(' ')
229
215
  }
230
216
 
231
217
  # If user doesn't pass a size or set scale: true
@@ -283,14 +269,17 @@ module Esvg
283
269
  end
284
270
 
285
271
  def exist?(name, fallback=nil)
272
+ !find_svg(name, fallback).nil?
273
+ end
274
+
275
+ def find_svg(name, fallback=nil)
286
276
  name = get_alias dasherize(name)
287
277
 
288
- if svgs[name].nil?
289
- exist?(fallback) if fallback
290
- else
291
- name
278
+ if svg = svgs.values.find { |v| v[:name] == name }
279
+ svg
280
+ elsif fallback
281
+ find_svg(fallback)
292
282
  end
293
-
294
283
  end
295
284
 
296
285
  alias_method :exists?, :exist?
@@ -344,7 +333,9 @@ module Esvg
344
333
  paths = []
345
334
 
346
335
  files.each do |file|
347
- write_file(file[:path], js(file[:name]))
336
+ content = js(file[:name])
337
+
338
+ write_file(file[:path], content)
348
339
  puts "Writing #{file[:path]}" if config[:print]
349
340
  paths << file[:path]
350
341
 
@@ -354,17 +345,29 @@ module Esvg
354
345
  end
355
346
  end
356
347
 
348
+ write_cache
349
+
357
350
  paths
358
351
  end
359
352
 
360
353
  def symbols(keys)
361
354
  symbols = valid_keys(keys).map { |key|
362
- svg_symbols[key][:symbols]
355
+ # Build on demand
356
+ build_symbols(svg_symbols[key][:files])
363
357
  }.join.gsub(/\n/,'')
364
358
 
365
359
  %Q{<svg id="esvg-#{key_id(keys)}" version="1.1" style="height:0;position:absolute">#{symbols}</svg>}
366
360
  end
367
361
 
362
+ def build_symbols(files)
363
+ files.map { |file|
364
+ if svgs[file][:optimized_at].nil? || svgs[file][:optimized_at] < svgs[file][:last_modified]
365
+ svgs[file][:optimized_content] = optimize(svgs[file])
366
+ end
367
+ svgs[file][:optimized_content]
368
+ }.join.gsub(/\n/,'')
369
+ end
370
+
368
371
  def js(key)
369
372
  keys = valid_keys(key)
370
373
  return if keys.empty?
@@ -471,10 +474,16 @@ module Esvg
471
474
 
472
475
  private
473
476
 
477
+ def file_key(path)
478
+ dasherize sub_path(path).sub('.svg','')
479
+ end
480
+
474
481
  def dir_key(path)
475
482
  dir = File.dirname(flatten_path(path))
476
483
 
477
484
  # Flattened paths which should be treated as assets will use '_' as their dir key
485
+ # - flatten: _foo - _foo/icon.svg will have a dirkey of _
486
+ # - filename: _icons - treats all root or flattened files as assets
478
487
  if dir == '.' && ( sub_path(path).start_with?('_') || config[:filename].start_with?('_') )
479
488
  '_'
480
489
  else
@@ -482,6 +491,10 @@ module Esvg
482
491
  end
483
492
  end
484
493
 
494
+ def file_name(path)
495
+ dasherize flatten_path(path).sub('.svg','')
496
+ end
497
+
485
498
  def sub_path(path)
486
499
  path.sub("#{config[:source]}/",'')
487
500
  end
@@ -490,10 +503,6 @@ module Esvg
490
503
  sub_path(path).sub(Regexp.new(config[:flatten]), '')
491
504
  end
492
505
 
493
- def file_key(path)
494
- dasherize flatten_path(path).sub('.svg', '')
495
- end
496
-
497
506
  def file_keys(paths)
498
507
  paths.flatten.map { |p| file_key(p) }
499
508
  end
@@ -518,31 +527,60 @@ module Esvg
518
527
  end
519
528
  end
520
529
 
521
- def prep_svg(content, attr)
522
- content = content.gsub(/<?.+\?>/,'').gsub(/<!.+?>/,'') # Get rid of doctypes and comments
530
+ def svgo?
531
+ !!(config[:optimize] && svgo_cmd)
532
+ end
533
+
534
+ def optimize(svg)
535
+ svg[:optimized_content] = pre_optimize svg[:content]
536
+ svg[:optimized_content] = sub_def_ids svg
537
+
538
+ if svgo?
539
+ response = Open3.capture3(%Q{#{} --disable=removeUselessDefs -s '#{svg[:content]}' -o -})
540
+ svg[:optimized_content] = response[0] if response[2].success?
541
+ end
542
+
543
+ svg[:optimized_at] = Time.now.to_i
544
+ svg[:optimized_content] = post_optimize svg
545
+ end
546
+
547
+ def pre_optimize(svg)
548
+ reg = %w(xmlns xmlns:xlink xml:space version).map { |m| "#{m}=\".+?\"" }.join('|')
549
+ svg.gsub(Regexp.new(reg), '') # Remove unwanted attributes
550
+ .gsub(/<?.+\?>/,'').gsub(/<!.+?>/,'') # Get rid of doctypes and comments
523
551
  .gsub(/\n/, '') # Remove endlines
524
552
  .gsub(/\s{2,}/, ' ') # Remove whitespace
525
553
  .gsub(/>\s+</, '><') # Remove whitespace between tags
526
554
  .gsub(/\s?fill="(#0{3,6}|black|rgba?\(0,0,0\))"/,'') # Strip black fill
527
555
  .gsub(/style="([^"]*?)fill:(.+?);/m, 'fill="\2" style="\1') # Make fill a property instead of a style
528
556
  .gsub(/style="([^"]*?)fill-opacity:(.+?);/m, 'fill-opacity="\2" style="\1') # Move fill-opacity a property instead of a style
557
+ end
529
558
 
530
- content = sub_def_ids content, attr[:id]
531
- content = strip_attributes content
532
- content = optimize(content) if optimize?
533
- content = set_attributes content, attr
534
- content.gsub(/<\/svg/,'</symbol') # Replace svgs with symbols
559
+ def post_optimize(svg)
560
+ svg[:optimized_content] = set_attributes(svg)
561
+ .gsub(/<\/svg/,'</symbol') # Replace svgs with symbols
535
562
  .gsub(/class="def-/,'id="def-') # Replace <def> classes with ids (classes are generated in sub_def_ids)
536
- .gsub(/\s{2,}/,'') # Remove extra spaces
537
563
  .gsub(/\w+=""/,'') # Remove empty attributes
538
564
  end
539
565
 
566
+ def set_attributes(svg)
567
+ svg[:attr].keys.each { |key| svg[:optimized_content].sub!(/ #{key}=".+?"/,'') }
568
+ svg[:optimized_content].sub(/<svg/, "<symbol #{attributes(svg[:attr])}")
569
+ end
570
+
571
+ def svgo_cmd
572
+ find_node_module('svgo')
573
+ end
574
+
540
575
  # Scans <def> blocks for IDs
541
576
  # If urls(#id) are used, ensure these IDs are unique to this file
542
577
  # Only replace IDs if urls exist to avoid replacing defs
543
578
  # used in other svg files
544
579
  #
545
- def sub_def_ids(content, name)
580
+ def sub_def_ids(svg)
581
+ content = svg[:optimized_content]
582
+ name = svg[:attr][:id]
583
+
546
584
  return content unless !!content.match(/<defs>/)
547
585
 
548
586
  content.scan(/<defs>.+<\/defs>/m).flatten.each do |defs|
@@ -562,35 +600,6 @@ module Esvg
562
600
  content
563
601
  end
564
602
 
565
- def strip_attributes(svg)
566
- reg = %w(xmlns xmlns:xlink xml:space version).map { |m| "#{m}=\".+?\"" }.join('|')
567
-
568
- svg.gsub(Regexp.new(reg), '')
569
- end
570
-
571
- def set_attributes(svg, attr)
572
- attr.keys.each { |key| svg.sub!(/ #{key}=".+?"/,'') }
573
- svg.sub(/<svg/, "<symbol #{attributes(attr)}")
574
- end
575
-
576
- def optimize?
577
- !!(config[:optimize] && svgo_cmd)
578
- end
579
-
580
- def svgo_cmd
581
- find_node_module('svgo')
582
- end
583
-
584
-
585
- def optimize(svg)
586
- path = write_tmp '.svgo-tmp', svg
587
- command = "#{svgo_cmd} --disable=removeUselessDefs '#{path}' -o -"
588
- svg = `#{command}`
589
- FileUtils.rm(path) if File.exist? path
590
-
591
- svg
592
- end
593
-
594
603
  def compress(file)
595
604
  return if !config[:compress]
596
605
 
data/lib/esvg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Esvg
2
- VERSION = "4.1.0"
2
+ VERSION = "4.1.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: esvg
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Mathis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-05-18 00:00:00.000000000 Z
11
+ date: 2017-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler