esvg 2.3.1 → 2.4.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
  SHA1:
3
- metadata.gz: c6e52e2c93bb2014d058e1e67b21993ae847b127
4
- data.tar.gz: 134f0a997e953e2216ab91a28ac2b8d78c6d49d6
3
+ metadata.gz: 0929542e9c469ca502b8d6beb9f5bbf779a23d41
4
+ data.tar.gz: 2dc3070f381f16fa28c993277fdf19d6632c35ed
5
5
  SHA512:
6
- metadata.gz: 6fa72d0b91eca23f0c230643ae9c7f61a97d41990a12e43bf654ee0356dc4224353acf0a3af4a1036f90fc32443cabfe3d232c6672b0026aff46734f1c535445
7
- data.tar.gz: 0e273e7eaa3f52dbacd920921d1b5f3c2e36031e8043293b1b6d916314981f784eb229af44ca186916676642b5715f8c708060e614d0536eb7f559bec4908b8c
6
+ metadata.gz: 5126999322552fa4ba22ccf16b459004e6851eb6b2f321992cc20ce93d720a0f3684dbdf34d2d84cd0431e6442cad7c2c5b072a63fcebaaef1ab39f3cf62ad77
7
+ data.tar.gz: 061e2c49fc7e2d537a5947048651da403c2b12fd78050d08756125b5dcd31d81397ff874bb7988b83e5b66ae346af700f85e3b0c4fa057303c9a6e4e7a3220d4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ### 2.4.0 (2015-10-17)
4
+ - New: Caching is now based on file modification times. It's much faster and more efficient.
5
+ - New: Optimization is much faster now too, as it happens after symbol concatenation, so it only runs once per build.
6
+ - Change: Configuration `svgo_path` is now `npm_path` and points to the path where the `node_modules` folder can be found.
7
+
3
8
  ### 2.3.1 (2015-10-15)
4
9
  - Minor: Added `svgo_path` config option to specifiy a direct path to the svgo binary.
5
10
 
data/exe/esvg CHANGED
@@ -19,6 +19,14 @@ OptionParser.new do |opts|
19
19
  opts.on("-c", "--config PATH", String, "Path to a config file (default: esvg.yml, config/esvg.yml)") do |path|
20
20
  options[:config_file] = path
21
21
  end
22
+
23
+ opts.on("-O", "--optimize", "Optimize svgs with svgo") do |svgo|
24
+ options[:optimize] = svgo
25
+ end
26
+
27
+ opts.on("-n", "--npm", String, "Path to node_modules director") do |path|
28
+ options[:npm_path] = path
29
+ end
22
30
  end.parse!
23
31
 
24
32
  options[:path] = ARGV.shift
data/lib/esvg.rb CHANGED
@@ -12,10 +12,10 @@ module Esvg
12
12
  extend self
13
13
 
14
14
  def icons(options={})
15
- @icons ||= SVG.new(options)
16
-
17
- if rails? && ::Rails.env != 'production' && @icons.modified?
18
- @icons.read_icons
15
+ if @icons.nil?
16
+ @icons = SVG.new(options)
17
+ elsif !rails? || (rails? && ::Rails.env.downcase != 'production')
18
+ @icons.read_files
19
19
  end
20
20
 
21
21
  @icons
@@ -26,7 +26,7 @@ module Esvg
26
26
  end
27
27
 
28
28
  def svg_icon(name, options={})
29
- icons.svg_icon(name, options)
29
+ @icons.svg_icon(name, options)
30
30
  end
31
31
 
32
32
  def rails?
data/lib/esvg/svg.rb CHANGED
@@ -9,7 +9,7 @@ module Esvg
9
9
  base_class: 'svg-icon',
10
10
  namespace: 'icon',
11
11
  optimize: false,
12
- svgo_path: false,
12
+ npm_path: false,
13
13
  namespace_before: true,
14
14
  font_size: '1em',
15
15
  output_path: Dir.pwd,
@@ -23,17 +23,42 @@ module Esvg
23
23
 
24
24
  def initialize(options={})
