]>
git.r.bdr.sh - rbdr/blog/blob - lib/blog.js
3b91943fbd87a21ce69352acce079d72f6619aea
3 const Fs
= require('fs');
4 const Markdown
= require('markdown');
5 const Mustache
= require('mustache');
6 const Ncp
= require('ncp');
7 const Path
= require('path');
8 const Rimraf
= require('rimraf');
9 const Util
= require('util');
13 // Promisified functions
16 access: Util
.promisify(Fs
.access
),
17 mkdir: Util
.promisify(Fs
.mkdir
),
18 readdir: Util
.promisify(Fs
.readdir
),
19 readFile: Util
.promisify(Fs
.readFile
),
20 writeFile: Util
.promisify(Fs
.writeFile
)
22 ncp: Util
.promisify(Ncp
.ncp
),
23 rimraf: Util
.promisify(Rimraf
),
24 debuglog: Util
.debuglog('blog'),
28 kAssetsDirectoryName: 'assets',
29 kIndexName: 'index.html',
30 kFileNotFoundError: 'ENOENT',
31 kMarkdownRe: /\.md$/i,
36 markdownNotFound: 'Markdown file was not found in blog directory. Please update.'
41 * The Blog class is the blog generator, it's in charge of adding and
42 * updating posts, and handling the publishing.
45 * @param {Potluck.tConfiguration} config the initialization options to
48 module
.exports
= class Blog
{
52 Object
.assign(this, config
);
56 * Shifts the blog posts, adds the passed path to slot 0, and
61 * @param {string} postLocation the path to the directory containing
63 * @return {Promise<undefined>} empty promise, returns no value
66 async
add(postLocation
) {
69 await
this.update(postLocation
);
73 * Adds the passed path to slot 0, and generates files.
77 * @param {string} postLocation the path to the directory containing
79 * @return {Promise<undefined>} empty promise, returns no value
82 async
update(postLocation
) {
84 await
this._copyPost(postLocation
);
85 await
this._generate();
89 * Publishes the files to a static host.
93 * @return {Promise<undefined>} empty promise, returns no value
98 console
.error('Publishing not yet implemented');
101 // Parses markdown for each page, copies assets and generates index.
105 const assetsTarget
= Path
.join(this.staticDirectory
, internals
.kAssetsDirectoryName
);
106 const indexTarget
= Path
.join(this.staticDirectory
, internals
.kIndexName
);
107 const indexLocation
= Path
.join(this.templatesDirectory
, internals
.kIndexName
);
110 internals
.debuglog(`Removing ${assetsTarget}`);
111 await internals
.rimraf(assetsTarget
);
113 for (let i
= 0; i
< this.maxPosts
; ++i
) {
114 const sourcePath
= Path
.join(this.postsDirectory
, `${i}`);
117 await internals
.fs
.access(this.postsDirectory
);
119 const assetsSource
= Path
.join(sourcePath
, internals
.kAssetsDirectoryName
);
120 const postContentPath
= await
this._findBlogContent(sourcePath
);
122 internals
.debuglog(`Copying ${assetsSource} to ${assetsTarget}`);
123 await internals
.ncp(assetsSource
, assetsTarget
);
125 internals
.debuglog(`Reading ${postContentPath}`);
126 const postContent
= await internals
.fs
.readFile(postContentPath
, { encoding: 'utf8' });
128 internals
.debuglog('Parsing markdown');
130 html: Markdown
.markdown
.toHTML(postContent
),
135 if (error
.code
=== internals
.kFileNotFoundError
) {
136 internals
.debuglog(`Skipping ${i}`);
144 internals
.debuglog(`Reading ${indexLocation}`);
145 const indexTemplate
= await internals
.fs
.readFile(indexLocation
, { encoding: 'utf8' });
147 internals
.debuglog('Generating HTML');
148 const indexHtml
= Mustache
.render(indexTemplate
, { posts
});
149 await internals
.fs
.writeFile(indexTarget
, indexHtml
);
152 // Shift the posts, delete any remainder.
156 await
this._ensurePostsDirectoryExists();
158 for (let i
= this.maxPosts
- 1; i
> 0; --i
) {
159 const targetPath
= Path
.join(this.postsDirectory
, `${i}`);
160 const sourcePath
= Path
.join(this.postsDirectory
, `${i - 1}`);
163 await internals
.fs
.access(sourcePath
);
165 internals
.debuglog(`Removing ${targetPath}`);
166 await internals
.rimraf(targetPath
);
168 internals
.debuglog(`Shifting blog post ${sourcePath} to ${targetPath}`);
169 await internals
.ncp(sourcePath
, targetPath
);
172 if (error
.code
=== internals
.kFileNotFoundError
) {
173 internals
.debuglog(`Skipping ${sourcePath}: Does not exist.`);
182 // Copies a post directory to the latest slot.
184 async
_copyPost(postLocation
) {
186 await
this._ensurePostsDirectoryExists();
188 const targetPath
= Path
.join(this.postsDirectory
, '0');
190 internals
.debuglog(`Removing ${targetPath}`);
191 await internals
.rimraf(targetPath
);
193 internals
.debuglog(`Adding ${postLocation} to ${targetPath}`);
194 await internals
.ncp(postLocation
, targetPath
);
197 // Ensures the posts directory exists.
199 async
_ensurePostsDirectoryExists() {
201 internals
.debuglog(`Checking if ${this.postsDirectory} exists.`);
203 await internals
.fs
.access(this.postsDirectory
);
206 if (error
.code
=== internals
.kFileNotFoundError
) {
207 internals
.debuglog('Creating posts directory');
208 await internals
.fs
.mkdir(this.postsDirectory
);
216 // Looks for a `.md` file in the blog directory, and returns the path
218 async
_findBlogContent(directory
) {
220 const entries
= await internals
.fs
.readdir(directory
);
222 const markdownEntries
= entries
223 .filter((entry
) => internals
.kMarkdownRe
.test(entry
))
224 .map((entry
) => Path
.join(directory
, entry
));
226 if (markdownEntries
.length
> 0) {
227 internals
.debuglog(`Found markdown file: ${markdownEntries[0]}`);
228 return markdownEntries
[0];
231 throw new Error(internals
.strings
.markdownNotFound
);