From 4c867a9e14184603fc479df31206cad4d8248434 Mon Sep 17 00:00:00 2001 From: Gabriel Csapo Date: Fri, 17 Nov 2017 01:36:14 -0800 Subject: [PATCH] 0.0.0 0.0.0 --- .eslintignore | 1 + .eslintrc | 15 + .gitignore | 3 +- CHANGELOG.md | 3 + README.md | 25 +- TODO.md | 4 + bin/sweeney.js | 12 + lib/build.js | 0 lib/markdown.js | 193 +++++++++ lib/template.js | 22 + package.json | 29 ++ test/fixtures/includes/footer.html | 55 +++ test/fixtures/includes/head.html | 9 + test/fixtures/includes/header.html | 25 ++ test/fixtures/layouts/default.html | 20 + test/fixtures/layouts/page.html | 11 + test/fixtures/layouts/post.html | 12 + .../posts/2014-10-18-welcome-to-jekyll.md | 14 + test/fixtures/site.css | 381 ++++++++++++++++++ test/fixtures/sweeney.js | 18 + test/markdown.js | 176 ++++++++ test/template.js | 82 ++++ 22 files changed, 1108 insertions(+), 2 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 CHANGELOG.md create mode 100644 TODO.md create mode 100755 bin/sweeney.js create mode 100644 lib/build.js create mode 100644 lib/markdown.js create mode 100644 lib/template.js create mode 100644 package.json create mode 100644 test/fixtures/includes/footer.html create mode 100644 test/fixtures/includes/head.html create mode 100644 test/fixtures/includes/header.html create mode 100644 test/fixtures/layouts/default.html create mode 100644 test/fixtures/layouts/page.html create mode 100644 test/fixtures/layouts/post.html create mode 100644 test/fixtures/posts/2014-10-18-welcome-to-jekyll.md create mode 100644 test/fixtures/site.css create mode 100644 test/fixtures/sweeney.js create mode 100644 test/markdown.js create mode 100644 test/template.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..4ebc8ae --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +coverage diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..a00fe0d --- /dev/null +++ b/.eslintrc @@ -0,0 +1,15 @@ +{ + "extends": "eslint:recommended", + "env": { + "es6": true, + "node": true + }, + "rules": { + "quotes": ["error", "single"], + "semi": ["error", "always"] + }, + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + } +} diff --git a/.gitignore b/.gitignore index 00cbbdf..821e72b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ pids *.pid *.seed *.pid.lock +package-lock.json # Directory for instrumented libs generated by jscoverage/JSCover lib-cov @@ -56,4 +57,4 @@ typings/ # dotenv environment variables file .env - +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..50029a7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.0.0 (11/17/2017) + +- idea started diff --git a/README.md b/README.md index dc9102b..42f8c20 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ # sweeney -💈 a blog-aware, static site generator + +> 💈 a blog-aware, static site generator + +[![Npm Version](https://img.shields.io/npm/v/sweeney.svg)](https://www.npmjs.com/package/sweeney) +[![Build Status](https://travis-ci.org/gabrielcsapo/sweeney.svg?branch=master)](https://travis-ci.org/gabrielcsapo/sweeney) +[![Coverage Status](https://lcov-server.gabrielcsapo.com/badge/github%2Ecom/gabrielcsapo/sweeney.svg)](https://lcov-server.gabrielcsapo.com/coverage/github%2Ecom/gabrielcsapo/sweeney) +[![Dependency Status](https://starbuck.gabrielcsapo.com/badge/github/gabrielcsapo/sweeney/status.svg)](https://starbuck.gabrielcsapo.com/github/gabrielcsapo/sweeney) +[![devDependency Status](https://starbuck.gabrielcsapo.com/badge/github/gabrielcsapo/sweeney/dev-status.svg)](https://starbuck.gabrielcsapo.com/github/gabrielcsapo/sweeney#info=devDependencies) +[![npm](https://img.shields.io/npm/dt/sweeney.svg)]() +[![npm](https://img.shields.io/npm/dm/sweeney.svg)]() + +## Installation + +``` +npm install sweeney -g +``` + +## Usage + +> work in progress! + +## Philosophy 🧠 + +> a zero dependency tool diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..7fbaa1f --- /dev/null +++ b/TODO.md @@ -0,0 +1,4 @@ +- [ ] support markdown footnotes +- [ ] add `serve` command to be able to generate a http server to show realtime changes +- [ ] add `new` command to bootstrap a template application in the given directory +- [ ] add `build` command which should use the sweeney.js file to build the site diff --git a/bin/sweeney.js b/bin/sweeney.js new file mode 100755 index 0000000..9fc3a1f --- /dev/null +++ b/bin/sweeney.js @@ -0,0 +1,12 @@ +#!/usr/bin/env node + +const args = process.argv.slice(2); + +switch(args[0]) { + case 'build': + console.log('not implemented :('); // eslint-disable-line + break; + default: + console.log(`sorry the command ${args[0]} is not supported`); // eslint-disable-line + break; +} diff --git a/lib/build.js b/lib/build.js new file mode 100644 index 0000000..e69de29 diff --git a/lib/markdown.js b/lib/markdown.js new file mode 100644 index 0000000..32c7c1d --- /dev/null +++ b/lib/markdown.js @@ -0,0 +1,193 @@ +// createParser and createBodyParser creates parsers. A parser takes a string, +// and if successful returns an array of two elements. The object representation +// of consumed portion and the remainder of of the string. If failure returns null. + +class Markdown { + constructor(src) { + this.src = src; + this.ast = {}; + } + // createParser takes a parser type, and regex pattern and + // returns the corresponding parser function. The first match + // in parenthesis is taken as the content. If this parser does not + // suit your requirement write your own parser. + + createParser(type, pattern, match) { + return function(src) { + var found = src.match(pattern); + if (found) { + return match ? match(src, pattern, found) : [{ + type: type, + content: found[1] + }, src.replace(pattern, '')]; + } else { + return null; + } + }; + } + // createBodyParser takes as its first argument the "type" of the parser. The rest of the + // arguments are the parsers that make up the bodyParser + createBodyParser() { + var parsers = Array.prototype.slice.call(arguments); + var ast = { + type: parsers.shift(), + body: [] + }; + + return function bodyParser(src) { + for (var i = 0; i < parsers.length; i++) { + var parser = parsers[i]; + var test = parser(src); + if (test) { + ast.body.push(test[0]); + return bodyParser(test[1]); + } + } + if (ast.body.length === 0) { + return null; + } else { + var ret = [ast, src]; + ast = { + type: ast.type, + body: [] + }; + return ret; + } + }; + } + attributesToHTML(attributes) { + return Object.keys(attributes).filter((a) => { + return ['content'].indexOf(a) === -1; + }).map((a) => { + return `${a}="${attributes[a]}"`; + }).join(' '); + } + toHTML(ast) { + var html = ''; + + ast.forEach((a) => { + switch(a.type) { + case 'markdown': + html += this.toHTML(a.body); + break; + case 'table': + html += ` + ${a.header ? ` + ${a.header.map((h) => ``).join('')} + ` : ''} + ${a.cells ? + a.cells.map((row) => { + return ` + ${row.map((c) => ``).join('')} + `; + }).join('') + : ''} +
${h}
${c}
`; + break; + case 'newline': + html += a.content.replace(/\n/g, '
'); + break; + case 'paragraph': + html += `