25
25
  config(options)
26
- read_icons
26
+
27
27
  @svgo = nil
28
- @cache = {}
28
+ @svgs = {}
29
+
30
+ read_files
29
31
  end
30
32
 
31
- def modified?
32
- @mtime != last_modified(find_files)
33
+ def config(options={})
34
+ @config ||= begin
35
+ paths = [options[:config_file], 'config/esvg.yml', 'esvg.yml'].compact
36
+
37
+ config = CONFIG
38
+ config.merge!(CONFIG_RAILS) if Esvg.rails?
39
+
40
+ if path = paths.select{ |p| File.exist?(p)}.first
41
+ config.merge!(symbolize_keys(YAML.load(File.read(path) || {})))
42
+ end
43
+
44
+ config.merge!(options)
45
+
46
+ if config[:verbose]
47
+ config[:path] = File.expand_path(config[:path])
48
+ config[:output_path] = File.expand_path(config[:output_path])
49
+ end
50
+
51
+ config[:js_path] ||= File.join(config[:output_path], 'esvg.js')
52
+ config[:css_path] ||= File.join(config[:output_path], 'esvg.css')
53
+ config[:html_path] ||= File.join(config[:output_path], 'esvg.html')
54
+ config.delete(:output_path)
55
+
56
+ config
57
+ end
33
58
  end
34
59
 
35
60
  def embed
36
- return if @files.empty?
61
+ return if files.empty?
37
62
  case config[:format]
38
63
  when "html"
39
64
  html
@@ -44,63 +69,120 @@ module Esvg
44
69
  end
45
70
  end
46
71
 
47
- def svgo?
48
- @svgo ||= begin
49
- local_path = config[:svgo_path] || "#{Dir.pwd}/node_modules/svgo/bin/svgo"
72
+ def read_files
73
+ @files = {}
50
74
 
51
- if File.exist?(local_path)
52
- local_path
53
- elsif `npm ls -g svgo`.match(/empty/).nil?
54
- "svgo"
55
- else
56
- false
75
+ # Get a list of svg files and modification times
76
+ #
77
+ find_files.each do |f|
78
+ files[f] = File.mtime(f)
79
+ end
80
+
81
+ process_files
82
+
83
+ if files.empty? && config[:verbose]
84
+ puts "No svgs found at #{config[:path]}"
85
+ end
86
+ end
87
+
88
+ # Add new svgs, update modified svgs, remove deleted svgs
89
+ #
90
+ def process_files
91
+ files.each do |file, mtime|
92
+ name = file_key(file)
93
+
94
+ if svgs[name].nil? || svgs[name][:last_modified] != mtime
95
+ svgs[name] = process_file(file, mtime, name)
57
96
  end
58
97
  end
98
+
99
+ # Remove deleted svgs
100
+ #
101
+ (svgs.keys - files.keys.map {|file| file_key(file) }).each do |file|
102
+ svgs.delete(file)
103
+ end
59
104
  end
60
105
 
61
- def cache_name(input, options)
62
- "#{input}#{options.flatten.join('-')}"
106
+ def process_file(file, mtime, name)
107
+ content = File.read(file).gsub(/<?.+\?>/,'').gsub(/<!.+?>/,'')
108
+ {
109
+ content: content,
110
+ use: use_svg(name, content),
111
+ last_modified: mtime
112
+ }
63
113
  end
64
114
 
