cdnget 0.3.0 → 1.0.2

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.
data/bin/cdnget CHANGED
@@ -2,29 +2,97 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  ##
5
- ## Download files from CDN (CDNJS, Google, jsDelivr).
5
+ ## Download files from CDN (CDNJS, jsDelivr, UNPKG, Google).
6
6
  ##
7
7
  ## - CDNJS (https://cdnjs.com/)
8
- ## - Google (https://developers.google.com/speed/libraries/)
9
8
  ## - jsDelivr (https://www.jsdelivr.com/)
9
+ ## - UNPKG (https://unpkg.com/)
10
+ ## - Google (https://developers.google.com/speed/libraries/)
10
11
  ##
11
12
  ## Example:
12
13
  ## $ cdnget # list public CDN
13
14
  ## $ cdnget [-q] cdnjs # list libraries
14
15
  ## $ cdnget [-q] cdnjs jquery # list versions
15
- ## $ cdnget [-q] cdnjs jquery 2.2.0 # list files
16
- ## $ cdnget [-q] cdnjs jquery 2.2.0 /tmp # download files
16
+ ## $ cdnget [-q] cdnjs jquery latest # detect latest version
17
+ ## $ cdnget [-q] cdnjs jquery 3.6.0 # list files
18
+ ## $ cdnget [-q] cdnjs jquery 3.6.0 /tmp # download files
17
19
  ##
18
20
 
19
21
  require 'open-uri'
22
+ require 'uri'
23
+ require 'net/http'
24
+ require 'openssl'
20
25
  require 'json'
21
26
  require 'fileutils'
27
+ require 'pp'
22
28
 
23
29
 
24
30
  module CDNGet
25
31
 
26
32
 
27
- RELEASE = '$Release: 0.3.0 $'.split()[1]
33
+ RELEASE = '$Release: 1.0.2 $'.split()[1]
34
+
35
+
36
+ class HttpConnection
37
+
38
+ def initialize(uri, headers=nil)
39
+ http = Net::HTTP.new(uri.host, uri.port)
40
+ if uri.scheme == 'https'
41
+ http.use_ssl = true
42
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
43
+ end
44
+ http.start()
45
+ @http = http
46
+ @headers = headers
47
+ end
48
+
49
+ def self.open(uri, headers=nil)
50
+ http = self.new(uri, headers)
51
+ return http unless block_given?()
52
+ begin
53
+ return yield http
54
+ ensure
55
+ http.close()
56
+ end
57
+ end
58
+
59
+ def get(uri)
60
+ resp = @http.send_request('GET', uri.path, nil, @headers)
61
+ case resp
62
+ when Net::HTTPSuccess
63
+ return resp.body
64
+ #when HTTPInformation, Net::HTTPRedirection, HTTPClientError, HTTPServerError
65
+ else
66
+ raise HttpError.new(resp.code.to_i, resp.message)
67
+ end
68
+ end
69
+
70
+ def post(uri, payload)
71
+ path = uri.path
72
+ path += "?"+uri.query if uri.query && !uri.query.empty?
73
+ resp = @http.send_request('POST', path, payload, @headers)
74
+ case resp
75
+ when Net::HTTPSuccess ; return resp.body
76
+ else ; raise HttpError.new(resp.code.to_i, resp.message)
77
+ end
78
+ end
79
+
80
+ def close()
81
+ @http.finish()
82
+ end
83
+
84
+ end
85
+
86
+
87
+ class HttpError < StandardError
88
+ def initialize(code, msgtext)
89
+ super("#{code} #{msgtext}")
90
+ @code = code
91
+ @msgtext = msgtext
92
+ end
93
+ attr_reader :code, :msgtext
94
+ end
95
+
28
96
 
29
97
  CLASSES = []
30
98
 
@@ -35,10 +103,22 @@ module CDNGet
35
103
  CLASSES << klass
36
104
  end
37
105
 
106
+ def initialize(debug_mode: false)
107
+ @debug_mode = debug_mode
108
+ end
109
+ attr_reader :debug_mode
110
+
38
111
  def list()
