From c0ee2db8a4642426aa7e15bf49b3277f847fab5e Mon Sep 17 00:00:00 2001 From: Brian Peacock Date: Wed, 7 May 2014 14:28:10 -0500 Subject: [PATCH] Added readme API with markdown support --- lib/GUI/css/main.less | 5 +- lib/GUI/css/markdown.css | 279 +++++++++++++++++++++++++++++++++++++++ lib/GUI/js/entry.js | 0 lib/GUI/js/main.js | 51 +------ lib/GUI/js/search.js | 49 +++++++ lib/index.js | 16 ++- lib/local-storage.js | 38 +++++- lib/static/main.css | 11 +- lib/static/main.js | 26 ++-- lib/storage.js | 4 + package.json | 3 + 11 files changed, 414 insertions(+), 68 deletions(-) create mode 100644 lib/GUI/css/markdown.css create mode 100644 lib/GUI/js/entry.js create mode 100644 lib/GUI/js/search.js diff --git a/lib/GUI/css/main.less b/lib/GUI/css/main.less index c5d2892bc..c5f1e1381 100644 --- a/lib/GUI/css/main.less +++ b/lib/GUI/css/main.less @@ -1,4 +1,5 @@ @import "../../../node_modules/helpers.less/helpers.less"; +@import "./markdown.css"; /*** Main Styles ***/ body { @@ -51,8 +52,8 @@ h1 { /*** Package Entries ***/ .entry { background: #EEE; - .border-radius(3px); - padding: 15px; + .border-radius(4px); + padding: 12px 15px; h3 { font-size: 24px; diff --git a/lib/GUI/css/markdown.css b/lib/GUI/css/markdown.css new file mode 100644 index 000000000..d45fe4472 --- /dev/null +++ b/lib/GUI/css/markdown.css @@ -0,0 +1,279 @@ +/*** Sourced from this Gist: https://gist.github.com/andyferra/2554919 ***/ + +body { + font-family: Helvetica, arial, sans-serif; + font-size: 14px; + line-height: 1.6; + padding-top: 10px; + padding-bottom: 10px; + background-color: white; + padding: 30px; } + +body > *:first-child { + margin-top: 0 !important; } +body > *:last-child { + margin-bottom: 0 !important; } + +a { + color: #4183C4; } +a.absent { + color: #cc0000; } +a.anchor { + display: block; + padding-left: 30px; + margin-left: -30px; + cursor: pointer; + position: absolute; + top: 0; + left: 0; + bottom: 0; } + +h1, h2, h3, h4, h5, h6 { + margin: 20px 0 10px; + padding: 0; + font-weight: bold; + -webkit-font-smoothing: antialiased; + cursor: text; + position: relative; } + +h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { + background: url("../../images/modules/styleguide/para.png") no-repeat 10px center; + text-decoration: none; } + +h1 tt, h1 code { + font-size: inherit; } + +h2 tt, h2 code { + font-size: inherit; } + +h3 tt, h3 code { + font-size: inherit; } + +h4 tt, h4 code { + font-size: inherit; } + +h5 tt, h5 code { + font-size: inherit; } + +h6 tt, h6 code { + font-size: inherit; } + +h1 { + font-size: 28px; + color: black; } + +h2 { + font-size: 24px; + border-bottom: 1px solid #cccccc; + color: black; } + +h3 { + font-size: 18px; } + +h4 { + font-size: 16px; } + +h5 { + font-size: 14px; } + +h6 { + color: #777777; + font-size: 14px; } + +p, blockquote, ul, ol, dl, li, table, pre { + margin: 15px 0; } + +hr { + background: transparent url("../../images/modules/pulls/dirty-shade.png") repeat-x 0 0; + border: 0 none; + color: #cccccc; + height: 4px; + padding: 0; } + +body > h2:first-child { + margin-top: 0; + padding-top: 0; } +body > h1:first-child { + margin-top: 0; + padding-top: 0; } + body > h1:first-child + h2 { + margin-top: 0; + padding-top: 0; } +body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child { + margin-top: 0; + padding-top: 0; } + +a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 { + margin-top: 0; + padding-top: 0; } + +h1 p, h2 p, h3 p, h4 p, h5 p, h6 p { + margin-top: 0; } + +li p.first { + display: inline-block; } + +ul, ol { + padding-left: 30px; } + +ul :first-child, ol :first-child { + margin-top: 0; } + +ul :last-child, ol :last-child { + margin-bottom: 0; } + +dl { + padding: 0; } + dl dt { + font-size: 14px; + font-weight: bold; + font-style: italic; + padding: 0; + margin: 15px 0 5px; } + dl dt:first-child { + padding: 0; } + dl dt > :first-child { + margin-top: 0; } + dl dt > :last-child { + margin-bottom: 0; } + dl dd { + margin: 0 0 15px; + padding: 0 15px; } + dl dd > :first-child { + margin-top: 0; } + dl dd > :last-child { + margin-bottom: 0; } + +blockquote { + border-left: 4px solid #dddddd; + padding: 0 15px; + color: #777777; } + blockquote > :first-child { + margin-top: 0; } + blockquote > :last-child { + margin-bottom: 0; } + +table { + padding: 0; } + table tr { + border-top: 1px solid #cccccc; + background-color: white; + margin: 0; + padding: 0; } + table tr:nth-child(2n) { + background-color: #f8f8f8; } + table tr th { + font-weight: bold; + border: 1px solid #cccccc; + text-align: left; + margin: 0; + padding: 6px 13px; } + table tr td { + border: 1px solid #cccccc; + text-align: left; + margin: 0; + padding: 6px 13px; } + table tr th :first-child, table tr td :first-child { + margin-top: 0; } + table tr th :last-child, table tr td :last-child { + margin-bottom: 0; } + +img { + max-width: 100%; } + +span.frame { + display: block; + overflow: hidden; } + span.frame > span { + border: 1px solid #dddddd; + display: block; + float: left; + overflow: hidden; + margin: 13px 0 0; + padding: 7px; + width: auto; } + span.frame span img { + display: block; + float: left; } + span.frame span span { + clear: both; + color: #333333; + display: block; + padding: 5px 0 0; } +span.align-center { + display: block; + overflow: hidden; + clear: both; } + span.align-center > span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: center; } + span.align-center span img { + margin: 0 auto; + text-align: center; } +span.align-right { + display: block; + overflow: hidden; + clear: both; } + span.align-right > span { + display: block; + overflow: hidden; + margin: 13px 0 0; + text-align: right; } + span.align-right span img { + margin: 0; + text-align: right; } +span.float-left { + display: block; + margin-right: 13px; + overflow: hidden; + float: left; } + span.float-left span { + margin: 13px 0 0; } +span.float-right { + display: block; + margin-left: 13px; + overflow: hidden; + float: right; } + span.float-right > span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: right; } + +code, tt { + margin: 0 2px; + padding: 0 5px; + white-space: nowrap; + border: 1px solid #eaeaea; + background-color: #f8f8f8; + border-radius: 3px; } + +pre code { + margin: 0; + padding: 0; + white-space: pre; + border: none; + background: transparent; } + +.highlight pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + font-size: 13px; + line-height: 19px; + overflow: auto; + padding: 6px 10px; + border-radius: 3px; } + +pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + font-size: 13px; + line-height: 19px; + overflow: auto; + padding: 6px 10px; + border-radius: 3px; } + pre code, pre tt { + background-color: transparent; + border: none; } \ No newline at end of file diff --git a/lib/GUI/js/entry.js b/lib/GUI/js/entry.js new file mode 100644 index 000000000..e69de29bb diff --git a/lib/GUI/js/main.js b/lib/GUI/js/main.js index 0e35c1461..c87595be0 100644 --- a/lib/GUI/js/main.js +++ b/lib/GUI/js/main.js @@ -1,49 +1,2 @@ -var $ = require('unopinionate').selector, - template = require('../entry.handlebars'); - -$(function() { - var $form = $('#search-form'), - $input = $form.find('input'), - $searchResults = $("#search-results"), - $body = $('body'), - request; - - $form.bind('submit keyup', function(e) { - e.preventDefault(); - - var q = $input.val(); - - $body.addClass('state-search'); - - if(q) { - if(request) { - request.abort(); - } - - request = $.getJSON('/-/search/' + q, function(results) { - if(results.length) { - var html = ''; - - $.each(results, function(i, package) { - html += template(package); - }); - - $searchResults.html(html); - } - else { - $searchResults.html("
No Results
"); - } - }); - } - else { - $searchResults.html(''); - $body.removeClass('state-search'); - } - }); - - $form.find('.clear').click(function(e) { - e.preventDefault(); - $input.val(''); - $form.keyup(); - }); -}); +require('./search'); +require('./entry'); diff --git a/lib/GUI/js/search.js b/lib/GUI/js/search.js new file mode 100644 index 000000000..0e35c1461 --- /dev/null +++ b/lib/GUI/js/search.js @@ -0,0 +1,49 @@ +var $ = require('unopinionate').selector, + template = require('../entry.handlebars'); + +$(function() { + var $form = $('#search-form'), + $input = $form.find('input'), + $searchResults = $("#search-results"), + $body = $('body'), + request; + + $form.bind('submit keyup', function(e) { + e.preventDefault(); + + var q = $input.val(); + + $body.addClass('state-search'); + + if(q) { + if(request) { + request.abort(); + } + + request = $.getJSON('/-/search/' + q, function(results) { + if(results.length) { + var html = ''; + + $.each(results, function(i, package) { + html += template(package); + }); + + $searchResults.html(html); + } + else { + $searchResults.html("
No Results
"); + } + }); + } + else { + $searchResults.html(''); + $body.removeClass('state-search'); + } + }); + + $form.find('.clear').click(function(e) { + e.preventDefault(); + $input.val(''); + $form.keyup(); + }); +}); diff --git a/lib/index.js b/lib/index.js index 7bf9abda5..ba2ac2ef8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -15,7 +15,8 @@ var express = require('express') , fs = require('fs') , localList = require('./local-list') , Search = require('./search') - , _ = require('underscore'); + , _ = require('underscore') + , marked = require('marked'); function match(regexp) { return function(req, res, next, value, name) { @@ -266,6 +267,19 @@ module.exports = function(config_hash) { } }); + // Readme + marked.setOptions({ + highlight: function (code) { + return require('highlight.js').highlightAuto(code).value; + } + }); + + app.get('/-/readme/:name/:version', function(req, res, next) { + storage.get_readme(req.params.name, req.params.version, function(readme) { + res.send(marked(readme)); + }); + }); + // tagging a package app.put('/:package/:tag', can('publish'), media('application/json'), function(req, res, next) { if (typeof(req.body) !== 'string') return next('route') diff --git a/lib/local-storage.js b/lib/local-storage.js index 4fc0af5c5..a6eb4c814 100644 --- a/lib/local-storage.js +++ b/lib/local-storage.js @@ -8,7 +8,8 @@ var fs = require('fs') , mystreams = require('./streams') , Logger = require('./logger') , info_file = 'package.json' - , localList = require('./local-list'); + , localList = require('./local-list') + , targz = require('tar.gz'); // // Implements Storage interface @@ -383,6 +384,41 @@ Storage.prototype.add_tarball = function(name, filename) { return stream } +Storage.prototype.unpack_tarball = function(file, callback) { + new targz().extract(file + '.tgz', file, callback); +}; + +Storage.prototype.get_readme = function(name, version, callback) { + var self = this, + fileName = this.storage(name).path + '/' + name + '-' + version; + + fs.exists(fileName, function(exists) { + if(exists) { + returnReadme(); + } + else { + self.unpack_tarball(fileName, function(err) { + returnReadme(); + }); + } + }); + + function returnReadme() { + var readmeFileName = fileName + '/package/README.md'; + + fs.exists(readmeFileName, function(exists) { + if(exists) { + fs.readFile(readmeFileName, {encoding: "UTF-8"}, function(err, file) { + callback(file); + }); + } + else { + callback(''); + } + }); + } +}; + Storage.prototype.get_tarball = function(name, filename, callback) { assert(utils.validate_name(filename)) diff --git a/lib/static/main.css b/lib/static/main.css index f0b7367f9..f95002c79 100644 --- a/lib/static/main.css +++ b/lib/static/main.css @@ -1,3 +1,4 @@ +@import "markdown.css"; /*** Main Styles ***/ body { margin: 0; @@ -43,10 +44,10 @@ h1 a:hover { /*** Package Entries ***/ .entry { background: #EEE; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - padding: 15px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + padding: 12px 15px; } .entry h3 { font-size: 24px; @@ -68,4 +69,4 @@ h1 a:hover { .state-search #all-packages { display: none; } -/*# sourceMappingURL=data:application/json,%7B%22version%22%3A3%2C%22sources%22%3A%5B%22lib%2FGUI%2Fcss%2Fmain.less%22%2C%22node_modules%2Fhelpers.less%2Fhelpers.less%22%5D%2C%22names%22%3A%5B%5D%2C%22mappings%22%3A%22%3BAAGA%3BEACC%2CSAAA%3BEACA%2CaAAa%2CiBAAiB%2C8CAA9B%3B%3BAAGD%3BEACC%2CkBAAA%3B%3BAADD%2CEAGC%3BAAHD%2CEAGI%2CEAAC%3BEACH%2CYAAA%3BEACA%2CqBAAA%3B%3BAALF%2CEAQC%2CEAAC%3BEACA%2C0BAAA%3B%3BAAIF%3BEACC%2CkBAAA%3B%3BAAGD%3BEACC%2CcAAA%3BEACA%2CgBAAA%3BEACA%2CaAAA%3B%3B%3BAAID%3BEACC%2CmBAAA%3BEACA%2CkBAAA%3BEACA%2CqBAAA%3BEClBG%2C0BAAA%3BEACA%2CuBAAA%3BEACA%2CkBAAA%3BEDkBH%2CgBAAA%3BEACA%2CWAAA%3B%3BAAND%2CMAQC%3BEACC%2CwCAAA%3B%3B%3BAAKF%3BEACC%2CYAAA%3B%3B%3BAAID%3BEACC%2CgBAAA%3BECnCG%2C0BAAA%3BEACA%2CuBAAA%3BEACA%2CkBAAA%3BEDmCH%2CaAAA%3B%3BAAHD%2CMAKC%3BEACC%2CeAAA%3BEACA%2CgBAAA%3B%3BAAPF%2CMAUC%3BEACC%2CeAAA%3BEACA%2CWAAA%3B%3BAAZF%2CMAeC%3BEACC%2CeAAA%3BEACA%2CYAAA%3BEACA%2CWAAA%3B%3BAAlBF%2CMAqBC%3BEACC%2CSAAA%3B%3B%3BAAKF%2CaAAc%3BEACb%2CaAAA%22%7D */ \ No newline at end of file +/*# sourceMappingURL=data:application/json,%7B%22version%22%3A3%2C%22sources%22%3A%5B%22lib%2FGUI%2Fcss%2Fmain.less%22%2C%22node_modules%2Fhelpers.less%2Fhelpers.less%22%5D%2C%22names%22%3A%5B%5D%2C%22mappings%22%3A%22QACQ%3B%3BAAGR%3BEACC%2CSAAA%3BEACA%2CaAAa%2CiBAAiB%2C8CAA9B%3B%3BAAGD%3BEACC%2CkBAAA%3B%3BAADD%2CEAGC%3BAAHD%2CEAGI%2CEAAC%3BEACH%2CYAAA%3BEACA%2CqBAAA%3B%3BAALF%2CEAQC%2CEAAC%3BEACA%2C0BAAA%3B%3BAAIF%3BEACC%2CkBAAA%3B%3BAAGD%3BEACC%2CcAAA%3BEACA%2CgBAAA%3BEACA%2CaAAA%3B%3B%3BAAID%3BEACC%2CmBAAA%3BEACA%2CkBAAA%3BEACA%2CqBAAA%3BECnBG%2C0BAAA%3BEACA%2CuBAAA%3BEACA%2CkBAAA%3BEDmBH%2CgBAAA%3BEACA%2CWAAA%3B%3BAAND%2CMAQC%3BEACC%2CwCAAA%3B%3B%3BAAKF%3BEACC%2CYAAA%3B%3B%3BAAID%3BEACC%2CgBAAA%3BECpCG%2C0BAAA%3BEACA%2CuBAAA%3BEACA%2CkBAAA%3BEDoCH%2CkBAAA%3B%3BAAHD%2CMAKC%3BEACC%2CeAAA%3BEACA%2CgBAAA%3B%3BAAPF%2CMAUC%3BEACC%2CeAAA%3BEACA%2CWAAA%3B%3BAAZF%2CMAeC%3BEACC%2CeAAA%3BEACA%2CYAAA%3BEACA%2CWAAA%3B%3BAAlBF%2CMAqBC%3BEACC%2CSAAA%3B%3B%3BAAKF%2CaAAc%3BEACb%2CaAAA%22%7D */ \ No newline at end of file diff --git a/lib/static/main.js b/lib/static/main.js index 1a8a61253..9e4f8d1a2 100644 --- a/lib/static/main.js +++ b/lib/static/main.js @@ -22,7 +22,13 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {}; + "

\n"; return buffer; }); -},{"handlebars/runtime":9}],2:[function(require,module,exports){ +},{"handlebars/runtime":11}],2:[function(require,module,exports){ + +},{}],3:[function(require,module,exports){ +require('./search'); +require('./entry'); + +},{"./entry":2,"./search":4}],4:[function(require,module,exports){ var $ = require('unopinionate').selector, template = require('../entry.handlebars'); @@ -73,7 +79,7 @@ $(function() { }); }); -},{"../entry.handlebars":1,"unopinionate":10}],3:[function(require,module,exports){ +},{"../entry.handlebars":1,"unopinionate":12}],5:[function(require,module,exports){ "use strict"; /*globals Handlebars: true */ var base = require("./handlebars/base"); @@ -106,7 +112,7 @@ var Handlebars = create(); Handlebars.create = create; exports["default"] = Handlebars; -},{"./handlebars/base":4,"./handlebars/exception":5,"./handlebars/runtime":6,"./handlebars/safe-string":7,"./handlebars/utils":8}],4:[function(require,module,exports){ +},{"./handlebars/base":6,"./handlebars/exception":7,"./handlebars/runtime":8,"./handlebars/safe-string":9,"./handlebars/utils":10}],6:[function(require,module,exports){ "use strict"; var Utils = require("./utils"); var Exception = require("./exception")["default"]; @@ -287,7 +293,7 @@ exports.log = log;var createFrame = function(object) { return obj; }; exports.createFrame = createFrame; -},{"./exception":5,"./utils":8}],5:[function(require,module,exports){ +},{"./exception":7,"./utils":10}],7:[function(require,module,exports){ "use strict"; var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; @@ -316,7 +322,7 @@ function Exception(message, node) { Exception.prototype = new Error(); exports["default"] = Exception; -},{}],6:[function(require,module,exports){ +},{}],8:[function(require,module,exports){ "use strict"; var Utils = require("./utils"); var Exception = require("./exception")["default"]; @@ -454,7 +460,7 @@ exports.program = program;function invokePartial(partial, name, context, helpers exports.invokePartial = invokePartial;function noop() { return ""; } exports.noop = noop; -},{"./base":4,"./exception":5,"./utils":8}],7:[function(require,module,exports){ +},{"./base":6,"./exception":7,"./utils":10}],9:[function(require,module,exports){ "use strict"; // Build out our basic SafeString type function SafeString(string) { @@ -466,7 +472,7 @@ SafeString.prototype.toString = function() { }; exports["default"] = SafeString; -},{}],8:[function(require,module,exports){ +},{}],10:[function(require,module,exports){ "use strict"; /*jshint -W004 */ var SafeString = require("./safe-string")["default"]; @@ -543,12 +549,12 @@ exports.escapeExpression = escapeExpression;function isEmpty(value) { } exports.isEmpty = isEmpty; -},{"./safe-string":7}],9:[function(require,module,exports){ +},{"./safe-string":9}],11:[function(require,module,exports){ // Create a simple path alias to allow browserify to resolve // the runtime on a supported path. module.exports = require('./dist/cjs/handlebars.runtime'); -},{"./dist/cjs/handlebars.runtime":3}],10:[function(require,module,exports){ +},{"./dist/cjs/handlebars.runtime":5}],12:[function(require,module,exports){ (function (global){ (function(root) { var unopinionate = { @@ -575,4 +581,4 @@ module.exports = require('./dist/cjs/handlebars.runtime'); })(typeof window != 'undefined' ? window : global); }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}]},{},[2]) \ No newline at end of file +},{}]},{},[3]) \ No newline at end of file diff --git a/lib/storage.js b/lib/storage.js index dd238ac2f..17e3bc351 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -174,6 +174,10 @@ Storage.prototype.add_tarball = function(name, filename) { return this.local.add_tarball(name, filename) } +Storage.prototype.get_readme = function(name, version, callback) { + return this.local.get_readme(name, version, callback); +}; + // // Get a tarball from a storage for {name} package // diff --git a/package.json b/package.json index 049fdde0f..6cab6def1 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,15 @@ "express": "3.4.x", "handlebars": "1.x.x", "helpers.less": "git://github.com/bpeacock/helpers.less.git", + "highlight.js": "^8.0.0", "js-yaml": ">= 3.0.1", "lunr": "^0.5.2", + "marked": "^0.3.2", "minimatch": ">= 0.2.14", "mkdirp": ">= 0.3.5", "request": ">= 2.31.0", "semver": ">= 2.2.1", + "tar.gz": "^0.1.1", "underscore": "^1.6.0", "unopinionate": "0.0.4" },