-
Notifications
You must be signed in to change notification settings - Fork 3
/
online.js
129 lines (106 loc) · 3.53 KB
/
online.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
'use strict';
var request = require('request')
var fs = require('fs')
var path = require('path')
var url = require('url')
var semver = require('semver')
var mkdirp = require('mkdirp')
var npmUrl = require('npmd-url')
var pull = require('pull-stream')
function readJson(file, cb) {
fs.stat(file, function (err, stat) {
if(err) return cb(err)
fs.readFile(file, 'utf-8', function (err, json) {
if(err) return cb(err)
try { json = JSON.parse(json) } catch (err) { return cb(err) }
cb(null, json, stat)
})
})
}
var chooser = require('./choose-from-registry')
var MIN = 60*1000
var HOUR = 60*MIN
var DAY = 24*HOUR
module.exports = function (module, vrange, opts, cb) {
var headers = {}
var auth
var registry = opts.registry || 'https://registry.npmjs.org'
var cache = opts.cache || path.join(process.env.HOME, '.npm')
//only resolve module@semver (NOT urls - leave that to npmd-cache)
//it's a url/other protocol
if(/\//.test(vrange)) return cb()
var u = url.resolve(registry, module)
var cachedfile = path.join(cache, module, '.cache.json')
if (opts['always-auth']) {
var creds = Buffer(opts['_auth'], 'base64').toString()
var segs = creds.split(':')
auth = { user: segs[0], pass: segs[1] }
}
var nowish = new Date()
var fetched = false
//stat & read the json file.
readJson(cachedfile, function (err, json, stat) {
if(!err)
headers['if-none-match'] = json._etag
if(json) {
//if the file is very new,
if(!opts.freshen && +stat.mtime > nowish - (opts.minAge || 10*MIN))
return next(null, json)
//if the file is not old, carry on and use it.
if(!opts.freshen && +stat.mtime > nowish - (opts.maxAge || DAY))
return next(null, json)
}
function fetch () {
fetched = true
request({
url: u, headers: headers, auth: auth
}, function (err, res, data) {
if(err) return cb(err)
console.error(''+res.statusCode, u)
//if the file was still current - update mtime, so will hit cache next time.
if(res.statusCode === 304)
return fs.utimes(cachedfile, nowish, nowish, function () {
next(null, json)
})
if(res.statusCode >= 400) {
return cb(new Error(res.statusCode + ' when requesting:' + module + '@' + vrange))
}
try { data = JSON.parse(data.toString('utf-8')) } catch (err) { return cb(err) }
data._etag = res.headers.etag
if(!data.versions)
return cb(new Error('package document invalid'))
//save the newly downloaded doc.
mkdirp(path.join(cache, module), function (err) {
if(err) return cb(err)
fs.writeFile(cachedfile, JSON.stringify(data), function (err) {
//put this into leveldb, if possible!
if('function' === typeof opts.onRegistry)
opts.onRegistry(data)
next(err, data)
})
})
})
}
function next (err, doc) {
if(err) return cb(err)
//read a singe value from the stream.
pull(
chooser(doc, vrange, opts),
pull.find(Boolean, cb)
)
}
// if the cache was too old, or missing,
// request doc from registry.
console.error('GET', u)
fetch()
})
}
if(!module.parent) {
var opts = require('minimist')(process.argv.slice(2))
var parts = opts._[0].split('@')
var name = parts.shift()
var range = parts.shift() || '*'
module.exports(name, range, opts, function (err, pkg) {
console.log(JSON.stringify(pkg, null, 2))
})
}