39
112
  raise NotImplementedError.new("#{self.class.name}#list(): not implemented yet.")
40
113
  end
41
114
 
115
+ def search(pattern)
116
+ return list().select {|d| File.fnmatch(pattern, d[:name], File::FNM_CASEFOLD) }
117
+ #rexp_str = pattern.split('*', -1).collect {|x| Regexp.escape(x) }.join('.*')
118
+ #rexp = Regexp.compile("\\A#{rexp_str}\\z", Regexp::IGNORECASE)
119
+ #return list().select {|d| d[:name] =~ rexp }
120
+ end
121
+
42
122
  def find(library)
43
123
  raise NotImplementedError.new("#{self.class.name}#find(): not implemented yet.")
44
124
  end
@@ -53,14 +133,36 @@ module CDNGet
53
133
  raise CommandError.new("#{basedir}: not exist.")
54
134
  File.directory?(basedir) or
55
135
  raise CommandError.new("#{basedir}: not a directory.")
56
- target_dir = File.join(basedir, library, version)
57
136
  d = get(library, version)
137
+ target_dir = d[:destdir] ? File.join(basedir, d[:destdir]) \
138
+ : File.join(basedir, library, version)
139
+ http = nil
140
+ skipfile = d[:skipfile] # ex: /\.DS_Store\z/
58
141
  d[:files].each do |file|
59
142
  filepath = File.join(target_dir, file)
143
+ #
144
+ if skipfile && file =~ skipfile
145
+ puts "#{filepath} ... Skipped" # for example, skip '.DS_Store' files
146
+ next
147
+ end
148
+ #
149
+ if filepath.end_with?('/')
150
+ if File.exist?(filepath)
151
+ puts "#{filepath} ... Done (Already exists)" unless quiet
152
+ else
153
+ print "#{filepath} ..." unless quiet
154
+ FileUtils.mkdir_p(filepath)
155
+ puts " Done (Created)" unless quiet
156
+ end
157
+ next
158
+ end
159
+ #
60
160
  dirpath = File.dirname(filepath)
61
161
  print "#{filepath} ..." unless quiet
62
162
  url = File.join(d[:baseurl], file) # not use URI.join!
63
- content = fetch(url)
163
+ uri = URI.parse(url)
164
+ http ||= HttpConnection.new(uri)
165
+ content = http.get(uri)
64
166
  content = content.force_encoding('ascii-8bit')
65
167
  print " Done (#{format_integer(content.bytesize)} byte)" unless quiet
66
168
  FileUtils.mkdir_p(dirpath) unless File.exist?(dirpath)
@@ -72,17 +174,32 @@ module CDNGet
72
174
  end
73
175
  puts() unless quiet
74
176
  end
177
+ http.close() if http
75
178
  nil
76
179
  end
77
180
 
78
181
  protected
79
182
 
183
+ def http_get(url)
184
+ ## * `open()` on Ruby 3.X can't open http url
185
+ ## * `URI.open()` on Ruby <= 2.4 raises NoMethodError (private method `open' called)
186
+ ## * `URI.__send__(:open)` is a hack to work on both Ruby 2.X and 3.X
187
+ return URI.__send__(:open, url, 'rb') {|f| f.read() }
188
+ end
189
+
80
190
  def fetch(url, library=nil)
81
191
  begin
82
- html = open(url, 'rb') {|f| f.read() }
192
+ html = http_get(url)
83
193
  return html
84
- rescue OpenURI::HTTPError => ex
85
- raise CommandError.new("GET #{url} : #{ex.message}")
194
+ rescue OpenURI::HTTPError => exc
195
+ if ! (exc.message == "404 Not Found" && library)
196
+ raise CommandError.new("GET #{url} : #{exc.message}")
197
+ elsif ! library.end_with?('js')
198
+ raise CommandError.new("#{library}: Library not found.")
199
+ else
200
+ maybe = library.end_with?('.js') ? library.sub('.js', 'js') : library.sub(/js$/, '.js')
201
+ raise CommandError.new("#{library}: Library not found (maybe '#{maybe}'?).")
202
+ end
86
203
  end
