esvg 2.3.1 → 2.4.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
  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