From cd2b9fae1b5620ead282d5aa3b9b664c0c031859 Mon Sep 17 00:00:00 2001 From: Gabriel Csapo Date: Tue, 21 Nov 2017 10:44:12 -0800 Subject: [PATCH] 0.0.1 - adds build command can be run as build or build --directory=./some/directory - adds markdown parsing for inlineCode - ensures inner table elements are parsed correctly (if inner table elements contain nested markdown) - adds slug attribute to post object (this is the name of the html file to be referenced when linking) - has the ability to generate posts and root level templates --- .eslintrc | 2 +- CHANGELOG.md | 8 + bin/sweeney.js | 22 +- docs/2017-11-20-welcome-to-sweeney.html | 22 ++ docs/about.html | 1 + docs/index.html | 1 + docs/site.css | 354 ++++++++++++++++++ lib/build.js | 0 lib/generate.js | 79 ++++ lib/markdown.js | 31 +- lib/template.js | 45 ++- lib/util.js | 15 + test/fixtures/about.html | 11 + test/fixtures/includes/footer.html | 74 ++-- test/fixtures/includes/head.html | 2 +- test/fixtures/includes/header.html | 4 +- test/fixtures/index.html | 16 + test/fixtures/layouts/default.html | 5 +- test/fixtures/layouts/page.html | 7 +- test/fixtures/layouts/post.html | 8 +- .../posts/2014-10-18-welcome-to-jekyll.md | 14 - .../posts/2017-11-20-welcome-to-sweeney.md | 18 + test/fixtures/site.css | 65 +--- .../site/2017-11-20-welcome-to-sweeney.html | 22 ++ test/fixtures/site/about.html | 1 + test/fixtures/site/index.html | 1 + test/fixtures/site/site.css | 354 ++++++++++++++++++ test/fixtures/sweeney.js | 7 +- test/markdown.js | 45 +-- test/template.js | 24 +- test/util.js | 32 ++ 31 files changed, 1101 insertions(+), 189 deletions(-) create mode 100644 docs/2017-11-20-welcome-to-sweeney.html create mode 100644 docs/about.html create mode 100644 docs/index.html create mode 100644 docs/site.css delete mode 100644 lib/build.js create mode 100644 lib/generate.js create mode 100644 lib/util.js create mode 100644 test/fixtures/about.html create mode 100644 test/fixtures/index.html delete mode 100644 test/fixtures/posts/2014-10-18-welcome-to-jekyll.md create mode 100644 test/fixtures/posts/2017-11-20-welcome-to-sweeney.md create mode 100644 test/fixtures/site/2017-11-20-welcome-to-sweeney.html create mode 100644 test/fixtures/site/about.html create mode 100644 test/fixtures/site/index.html create mode 100644 test/fixtures/site/site.css create mode 100644 test/util.js diff --git a/.eslintrc b/.eslintrc index a00fe0d..471d2ff 100644 --- a/.eslintrc +++ b/.eslintrc @@ -9,7 +9,7 @@ "semi": ["error", "always"] }, "parserOptions": { - "ecmaVersion": 6, + "ecmaVersion": 2017, "sourceType": "module" } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 50029a7..fa8ca97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 0.0.1 (11/21/2017) + +- adds `build` command can be run as `build` or `build --directory=./some/directory` +- adds markdown parsing for `inlineCode` +- ensures inner table elements are parsed correctly (if inner table elements contain nested markdown) +- adds slug attribute to post object (this is the name of the html file to be referenced when linking) +- has the ability to generate posts and root level templates + # 0.0.0 (11/17/2017) - idea started diff --git a/bin/sweeney.js b/bin/sweeney.js index 9fc3a1f..1b6ac74 100755 --- a/bin/sweeney.js +++ b/bin/sweeney.js @@ -1,10 +1,30 @@ #!/usr/bin/env node +const path = require('path'); +const qs = require('qs'); + +const generate = require('../lib/generate'); const args = process.argv.slice(2); +const options = {}; +args.filter((a) => a.indexOf('--') > -1).forEach((a) => Object.assign(options, qs.parse(a))); + switch(args[0]) { case 'build': - console.log('not implemented :('); // eslint-disable-line + (async function() { + try { + const directory = path.resolve(process.cwd(), options['--directory'] || './'); + const config = require(path.resolve(directory, 'sweeney.js')); + + await generate(directory, config); + console.log(`site built at ${path.resolve(process.cwd(), options['--directory'] || './')}`); // eslint-disable-line + } catch(ex) { + console.log(`uhoh something happened \n ${ex.toString()}`); // eslint-disable-line + } + }()); + break; + case 'serve': + break; default: console.log(`sorry the command ${args[0]} is not supported`); // eslint-disable-line diff --git a/docs/2017-11-20-welcome-to-sweeney.html b/docs/2017-11-20-welcome-to-sweeney.html new file mode 100644 index 0000000..80ea543 --- /dev/null +++ b/docs/2017-11-20-welcome-to-sweeney.html @@ -0,0 +1,22 @@ + template