87
204
  end
88
205
 
@@ -101,43 +218,39 @@ module CDNGet
101
218
  return value.to_s.reverse.scan(/..?.?/).collect {|s| s.reverse }.reverse.join(',')
102
219
  end
103
220
 
104
- end
105
-
221
+ def _debug_print(x)
222
+ if @debug_mode
223
+ $stderr.puts "\e[0;35m*** #{PP.pp(x,'')}\e[0m"
224
+ end
225
+ end
106
226
 
107
- class HttpError < StandardError
108
227
  end
109
228
 
110
229
 
111
- class CDNJS < Base # TODO: use jsdelivr api
230
+ class CDNJS < Base
112
231
  CODE = "cdnjs"
113
232
  SITE_URL = 'https://cdnjs.com/'
114
233
 
115
234
  def fetch(url, library=nil)
116
- begin
117
- json_str = open(url, 'rb') {|f| f.read() }
118
- if library
119
- if json_str == "{}"
120
- if library.end_with?('js')
121
- maybe = library.end_with?('.js') \
122
- ? library.sub('.js', 'js') \
123
- : library.sub(/js$/, '.js')
124
- raise CommandError.new("#{library}: Library not found (maybe '#{maybe}'?).")
125
- else
126
- raise CommandError.new("#{library}: Library not found.")
127
- end
128
- end
235
+ json_str = super
236
+ if json_str == "{}" && library
237
+ if library.end_with?('js')
238
+ maybe = library.end_with?('.js') \
239
+ ? library.sub('.js', 'js') \
240
+ : library.sub(/js$/, '.js')
241
+ raise CommandError.new("#{library}: Library not found (maybe '#{maybe}'?).")
242
+ else
243
+ raise CommandError.new("#{library}: Library not found.")
129
244
  end
130
- return json_str
131
- rescue OpenURI::HTTPError => ex
132
- raise HttpError.new("GET #{url} : #{ex.message}")
133
245
  end
246
+ return json_str
134
247
  end
135
248
  protected :fetch
136
249
 
137
- def list
138
- libs = []
250
+ def list()
139
251
  jstr = fetch("https://api.cdnjs.com/libraries?fields=name,description")
140
252
  jdata = JSON.parse(jstr)
253
+ _debug_print(jdata)
141
254
  libs = jdata['results'].collect {|d| {name: d['name'], desc: d['description']} }
142
255
  return libs.sort_by {|d| d[:name] }.uniq
143
256
  end
@@ -146,11 +259,17 @@ module CDNGet
146
259
  validate(library, nil)
147
260
  jstr = fetch("https://api.cdnjs.com/libraries/#{library}", library)
148
261
  jdata = JSON.parse(jstr)
262
+ _debug_print(jdata)
263
+ versions = jdata['assets'].collect {|d| d['version'] }\
264
+ .sort_by {|v| v.split(/[-.]/).map(&:to_i) }
149
265
  return {
150
266
  name: library,
151
267
  desc: jdata['description'],
152
268
  tags: (jdata['keywords'] || []).join(", "),
153
- versions: jdata['assets'].collect {|d| d['version'] },
269
+ site: jdata['homepage'],
270
+ info: File.join(SITE_URL, "/libraries/#{library}"),
271
+ license: jdata['license'],
272
+ versions: versions.reverse(),
154
273
  }
155
274
  end
156
275
 
@@ -158,17 +277,21 @@ module CDNGet
158
277
  validate(library, version)
159
278
  jstr = fetch("https://api.cdnjs.com/libraries/#{library}", library)
160
279
  jdata = JSON.parse(jstr)
280
+ _debug_print(jdata)
161
281
  d = jdata['assets'].find {|d| d['version'] == version } or
162
282
  raise CommandError.new("#{library}/#{version}: Library or version not found.")
163
283
  baseurl = "https://cdnjs.cloudflare.com/ajax/libs/#{library}/#{version}/"
