From e7b08b1dfe3f2a2596deb6e2a72bb79805d3708f Mon Sep 17 00:00:00 2001 From: Alan Pearce Date: Mon, 3 Jul 2017 21:39:43 +0200 Subject: feat: Add code block highlighting Theme is configurable --- config/default.toml | 2 + package.json | 3 + src/actions.js | 22 ++++++++ src/app.js | 50 +++++++++------- src/domain/posts.js | 42 +++++++++----- src/index.js | 14 +++-- src/modules/markdown.js | 24 ++++++-- src/templates/layout.html | 1 + test/app.test.js | 22 ++++++-- test/domain/posts.test.js | 28 +++++---- test/snapshots/app.test.js.md | 46 ++++++++++++--- test/snapshots/app.test.js.snap | Bin 1236 -> 1550 bytes test/testsite/posts/testfile.md | 11 ++++ yarn.lock | 122 ++++++++++++++++++++++++++++++++++++++-- 14 files changed, 312 insertions(+), 75 deletions(-) diff --git a/config/default.toml b/config/default.toml index 3b6ceb6..bb07508 100644 --- a/config/default.toml +++ b/config/default.toml @@ -18,3 +18,5 @@ folder = "./posts" tag = "tags" category = "categories" +[posts.code] +theme = "default" \ No newline at end of file diff --git a/package.json b/package.json index d324fdf..517687f 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,8 @@ "case": "^1.5.2", "configly": "^4.1.0", "gray-matter": "^2.1.1", + "highland": "^2.11.0", + "highlight.js": "^9.12.0", "hyperfast": "^2.1.0", "indent-string": "^3.1.0", "koa": "^2.2.0", @@ -55,6 +57,7 @@ "koa-router": "^7.2.1", "koa-send": "^4.1.0", "markdown-it": "^8.3.1", + "predentation": "alanpearce/predentation#fix-code-class", "toml": "^2.3.2" } } diff --git a/src/actions.js b/src/actions.js index 8a04671..7c7482f 100644 --- a/src/actions.js +++ b/src/actions.js @@ -1,5 +1,7 @@ "use strict"; +const fs = require("fs"); +const path = require("path"); const send = require("koa-send"); const responders = require("./responders"); @@ -10,6 +12,25 @@ function home(config, posts) { }; } +function highlightTheme(config) { + const theme = config.posts.code.theme; + const themeFile = path.resolve( + __dirname, + `../node_modules/highlight.js/styles/${theme}.css` + ); + + if (!fs.existsSync(themeFile)) { + throw new Error(`Couldn't find highlight theme ${theme}`); + } + + const css = fs.readFileSync(themeFile, "utf-8"); + + return async function(ctx, next) { + ctx.type = "css"; + ctx.body = css; + }; +} + function post(config, posts) { return async function(ctx, next) { ctx.assert(posts.has(ctx.params.filename), 404, "Post not found"); @@ -41,6 +62,7 @@ async function serveFiles(ctx) { module.exports = { home, + highlightTheme, post, taxonGenerator, serveFiles diff --git a/src/app.js b/src/app.js index 1a0c5cb..6be44bd 100644 --- a/src/app.js +++ b/src/app.js @@ -14,32 +14,40 @@ const router = new Router(); app.context.getURL = router.url.bind(router); -const Posts = require("./domain/posts.js")(config.posts, basename => - router.url("post", basename) -); - -router.get("home", "/", actions.home(config, Posts.posts)); +module.exports = async function() { + const Posts = await require("./domain/posts.js")(config.posts, basename => + router.url("post", basename) + ); -router.get("post", "/post/:filename", actions.post(config, Posts.posts)); + router.get("home", "/", actions.home(config, Posts.posts)); -for (let [term, items] of Posts.taxonomies) { router.get( - `taxon-${term}`, - `/${term}/:value`, - actions.taxonGenerator(config, term, items) + "highlight-theme", + "/css/code.css", + actions.highlightTheme(config) ); -} -app.use( - helmet({ - hsts: { - setIf: ctx => ctx.secure - } - }) -); + router.get("post", "/post/:filename", actions.post(config, Posts.posts)); + + for (let [term, items] of Posts.taxonomies) { + router.get( + `taxon-${term}`, + `/${term}/:value`, + actions.taxonGenerator(config, term, items) + ); + } + + app.use( + helmet({ + hsts: { + setIf: ctx => ctx.secure + } + }) + ); -app.use(router.routes()).use(router.allowedMethods()); + app.use(router.routes()).use(router.allowedMethods()); -app.use(actions.serveFiles); + app.use(actions.serveFiles); -module.exports = app; + return app; +}; diff --git a/src/domain/posts.js b/src/domain/posts.js index fd4fb3d..98488ba 100644 --- a/src/domain/posts.js +++ b/src/domain/posts.js @@ -1,9 +1,12 @@ "use strict"; +const h = require("highland"); const fs = require("fs"); const path = require("path"); +const { promisify } = require("util"); const matter = require("gray-matter"); const markdown = require("../modules/markdown.js"); +const predentation = require("predentation"); const { indentForTemplate, postIndentLevel } = require("../responders.js"); const grayMatterOptions = { @@ -30,26 +33,35 @@ function getTitle(file) { return path.basename(file.path, path.extname(file.path)); } -function get(getURL, filename) { +async function indentBody(content) { + return await h( + h + .of(content) + .map(markdown) + .map(html => indentForTemplate(html, postIndentLevel)) + .pipe(predentation()) + ) + .invoke("toString", ["utf-8"]) + .toPromise(Promise); +} + +async function get(getURL, filename) { const fileMatter = matter.read(filename, grayMatterOptions); fileMatter.basename = getTitle(fileMatter); delete fileMatter.orig; - fileMatter.body = indentForTemplate( - markdown(fileMatter.content), - postIndentLevel - ); + fileMatter.body = await indentBody(fileMatter.content); fileMatter.url = getURL(fileMatter.basename); - return canonicaliseMetadata(fileMatter); + return Promise.resolve(canonicaliseMetadata(fileMatter)); } -function getFolder(folder, getURL) { - return new Map( - fs - .readdirSync(folder) - .map(f => path.resolve(folder, f)) - .map(get.bind(this, getURL)) - .map(f => [getTitle(f), f]) +async function getFolder(folder, getURL) { + const files = (await promisify(fs.readdir)(folder)).map(f => + path.resolve(folder, f) ); + + const posts = await Promise.all(files.map(get.bind(this, getURL))); + + return new Map(posts.map(f => [getTitle(f), f])); } function taxonomise(taxonomies, posts) { @@ -71,8 +83,8 @@ function taxonomise(taxonomies, posts) { return taxons; } -module.exports = function(config, getURL) { - const posts = getFolder(config.folder, getURL); +module.exports = async function(config, getURL) { + const posts = await getFolder(config.folder, getURL); const taxonomies = taxonomise(config.taxonomies, posts); return { posts, diff --git a/src/index.js b/src/index.js index 2c75554..b7bc1a3 100644 --- a/src/index.js +++ b/src/index.js @@ -11,8 +11,12 @@ if (targetDir) { const app = require("./app.js"); -module.exports = app; - -app.listen(PORT, () => { - console.log(`App listening on port ${PORT}`); -}); +(async function() { + try { + (await app()).listen(PORT, () => { + console.log(`App listening on port ${PORT}`); + }); + } catch (error) { + console.error("App startup error", error); + } +})(); diff --git a/src/modules/markdown.js b/src/modules/markdown.js index 9f0af45..6a0ec9d 100644 --- a/src/modules/markdown.js +++ b/src/modules/markdown.js @@ -1,12 +1,24 @@ -'use strict' +"use strict"; -const Markdown = require('markdown-it') +const highlight = require("highlight.js"); +const Markdown = require("markdown-it"); const markdownOptions = { html: true, - typographer: true -} + typographer: true, + highlight: function(str, lang) { + if (lang && highlight.getLanguage(lang)) { + try { + return ` +${highlight.highlight(lang, str).value}`; + } catch (error) { + console.error("highlighting failed", error); + } + } + return ""; + } +}; -const markdown = new Markdown(markdownOptions) +const markdown = new Markdown(markdownOptions); -module.exports = markdown.render.bind(markdown) +module.exports = markdown.render.bind(markdown); diff --git a/src/templates/layout.html b/src/templates/layout.html index 65fdede..86eec59 100644 --- a/src/templates/layout.html +++ b/src/templates/layout.html @@ -2,6 +2,7 @@ + diff --git a/test/app.test.js b/test/app.test.js index 2445493..ac0ce2d 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -9,7 +9,9 @@ const mf = require("microformat-node"); process.chdir(path.resolve(__dirname, "./testsite/")); const config = require(path.resolve(__dirname, "../src/modules/config.js")); -const app = require("../src/app.js"); +const App = require("../src/app.js"); +let app; +test.before(async t => (app = await App())); const parseResponse = res => cheerio.load(res.text, { @@ -52,7 +54,8 @@ test("homepage", async function(t) { t.deepEqual(count, { "h-card": 1, "h-feed": 1, - "h-entry": 1 + "h-entry": 1, + rels: 1 }); const data = await mf.getAsync(options); @@ -82,7 +85,8 @@ test("post", async function(t) { t.deepEqual(count, { "h-card": 1, - "h-entry": 1 + "h-entry": 1, + rels: 1 }); const data = await mf.getAsync(options); @@ -111,7 +115,8 @@ test("tags", async function(t) { t.deepEqual(count, { "h-card": 1, "h-feed": 1, - "h-entry": 1 + "h-entry": 1, + rels: 1 }); const data = await mf.getAsync(options); @@ -119,5 +124,14 @@ test("tags", async function(t) { t.snapshot(data, "should contain relevant microformats data"); }); +test("highlight css", async function(t) { + const res = await request(app.listen()).get("/css/code.css"); + + t.is(res.statusCode, 200); + t.is(res.type, "text/css"); + t.is(res.charset, "utf-8"); + t.regex(res.text, /^\.hljs/m); +}); + test(notFound, "/post/non-existent", /Post not found/); test(notFound, "/tag/non-existent", /tag non-existent not found/); diff --git a/test/domain/posts.test.js b/test/domain/posts.test.js index 8b27698..bb97361 100644 --- a/test/domain/posts.test.js +++ b/test/domain/posts.test.js @@ -1,18 +1,22 @@ const test = require("ava"); const path = require("path"); -const Posts = require("../../src/domain/posts.js")( - { - folder: path.resolve(__dirname, "../testsite/posts/"), - taxonomies: { - tag: "tags", - category: "categories" - } - }, - basename => basename -); +const Posts = require("../../src/domain/posts.js"); -test("get", t => { +test.beforeEach(async t => { + t.context = await Posts( + { + folder: path.resolve(__dirname, "../testsite/posts/"), + taxonomies: { + tag: "tags", + category: "categories" + } + }, + basename => basename + ); +}); + +test("get", async t => { const expected = new Map( Object.entries({ title: "This is a test", @@ -21,7 +25,7 @@ test("get", t => { tags: ["a", "b"] }) ); - const post = Posts.get( + const post = await t.context.get( basename => basename, path.resolve(__dirname, "../testsite/posts/testfile.md") ); diff --git a/test/snapshots/app.test.js.md b/test/snapshots/app.test.js.md index 99811f9..a595585 100644 --- a/test/snapshots/app.test.js.md +++ b/test/snapshots/app.test.js.md @@ -56,8 +56,18 @@ Generated by [AVA](https://ava.li). ], }, ], - 'rel-urls': {}, - rels: {}, + 'rel-urls': { + '/css/code.css': { + rels: [ + 'stylesheet', + ], + }, + }, + rels: { + stylesheet: [ + '/css/code.css', + ], + }, } ## post @@ -86,8 +96,8 @@ Generated by [AVA](https://ava.li). properties: { content: [ { - html: '

Ut enim blandit volutpat maecenas? Volutpat blandit aliquam etiam erat velit, scelerisque in dictum non, consectetur a erat nam at lectus urna duis convallis convallis tellus, id interdum velit laoreet!

', - value: 'Ut enim blandit volutpat maecenas? Volutpat blandit aliquam etiam erat velit, scelerisque in dictum non, consectetur a erat nam at lectus urna duis convallis convallis tellus, id interdum velit laoreet!', + html: '

Ut enim blandit volutpat maecenas? Volutpat blandit aliquam etiam erat velit, scelerisque in dictum non, consectetur a erat nam at lectus urna duis convallis convallis tellus, id interdum velit laoreet!

 #!/usr/bin/env zsh  echo this is some shell code if [[ -n $test ]] then echo test passed fi 
', + value: 'Ut enim blandit volutpat maecenas? Volutpat blandit aliquam etiam erat velit, scelerisque in dictum non, consectetur a erat nam at lectus urna duis convallis convallis tellus, id interdum velit laoreet! #!/usr/bin/env zsh echo this is some shell code if [[ -n $test ]] then echo test passed fi', }, ], name: [ @@ -102,8 +112,18 @@ Generated by [AVA](https://ava.li). ], }, ], - 'rel-urls': {}, - rels: {}, + 'rel-urls': { + '/css/code.css': { + rels: [ + 'stylesheet', + ], + }, + }, + rels: { + stylesheet: [ + '/css/code.css', + ], + }, } ## tags @@ -158,6 +178,16 @@ Generated by [AVA](https://ava.li). ], }, ], - 'rel-urls': {}, - rels: {}, + 'rel-urls': { + '/css/code.css': { + rels: [ + 'stylesheet', + ], + }, + }, + rels: { + stylesheet: [ + '/css/code.css', + ], + }, } diff --git a/test/snapshots/app.test.js.snap b/test/snapshots/app.test.js.snap index bbc5c61..e356c77 100644 Binary files a/test/snapshots/app.test.js.snap and b/test/snapshots/app.test.js.snap differ diff --git a/test/testsite/posts/testfile.md b/test/testsite/posts/testfile.md index 84d8ed1..d6a4292 100644 --- a/test/testsite/posts/testfile.md +++ b/test/testsite/posts/testfile.md @@ -7,3 +7,14 @@ Tags = ["a", "b"] Ut enim blandit volutpat maecenas? Volutpat blandit aliquam etiam erat velit, scelerisque in dictum non, consectetur a erat nam at lectus urna duis convallis convallis tellus, id interdum velit laoreet! + +```sh +#!/usr/bin/env zsh + +echo this is some shell code + +if [[ -n $test ]] +then + echo test passed +fi +``` diff --git a/yarn.lock b/yarn.lock index a43ee0a..60f4071 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48,6 +48,17 @@ version "6.0.79" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.79.tgz#5efe7d4a6d8c453c7e9eaf55d931f4a22fac5169" +CSSselect@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/CSSselect/-/CSSselect-0.4.1.tgz#f8ab7e1f8418ce63cda6eb7bd778a85d7ec492b2" + dependencies: + CSSwhat "0.4" + domutils "1.4" + +CSSwhat@0.4: + version "0.4.7" + resolved "https://registry.yarnpkg.com/CSSwhat/-/CSSwhat-0.4.7.tgz#867da0ff39f778613242c44cfea83f0aa4ebdf9b" + abbrev@1: version "1.1.0" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" @@ -225,6 +236,10 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" +async@^1.2.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + async@^2.0.1: version "2.4.1" resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" @@ -852,6 +867,16 @@ cheerio@0.22.x: lodash.reject "^4.4.0" lodash.some "^4.4.0" +cheerio@^0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.17.0.tgz#fa5ae42cc60121133d296d0b46d983215f7268ea" + dependencies: + CSSselect "~0.4.0" + dom-serializer "~0.0.0" + entities "~1.1.1" + htmlparser2 "~3.7.2" + lodash "~2.4.1" + cheerio@^1.0.0-rc.1: version "1.0.0-rc.1" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.1.tgz#2af37339eab713ef6b72cde98cefa672b87641fe" @@ -1009,7 +1034,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.9.0: +commander@^2.8.1, commander@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: @@ -1413,6 +1438,13 @@ dom-serializer@0, dom-serializer@~0.1.0: domelementtype "~1.1.1" entities "~1.1.1" +dom-serializer@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.0.1.tgz#9589827f1e32d22c37c829adabd59b3247af8eaf" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + domelementtype@1, domelementtype@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" @@ -1421,13 +1453,25 @@ domelementtype@~1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" +domhandler@2.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.2.1.tgz#59df9dcd227e808b365ae73e1f6684ac3d946fc2" + dependencies: + domelementtype "1" + domhandler@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" dependencies: domelementtype "1" -domutils@1.5.1: +domutils@1.4: + version "1.4.3" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f" + dependencies: + domelementtype "1" + +domutils@1.5, domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" dependencies: @@ -1502,6 +1546,10 @@ ent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" +entities@1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" + entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" @@ -2200,6 +2248,16 @@ glob@7.1.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^5.0.3: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" @@ -2226,6 +2284,15 @@ globals@^9.0.0, globals@^9.14.0, globals@^9.17.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" +globby@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-2.1.0.tgz#9e9192bcd33f4ab6a4f894e5e7ea8b713213c482" + dependencies: + array-union "^1.0.1" + async "^1.2.1" + glob "^5.0.3" + object-assign "^3.0.0" + globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -2398,6 +2465,16 @@ hide-powered-by@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.0.0.tgz#4a85ad65881f62857fc70af7174a1184dccce32b" +highland@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/highland/-/highland-2.11.0.tgz#4d156709c5f10bc31cab6a97c7feb6c373e2466d" + dependencies: + util-deprecate "^1.0.2" + +highlight.js@^9.12.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" + hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" @@ -2434,6 +2511,16 @@ htmlparser2@^3.8.2, htmlparser2@^3.9.1: inherits "^2.0.1" readable-stream "^2.0.2" +htmlparser2@~3.7.2: + version "3.7.3" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.7.3.tgz#6a64c77637c08c6f30ec2a8157a53333be7cb05e" + dependencies: + domelementtype "1" + domhandler "2.2" + domutils "1.5" + entities "1.0" + readable-stream "1.1" + http-assert@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" @@ -3285,6 +3372,10 @@ lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, l version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" +lodash@~2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" + log-symbols@1.0.2, log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -3485,7 +3576,7 @@ mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -3700,6 +3791,10 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -4049,6 +4144,16 @@ precise-typeof@^1.0.2: dependencies: is-buffer "^1.1.1" +predentation@alanpearce/predentation#fix-code-class: + version "0.1.1" + resolved "https://codeload.github.com/alanpearce/predentation/tar.gz/cb370272545eb85917aa7df0de6aa771cf7cbf63" + dependencies: + chalk "^1.0.0" + cheerio "^0.17.0" + commander "^2.8.1" + globby "^2.1.0" + through2 "^2.0.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -4206,6 +4311,15 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" +readable-stream@1.1: + version "1.1.13" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2: version "2.2.11" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a8088d34aa17c0f72" @@ -5041,7 +5155,7 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" -util-deprecate@~1.0.1: +util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -- cgit 1.4.1