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) => `${h} `).join('')}
+ ` : ''}
+ ${a.cells ?
+ a.cells.map((row) => {
+ return `
+ ${row.map((c) => `${c} `).join('')}
+ `;
+ }).join('')
+ : ''}
+
`;
+ 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") }}
+
+
+
+ {{ 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 @@
+
+
+
+
+
+ {{ 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 @@
+
+
+
+
+
+ {{ 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, ''), 'Tables Are Cool col3is r-l $1600 col2is centered $12 zebrastripes areneat $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, `
+ 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();
+ });
+
+});