164
284
  return {
165
285
  name: library,
286
+ version: version,
166
287
  desc: jdata['description'],
167
288
  tags: (jdata['keywords'] || []).join(", "),
168
- version: version,
289
+ site: jdata['homepage'],
290
+ info: File.join(SITE_URL, "/libraries/#{library}/#{version}"),
169
291
  urls: d['files'].collect {|s| baseurl + s },
170
292
  files: d['files'],
171
293
  baseurl: baseurl,
294
+ license: jdata['license'],
172
295
  }
173
296
  end
174
297
 
@@ -178,59 +301,201 @@ module CDNGet
178
301
  class JSDelivr < Base
179
302
  CODE = "jsdelivr"
180
303
  SITE_URL = "https://www.jsdelivr.com/"
181
- API_URL = "https://api.jsdelivr.com/v1/jsdelivr/libraries"
304
+ #API_URL = "https://api.jsdelivr.com/v1/jsdelivr/libraries"
305
+ API_URL = "https://data.jsdelivr.com/v1"
306
+ HEADERS = {
307
+ "x-algo""lia-app""lication-id"=>"OFCNC""OG2CU",
308
+ "x-algo""lia-api""-key"=>"f54e21fa3a2""a0160595bb05""8179bfb1e",
309
+ }
310
+
311
+ def list()
312
+ return nil # nil means that this CDN can't list libraries without pattern
313
+ end
182
314
 