Welcome to Sweeney!


+ 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 sweeney 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. +



+ + + + + + +

+ before +

+ after +

+ hard +

+ easy +

+ Check out the Sweeney docs for more info on how to get the most out of Sweeney. File all bugs/feature requests at Sweeney’s GitHub repo. +


\ No newline at end of file diff --git a/docs/about.html b/docs/about.html new file mode 100644 index 0000000..a3994f8 --- /dev/null +++ b/docs/about.html @@ -0,0 +1 @@ + template

This is an about page!

here is some info
\ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..857103d --- /dev/null +++ b/docs/index.html @@ -0,0 +1 @@ + template

This is a website!

Checkout some of the posts

\ No newline at end of file diff --git a/docs/site.css b/docs/site.css new file mode 100644 index 0000000..9c5ba2c --- /dev/null +++ b/docs/site.css @@ -0,0 +1,354 @@ +/** + * 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; +} + +table { + text-align: left; + width: 100%; + border-collapse: collapse; + border-spacing: 0 +} + +table th, table td { + padding: 10px; + border: 1px solid #e8e8e8; +} + +/** + * 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; +} + +/** + * Page content + */ +.page-content { + width: 100%; + position: relative; + flex: 1; + 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-tags { + color: #adadad; +} + +.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/lib/build.js b/lib/build.js deleted file mode 100644 index e69de29..0000000 diff --git a/lib/generate.js b/lib/generate.js new file mode 100644 index 0000000..6ffb886 --- /dev/null +++ b/lib/generate.js @@ -0,0 +1,79 @@ +const fs = require('fs'); +const path = require('path'); + +const Template = require('./template'); +const Markdown = require('./markdown'); + +const { parse } = require('./util'); + +function getPosts(directory) { + const postsDir = path.resolve(directory); + + if (!fs.existsSync(postsDir)) throw new Error(`no posts found in ${postsDir}`); + return fs.readdirSync(postsDir).map(function(file) { + let { options, content } = parse(fs.readFileSync(postsDir + '/' + file).toString('utf8')); + if(!options) throw new Error('post should contain options block as the first entry in the file'); + + const entry = Markdown.parse(content); + + options.slug = `${file.substr(0, file.lastIndexOf('.'))}.html`; + options.file = file.substr(0, file.lastIndexOf('.')); + options.content = Markdown.toHTML(entry); + options.date = new Date(options.date || ''); + + return options; + }); +} + +function getRootFile(directory) { + return fs.readdirSync(directory) + .filter((file) => { + return file.substr(file.lastIndexOf('.') + 1, file.length) === 'html'; + }).map((file) => { + let { options, content } = parse(fs.readFileSync(directory + '/' + file).toString('utf8')); + + const entry = Markdown.parse(content); + + options.file = file.substr(0, file.lastIndexOf('.')); + options.content = Markdown.toHTML(entry); + + return options; + }); +} + +function render(directory, file, opts) { + const tmpl = path.resolve(directory, `${file}.html`); + const sourceDirectory = path.resolve(directory, 'layouts'); + const outputDirectory = path.resolve(directory, 'site'); + + const { options, content } = parse(fs.readFileSync(tmpl).toString('utf8')); + + if(options && options.layout) { + return render(directory, `layouts/${options.layout}`, Object.assign({ + content: Template(content, opts, sourceDirectory, outputDirectory) + }, opts)); + } + + return Template(content, opts, sourceDirectory, outputDirectory); +} + +// TODO: expose related articles +// TODO: expose next articles (can be configurable by default 3) +module.exports = function generate(directory, config) { + const posts = getPosts(directory + '/posts'); + + posts.forEach((post) => { + const out = render(directory, `layouts/${(post.template || 'post')}`, Object.assign({ post, page: {}, site: {} }, config)); + + fs.writeFileSync(path.resolve(directory, 'site', `${post.file}.html`), out); + }); + + // generate root level files + const root = getRootFile(directory); + + root.forEach((r) => { + const out = render(directory, r.file, Object.assign({ posts, page: {}, site: {} }, config)); + + fs.writeFileSync(path.resolve(directory, 'site', `${r.file}.html`), out); + }); +}; diff --git a/lib/markdown.js b/lib/markdown.js index 32c7c1d..600a2fe 100644 --- a/lib/markdown.js +++ b/lib/markdown.js @@ -63,6 +63,10 @@ class Markdown { }).join(' '); } toHTML(ast) { + if(typeof ast === 'string') { + return ast; + } + var html = ''; ast.forEach((a) => { @@ -70,15 +74,18 @@ class Markdown { case 'markdown': html += this.toHTML(a.body); break; + case 'inlineCode': + html += `${a.content}`; + break; case 'table': html += ` ${a.header ? ` - ${a.header.map((h) => ``).join('')} + ${a.header.map((h) => ``).join('')} ` : ''} ${a.cells ? a.cells.map((row) => { return ` - ${row.map((c) => ``).join('')} + ${row.map((c) => ``).join('')} `; }).join('') : ''} @@ -104,21 +111,10 @@ class Markdown { return html; } parse(src) { + const self = this; 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]+)/), @@ -144,7 +140,7 @@ class Markdown { } function parseTableHeader(capture) { - return capture[1].replace(TABLE_HEADER_TRIM, '').split(TABLE_ROW_SPLIT); + return capture[1].replace(TABLE_HEADER_TRIM, '').split(TABLE_ROW_SPLIT).map((c) => self.parse(c)); } function parseTableAlign(capture) { @@ -157,7 +153,7 @@ class Markdown { 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); + return rowText.replace(PLAIN_TABLE_ROW_TRIM, '').split(TABLE_ROW_SPLIT).map((c) => self.parse(c)); }); } @@ -179,7 +175,8 @@ class Markdown { createParser('orderedList', /^[0-9]+ ([^\n]+)\n/) ), createBodyParser('paragraph', - createParser('inlinetext', /^([^\n[!*]+)/), + createParser('inlinetext', /^([^\n[!*`]+)/), + createParser('inlineCode', /^`(.+?)`/), 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', /^\*(.+?)\*/), diff --git a/lib/template.js b/lib/template.js index 4a9b4a9..97ba132 100644 --- a/lib/template.js +++ b/lib/template.js @@ -1,22 +1,43 @@ const fs = require('fs'); const path = require('path'); -module.exports = function template(str, data, directory) { +module.exports = function Template(tmpl, data, directory=process.cwd(), output=path.resolve(process.cwd(), 'site')) { function include(file) { - return template(fs.readFileSync(path.resolve(directory, file)).toString(), data, directory); + const extension = file.substr(file.lastIndexOf('.') + 1, file.length); + const fileName = file.substr(file.lastIndexOf('/') + 1, file.length); + + switch (extension) { + case 'html': + return Template(fs.readFileSync(path.resolve(directory, file)).toString(), data, directory, output); + case 'css': + fs.createReadStream(path.resolve(directory, file)).pipe(fs.createWriteStream(path.resolve(output, fileName))); + return ``; + case 'img': + fs.createReadStream(path.resolve(directory, file)).pipe(fs.createWriteStream(path.resolve(output, fileName))); + return ``; + } } + 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(\'\');'); + var fn = new Function('data', ` + var p = []; + var print = function(){ + p.push.apply(p,arguments); + }; + with(data){ + p.push('${tmpl + .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/lib/util.js b/lib/util.js new file mode 100644 index 0000000..f44ce07 --- /dev/null +++ b/lib/util.js @@ -0,0 +1,15 @@ +module.exports.parse = function parse(content) { + if (/^---\n/.test(content)) { + var end = content.search(/\n---\n/); + if (end != -1) { + return { + options: JSON.parse(content.slice(4, end + 1)) || {}, + content: content.slice(end + 5) + }; + } + } + return { + options: {}, + content: content + }; +}; diff --git a/test/fixtures/about.html b/test/fixtures/about.html new file mode 100644 index 0000000..87779f7 --- /dev/null +++ b/test/fixtures/about.html @@ -0,0 +1,11 @@ +--- +{ + "layout": "default", + "title": "About" +} +--- + +
+

This is an about page!

+ here is some info +
diff --git a/test/fixtures/includes/footer.html b/test/fixtures/includes/footer.html index 7e04ca7..7d378a1 100644 --- a/test/fixtures/includes/footer.html +++ b/test/fixtures/includes/footer.html @@ -1,55 +1,49 @@ diff --git a/test/fixtures/includes/head.html b/test/fixtures/includes/head.html index 8d3e1c7..478adf2 100644 --- a/test/fixtures/includes/head.html +++ b/test/fixtures/includes/head.html @@ -4,6 +4,6 @@ {{ page.title || site.title }} - + {{ include('../site.css') }} diff --git a/test/fixtures/includes/header.html b/test/fixtures/includes/header.html index d1bc89b..09cc98d 100644 --- a/test/fixtures/includes/header.html +++ b/test/fixtures/includes/header.html @@ -2,7 +2,7 @@
- {{ site.title }} + {{ site.title }} diff --git a/test/fixtures/index.html b/test/fixtures/index.html new file mode 100644 index 0000000..d90ed4b --- /dev/null +++ b/test/fixtures/index.html @@ -0,0 +1,16 @@ +--- +{ + "layout": "default", + "title": "Home" +} +--- + +
+

This is a website!

+ +

Checkout some of the posts

+ + +
diff --git a/test/fixtures/layouts/default.html b/test/fixtures/layouts/default.html index 3a6e41b..285ee46 100644 --- a/test/fixtures/layouts/default.html +++ b/test/fixtures/layouts/default.html @@ -3,7 +3,10 @@ {{ include("../includes/head.html") }} - + {{ include("../includes/header.html") }} diff --git a/test/fixtures/layouts/page.html b/test/fixtures/layouts/page.html index 2ce09cf..4315ab2 100644 --- a/test/fixtures/layouts/page.html +++ b/test/fixtures/layouts/page.html @@ -1,7 +1,12 @@ +--- +{ + "layout": "default" +} +---
-

{{ page.title }}

+

{{ post.title || page.title }}

diff --git a/test/fixtures/layouts/post.html b/test/fixtures/layouts/post.html index 3d959c7..b51d199 100644 --- a/test/fixtures/layouts/post.html +++ b/test/fixtures/layouts/post.html @@ -1,12 +1,18 @@ +--- +{ + "layout": "default" +} +---

{{ post.title }}

+
- {{ content }} + {{ post.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 deleted file mode 100644 index 4e4eaa2..0000000 --- a/test/fixtures/posts/2014-10-18-welcome-to-jekyll.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -{ - "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/posts/2017-11-20-welcome-to-sweeney.md b/test/fixtures/posts/2017-11-20-welcome-to-sweeney.md new file mode 100644 index 0000000..a95a58f --- /dev/null +++ b/test/fixtures/posts/2017-11-20-welcome-to-sweeney.md @@ -0,0 +1,18 @@ +--- +{ + "layout": "post", + "title": "Welcome to Sweeney!", + "date": "2017-11-20 12:58:29", + "tags": ["sweeney", "example"] +} +--- + +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 `sweeney 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. + +| before | after | +|--------|-------| +| `hard` | `easy`| + +Check out the [Sweeney docs](https://github.com/gabrielcsapo/sweeney) for more info on how to get the most out of Sweeney. File all bugs/feature requests at [Sweeney’s GitHub repo](https://github.com/gabrielcsapo/sweeney). diff --git a/test/fixtures/site.css b/test/fixtures/site.css index de12de2..9c5ba2c 100644 --- a/test/fixtures/site.css +++ b/test/fixtures/site.css @@ -123,6 +123,18 @@ pre > code { padding-left: 0; } +table { + text-align: left; + width: 100%; + border-collapse: collapse; + border-spacing: 0 +} + +table th, table td { + padding: 10px; + border: 1px solid #e8e8e8; +} + /** * Wrapper */ @@ -259,56 +271,13 @@ pre > code { 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 { + width: 100%; + position: relative; + flex: 1; padding: 30px 0; } @@ -329,6 +298,10 @@ pre > code { color: #828282; } +.post-tags { + color: #adadad; +} + .post-link { display: block; font-size: 24px; diff --git a/test/fixtures/site/2017-11-20-welcome-to-sweeney.html b/test/fixtures/site/2017-11-20-welcome-to-sweeney.html new file mode 100644 index 0000000..80ea543 --- /dev/null +++ b/test/fixtures/site/2017-11-20-welcome-to-sweeney.html @@ -0,0 +1,22 @@ + template

Welcome to Sweeney!


+ 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 sweeney 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. +



${h}${this.toHTML(h)}
${c}${this.toHTML(c)}
+ + + + + + +

+ before +

+ after +

+ hard +

+ easy +

+ Check out the Sweeney docs for more info on how to get the most out of Sweeney. File all bugs/feature requests at Sweeney’s GitHub repo. +


\ No newline at end of file diff --git a/test/fixtures/site/about.html b/test/fixtures/site/about.html new file mode 100644 index 0000000..a3994f8 --- /dev/null +++ b/test/fixtures/site/about.html @@ -0,0 +1 @@ + template

This is an about page!

here is some info
\ No newline at end of file diff --git a/test/fixtures/site/index.html b/test/fixtures/site/index.html new file mode 100644 index 0000000..857103d --- /dev/null +++ b/test/fixtures/site/index.html @@ -0,0 +1 @@ + template

This is a website!

Checkout some of the posts

\ No newline at end of file diff --git a/test/fixtures/site/site.css b/test/fixtures/site/site.css new file mode 100644 index 0000000..9c5ba2c --- /dev/null +++ b/test/fixtures/site/site.css @@ -0,0 +1,354 @@ +/** + * 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; +} + +table { + text-align: left; + width: 100%; + border-collapse: collapse; + border-spacing: 0 +} + +table th, table td { + padding: 10px; + border: 1px solid #e8e8e8; +} + +/** + * 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; +} + +/** + * Page content + */ +.page-content { + width: 100%; + position: relative; + flex: 1; + 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-tags { + color: #adadad; +} + +.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 index eba1e7d..9e44229 100644 --- a/test/fixtures/sweeney.js +++ b/test/fixtures/sweeney.js @@ -2,6 +2,7 @@ module.exports = { source: './', output: './docs', site: { + main: './index.html', title: 'template', description: 'this is a template site', user: { @@ -10,9 +11,11 @@ module.exports = { twitter_username: '@gabrielcsapo' }, pages: [{ + title: 'About', + url: './about.html' + }, { title: 'Source', url: 'http://github.com/gabrielcsapo/sweeney' }] - }, - content: 'hello world' + } }; diff --git a/test/markdown.js b/test/markdown.js index 57bb2c9..65c51a3 100644 --- a/test/markdown.js +++ b/test/markdown.js @@ -75,16 +75,7 @@ test('markdown', (t) => { | 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.equal(ast[0].body[1].type, 'table'); t.end(); }); t.test('should be able parse image', (t) => { @@ -111,6 +102,16 @@ test('markdown', (t) => { t.end(); }); + t.test('should be able parse inlineCode', (t) => { + let ast = Markdown.parse('this is `sweeney`'); + + t.equal(ast[0].type, 'markdown'); + t.equal(ast[0].body[0].body[1].type, 'inlineCode'); + t.equal(ast[0].body[0].body[1].content, 'sweeney'); + + t.end(); + }); + t.test('should be able parse task list', (t) => { let ast = Markdown.parse(` - [ ] buy groceries @@ -130,30 +131,6 @@ test('markdown', (t) => { 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) => { diff --git a/test/template.js b/test/template.js index 3003998..003b394 100644 --- a/test/template.js +++ b/test/template.js @@ -2,6 +2,8 @@ const fs = require('fs'); const path = require('path'); const test = require('tape'); +const { parse } = require('../lib/util'); + const Markdown = require('../lib/markdown'); const template = require('../lib/template'); @@ -52,30 +54,20 @@ test('template', (t) => { }] }, content: 'hello world' - }, path.resolve(__dirname, 'fixtures', 'layouts')); + }, path.resolve(__dirname, 'fixtures', 'layouts'), path.resolve(__dirname, 'fixtures', 'site')); 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')); + const layout = parse(fs.readFileSync(path.resolve(__dirname, 'fixtures', 'layouts', 'post.html')).toString('utf8')); + const { options, content } = parse(fs.readFileSync(path.resolve(__dirname, 'fixtures', 'posts', '2017-11-20-welcome-to-sweeney.md')).toString('utf8')); - let options = { - post: post[0].body[0].content, - content: Markdown.toHTML(post) - }; + options.content = Markdown.toHTML(Markdown.parse(content)); - 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. -


`); + const html = template(layout.content, { post: options }, path.resolve(__dirname, 'fixtures', 'layouts'), path.resolve(__dirname, 'fixtures', 'layouts')); + t.equal(html, '

Welcome to Sweeney!


\n 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 sweeney serve --watch, which launches a web server and auto-regenerates your site when a file is updated.\n



\n 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.\n



\n \n \n \n \n \n \n

\n before\n

\n after\n

\n hard\n

\n easy\n

\n Check out the Sweeney docs for more info on how to get the most out of Sweeney. File all bugs/feature requests at Sweeney’s GitHub repo.\n


'); t.end(); }); diff --git a/test/util.js b/test/util.js new file mode 100644 index 0000000..27d5dfa --- /dev/null +++ b/test/util.js @@ -0,0 +1,32 @@ +const test = require('tape'); + +const Util = require('../lib/util'); + +test('util', (t) => { + t.plan(1); + + t.test('should be able to parse file with options', (t) => { + let post = Util.parse(`--- +{ +"layout": "post", +"title": "Welcome to Jekyll!", +"date": "2014-10-18 12:58:29", +"categories": "jekyll update" +} +--- + +# Hello world +`); + + t.deepEqual(post.options, { + layout: 'post', + title: 'Welcome to Jekyll!', + date: '2014-10-18 12:58:29', + categories: 'jekyll update' + }); + t.equal(post.content, '\n# Hello world\n'); + + t.end(); + }); + +});