+ ${this.toHTML(a.body)} +

`; + break; + case 'inlinetext': + html += a.content; + break; + case 'link': + html += `${a.content}`; + break; + } + }); + + return html; + } + parse(src) { + let { createParser, createBodyParser } = this; + + return createBodyParser('markdown', + function options(src) { + var pattern = /^(-{3}(?:\n|\r)([\w\W]+?)(?:\n|\r)-{3})?/; + var found = pattern.exec(src); + if (found[2]) { + return [{ + type: 'options', + content: JSON.parse(found[2]) + }, src.replace(pattern, '')]; + } else { + return null; + } + }, + createParser('newline', /^(\n+)/), + createParser('h1', /^# ([^\n]+)/), + createParser('h2', /^## ([^\n]+)/), + createParser('h3', /^### ([^\n]+)/), + createParser('h4', /^#### ([^\n]+)/), + createParser('h5', /^##### ([^\n]+)/), + createParser('h6', /^###### ([^\n]+)/), + createParser('table', /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/, (src, pattern, match) => { + const TABLE_HEADER_TRIM = /^ *| *\| *$/g; + const TABLE_ALIGN_TRIM = /^ *|\| *$/g; + const TABLE_CELLS_TRIM = /(?: *\| *)?\n$/; + const PLAIN_TABLE_ROW_TRIM = /^ *\| *| *\| *$/g; + const TABLE_ROW_SPLIT = / *\| */; + const TABLE_RIGHT_ALIGN = /^ *-+: *$/; + const TABLE_CENTER_ALIGN = /^ *:-+: *$/; + const TABLE_LEFT_ALIGN = /^ *:-+ *$/; + + function parseTableAlignCapture(alignCapture) { + if (TABLE_RIGHT_ALIGN.test(alignCapture)) return 'right'; + if (TABLE_CENTER_ALIGN.test(alignCapture)) return 'center'; + if (TABLE_LEFT_ALIGN.test(alignCapture)) return 'left'; + return null; + } + + function parseTableHeader(capture) { + return capture[1].replace(TABLE_HEADER_TRIM, '').split(TABLE_ROW_SPLIT); + } + + function parseTableAlign(capture) { + var alignText = capture[2].replace(TABLE_ALIGN_TRIM, '').split(TABLE_ROW_SPLIT); + + return alignText.map(parseTableAlignCapture); + } + + function parseTableCells(capture) { + var rowsText = capture[3].replace(TABLE_CELLS_TRIM, '').split('\n'); + + return rowsText.map((rowText) => { + return rowText.replace(PLAIN_TABLE_ROW_TRIM, '').split(TABLE_ROW_SPLIT); + }); + } + + const header = parseTableHeader(match); + const align = parseTableAlign(match); + const cells = parseTableCells(match); + + return [{ type: 'table', header, align, cells }, src.replace(pattern, '')]; + }), + createParser('blockquote', /^> ([^\n]+)/), + createBodyParser('codeblock', + createParser('code', /^ {4}([^\n]+)\n/) + ), + createBodyParser('listblock', + createParser('list', /^\* ([^\n]+)\n/), + createParser('list', /^- ([^\n]+)\n/) + ), + createBodyParser('orderedListblock', + createParser('orderedList', /^[0-9]+ ([^\n]+)\n/) + ), + createBodyParser('paragraph', + createParser('inlinetext', /^([^\n[!*]+)/), + createParser('link', /^\[(.+?)\]\((.+?)\)/, (src, pattern, match) => [{ type: 'link', content: match[1], href: match[2] }, src.replace(pattern, '')]), + createParser('image', /^!\[(.+?)\]\((.+?)\)/, (src, pattern, match) => [{ type: 'image', alt: match[1], href: match[2] }, src.replace(pattern, '')]), + createParser('emphasis', /^\*(.+?)\*/), + createParser('strong', /^\*\*(.+?)\*\*/), + createParser('text', /^([^\n]+)/) + ) + )(src); + } +} + +module.exports = new Markdown(); diff --git a/lib/template.js b/lib/template.js new file mode 100644 index 0000000..4a9b4a9 --- /dev/null +++ b/lib/template.js @@ -0,0 +1,22 @@ +const fs = require('fs'); +const path = require('path'); + +module.exports = function template(str, data, directory) { + function include(file) { + return template(fs.readFileSync(path.resolve(directory, file)).toString(), data, directory); + } + data.include = include; + + var fn = new Function('obj', 'var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push(\'' + + str + .replace(/[\r\t\n]/g, ' ') + .split('{{').join('\t') + .replace(/((^|\}\})[^\t]*)'/g, '$1\r') + .replace(/\t(.*?)\}\}/g, '\',$1,\'') + .split('\t').join('\');') + .split('}}').join('p.push(\'') + .split('\r').join('\\\'') + + '\');}return p.join(\'\');'); + + return data ? fn(data) : fn; +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..44d25c0 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "sweeney", + "version": "0.0.0", + "description": "💈 a blog-aware, static site generator", + "author": "Gabriel J. Csapo ", + "license": "Apache-2.0", + "main": "index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/gabrielcsapo/sweeney.git" + }, + "bugs": { + "url": "https://github.com/gabrielcsapo/sweeney/issues" + }, + "homepage": "https://github.com/gabrielcsapo/sweeney#readme", + "scripts": { + "lint": "eslint .", + "test": "tape test/**.js", + "coverage": "tap test --coverage --coverage-report=lcov" + }, + "bin": { + "sweeney": "./bin/sweeney.js" + }, + "devDependencies": { + "eslint": "^4.11.0", + "tap": "^10.7.3", + "tape": "^4.8.0" + } +} diff --git a/test/fixtures/includes/footer.html b/test/fixtures/includes/footer.html new file mode 100644 index 0000000..7e04ca7 --- /dev/null +++ b/test/fixtures/includes/footer.html @@ -0,0 +1,55 @@ + diff --git a/test/fixtures/includes/head.html b/test/fixtures/includes/head.html new file mode 100644 index 0000000..8d3e1c7 --- /dev/null +++ b/test/fixtures/includes/head.html @@ -0,0 +1,9 @@ + + + + + + {{ page.title || site.title }} + + + diff --git a/test/fixtures/includes/header.html b/test/fixtures/includes/header.html new file mode 100644 index 0000000..d1bc89b --- /dev/null +++ b/test/fixtures/includes/header.html @@ -0,0 +1,25 @@ + diff --git a/test/fixtures/layouts/default.html b/test/fixtures/layouts/default.html new file mode 100644 index 0000000..3a6e41b --- /dev/null +++ b/test/fixtures/layouts/default.html @@ -0,0 +1,20 @@ + + + + {{ include("../includes/head.html") }} + + + + {{ include("../includes/header.html") }} + +
+
+ {{ content }} +
+
+ + {{ include("../includes/footer.html") }} + + + + diff --git a/test/fixtures/layouts/page.html b/test/fixtures/layouts/page.html new file mode 100644 index 0000000..2ce09cf --- /dev/null +++ b/test/fixtures/layouts/page.html @@ -0,0 +1,11 @@ +
+ +
+

{{ page.title }}

+
+ +
+ {{ content }} +
+ +
diff --git a/test/fixtures/layouts/post.html b/test/fixtures/layouts/post.html new file mode 100644 index 0000000..3d959c7 --- /dev/null +++ b/test/fixtures/layouts/post.html @@ -0,0 +1,12 @@ +
+ +
+

{{ post.title }}

+ +
+ +
+ {{ content }} +
+ +
diff --git a/test/fixtures/posts/2014-10-18-welcome-to-jekyll.md b/test/fixtures/posts/2014-10-18-welcome-to-jekyll.md new file mode 100644 index 0000000..4e4eaa2 --- /dev/null +++ b/test/fixtures/posts/2014-10-18-welcome-to-jekyll.md @@ -0,0 +1,14 @@ +--- +{ + "layout": "post", + "title": "Welcome to Jekyll!", + "date": "2014-10-18 12:58:29", + "categories": "jekyll update" +} +--- + +You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve --watch`, which launches a web server and auto-regenerates your site when a file is updated. + +To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works. + +Check out the [Jekyll docs](jekyll) for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo](jekyll-gh). If you have questions, you can ask them on [Jekyll’s dedicated Help repository](jekyll-help). diff --git a/test/fixtures/site.css b/test/fixtures/site.css new file mode 100644 index 0000000..de12de2 --- /dev/null +++ b/test/fixtures/site.css @@ -0,0 +1,381 @@ +/** + * Reset some basic elements + */ +body, h1, h2, h3, h4, h5, h6, +p, blockquote, pre, hr, +dl, dd, ol, ul, figure { + margin: 0; + padding: 0; +} + +/** + * Basic styling + */ +body { + font-family: Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 1.5; + font-weight: 300; + color: #111; + background-color: #fdfdfd; + -webkit-text-size-adjust: 100%; +} + +/** + * Set `margin-bottom` to maintain vertical rhythm + */ +h1, h2, h3, h4, h5, h6, +p, blockquote, pre, +ul, ol, dl, figure { + margin-bottom: 15px; +} + +/** + * Images + */ +img { + max-width: 100%; + vertical-align: middle; +} + +/** + * Figures + */ +figure > img { + display: block; +} + +figcaption { + font-size: 14px; +} + +/** + * Lists + */ +ul, ol { + margin-left: 30px; +} + +li > ul, +li > ol { + margin-bottom: 0; +} + +/** + * Headings + */ +h1, h2, h3, h4, h5, h6 { + font-weight: 300; +} + +/** + * Links + */ +a { + color: #2a7ae2; + text-decoration: none; +} +a:visited { + color: #1756a9; +} +a:hover { + color: #111; + text-decoration: underline; +} + +/** + * Blockquotes + */ +blockquote { + color: #828282; + border-left: 4px solid #e8e8e8; + padding-left: 15px; + font-size: 18px; + letter-spacing: -1px; + font-style: italic; +} +blockquote > :last-child { + margin-bottom: 0; +} + +/** + * Code formatting + */ +pre, +code { + font-size: 15px; + border: 1px solid #e8e8e8; + border-radius: 3px; + background-color: #eef; +} + +code { + padding: 1px 5px; +} + +pre { + padding: 8px 12px; + overflow-x: scroll; +} +pre > code { + border: 0; + padding-right: 0; + padding-left: 0; +} + +/** + * Wrapper + */ +.wrapper { + max-width: -webkit-calc(800px - (30px * 2)); + max-width: calc(800px - (30px * 2)); + margin-right: auto; + margin-left: auto; + padding-right: 30px; + padding-left: 30px; +} +@media screen and (max-width: 800px) { + .wrapper { + max-width: -webkit-calc(800px - (30px)); + max-width: calc(800px - (30px)); + padding-right: 15px; + padding-left: 15px; + } +} + +/** + * Clearfix + */ +.wrapper:after, .footer-col-wrapper:after { + content: ""; + display: table; + clear: both; +} + +/** + * Icons + */ +.icon > svg { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; +} +.icon > svg path { + fill: #828282; +} + +/** + * Site header + */ +.site-header { + border-top: 5px solid #424242; + border-bottom: 1px solid #e8e8e8; + min-height: 56px; + position: relative; +} + +.site-title { + font-size: 26px; + line-height: 56px; + letter-spacing: -1px; + margin-bottom: 0; + float: left; +} +.site-title, .site-title:visited { + color: #424242; +} + +.site-nav { + float: right; + line-height: 56px; +} +.site-nav .menu-icon { + display: none; +} +.site-nav .page-link { + color: #111; + line-height: 1.5; +} +.site-nav .page-link:not(:first-child) { + margin-left: 20px; +} +@media screen and (max-width: 600px) { + .site-nav { + position: absolute; + top: 9px; + right: 30px; + background-color: #fdfdfd; + border: 1px solid #e8e8e8; + border-radius: 5px; + text-align: right; + } + .site-nav .menu-icon { + display: block; + float: right; + width: 36px; + height: 26px; + line-height: 0; + padding-top: 10px; + text-align: center; + } + .site-nav .menu-icon > svg { + width: 18px; + height: 15px; + } + .site-nav .menu-icon > svg path { + fill: #424242; + } + .site-nav .trigger { + clear: both; + display: none; + } + .site-nav:hover .trigger { + display: block; + padding-bottom: 5px; + } + .site-nav .page-link { + display: block; + padding: 5px 10px; + } +} + +/** + * Site footer + */ +.site-footer { + border-top: 1px solid #e8e8e8; + padding: 30px 0; +} + +.footer-heading { + font-size: 18px; + margin-bottom: 15px; +} + +.contact-list, +.social-media-list { + list-style: none; + margin-left: 0; +} + +.footer-col-wrapper { + font-size: 15px; + color: #828282; + margin-left: -15px; +} + +.footer-col { + float: left; + margin-bottom: 15px; + padding-left: 15px; +} + +.footer-col-1 { + width: -webkit-calc(35% - (30px / 2)); + width: calc(35% - (30px / 2)); +} + +.footer-col-2 { + width: -webkit-calc(20% - (30px / 2)); + width: calc(20% - (30px / 2)); +} + +.footer-col-3 { + width: -webkit-calc(45% - (30px / 2)); + width: calc(45% - (30px / 2)); +} + +@media screen and (max-width: 800px) { + .footer-col-1, + .footer-col-2 { + width: -webkit-calc(50% - (30px / 2)); + width: calc(50% - (30px / 2)); + } + + .footer-col-3 { + width: -webkit-calc(100% - (30px / 2)); + width: calc(100% - (30px / 2)); + } +} +@media screen and (max-width: 600px) { + .footer-col { + float: none; + width: -webkit-calc(100% - (30px / 2)); + width: calc(100% - (30px / 2)); + } +} +/** + * Page content + */ +.page-content { + padding: 30px 0; +} + +.page-heading { + font-size: 20px; +} + +.post-list { + margin-left: 0; + list-style: none; +} +.post-list > li { + margin-bottom: 30px; +} + +.post-meta { + font-size: 14px; + color: #828282; +} + +.post-link { + display: block; + font-size: 24px; +} + +/** + * Posts + */ +.post-header { + margin-bottom: 30px; +} + +.post-title { + font-size: 42px; + letter-spacing: -1px; + line-height: 1; +} +@media screen and (max-width: 800px) { + .post-title { + font-size: 36px; + } +} + +.post-content { + margin-bottom: 30px; +} +.post-content h2 { + font-size: 32px; +} +@media screen and (max-width: 800px) { + .post-content h2 { + font-size: 28px; + } +} +.post-content h3 { + font-size: 26px; +} +@media screen and (max-width: 800px) { + .post-content h3 { + font-size: 22px; + } +} +.post-content h4 { + font-size: 20px; +} +@media screen and (max-width: 800px) { + .post-content h4 { + font-size: 18px; + } +} diff --git a/test/fixtures/sweeney.js b/test/fixtures/sweeney.js new file mode 100644 index 0000000..eba1e7d --- /dev/null +++ b/test/fixtures/sweeney.js @@ -0,0 +1,18 @@ +module.exports = { + source: './', + output: './docs', + site: { + title: 'template', + description: 'this is a template site', + user: { + email: 'gabecsapo@gmail.com', + github_username: '@gabrielcsapo', + twitter_username: '@gabrielcsapo' + }, + pages: [{ + title: 'Source', + url: 'http://github.com/gabrielcsapo/sweeney' + }] + }, + content: 'hello world' +}; diff --git a/test/markdown.js b/test/markdown.js new file mode 100644 index 0000000..57bb2c9 --- /dev/null +++ b/test/markdown.js @@ -0,0 +1,176 @@ +const test = require('tape'); + +const Markdown = require('../lib/markdown'); + +test('markdown', (t) => { + t.plan(2); + + t.test('@parse', (t) => { + //t.test('should be able to parse known markdown elements', (t) => { + // let ast = Markdown.parse(` + // # h1 Heading + // ## h2 Heading + // ### h3 Heading + // #### h4 Heading + // ##### h5 Heading + // ###### h6 Heading + // + // ___ + // --- + // *** + // + // **rendered as bold text** + // _rendered as italicized text_ + // ~~Strike through this text.~~ + // + // > blockquotes!! + // + // > Donec massa lacus, ultricies a ullamcorper in, fermentum sed augue. + // Nunc augue augue, aliquam non hendrerit ac, commodo vel nisi. + // >> Sed adipiscing elit vitae augue consectetur a gravida nunc vehicula. Donec auctor + // odio non est accumsan facilisis. Aliquam id turpis in dolor tincidunt mollis ac eu diam. + // >>> Donec massa lacus, ultricies a ullamcorper in, fermentum sed augue. + // Nunc augue augue, aliquam non hendrerit ac, commodo vel nisi. + // + // * valid bullet + // - valid bullet + // + valid bullet + // + // 1. Lorem ipsum dolor sit amet + // 2. Consectetur adipiscing elit + // 3. Integer molestie lorem at massa + // 4. Facilisis in pretium nisl aliquet + // 5. Nulla volutpat aliquam velit + // 6. Faucibus porta lacus fringilla vel + // 7. Aenean sit amet erat nunc + // 8. Eget porttitor lorem + // + // \`\`\` html + // Sample text here... + // \`\`\` + // + // | Option | Description | + // | ------ | ----------- | + // | data | path to data files to supply the data that will be passed into templates. | + // | engine | engine to be used for processing templates. Handlebars is the default. | + // | ext | extension to be used for dest files. | + // + // [sweeney](http://github.com/gabrielcsapo/sweeney) + // [sweeney](http://github.com/gabrielcsapo/sweeney "Visit Sweeney") + // + // ![Minion](http://octodex.github.com/images/minion.png) + // ![Alt text](http://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat") + // + // ![Alt text][id] + // [id]: http://octodex.github.com/images/dojocat.jpg "The Dojocat" + // `); + // // console.log(JSON.stringify(ast, null, 4)); + // t.end(); + // }); + t.test('should be able to parse table', (t) => { + let ast = Markdown.parse(` +| Tables | Are | Cool | +|:-------------:|:-------------:|:-----:| +| col 3 is | r-l | $1600 | +| col 2 is | centered | $12 | +| zebra stripes | are neat | $1 | +`); + t.deepEqual(ast[0].body[1], { + type: 'table', + header: ['Tables', 'Are', 'Cool'], + align: ['center', 'center', 'center'], + cells: [ + ['col 3 is', 'r-l', '$1600'], + ['col 2 is', 'centered', '$12'], + ['zebra stripes', 'are neat', '$1'] + ] + }); + t.end(); + }); + t.test('should be able parse image', (t) => { + let ast = Markdown.parse('![helloworld](./this/is/a/path.png)'); + + t.equal(ast[0].type, 'markdown'); + t.equal(ast[0].body[0].type, 'paragraph'); + t.equal(ast[0].body[0].body[0].type, 'image'); + t.equal(ast[0].body[0].body[0].alt, 'helloworld'); + t.equal(ast[0].body[0].body[0].href, './this/is/a/path.png'); + + t.end(); + }); + + t.test('should be able parse link', (t) => { + let ast = Markdown.parse('[helloworld](http://www.helloworld.com)'); + + t.equal(ast[0].type, 'markdown'); + t.equal(ast[0].body[0].type, 'paragraph'); + t.equal(ast[0].body[0].body[0].type, 'link'); + t.equal(ast[0].body[0].body[0].content, 'helloworld'); + t.equal(ast[0].body[0].body[0].href, 'http://www.helloworld.com'); + + t.end(); + }); + + t.test('should be able parse task list', (t) => { + let ast = Markdown.parse(` +- [ ] buy groceries +* [ ] use groceries +`); + + t.equal(ast[0].type, 'markdown'); + t.equal(ast[0].body[0].type, 'newline'); + t.equal(ast[0].body[0].content, '\n'); + t.equal(ast[0].body[1].type, 'listblock'); + t.equal(ast[0].body[1].body.length, 2); + t.equal(ast[0].body[1].body[0].type, 'list'); + t.equal(ast[0].body[1].body[0].content, '[ ] buy groceries'); + t.equal(ast[0].body[1].body[1].type, 'list'); + t.equal(ast[0].body[1].body[1].content, '[ ] use groceries'); + + t.end(); + }); + + t.test('should be able to parse options block', (t) => { + let ast = Markdown.parse(` +--- +{ + "layout": "post", + "title": "Welcome to Jekyll!", + "date": "2014-10-18 12:58:29", + "categories": "jekyll update" +} +--- +`); + + t.equal(ast[0].type, 'markdown'); + t.equal(ast[0].body[0].type, 'newline'); + t.equal(ast[0].body[0].content, '\n'); + t.equal(ast[0].body[1].type, 'options'); + t.equal(typeof ast[0].body[1].content, 'object'); + t.equal(ast[0].body[1].content.layout, 'post'); + t.equal(ast[0].body[1].content.title, 'Welcome to Jekyll!'); + t.equal(ast[0].body[1].content.date, '2014-10-18 12:58:29'); + t.equal(ast[0].body[1].content.categories, 'jekyll update'); + + t.end(); + }); + }); + + t.test('@toHTML', (t) => { + t.test('should be generate table', (t) => { + const html = Markdown.toHTML([{ + type: 'table', + header: ['Tables', 'Are', 'Cool'], + align: ['center', 'center', 'center'], + cells: [ + ['col 3 is', 'r-l', '$1600'], + ['col 2 is', 'centered', '$12'], + ['zebra stripes', 'are neat', '$1'] + ] + }]); + t.equal(html.replace(/\n/g, '').replace(/ /g, ''), '
TablesAreCool
col3isr-l$1600
col2iscentered$12
zebrastripesareneat$1
'); + t.end(); + }); + }); + +}); diff --git a/test/template.js b/test/template.js new file mode 100644 index 0000000..3003998 --- /dev/null +++ b/test/template.js @@ -0,0 +1,82 @@ +const fs = require('fs'); +const path = require('path'); +const test = require('tape'); + +const Markdown = require('../lib/markdown'); +const template = require('../lib/template'); + +test('template', (t) => { + t.plan(4); + + t.test('should be able template basic data', (t) => { + const html = template(' {{ page.title }} ', { + page: { + title: 'blue' + } + }); + + t.equal(' blue ', html); + t.end(); + }); + + t.test('should be able template with additional functionality', (t) => { + const html = template(' {{ decorate(page.title) }} ', { + decorate: (str) => `!${str}!`, + page: { + title: 'blue' + } + }); + + t.equal(' !blue! ', html); + t.end(); + }); + + t.test('should be able to render template from file', (t) => { + const layout = fs.readFileSync(path.resolve(__dirname, 'fixtures', 'layouts', 'default.html')).toString('utf8'); + + const html = template(layout, { + page: { + title: 'hello world' + }, + site: { + user: { + email: 'gabecsapo@gmail.com', + github_username: '@gabrielcsapo', + twitter_username: '@gabrielcsapo' + }, + title: 'template', + description: 'this is a template site', + pages: [{ + title: 'Source', + url: 'http://github.com/gabrielcsapo/sweeney' + }] + }, + content: 'hello world' + }, path.resolve(__dirname, 'fixtures', 'layouts')); + + t.ok(typeof html === 'string'); + t.end(); + }); + + t.test('should be able to render template from file', (t) => { + const layout = fs.readFileSync(path.resolve(__dirname, 'fixtures', 'layouts', 'post.html')).toString('utf8'); + + const post = Markdown.parse(fs.readFileSync(path.resolve(__dirname, 'fixtures', 'posts', '2014-10-18-welcome-to-jekyll.md')).toString('utf8')); + + let options = { + post: post[0].body[0].content, + content: Markdown.toHTML(post) + }; + + const html = template(layout, options, path.resolve(__dirname, 'fixtures', 'layouts')); + t.equal(html, `

Welcome to Jekyll!



+ You’ll find this post in your \`_posts\` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run \`jekyll serve --watch\`, which launches a web server and auto-regenerates your site when a file is updated. +



+ To add new posts, simply add a file in the \`_posts\` directory that follows the convention \`YYYY-MM-DD-name-of-post.ext\` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works. +



+ Check out the Jekyll docs for more info on how to get the most out of Jekyll. File all bugs/feature requests at Jekyll’s GitHub repo. If you have questions, you can ask them on Jekyll’s dedicated Help repository. +


`); + t.end(); + }); + +});