183
- def list
184
- json = fetch("#{API_URL}?fields=name,description,homepage")
185
- arr = JSON.load(json)
186
- return arr.collect {|d|
187
- {name: d["name"], desc: d["description"], site: d["homepage"] }
315
+ def search(pattern)
316
+ form_data = {
317
+ query: pattern,
318
+ page: "0",
319
+ hitsPerPage: "1000",
320
+ attributesToHighlight: '[]',
321
+ attributesToRetrieve: '["name","description","version"]'
322
+ }
323
+ payload = JSON.dump({"params"=>URI.encode_www_form(form_data)})
324
+ url = "https://ofcncog2cu-3.algolianet.com/1/indexes/npm-search/query"
325
+ uri = URI.parse(url)
326
+ json = HttpConnection.open(uri, HEADERS) {|http| http.post(uri, payload) }
327
+ jdata = JSON.load(json)
328
+ _debug_print(jdata)
329
+ return jdata["hits"].select {|d|
330
+ File.fnmatch(pattern, d["name"], File::FNM_CASEFOLD)
331
+ }.collect {|d|
332
+ {name: d["name"], desc: d["description"], version: d["version"]}
188
333
  }
189
334
  end
190
335
 
191
336
  def find(library)
192
337
  validate(library, nil)
193
- json = fetch("#{API_URL}?name=#{library}&fields=name,description,homepage,versions")
194
- arr = JSON.load(json)
195
- d = arr.first or
196
- raise CommandError.new("#{library}: Library not found.")
338
+ url = "https://ofcncog2cu-dsn.algolia.net/1/indexes/npm-search/#{library}"
339
+ uri = URI.parse(url)
340
+ begin
341
+ json = HttpConnection.open(uri, HEADERS) {|http| http.get(uri) }
342
+ rescue HttpError
343
+ raise CommandError, "#{library}: Library not found."
344
+ end
345
+ dict1 = JSON.load(json)
346
+ _debug_print(dict1)
347
+ #
348
+ json = fetch("#{API_URL}/package/npm/#{library}")
349
+ dict2 = JSON.load(json)
350
+ _debug_print(dict2)
351
+ #
352
+ d = dict1
197
353
  return {
198
- name: d['name'],
199
- desc: d['description'],
200
- site: d['homepage'],
201
- versions: d['versions'],
354
+ name: d['name'],
355
+ desc: d['description'],
356
+ #versions: d['versions'].collect {|k,v| k },
357
+ versions: dict2['versions'],
358
+ tags: (d['keywords'] || []).join(", "),
359
+ site: d['homepage'],
360
+ info: File.join(SITE_URL, "/package/npm/#{library}"),
361
+ license: d['license'],
202
362
  }
203
363
  end
204
364
 
205
365
  def get(library, version)
206
366
  validate(library, version)
207
- baseurl = "https://cdn.jsdelivr.net/#{library}/#{version}"
208
- url = "#{API_URL}/#{library}/#{version}"
209
- json = fetch("#{API_URL}/#{library}/#{version}")
210
- files = JSON.load(json)
211
- ! files.empty? or
212
- raise CommandError.new("#{library}: Library not found.")
213
- urls = files.collect {|x| "#{baseurl}/#{x}" }
367
+ url = File.join(API_URL, "/package/npm/#{library}@#{version}/flat")
368
+ begin
369
+ json = fetch(url, library)
370
+ rescue CommandError
371
+ raise CommandError.new("#{library}@#{version}: Library or version not found.")
372
+ end
373
+ jdata = JSON.load(json)
374
+ files = jdata["files"].collect {|d| d["name"] }
375
+ baseurl = "https://cdn.jsdelivr.net/npm/#{library}@#{version}"
376
+ _debug_print(jdata)
377
+ #
378
+ dict = find(library)
379
+ dict.delete(:versions)
380
+ dict.update({
381
+ version: version,
382
+ info: File.join(SITE_URL, "/package/npm/#{library}?version=#{version}"),
383
+ npmpkg: "https://registry.npmjs.org/#{library}/-/#{library}-#{version}.tgz",
384
+ urls: files.collect {|x| baseurl + x },
385
+ files: files,
386
+ baseurl: baseurl,
387
+ default: jdata["default"],
388
+ destdir: "#{library}@#{version}",
389
+ })
390
+ return dict
391
+ end
392
+
393
+ end
394
+
395
+
396
+ class Unpkg < Base
397
+ CODE = "unpkg"
398
+ SITE_URL = "https://unpkg.com/"
399
+ #API_URL = "https://www.npmjs.com"
400
+ API_URL = "https://api.npms.io/v2"
401
+
402
+ protected
403
+
404
+ def http_get(url)
405
+ return URI.__send__(:open, url, 'rb', {"x-spiferack"=>"1"}) {|f| f.read() }
406
+ end
407
+
408
+ public
409
+
410
+ def list()
411
+ return nil # nil means that this CDN can't list libraries without pattern
412
+ end
413
+
414
+ def search(pattern)
415
+ #json = fetch("#{API_URL}/search?q=#{pattern}")
416
+ json = fetch("#{API_URL}/search?q=#{pattern}&size=250")
417
+ jdata = JSON.load(json)
418
+ _debug_print(jdata)
419
+ #arr = jdata["objects"] # www.npmjs.com
420
+ arr = jdata["results"] # api.npms.io
421
+ return arr.select {|dict|
422
+ File.fnmatch(pattern, dict["package"]["name"], File::FNM_CASEFOLD)
423
+ }.collect {|dict|
424
+ d = dict["package"]
425
+ {name: d["name"], desc: d["description"], version: d["version"]}
426
+ }
427
+ end
428
+
429
+ def find(library)
430
+ validate(library, nil)
431
+ json = fetch("#{API_URL}/package/#{library}", library)
432
+ jdata = JSON.load(json)
433
+ _debug_print(jdata)
434
+ dict = jdata["collected"]["metadata"]
435
+ versions = [dict["version"]]
436
+ #
437
+ url = File.join(SITE_URL, "/browse/#{library}/")
438
+ html = fetch(url, library)
439
+ _debug_print(html)
440
+ if html =~ /<script>window.__DATA__\s*=\s*(.*?)<\/script>/m
441
+ jdata2 = JSON.load($1)
442
+ versions = jdata2["availableVersions"].reverse()
443
+ end
444
+ #
214
445
  return {
446
+ name: dict["name"],
447
+ desc: dict["description"],
448
+ tags: (dict["keywords"] || []).join(", "),
449
+ site: dict["links"] ? dict["links"]["homepage"] : dict["links"]["npm"],
450
+ info: File.join(SITE_URL, "/browse/#{library}/"),
451
+ versions: versions,
452
+ license: dict["license"],
453
+ }
454
+ end
455
+
456
+ def get(library, version)
457
+ validate(library, version)
458
+ dict = find(library)
459
+ dict.delete(:versions)
460
+ #
461
+ url = "https://data.jsdelivr.com/v1/package/npm/#{library}@#{version}/flat"
462
+ begin
463
+ json = fetch(url, library)
464
+ rescue CommandError
465
+ raise CommandError.new("#{library}@#{version}: Library or version not found.")
466
+ end
467
+ jdata = JSON.load(json)
468
+ files = jdata["files"].collect {|d| d["name"] }
469
+ baseurl = File.join(SITE_URL, "/#{library}@#{version}")
470
+ _debug_print(jdata)
471
+ #
472
+ dict.update({
215
473
  name: library,
216
474
  version: version,
217
- urls: urls,
475
+ info: File.join(SITE_URL, "/browse/#{library}@#{version}/"),
476
+ npmpkg: "https://registry.npmjs.org/#{library}/-/#{library}-#{version}.tgz",
477
+ urls: files.collect {|x| baseurl+x },
218
478
  files: files,
219
479
  baseurl: baseurl,
220
- }
480
+ default: jdata["default"],
481
+ destdir: "#{library}@#{version}",
482
+ skipfile: /\.DS_Store\z/, # downloading '.DS_Store' from UNPKG results in 403
483
+ })
484
+ return dict
221
485
  end
