aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Beltran <ben@nsovocal.com>2017-07-03 00:26:08 -0500
committerBen Beltran <ben@nsovocal.com>2017-07-03 00:26:08 -0500
commitcf6302904d61ead65e6294e7f1be406eb68ef5f9 (patch)
tree2d034809bbf8db2db79e413ace9cab44a7044ae6
parent7a5a585ead8c4e967980adc2ab9c7502e47694fd (diff)
✨📝🔧 Add generator bin and files
Includes config for eslint, and jsdoc, plus the executable Squashed commit of the following: commit 7726a2743994800f5c0afbc23e5b529107b79450 Author: Ben Beltran <ben@nsovocal.com> Date: Mon Jul 3 00:25:43 2017 -0500 Update changelog commit 78ad8ab788020c09848a852e48827781aa17341a Author: Ben Beltran <ben@nsovocal.com> Date: Mon Jul 3 00:25:38 2017 -0500 Remove unused image commit 1cd659307a39669cf0fc8d0f676b25625fd16ba2 Author: Ben Beltran <ben@nsovocal.com> Date: Mon Jul 3 00:23:53 2017 -0500 Remove trailing slash on index commit 944160fcbc364e4a829be91e6ad32521b4f94987 Author: Ben Beltran <ben@nsovocal.com> Date: Mon Jul 3 00:21:50 2017 -0500 Add example blog post commit 70645d0214d5d363accfedc3e2583e97232c930e Author: Ben Beltran <ben@nsovocal.com> Date: Mon Jul 3 00:20:44 2017 -0500 ✨ Add binary / generator to lib commit bb8f8bb6e12c89bc4b9b3766fd2ef8e924422005 Author: Ben Beltran <ben@nsovocal.com> Date: Mon Jul 3 00:20:29 2017 -0500 🔧 Add package.json commit 7e9b6f52c1c91bf963319ddeb1696bfac4130b95 Author: Ben Beltran <ben@nsovocal.com> Date: Mon Jul 3 00:19:57 2017 -0500 🔧 Add eslint config commit 26ee06bbf1c2f20620c81fa94c4a9b40460bd401 Author: Ben Beltran <ben@nsovocal.com> Date: Mon Jul 3 00:19:31 2017 -0500 Move static files to template / static commit db84351cbb8e8c4729123650d1cb3a6150da0502 Author: Ben Beltran <ben@nsovocal.com> Date: Sun Jul 2 21:39:20 2017 -0500 Use a hidden name for posts directory
-rw-r--r--.eslintrc14
-rw-r--r--.gitignore2
-rw-r--r--CHANGELOG.md4
-rwxr-xr-xbin/blog.js92
-rw-r--r--config/config.js31
-rw-r--r--config/env.dist0
-rw-r--r--config/jsdoc.json9
-rw-r--r--example/test-blog-post/assets/example_image.png (renamed from images/example_image.png)bin51624 -> 51624 bytes
-rw-r--r--example/test-blog-post/assets/ok/ok.txt0
-rw-r--r--example/test-blog-post/test-blog-post.md57
-rw-r--r--index.html197
-rw-r--r--lib/blog.js220
-rw-r--r--package.json42
-rw-r--r--static/css/style.css (renamed from css/style.css)0
-rw-r--r--static/images/header_background.png (renamed from images/header_background.png)bin5247 -> 5247 bytes
-rw-r--r--static/images/header_foreground.png (renamed from images/header_foreground.png)bin5273 -> 5273 bytes
-rw-r--r--templates/index.html32
17 files changed, 501 insertions, 199 deletions
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..0a1dc80
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,14 @@
+{
+ "extends": "eslint-config-hapi",
+ "parserOptions": {
+ "ecmaVersion": 2017
+ },
+ "rules": {
+ "indent": [
+ 2,
+ 2
+ ],
+ "no-undef": 2,
+ "require-yield": 0
+ }
+}
diff --git a/.gitignore b/.gitignore
index 719089d..997ace3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,7 +61,7 @@ typings/
.DS_Store
# Data store
-_posts
+.posts
# Generated files
static/assets
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a695952..728047b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
-- Static HTML / CSS
+- Binary to add and update blog posts
+- Template for index
+- Static Files
- Simple contributing guidelines
- This CHANGELOG
- A Readme
diff --git a/bin/blog.js b/bin/blog.js
new file mode 100755
index 0000000..c88a8aa
--- /dev/null
+++ b/bin/blog.js
@@ -0,0 +1,92 @@
+#!/usr/bin/env node
+'use strict';
+
+const Config = require('../config/config');
+const Blog = require('..');
+const Minimist = require('minimist');
+
+const internals = {
+ blog: new Blog(Config),
+ expectedKeys: ['add', 'update', 'publish'],
+
+ // Application entry point. Reads arguments and calls the
+ // corresponding method from the blog lib
+
+ async main() {
+
+ try {
+ const parsedArguments = this._parseArguments();
+
+ for (const argument in parsedArguments) {
+ if (parsedArguments.hasOwnProperty(argument)) {
+
+ const value = parsedArguments[argument];
+
+ if (argument === 'add') {
+ await internals.blog.add(value);
+ return;
+ }
+
+ if (argument === 'update') {
+ await internals.blog.update(value);
+ return;
+ }
+
+ if (argument === 'publish') {
+ await internals.blog.update(value);
+ return;
+ }
+ }
+ }
+ console.log('Not yet implemented');
+ }
+ catch (err) {
+ console.error(err.message || err);
+ this._printUsage();
+ process.exit(1);
+ }
+ },
+
+ // Parses arguments and returns them if valid. otherwise Throws
+
+ _parseArguments() {
+
+ const parsedArguments = Minimist(process.argv.slice(2));
+
+ if (!this._areArgumentsValid(parsedArguments)) {
+ throw new Error(internals.strings.invalidArguments);
+ }
+
+ return parsedArguments;
+ },
+
+ // Checks if the arguments are valid, returns a boolean value.
+
+ _areArgumentsValid(parsedArguments) {
+
+ const argumentKeys = Object.keys(parsedArguments);
+
+ return argumentKeys.some((key) => internals.expectedKeys.indexOf(key) >= 0);
+ },
+
+ // Prints the usage to stderr
+
+ _printUsage() {
+
+ console.error('\nUsage:\n');
+ console.error('blog --add path/to/blog_post\t\t(creates new blog post)');
+ console.error('blog --update path/to/blog_post\t(updates latest blog post)');
+ console.error('blog --publish \t\t\t(publishes the blog)');
+ }
+};
+
+// Add the strings, added after declaration so they can consume the
+// internals object.
+
+internals.strings = {
+ invalidArguments: `Invalid Arguments, expecting one of: ${internals.expectedKeys.join(', ')}`
+};
+
+
+
+internals.main();
diff --git a/config/config.js b/config/config.js
new file mode 100644
index 0000000..d16ff2d
--- /dev/null
+++ b/config/config.js
@@ -0,0 +1,31 @@
+'use strict';
+
+const Path = require('path');
+const Getenv = require('getenv');
+
+const internals = {};
+
+/**
+ * The main configuration object for Blog. It will be used to
+ * initialize all of the sub-components. It can extend any property of
+ * the blog object.
+ *
+ * @memberof Blog
+ * @typedef {object} tConfiguration
+ * @property {number} maxPosts=3 the max number of posts that can exist
+ * at one time
+ * @property {string} postsDirectory=<project_root>/.posts the location of
+ * the directory where the posts will be stored.
+ * @property {string} staticDirectory=<project_root>/static the location of
+ * the directory where the generated files will be placed. NOTE: There
+ * are some pre-built style files in the default directory, if you
+ * select another one, make sure you include them manually.
+ * @property {string} templatesDirectory=<project_root>/templates the
+ * location of the templates we'll use to generate the index.html
+ */
+module.exports = internals.Config = {
+ maxPosts: Getenv.int('BLOG_MAX_POSTS', 3),
+ postsDirectory: Getenv('BLOG_POSTS_DIRECTORY', Path.resolve(Path.join(__dirname, '../.posts'))),
+ staticDirectory: Getenv('BLOG_STATIC_DIRECTORY', Path.resolve(Path.join(__dirname, '../static'))),
+ templatesDirectory: Getenv('BLOG_TEMPLATES_DIRECTORY', Path.resolve(Path.join(__dirname, '../templates')))
+};
diff --git a/config/env.dist b/config/env.dist
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/env.dist
diff --git a/config/jsdoc.json b/config/jsdoc.json
new file mode 100644
index 0000000..9e05753
--- /dev/null
+++ b/config/jsdoc.json
@@ -0,0 +1,9 @@
+{
+ "plugins": ["plugins/markdown"],
+ "opts": {
+ "destination": "doc",
+ "readme": "README.md",
+ "template": "node_modules/docdash",
+ "recurse": true
+ }
+}
diff --git a/images/example_image.png b/example/test-blog-post/assets/example_image.png
index 672e3a1..672e3a1 100644
--- a/images/example_image.png
+++ b/example/test-blog-post/assets/example_image.png
Binary files differ
diff --git a/example/test-blog-post/assets/ok/ok.txt b/example/test-blog-post/assets/ok/ok.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/example/test-blog-post/assets/ok/ok.txt
diff --git a/example/test-blog-post/test-blog-post.md b/example/test-blog-post/test-blog-post.md
new file mode 100644
index 0000000..88209ea
--- /dev/null
+++ b/example/test-blog-post/test-blog-post.md
@@ -0,0 +1,57 @@
+# This is the title of another entry
+
+An entry will have paragraphs, these paragraphs will contain text. The
+text should be formatted correctly: visitors will want to read whatever
+is in the blog, so it should be readable. It should account for several
+types of tags, like **strong**, or *emphasised*. It should even support
+[links](/)
+
+An entry will have paragraphs, these paragraphs will contain text. The
+text should be formatted correctly: visitors will want to read whatever
+is in the blog, so it should be readable. It should account for several
+types of tags, like **strong**, or *emphasised*. It
+should even support [links](/)
+
+An entry will have paragraphs, these paragraphs will contain text. The
+text should be formatted correctly: visitors will want to read whatever
+is in the blog, so it should be readable. It should account for several
+types of tags, like **strong**, or *emphasised*. It
+should even support [links](/)
+
+## Subheading 1 (h2)
+
+An entry will have paragraphs, these paragraphs will contain text. The
+text should be formatted correctly: visitors will want to read whatever
+is in the blog, so it should be readable. It should account for several
+types of tags, like **strong**, or *emphasised*. It
+should even support [links](/)
+
+* There will be **lists**
+* Lists will have *tags*
+* And everything else [in the world](/)
+
+An entry will have paragraphs, these paragraphs will contain text. The
+text should be formatted correctly: visitors will want to read whatever
+is in the blog, so it should be readable. It should account for several
+types of tags, like **strong**, or *emphasised*. It
+should even support [links](/)
+
+![Picture: two persons in a ceremony][example-image]
+
+### Other types of subheadings, other types of lists (h3)
+
+1. There will be **lists**
+2. Lists will have *tags*
+3. And everything else [in the world](/)
+
+An entry will have paragraphs, these paragraphs will contain text. The
+text should be formatted correctly: visitors will want to read whatever
+is in the blog, so it should be readable. It should account for several
+types of tags, like **strong**, or *emphasised*. It
+should even support [links](/)
+
+#### Finally there are hfours (h4)
+
+And that's about it!
+
+[example-image]: /assets/example_image.png
diff --git a/index.html b/index.html
deleted file mode 100644
index 10d4919..0000000
--- a/index.html
+++ /dev/null
@@ -1,197 +0,0 @@
-<!doctype html>
-<html>
- <head>
- <meta charset="utf-8">
- <meta name="description" content="This is the blog at unlimited.pizza">
-
- <title>blog 🍕</title>
-
- <link href="css/style.css" rel="stylesheet">
-
- </head>
- <body>
- <header class="main-header">
- <a href="/">Blog</a>
- </header>
- <main>
- <article id="1">
- <h1>This is the title of an entry</h1>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <h2>Subheading 1 (h2)</h2>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <ul>
- <li>There will be <strong>lists</strong></li>
- <li>Lists will have <em>tags</em></li>
- <li>And everything else <a href="/">in the world</a></li>
- </ul>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <h3>Other types of subheadings, other types of lists (h3)</h3>
- <ol>
- <li>There will be <strong>lists</strong></li>
- <li>Lists will have <em>tags</em></li>
- <li>And everything else <a href="/">in the world</a></li>
- </ol>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- </article>
- <hr>
- <article id="2">
- <h1>This is the title of another entry</h1>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <h2>Subheading 1 (h2)</h2>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <ul>
- <li>There will be <strong>lists</strong></li>
- <li>Lists will have <em>tags</em></li>
- <li>And everything else <a href="/">in the world</a></li>
- </ul>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <img src="/images/example_image.png" alt="Picture: two persons in a ceremony">
- <h3>Other types of subheadings, other types of lists (h3)</h3>
- <ol>
- <li>There will be <strong>lists</strong></li>
- <li>Lists will have <em>tags</em></li>
- <li>And everything else <a href="/">in the world</a></li>
- </ol>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- </article>
- <hr>
- <article id="3">
- <h1>This is the title of the last entry</h1>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <h2>Subheading 1 (h2)</h2>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <ul>
- <li>There will be <strong>lists</strong></li>
- <li>Lists will have <em>tags</em></li>
- <li>And everything else <a href="/">in the world</a></li>
- </ul>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- <h3>Other types of subheadings, other types of lists (h3)</h3>
- <ol>
- <li>There will be <strong>lists</strong></li>
- <li>Lists will have <em>tags</em></li>
- <li>And everything else <a href="/">in the world</a></li>
- </ol>
- <p>
- An entry will have paragraphs, these paragraphs will contain text. The
- text should be formatted correctly: visitors will want to read whatever
- is in the blog, so it should be readable. It should account for several
- types of tags, like <strong>strong</strong>, or <em>emphasised</em>. It
- should even support <a href="/">links</a>
- </p>
- </article>
- </main>
- <footer>
- <p>Only 3 entries kept at any time. Press 1, 2, and 3 to switch. <a href="https://unlimited.pizza">unlimited.pizza</a></p>
- </footer>
- </body>
-</html>
-
diff --git a/lib/blog.js b/lib/blog.js
new file mode 100644
index 0000000..483577f
--- /dev/null
+++ b/lib/blog.js
@@ -0,0 +1,220 @@
+'use strict';
+
+const Fs = require('fs');
+const Markdown = require('markdown');
+const Mustache = require('mustache');
+const Ncp = require('ncp');
+const Path = require('path');
+const Rimraf = require('rimraf');
+const Util = require('util');
+
+const internals = {
+
+ // Promisified functions
+
+ fs: {
+ access: Util.promisify(Fs.access),
+ mkdir: Util.promisify(Fs.mkdir),
+ readdir: Util.promisify(Fs.readdir),
+ readFile: Util.promisify(Fs.readFile),
+ writeFile: Util.promisify(Fs.writeFile)
+ },
+ ncp: Util.promisify(Ncp.ncp),
+ rimraf: Util.promisify(Rimraf),
+ debuglog: Util.debuglog('blog'),
+
+ // constants
+
+ kAssetsDirectoryName: 'assets',
+ kIndexName: 'index.html',
+ kFileNotFoundError: 'ENOENT',
+ kMarkdownRe: /\.md$/i,
+
+ // Strings
+
+ strings: {
+ markdownNotFound: 'Markdown file was not found in blog directory. Please update.'
+ }
+};
+
+/**
+ * The Blog class is the blog generator, it's in charge of adding and
+ * updating posts, and handling the publishing.
+ *
+ * @class Blog
+ * @param {Potluck.tConfiguration} config the initialization options to
+ * extend the instance
+ */
+module.exports = class Blog {
+
+ constructor(config) {
+
+ Object.assign(this, config);
+ }
+
+ /**
+ * Shifts the blog posts, adds the passed path to slot 0, and
+ * generates files.
+ *
+ * @function add
+ * @memberof Blog
+ * @param {string} postLocation the path to the directory containing
+ * the post structure
+ * @return {Promise<undefined>} empty promise, returns no value
+ * @instance
+ */
+ async add(postLocation) {
+
+ await this._shift();
+ await this.update(postLocation);
+ }
+
+ /**
+ * Adds the passed path to slot 0, and generates files.
+ *
+ * @function update
+ * @memberof Blog
+ * @param {string} postLocation the path to the directory containing
+ * the post structure
+ * @return {Promise<undefined>} empty promise, returns no value
+ * @instance
+ */
+ async update(postLocation) {
+
+ await this._copyPost(postLocation);
+ await this._generate();
+ }
+
+ /**
+ * Publishes the files to a static host.
+ *
+ * @function publish
+ * @memberof Blog
+ * @return {Promise<undefined>} empty promise, returns no value
+ * @instance
+ */
+ async publish() {
+
+ console.error('Publishing not yet implemented');
+ }
+
+ // Parses markdown for each page, copies assets and generates index.
+
+ async _generate() {
+
+ const assetsTarget = Path.join(this.staticDirectory, internals.kAssetsDirectoryName);
+ const indexTarget = Path.join(this.staticDirectory, internals.kIndexName);
+ const indexLocation = Path.join(this.templatesDirectory, internals.kIndexName);
+ const posts = [];
+
+ internals.debuglog(`Removing ${assetsTarget}`);
+ await internals.rimraf(assetsTarget);
+
+ for (let i = 0; i < this.maxPosts; ++i) {
+ const sourcePath = Path.join(this.postsDirectory, `${i}`);
+ const assetsSource = Path.join(sourcePath, internals.kAssetsDirectoryName);
+ const postContentPath = await this._findBlogContent(sourcePath);
+
+ internals.debuglog(`Copying ${assetsSource} to ${assetsTarget}`);
+ await internals.ncp(assetsSource, assetsTarget);
+
+ internals.debuglog(`Reading ${postContentPath}`);
+ const postContent = await internals.fs.readFile(postContentPath, { encoding: 'utf8' });
+
+ internals.debuglog('Parsing markdown');
+ posts.push({
+ html: Markdown.markdown.toHTML(postContent),
+ id: i + 1
+ });
+ }
+
+ internals.debuglog(`Reading ${indexLocation}`);
+ const indexTemplate = await internals.fs.readFile(indexLocation, { encoding: 'utf8' });
+
+ internals.debuglog('Generating HTML');
+ const indexHtml = Mustache.render(indexTemplate, { posts });
+ await internals.fs.writeFile(indexTarget, indexHtml);
+ }
+
+ // Shift the posts, delete any remainder.
+
+ async _shift() {
+
+ await this._ensurePostsDirectoryExists();
+
+ for (let i = this.maxPosts - 1; i > 0; --i) {
+ const targetPath = Path.join(this.postsDirectory, `${i}`);
+ const sourcePath = Path.join(this.postsDirectory, `${i - 1}`);
+
+ try {
+ await internals.fs.access(sourcePath);
+
+ internals.debuglog(`Removing ${targetPath}`);
+ await internals.rimraf(targetPath);
+
+ internals.debuglog(`Shifting blog post ${sourcePath} to ${targetPath}`);
+ await internals.ncp(sourcePath, targetPath);
+ }
+ catch (error) {
+ if (error.code === internals.kFileNotFoundError) {
+ internals.debuglog(`Skipping ${sourcePath}: Does not exist.`);
+ continue;
+ }
+
+ throw error;
+ }
+ }
+ }
+
+ // Copies a post directory to the latest slot.
+
+ async _copyPost(postLocation) {
+
+ await this._ensurePostsDirectoryExists();
+
+ const targetPath = Path.join(this.postsDirectory, '0');
+
+ internals.debuglog(`Removing ${targetPath}`);
+ await internals.rimraf(targetPath);
+
+ internals.debuglog(`Adding ${postLocation} to ${targetPath}`);
+ await internals.ncp(postLocation, targetPath);
+ }
+
+ // Ensures the posts directory exists.
+
+ async _ensurePostsDirectoryExists() {
+
+ internals.debuglog(`Checking if ${this.postsDirectory} exists.`);
+ try {
+ await internals.fs.access(this.postsDirectory);
+ }
+ catch (error) {
+ if (error.code === internals.kFileNotFoundError) {
+ internals.debuglog('Creating posts directory');
+ await internals.fs.mkdir(this.postsDirectory);
+ return;
+ }
+
+ throw error;
+ }
+ }
+
+ // Looks for a `.md` file in the blog directory, and returns the path
+
+ async _findBlogContent(directory) {
+
+ const entries = await internals.fs.readdir(directory);
+
+ const markdownEntries = entries
+ .filter((entry) => internals.kMarkdownRe.test(entry))
+ .map((entry) => Path.join(directory, entry));
+
+ if (markdownEntries.length > 0) {
+ internals.debuglog(`Found markdown file: ${markdownEntries[0]}`);
+ return markdownEntries[0];
+ }
+
+ throw new Error(internals.strings.markdownNotFound);
+ }
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..e5303b1
--- /dev/null
+++ b/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "blog",
+ "version": "1.0.0",
+ "description": "An ephemeral blog",
+ "main": "lib/blog.js",
+ "bin": {
+ "blog": "./bin/blog.js"
+ },
+ "scripts": {
+ "document": "jsdoc -c ./config/jsdoc.json lib config",
+ "lint": "eslint .",
+ "test": "echo \":(\""
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/rbdr/blog.git"
+ },
+ "author": "Ben Beltran <ben@nsovocal.com>",
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/rbdr/blog/issues"
+ },
+ "homepage": "https://github.com/rbdr/blog#readme",
+ "dependencies": {
+ "getenv": "0.7.x",
+ "markdown": "0.5.x",
+ "minimist": "1.2.x",
+ "mustache": "2.3.x",
+ "ncp": "2.0.x",
+ "rimraf": "2.6.x"
+ },
+ "devDependencies": {
+ "docdash": "0.4.x",
+ "eslint": "4.1.x",
+ "eslint-config-hapi": "10.0.x",
+ "eslint-plugin-hapi": "4.0.x",
+ "jsdoc": "3.4.x"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+}
diff --git a/css/style.css b/static/css/style.css
index aeda780..aeda780 100644
--- a/css/style.css
+++ b/static/css/style.css
diff --git a/images/header_background.png b/static/images/header_background.png
index 8352b5e..8352b5e 100644
--- a/images/header_background.png
+++ b/static/images/header_background.png
Binary files differ
diff --git a/images/header_foreground.png b/static/images/header_foreground.png
index eb9b34d..eb9b34d 100644
--- a/images/header_foreground.png
+++ b/static/images/header_foreground.png
Binary files differ
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..858396b
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="description" content="This is the blog at unlimited.pizza">
+
+ <title>blog 🍕</title>
+
+ <link href="css/style.css" rel="stylesheet">
+
+ </head>
+ <body>
+ <header class="main-header">
+ <a href="/">Blog</a>
+ </header>
+ <main>
+ {{#posts}}
+ <article id="{{id}}">
+ {{{html}}}
+ </article>
+ <hr>
+ {{/posts}}
+ {{^posts}}
+ <h1>This is a fresh blog!</h1>
+ <p>There are no posts yet.</p>
+ {{/posts}}
+ </main>
+ <footer>
+ <p>Only 3 entries kept at any time. Press 1, 2, and 3 to switch. <a href="https://unlimited.pizza">unlimited.pizza</a></p>
+ </footer>
+ </body>
+</html>