Commit 2eb748d24d3ee0d13f990d2b7eab167a1b41b930
Render npm package readmes
cel committed on 9/20/2017, 8:44:17 PMParent: a9feab3b1f5335b14fc0feba4da2b9fe40aa1224
Files changed
lib/app.js | changed |
lib/render.js | changed |
lib/serve.js | changed |
lib/app.js | ||
---|---|---|
@@ -11,8 +11,10 @@ | ||
11 | 11 … | var Serve = require('./serve') |
12 | 12 … | var Render = require('./render') |
13 | 13 … | var Git = require('./git') |
14 | 14 … | var cat = require('pull-cat') |
15 … | +var proc = require('child_process') | |
16 … | +var toPull = require('stream-to-pull-stream') | |
15 | 17 … | |
16 | 18 … | module.exports = App |
17 | 19 … | |
18 | 20 … | function App(sbot, config) { |
@@ -39,13 +41,17 @@ | ||
39 | 41 … | this._getAbout.bind(this)) |
40 | 42 … | this.unboxContent = memo({cache: lru(100)}, sbot.private.unbox) |
41 | 43 … | this.reverseNameCache = lru(500) |
42 | 44 … | this.reverseEmojiNameCache = lru(500) |
45 … | + this.getBlobSize = memo({cache: this.blobSizeCache = lru(100)}, | |
46 … | + sbot.blobs.size.bind(sbot.blobs)) | |
43 | 47 … | |
44 | 48 … | this.unboxMsg = this.unboxMsg.bind(this) |
45 | 49 … | |
46 | 50 … | this.render = new Render(this, this.opts) |
47 | 51 … | this.git = new Git(this) |
52 … | + | |
53 … | + this.monitorBlobWants() | |
48 | 54 … | } |
49 | 55 … | |
50 | 56 … | App.prototype.go = function () { |
51 | 57 … | var self = this |
@@ -178,11 +184,14 @@ | ||
178 | 184 … | tryPublish(2) |
179 | 185 … | } |
180 | 186 … | |
181 | 187 … | App.prototype.wantSizeBlob = function (id, cb) { |
188 … | + // only want() the blob if we don't already have it | |
189 … | + var self = this | |
182 | 190 … | var blobs = this.sbot.blobs |
183 | 191 … | blobs.size(id, function (err, size) { |
184 | 192 … | if (size != null) return cb(null, size) |
193 … | + self.blobWants[id] = true | |
185 | 194 … | blobs.want(id, function (err) { |
186 | 195 … | if (err) return cb(err) |
187 | 196 … | blobs.size(id, cb) |
188 | 197 … | }) |
@@ -564,4 +573,51 @@ | ||
564 | 573 … | }} |
565 | 574 … | ] |
566 | 575 … | }) |
567 | 576 … | } |
577 … | + | |
578 … | +App.prototype.monitorBlobWants = function () { | |
579 … | + var self = this | |
580 … | + self.blobWants = {} | |
581 … | + pull( | |
582 … | + this.sbot.blobs.createWants(), | |
583 … | + pull.drain(function (wants) { | |
584 … | + for (var id in wants) { | |
585 … | + if (wants[id] < 0) self.blobWants[id] = true | |
586 … | + else delete self.blobWants[id] | |
587 … | + self.blobSizeCache.remove(id) | |
588 … | + } | |
589 … | + }, function (err) { | |
590 … | + if (err) console.trace(err) | |
591 … | + }) | |
592 … | + ) | |
593 … | +} | |
594 … | + | |
595 … | +App.prototype.getBlobState = function (id, cb) { | |
596 … | + var self = this | |
597 … | + if (self.blobWants[id]) return cb(null, 'wanted') | |
598 … | + self.getBlobSize(id, function (err, size) { | |
599 … | + if (err) return cb(err) | |
600 … | + cb(null, size != null) | |
601 … | + }) | |
602 … | +} | |
603 … | + | |
604 … | +App.prototype.getNpmReadme = function (tarballId, cb) { | |
605 … | + var self = this | |
606 … | + // TODO: make this portable, and handle plaintext readmes | |
607 … | + var tar = proc.spawn('tar', ['--ignore-case', '-Oxz', | |
608 … | + 'package/README.md', 'package/readme.markdown', 'package/readme.mkd']) | |
609 … | + var done = multicb({pluck: 1, spread: true}) | |
610 … | + pull( | |
611 … | + self.sbot.blobs.get(tarballId), | |
612 … | + toPull.sink(tar.stdin, done()) | |
613 … | + ) | |
614 … | + pull( | |
615 … | + toPull.source(tar.stdout), | |
616 … | + pull.collect(done()) | |
617 … | + ) | |
618 … | + done(function (err, _, bufs) { | |
619 … | + if (err) return cb(err) | |
620 … | + var text = Buffer.concat(bufs).toString('utf8') | |
621 … | + cb(null, text, true) | |
622 … | + }) | |
623 … | +} |
lib/render.js | ||
---|---|---|
@@ -421,14 +421,16 @@ | ||
421 | 421 … | var distTag = parts[3] |
422 | 422 … | var self = this |
423 | 423 … | var done = multicb({pluck: 1, spread: true}) |
424 | 424 … | var base = '/npm/' + (opts.author ? u.escapeId(link.author) + '/' : '') |
425 | - var pathWithAuthor = '/npm/' + | |
425 … | + var pathWithAuthor = opts.withAuthor ? '/npm/' + | |
426 | 426 … | u.escapeId(link.author) + '/' + |
427 | 427 … | (opts.name ? opts.name + '/' + |
428 | 428 … | (opts.version ? opts.version + '/' + |
429 | - (opts.distTag ? opts.distTag + '/' : '') : '') : '') | |
430 | - self.app.getAbout(link.author, function (err, about) { | |
429 … | + (opts.distTag ? opts.distTag + '/' : '') : '') : '') : '' | |
430 … | + self.app.getAbout(link.author, done()) | |
431 … | + self.app.getBlobState(link.link, done()) | |
432 … | + done(function (err, about, blobState) { | |
431 | 433 … | if (err) return cb(err) |
432 | 434 … | cb(null, h('tr', [ |
433 | 435 … | opts.withAuthor ? h('td', h('a', { |
434 | 436 … | href: self.toUrl(pathWithAuthor), |
@@ -451,8 +453,21 @@ | ||
451 | 453 … | }, self.formatSize(link.size)), ' '] : ''), |
452 | 454 … | h('td', typeof link.link === 'string' ? h('code', h('a', { |
453 | 455 … | href: self.toUrl('/links/' + link.link), |
454 | 456 … | title: 'package tarball' |
455 | - }, link.link.substr(0, 8) + '…')) : '') | |
457 … | + }, link.link.substr(0, 8) + '…')) : ''), | |
458 … | + h('td', | |
459 … | + blobState === 'wanted' ? | |
460 … | + 'fetching...' | |
461 … | + : blobState ? h('a', { | |
462 … | + href: self.toUrl('/npm-readme/' + encodeURIComponent(link.link)), | |
463 … | + title: 'package contents' | |
464 … | + }, 'readme') | |
465 … | + : h('form', {action: '', method: 'post'}, | |
466 … | + h('input', {type: 'hidden', name: 'action', value: 'want-blobs'}), | |
467 … | + h('input', {type: 'hidden', name: 'async_want', value: '1'}), | |
468 … | + h('input', {type: 'hidden', name: 'blob_ids', value: link.link}), | |
469 … | + h('input', {type: 'submit', value: 'fetch'}) | |
470 … | + )) | |
456 | 471 … | ])) |
457 | 472 … | }) |
458 | 473 … | } |
lib/serve.js | ||
---|---|---|
@@ -200,10 +200,11 @@ | ||
200 | 200 … | var ids = self.data.blob_ids.split(',') |
201 | 201 … | if (!ids.every(u.isRef)) return cb(new Error('bad blob ids ' + ids.join(','))) |
202 | 202 … | var done = multicb({pluck: 1}) |
203 | 203 … | ids.forEach(function (id) { |
204 | - self.app.sbot.blobs.want(id, done()) | |
204 … | + self.app.wantSizeBlob(id, done()) | |
205 | 205 … | }) |
206 … | + if (self.data.async_want) return cb() | |
206 | 207 … | done(function (err) { |
207 | 208 … | if (err) return cb(err) |
208 | 209 … | // self.note = h('div', 'wanted blobs: ' + ids.join(', ') + '.') |
209 | 210 … | cb() |
@@ -302,8 +303,9 @@ | ||
302 | 303 … | case '/about': return this.about(m[2]) |
303 | 304 … | case '/git': return this.git(m[2]) |
304 | 305 … | case '/image': return this.image(m[2]) |
305 | 306 … | case '/npm': return this.npm(m[2]) |
307 … | + case '/npm-readme': return this.npmReadme(m[2]) | |
306 | 308 … | } |
307 | 309 … | return this.respond(404, 'Not found') |
308 | 310 … | } |
309 | 311 … | |
@@ -1910,9 +1912,10 @@ | ||
1910 | 1912 … | ph('td', 'package'), |
1911 | 1913 … | ph('td', 'version'), |
1912 | 1914 … | ph('td', 'tag'), |
1913 | 1915 … | ph('td', 'size'), |
1914 | - ph('td', 'tarball') | |
1916 … | + ph('td', 'tarball'), | |
1917 … | + ph('td', 'readme') | |
1915 | 1918 … | ])), |
1916 | 1919 … | ph('tbody', pull( |
1917 | 1920 … | self.app.blobMentions({ |
1918 | 1921 … | name: {$prefix: prefix}, |
@@ -1933,13 +1936,36 @@ | ||
1933 | 1936 … | pull.map(u.toHTML) |
1934 | 1937 … | )) |
1935 | 1938 … | ]) |
1936 | 1939 … | ]), |
1937 | - self.wrapPage('npm:'), | |
1940 … | + self.wrapPage(prefix), | |
1938 | 1941 … | self.respondSink(200) |
1939 | 1942 … | ) |
1940 | 1943 … | } |
1941 | 1944 … | |
1945 … | +Serve.prototype.npmReadme = function (url) { | |
1946 … | + var self = this | |
1947 … | + var id = decodeURIComponent(url.substr(1)) | |
1948 … | + return pull( | |
1949 … | + ph('section', {}, [ | |
1950 … | + ph('h3', [ | |
1951 … | + 'npm readme for ', | |
1952 … | + ph('a', {href: '/links/' + id}, id.substr(0, 8) + '…') | |
1953 … | + ]), | |
1954 … | + ph('blockquote', u.readNext(function (cb) { | |
1955 … | + self.app.getNpmReadme(id, function (err, readme, isMarkdown) { | |
1956 … | + if (err) return cb(null, ph('div', u.renderError(err).outerHTML)) | |
1957 … | + cb(null, isMarkdown | |
1958 … | + ? ph('div', self.app.render.markdown(readme)) | |
1959 … | + : ph('pre', readme)) | |
1960 … | + }) | |
1961 … | + })) | |
1962 … | + ]), | |
1963 … | + self.wrapPage('npm readme'), | |
1964 … | + self.respondSink(200) | |
1965 … | + ) | |
1966 … | +} | |
1967 … | + | |
1942 | 1968 … | // wrap a binary source and render it or turn into an embed |
1943 | 1969 … | Serve.prototype.wrapBinary = function (opts) { |
1944 | 1970 … | var self = this |
1945 | 1971 … | var ext = opts.ext |
Built with git-ssb-web