222
486
 
223
487
  end
224
488
 
225
489
 
226
- class GoogleCDN < Base # TODO: use jsdelivr api
490
+ class GoogleCDN < Base
227
491
  CODE = "google"
228
492
  SITE_URL = 'https://developers.google.com/speed/libraries/'
229
493
 
230
- def list
231
- libs = []
494
+ def list()
232
495
  html = fetch("https://developers.google.com/speed/libraries/")
496
+ _debug_print(html)
233
497
  rexp = %r`"https://ajax\.googleapis\.com/ajax/libs/([^/]+)/([^/]+)/([^"]+)"`
498
+ libs = []
234
499
  html.scan(rexp) do |lib, ver, file|
235
500
  libs << {name: lib, desc: "latest version: #{ver}" }
236
501
  end
@@ -240,6 +505,7 @@ module CDNGet
240
505
  def find(library)
241
506
  validate(library, nil)
242
507
  html = fetch("https://developers.google.com/speed/libraries/")
508
+ _debug_print(html)
243
509
  rexp = %r`"https://ajax\.googleapis\.com/ajax/libs/#{library}/`
244
510
  site_url = nil
245
511
  versions = []
@@ -250,9 +516,7 @@ module CDNGet
250
516
  found = true
251
517
  if text =~ /<dt>.*?snippet:<\/dt>\s*<dd>(.*?)<\/dd>/m
252
518
  s = $1
253
- s.scan(/\b(?:src|href)="([^"]*?)"/) do |href,|
254
- urls << href
255
- end
519
+ s.scan(/\b(?:src|href)="([^"]*?)"/) {|href,| urls << href }
256
520
  end
257
521
  if text =~ /<dt>site:<\/dt>\s*<dd>(.*?)<\/dd>/m
258
522
  s = $1
@@ -273,6 +537,7 @@ module CDNGet
273
537
  return {
274
538
  name: library,
275
539
  site: site_url,
540
+ info: "#{SITE_URL}\##{library}",
276
541
  urls: urls,
277
542
  versions: versions,
278
543
  }
@@ -293,6 +558,7 @@ module CDNGet
293
558
  return {
294
559
  name: d[:name],
295
560
  site: d[:site],
561
+ info: "#{SITE_URL}\##{library}",
296
562
  urls: urls,
297
563
  files: files,
298
564
  baseurl: baseurl,
@@ -325,24 +591,34 @@ module CDNGet
325
591
  @script = script || File.basename($0)
326
592
  end
327
593
 
328
- def help_message
594
+ def help_message()
329
595
  script = @script
330
596
  return <<END
331
- #{script} -- download files from public CDN
597
+ #{script} -- download files from public CDN (cdnjs/jsdelivr/unpkg/google)
332
598
 
333
- Usage: #{script} [options] [CDN] [library] [version] [directory]
599
+ Usage: #{script} [<options>] [<CDN> [<library> [<version> [<directory>]]]]
334
600
 
335
601
  Options:
336
602
  -h, --help : help
337
603
  -v, --version : version
338
604
  -q, --quiet : minimal output
605
+ --debug : (debug mode)
339
606
 
340
607
  Example:
341
- $ #{script} # list public CDN
608
+ $ #{script} # list public CDN names
342
609
  $ #{script} [-q] cdnjs # list libraries
610
+ $ #{script} [-q] cdnjs 'jquery*' # search libraries
343
611
  $ #{script} [-q] cdnjs jquery # list versions
612
+ $ #{script} [-q] cdnjs jquery latest # show latest version
344
613
  $ #{script} [-q] cdnjs jquery 2.2.0 # list files
345
- $ #{script} [-q] cdnjs jquery 2.2.0 /tmp # download files
614
+ $ mkdir -p static/lib # create a directory
615
+ $ #{script} [-q] cdnjs jquery 2.2.0 static/lib # download files
616
+ static/lib/jquery/2.2.0/jquery.js ... Done (258,388 byte)
617
+ static/lib/jquery/2.2.0/jquery.min.js ... Done (85,589 byte)
618
+ static/lib/jquery/2.2.0/jquery.min.map ... Done (129,544 byte)
619
+ $ ls static/lib/jquery/2.2.0
620
+ jquery.js jquery.min.js jquery.min.map
621
+
346
622
  END
347
623
  end
348
624
 
@@ -357,10 +633,11 @@ END
357
633
  end
358
634
 
359
635
  def run(*args)
360
- cmdopts = parse_cmdopts(args, "hvq", ["help", "version", "quiet"])
636
+ cmdopts = parse_cmdopts(args, "hvq", ["help", "version", "quiet", "debug"])
361
637
  return help_message() if cmdopts['h'] || cmdopts['help']
362
638
  return RELEASE + "\n" if cmdopts['v'] || cmdopts['version']
363
639
  @quiet = cmdopts['quiet'] || cmdopts['q']
640
+ @debug_mode = cmdopts['debug']
364
641
  #
365
642
  validate(args[1], args[2])
366
643
  #
@@ -372,11 +649,9 @@ END
372
649
  return do_list_libraries(cdn_code)
373
650
  when 2
374
651
  cdn_code, library = args
375
- if library.include?('*')
376
- return do_search_libraries(cdn_code, library)
377
- else
378
- return do_find_library(cdn_code, library)
379
- end
652
+ return library.include?('*') \
653
+ ? do_search_libraries(cdn_code, library) \
654
+ : do_find_library(cdn_code, library)
380
655
  when 3
381
656
  cdn_code, library, version = args
382
657
  return do_get_library(cdn_code, library, version)
@@ -430,35 +705,29 @@ END
430
705
  def find_cdn(cdn_code)
431
706
  klass = CLASSES.find {|c| c::CODE == cdn_code } or
432
707
  raise CommandError.new("#{cdn_code}: no such CDN.")
433
- return klass.new
708
+ return klass.new(debug_mode: @debug_mode)
434
709
  end
435
710
 
436
711
  def render_list(list)
437
- if @quiet
438
- return list.collect {|d| "#{d[:name]}\n" }.join()
439
- else
440
- return list.collect {|d| "%-20s # %s\n" % [d[:name], d[:desc]] }.join()
441
- end
712
+ return list.collect {|d| "#{d[:name]}\n" }.join() if @quiet
713
+ return list.collect {|d| "%-20s # %s\n" % [d[:name], d[:desc]] }.join()
442
714
  end
443
715
 
444
- def do_list_cdns
445
- if @quiet
446
- return CLASSES.map {|c| "#{c::CODE}\n" }.join()
447
- else
448
- return CLASSES.map {|c| "%-10s # %s\n" % [c::CODE, c::SITE_URL] }.join()
449
- end
716
+ def do_list_cdns()
717
+ return CLASSES.map {|c| "#{c::CODE}\n" }.join() if @quiet
718
+ return CLASSES.map {|c| "%-10s # %s\n" % [c::CODE, c::SITE_URL] }.join()
450
719
  end
451
720
 
452
721
  def do_list_libraries(cdn_code)
453
722
  cdn = find_cdn(cdn_code)
454
- return render_list(cdn.list)
723
+ list = cdn.list() or
724
+ raise CommandError.new("#{cdn_code}: cannot list libraries; please specify pattern such as 'jquery*'.")
725
+ return render_list(list)
455
726
  end
456
727
 
457
728
  def do_search_libraries(cdn_code, pattern)
458
729
  cdn = find_cdn(cdn_code)
459
- rexp_str = pattern.split('*', -1).collect {|x| Regexp.escape(x) }.join('.*')
460
- rexp = Regexp.compile("\\A#{rexp_str}\\z", Regexp::IGNORECASE)
461
- return render_list(cdn.list.select {|a| a[:name] =~ rexp })
730
+ return render_list(cdn.search(pattern))
462
731
  end
463
732
 
464
733
  def do_find_library(cdn_code, library)
@@ -470,10 +739,12 @@ END
470
739
  s << "#{ver}\n"
471
740
  end if d[:versions]
472
741
  else
473
- s << "name: #{d[:name]}\n"
474
- s << "desc: #{d[:desc]}\n" if d[:desc]
475
- s << "tags: #{d[:tags]}\n" if d[:tags]
476
- s << "site: #{d[:site]}\n" if d[:site]
742
+ s << "name: #{d[:name]}\n"
743
+ s << "desc: #{d[:desc]}\n" if d[:desc]
744
+ s << "tags: #{d[:tags]}\n" if d[:tags]
745
+ s << "site: #{d[:site]}\n" if d[:site]
746
+ s << "info: #{d[:info]}\n" if d[:info]
747
+ s << "license: #{d[:license]}\n" if d[:license]
477
748
  s << "snippet: |\n" << d[:snippet].gsub(/^/, ' ') if d[:snippet]
478
749
  s << "versions:\n"
479
750
  d[:versions].each do |ver|
@@ -485,6 +756,7 @@ END
485
756
 
486
757
  def do_get_library(cdn_code, library, version)
487
758
  cdn = find_cdn(cdn_code)
759
+ version = _latest_version(cdn, library) if version == 'latest'
488
760
  d = cdn.get(library, version)
489
761
  s = ""
490
762
  if @quiet
@@ -494,9 +766,13 @@ END
494
766
  else
495
767
  s << "name: #{d[:name]}\n"
496
768
  s << "version: #{d[:version]}\n"
497
- s << "desc: #{d[:desc]}\n" if d[:desc]
498
- s << "tags: #{d[:tags]}\n" if d[:tags]
499
- s << "site: #{d[:site]}\n" if d[:site]
769
+ s << "desc: #{d[:desc]}\n" if d[:desc]
770
+ s << "tags: #{d[:tags]}\n" if d[:tags]
771
+ s << "site: #{d[:site]}\n" if d[:site]
772
+ s << "info: #{d[:info]}\n" if d[:info]
773
+ s << "npmpkg: #{d[:npmpkg]}\n" if d[:npmpkg]
774
+ s << "default: #{d[:default]}\n" if d[:default]
775
+ s << "license: #{d[:license]}\n" if d[:license]
500
776
  s << "snippet: |\n" << d[:snippet].gsub(/^/, ' ') if d[:snippet]
501
777
  s << "urls:\n" if d[:urls]
502
778
  d[:urls].each do |url|
@@ -508,10 +784,18 @@ END
508
784
 
509
785
  def do_download_library(cdn_code, library, version, basedir)
510
786
  cdn = find_cdn(cdn_code)
787
+ version = _latest_version(cdn, library) if version == 'latest'
511
788
  cdn.download(library, version, basedir, quiet: @quiet)
512
789
  return nil
513
790
  end
514
791
 
792
+ private
793
+
794
+ def _latest_version(cdn, library)
795
+ d = cdn.find(library)
796
+ return d[:versions].first
797
+ end
798
+
515
799
  end
516
800
 
517
801