65
- def read_icons
66
- @files = {}
67
- @svgs = {}
115
+ #def compress(file)
116
+ #if config[:optimize] && svgo?
117
+ #Compress files outputting to $STDOUT which returns as a string
118
+ #`#{@svgo} #{file} -o -`
119
+ #else
120
+ #File.read(file)
121
+ #end
122
+ #end
123
+
124
+ def use_svg(file, content)
125
+ name = classname(file)
126
+ %Q{<svg class="#{config[:base_class]} #{name}" #{dimensions(content)}><use xlink:href="##{name}"/></svg>}
127
+ end
68
128
 
69
- found = find_files
70
- @mtime = last_modified(found)
129
+ def svg_icon(file, options={})
130
+ file = icon_name(file.to_s)
131
+ embed = svgs[file][:use]
132
+ embed = embed.sub(/class="(.+?)"/, 'class="\1 '+options[:class]+'"') if options[:class]
133
+ embed = embed.sub(/><\/svg/, ">#{title(options)}#{desc(options)}</svg")
134
+ embed
135
+ end
71
136
 
72
- found.each do |f|
73
- @files[dasherize(File.basename(f, ".*"))] = read(f)
74
- end
137
+ def dimensions(input)
138
+ dimension = input.scan(/<svg.+(viewBox=["'](.+?)["'])/).flatten
139
+ viewbox = dimension.first
140
+ coords = dimension.last.split(' ')
141
+
142
+ width = coords[2].to_i - coords[0].to_i
143
+ height = coords[3].to_i - coords[1].to_i
144
+ %Q{#{viewbox} width="#{width}" height="#{height}"}
145
+ end
75
146
 
76
- if @files.empty? && config[:verbose]
77
- puts "No icons found at #{config[:path]}"
147
+ def icon_name(name)
148
+ if svgs[name].nil?
149
+ raise "No svg named '#{name}' exists at #{config[:path]}"
78
150
  end
151
+ classname(name)
79
152
  end
80
153
 
81
- def last_modified(files)
82
- if files.size > 0
83
- File.mtime(files.sort_by{ |f| File.mtime(f) }.last)
154
+ def classname(name)
155
+ name = dasherize(name)
156
+ if config[:namespace_before]
157
+ "#{config[:namespace]}-#{name}"
158
+ else
159
+ "#{name}-#{config[:namespace]}"
84
160
  end
85
161
  end
86
162
 
87
- def read(file)
88
- if config[:optimize] && svgo?
89
- # Compress files outputting to $STDOUT which returns as a string
90
- `#{@svgo} #{file} -o -`
163
+ def dasherize(input)
164
+ input.gsub(/[\W,_]/, '-').gsub(/-{2,}/, '-')
165
+ end
166
+
167
+ def find_files
168
+ path = File.expand_path(File.join(config[:path], '*.svg'))
169
+ Dir[path].uniq
170
+ end
171
+
172
+
173
+ def title(options)
174
+ if options[:title]
175
+ "<title>#{options[:title]}</title>"
91
176
  else
92
- File.read(file)
177
+ ''
93
178
  end
94
179
  end
95
180
 
96
- # Optiize all svg source files
97
- #
98
- def optimize
99
- if svgo?
100
- puts "Optimzing #{config[:path]}"
101
- system "svgo -f #{config[:path]}"
181
+ def desc(options)
182
+ if options[:desc]
183
+ "<desc>#{options[:desc]}</desc>"
102
184
  else
103
- abort 'To optimize files, please install svgo; `npm install svgo -g`'
185
+ ''
104
186
  end
105
187
  end
106
188
 
@@ -116,13 +198,6 @@ module Esvg
116
198
  end
117
199
  end
118
200
 
119
- def write_file(path, contents)
120
- FileUtils.mkdir_p(File.expand_path(File.dirname(path)))
121
- File.open(path, 'w') do |io|
122
- io.write(contents)
123
- end
124
- end
125
-
126
201
  def write_js
127
202
  write_file config[:js_path], js
128
203
  end
@@ -135,12 +210,18 @@ module Esvg
135
210
  write_file config[:html_path], html
136
211
  end
137
212
 
213
+ def write_file(path, contents)
214
+ FileUtils.mkdir_p(File.expand_path(File.dirname(path)))
215
+ File.open(path, 'w') do |io|
216
+ io.write(contents)
217
+ end
218
+ end
219
+
138
220
  def css
139
- @cache['css'] ||= begin
140
- styles = []
141
-
142
- classes = files.keys.map{|k| ".#{icon_name(k)}"}.join(', ')
143
- preamble = %Q{#{classes} {
221
+ styles = []
222
+
223
+ classes = svgs.keys.map{|k| ".#{classname(k)}"}.join(', ')
224
+ preamble = %Q{#{classes} {
144
225
  font-size: #{config[:font_size]};
145
226
  clip: auto;
146
227
  background-size: auto;
@@ -156,43 +237,52 @@ module Esvg
156
237
  vertical-align: middle;
157
238
  line-height: 1em;
158
239
  }}
159
- styles << preamble
160
-
161
- files.each do |name, contents|
162
- f = contents.gsub(/</, '%3C') # escape <
163
- .gsub(/>/, '%3E') # escape >
164
- .gsub(/#/, '%23') # escape #
165
- .gsub(/\n/,'') # remove newlines
166
- styles << ".#{icon_name(name)} { background-image: url('data:image/svg+xml;utf-8,#{f}'); }"
240
+ styles << preamble
241
+
242
+ svgs.each do |name, data|
243
+ if data[:css]
244
+ styles << css
245
+ else
246
+ svg_css = data[:content].gsub(/</, '%3C') # escape <
247
+ .gsub(/>/, '%3E') # escape >
248
+ .gsub(/#/, '%23') # escape #
249
+ .gsub(/\n/,'') # remove newlines
250
+ styles << data[:css] = ".#{classname(name)} { background-image: url('data:image/svg+xml;utf-8,#{svg_css}'); }"
167
251
  end
168
- styles.join("\n")
169
252
  end
253
+ styles.join("\n")
254
+ end
255
+
256
+ def prep_svg(file, content)
257
+ content.gsub(/<svg.+?>/, %Q{<svg class="#{classname(file)}" #{dimensions(content)}>}) # convert svg to symbols
258
+ .gsub(/\n/, '') # remove endlines
259
+ .gsub(/\s{2,}/, ' ') # remove whitespace
260
+ .gsub(/>\s+</, '><') # remove whitespace between tags
170
261
  end
171
262
 
172
263
  def html
173
- @cache['html'] ||= begin
174
- if @files.empty?
175
- ''
264
+ if @files.empty?
265
+ ''
266
+ else
267
+ symbols = []
268
+ svgs.each do |name, data|
269
+ symbols << prep_svg(name, data[:content])
270
+ end
271
+
272
+ symbols = if config[:optimize] && svgo?
273
+ `svgo -s '#{symbols.join}' -o -`
176
274
  else
177
- files.each do |name, contents|
178
- @svgs[name] = contents.gsub(/<svg.+?>/, %Q{<symbol id="#{icon_name(name)}" #{dimensions(contents)}>}) # convert svg to symbols
179
- .gsub(/<\/svg/, '</symbol') # convert svg to symbols
180
- .gsub(/style=['"].+?['"]/, '') # remove inline styles
181
- .gsub(/\n/, '') # remove endlines
182
- .gsub(/\s{2,}/, ' ') # remove whitespace
183
- .gsub(/>\s+</, '><') # remove whitespace between tags
184
- end
185
-
186
- icons = @svgs
187
-
188
- %Q{<svg id="esvg-symbols" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none">#{icons.values.join("\n")}</svg>}
275
+ symbols.join
189
276
  end
277
+
278
+ symbols = symbols.gsub(/class/,'id').gsub(/svg/,'symbol')
279
+
280
+ %Q{<svg id="esvg-symbols" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none">#{symbols}</svg>}
190
281
  end
191
282
  end
192
283
 
193
284
  def js
194
- @cache['js'] ||= begin
195
- %Q{var esvg = {
285
+ %Q{var esvg = {
196
286
  embed: function(){
197
287
  if (!document.querySelector('#esvg-symbols')) {
198
288
  document.querySelector('body').insertAdjacentHTML('afterbegin', '#{html.gsub(/\n/,'').gsub("'"){"\\'"}}')
@@ -239,96 +329,37 @@ esvg.load()
239
329
  // Work with module exports:
240
330
  if(typeof(module) != 'undefined') { module.exports = esvg }
241
331
  }
242
- end
243
- end
244
-
245
- def svg_icon(file, options={})
246
- file = dasherize(file.to_s)
247
- @cache[cache_name(file, options)] ||= begin
248
- name = icon_name(file)
249
- %Q{<svg class="#{config[:base_class]} #{name} #{options[:class] || ""}" #{dimensions(@files[file])}><use xlink:href="##{name}"/>#{title(options)}#{desc(options)}</svg>}
250
- end
251
- end
252
-
253
- def title(options)
254
- if options[:title]
255
- "<title>#{options[:title]}</title>"
256
- end
257
- end
258
-
259
- def desc(options)
260
- if options[:desc]
261
- "<desc>#{options[:desc]}</desc>"
262
- end
263
332
  end
264
333
 
265
- def config(options={})
266
- @config ||= begin
267
- paths = [options[:config_file], 'config/esvg.yml', 'esvg.yml'].compact
268
-
269
- config = CONFIG
270
- config.merge!(CONFIG_RAILS) if Esvg.rails?
334
+ def svgo?
335
+ @svgo ||= begin
336
+ npm_path = "#{config[:npm_path] || Dir.pwd}/node_modules"
337
+ local_path = File.join(npm_path, "svgo/bin/svgo")
271
338
 
272
- if path = paths.select{ |p| File.exist?(p)}.first
273
- config.merge!(symbolize_keys(YAML.load(File.read(path) || {})))
339
+ if config[:npm_path] && !File.exist?(npm_path)
340
+ abort "NPM Path not found: #{File.expand_path(config[:npm_path])}"
274
341
  end
275
342
 
276
- config.merge!(options)
277
-
278
- if config[:verbose]
279
- config[:path] = File.expand_path(config[:path])
280
- config[:output_path] = File.expand_path(config[:output_path])
343
+ if File.exist?(local_path)
344
+ local_path
345
+ elsif `npm ls -g svgo`.match(/empty/).nil?
346
+ "svgo"
347
+ else
348
+ false
281
349
  end
282
-
283
- config[:js_path] ||= File.join(config[:output_path], 'esvg.js')
284
- config[:css_path] ||= File.join(config[:output_path], 'esvg.css')
285
- config[:html_path] ||= File.join(config[:output_path], 'esvg.html')
286
- config.delete(:output_path)
287
-
288
- config
289
350
  end
290
351
  end
291
352
 
353
+ def file_key(name)
354
+ dasherize(File.basename(name, ".*"))
355
+ end
356
+
357
+
292
358
  def symbolize_keys(hash)
293
359
  h = {}
294
360
  hash.each {|k,v| h[k.to_sym] = v }
295
361
  h
296
362
  end
297
363
 
298
- def dimensions(input)
299
- dimension = input.scan(/<svg.+(viewBox=["'](.+?)["'])/).flatten
300
- viewbox = dimension.first
301
- coords = dimension.last.split(' ')
302
-
303
- width = coords[2].to_i - coords[0].to_i
304
- height = coords[3].to_i - coords[1].to_i
305
- %Q{#{viewbox} width="#{width}" height="#{height}"}
306
- end
307
-
308
- def icon_name(name)
309
- if @files[name].nil?
310
- raise "No icon named '#{name}' exists at #{config[:path]}"
311
- end
312
- classname(name)
313
- end
314
-
315
- def classname(name)
316
- name = dasherize(name)
317
- if config[:namespace_before]
318
- "#{config[:namespace]}-#{name}"
319
- else
320
- "#{name}-#{config[:namespace]}"
321
- end
322
- end
323
-
324
- def dasherize(input)
325
- input.gsub(/[\W,_]/, '-').gsub(/-{2,}/, '-')
326
- end
327
-
328
- def find_files
329
- path = File.expand_path(File.join(config[:path], '*.svg'))
330
- Dir[path].uniq
331
- end
332
-
333
364
  end
334
365
  end
data/lib/esvg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Esvg
2
- VERSION = "2.3.1"
2
+ VERSION = "2.4.0"
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: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Mathis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-10-15 00:00:00.000000000 Z
11
+ date: 2015-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler