summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorPatryk Niedźwiedziński <pniedzwiedzinski19@gmail.com>2020-01-28 18:16:03 +0100
committerGitHub <noreply@github.com>2020-01-28 18:16:03 +0100
commit42c089293fed2db9c8a09d8e55f046169b5f0844 (patch)
tree18a053fe7f1e03fc5419a8c3d2a8bc8193cb27f0 /lib
parent7b8d75cd87e392d9ffb3defa538a041c4f7e0f0c (diff)
parentacfd4d0c722ceeca6aad51be64baf117e73f286c (diff)
downloadkronikarz-42c089293fed2db9c8a09d8e55f046169b5f0844.tar.gz
kronikarz-42c089293fed2db9c8a09d8e55f046169b5f0844.zip
Merge pull request #8 from pniedzwiedzinski/feature/generateApi v2.0
Feature/generate api
Diffstat (limited to 'lib')
-rw-r--r--lib/Post.ts73
-rw-r--r--lib/generateApi.ts99
-rw-r--r--lib/getPost.ts5
-rw-r--r--lib/getPosts.ts5
-rw-r--r--lib/index.ts7
-rw-r--r--lib/interfaces.ts35
-rw-r--r--lib/parsePost.ts46
-rw-r--r--lib/utils.ts14
8 files changed, 218 insertions, 66 deletions
diff --git a/lib/Post.ts b/lib/Post.ts
new file mode 100644
index 0000000..f99ef83
--- /dev/null
+++ b/lib/Post.ts
@@ -0,0 +1,73 @@
+import * as fs from 'fs';
+import { JSDOM } from 'jsdom';
+
+const frontmatter = require('front-matter');
+const md = require('markdown-it')();
+
+import { dateToPath, dateToString } from './utils';
+import { Date, FrontMatterObject, PostApiEntry, Meta } from './interfaces';
+
+export default class Post {
+  date: Date;
+  fileTitle: string;
+  fileContent: string;
+  post: FrontMatterObject;
+
+  constructor(filePath: string) {
+    let [year, month, day, title] = filePath.split('/').splice(-4, 4);
+    this.fileTitle = title.substr(0, title.lastIndexOf('.'));
+    this.date = { year, month, day };
+    this.fileContent = fs.readFileSync(filePath, 'utf-8');
+    this.post = frontmatter(this.fileContent);
+  }
+
+  toApi(): PostApiEntry {
+    const { author, title, additionalMeta } = this.getMeta();
+    const { description, html } = this.getPostContent();
+    return {
+      date: dateToString(this.date),
+      author,
+      title,
+      path: `/kronika/${dateToPath(this.date)}/${this.fileTitle}`,
+      description,
+      meta: additionalMeta,
+      content: html,
+    };
+  }
+
+  getMeta(): Meta {
+    const { attributes } = this.post;
+    const author: string = attributes.author;
+    if (!author) throw 'Error while parsing the author';
+    const title: string = attributes.title;
+    if (!title) throw 'Error while parsing the title';
+    const additionalMeta = Object.keys(attributes)
+      .filter((key) => key != 'title' && key != 'author')
+      .reduce((acc, key) => ({ ...acc, [key]: attributes[key] }), {});
+    return {
+      author,
+      title,
+      additionalMeta,
+    };
+  }
+
+  getPostContent() {
+    const { body } = this.post;
+    const html = `<div>${md.render(body)}</div>`;
+    const description = this.getDescription(html);
+
+    return {
+      html,
+      description,
+    };
+  }
+
+  getDescription(html: string): string {
+    const { document } = new JSDOM(`<div>${html}</div>`).window;
+    const elements = document.getElementsByTagName('p');
+
+    const description = elements[1].textContent;
+
+    return description || '';
+  }
+}
diff --git a/lib/generateApi.ts b/lib/generateApi.ts
new file mode 100644
index 0000000..0024d9f
--- /dev/null
+++ b/lib/generateApi.ts
@@ -0,0 +1,99 @@
+import fs from 'fs';
+import path from 'path';
+import Post from './Post';
+import { dateToPath } from './utils';
+import { Object as PlainObject } from './interfaces';
+
+function mkDirByPathSync(targetDir: string) {
+  const sep = path.sep;
+  const initDir = path.isAbsolute(targetDir) ? sep : '';
+  const baseDir = '.';
+
+  return targetDir.split(sep).reduce((parentDir, childDir) => {
+    const curDir = path.resolve(baseDir, parentDir, childDir);
+    try {
+      fs.mkdirSync(curDir);
+    } catch (err) {
+      if (err.code === 'EEXIST') {
+        // curDir already exists!
+        return curDir;
+      }
+
+      // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
+      if (err.code === 'ENOENT') {
+        // Throw the original parentDir error on curDir `ENOENT` failure.
+        throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);
+      }
+
+      const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
+      if (!caughtErr || (caughtErr && targetDir === curDir)) {
+        throw err; // Throw if it's just the last created dir.
+      }
+    }
+
+    return curDir;
+  }, initDir);
+}
+
+export function saveApiEntry(post: Post, path: string) {
+  const dir = `${path}/api/posts/${dateToPath(post.date)}`;
+
+  mkDirByPathSync(dir);
+
+  const apiEntry = post.toApi();
+
+  fs.writeFile(
+    `${dir}/${post.fileTitle}.json`,
+    JSON.stringify(apiEntry),
+    (err) => (err ? console.log(err) : null)
+  );
+}
+
+/*
+ * This function generates paged api index on path `/api/posts`
+ */
+function generateApiIndex(posts: Array<Post>, path: string) {
+  const parsedPosts = posts.map((post) => {
+    const apiEntry = post.toApi();
+    delete apiEntry.content;
+    return apiEntry;
+  });
+  const filePath = `${path}/api/posts.json`;
+  fs.writeFile(filePath, JSON.stringify(parsedPosts), (err) =>
+    err ? console.log(err) : null
+  );
+}
+
+function generateCategories(posts: Array<Post>, path: string) {
+  const categories: PlainObject = {};
+  posts.forEach((post) => {
+    const apiEntry = post.toApi();
+    delete apiEntry.content;
+    if (apiEntry.meta['category']) {
+      apiEntry.meta['category'].forEach((category: string) =>
+        categories[category]
+          ? categories[category].push(apiEntry)
+          : (categories[category] = [apiEntry])
+      );
+    }
+  });
+
+  const dir = `${path}/api/category`;
+  mkDirByPathSync(dir);
+  Object.keys(categories).forEach((category) => {
+    const filePath = `${dir}/${category}.json`;
+    fs.writeFile(filePath, JSON.stringify(categories[category]), (err) =>
+      err ? console.log(err) : null
+    );
+  });
+}
+
+export function generateApi(posts: Array<Post>, path: string) {
+  try {
+    generateApiIndex(posts, path);
+    generateCategories(posts, path);
+    posts.forEach((post) => saveApiEntry(post, path));
+  } catch (err) {
+    console.log(err);
+  }
+}
diff --git a/lib/getPost.ts b/lib/getPost.ts
index 2555215..4860ff9 100644
--- a/lib/getPost.ts
+++ b/lib/getPost.ts
@@ -1,5 +1,4 @@
-import { parsePost } from "./parsePost";
-import { Post } from "./interfaces";
+import Post from "./Post";
 
 interface getPostArgument {
   year: string;
@@ -14,6 +13,6 @@ export function getPost(
 ): Post {
   const filePath = `${path}/${year}/${month}/${day}/${title}.md`;
 
-  const post = parsePost(filePath);
+  const post = new Post(filePath);
   return post;
 }
diff --git a/lib/getPosts.ts b/lib/getPosts.ts
index c363c67..814f6d8 100644
--- a/lib/getPosts.ts
+++ b/lib/getPosts.ts
@@ -1,5 +1,4 @@
-import { Post } from "./interfaces";
-import { parsePost } from "./parsePost";
+import Post from "./Post";
 import { readDir } from "./utils";
 
 function getPosts(path: string): Array<Post> {
@@ -14,7 +13,7 @@ function getPosts(path: string): Array<Post> {
         files.forEach((file: string) => {
           const fsRoute = `${path}/${year}/${month}/${day}/${file}`;
           try {
-            const post = parsePost(fsRoute);
+            const post = new Post(fsRoute);
             routesArray.push(post);
           } catch (err) {
             console.log(err);
diff --git a/lib/index.ts b/lib/index.ts
index ade7180..4bf4dd3 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -1,6 +1,7 @@
 import { getPost as apiGetPost } from "./getPost";
 import { getPosts as apiGetPosts } from "./getPosts";
-import { Post } from "./interfaces";
+import { generateApi as apiGenerateApi } from "./generateApi"
+import Post from "./Post";
 
 export default class Kronikarz {
   postPath: string;
@@ -15,4 +16,8 @@ export default class Kronikarz {
   getPost(year: string, month: string, day: string, title: string): Post {
     return apiGetPost({ year, month, day, title }, this.postPath);
   }
+    generateApi(path: string) {
+      const posts = this.getPosts();
+      apiGenerateApi(posts, path)
+  }
 }
diff --git a/lib/interfaces.ts b/lib/interfaces.ts
index 1d598c6..1fbb226 100644
--- a/lib/interfaces.ts
+++ b/lib/interfaces.ts
@@ -1,24 +1,33 @@
-interface Date {
+export interface Date {
   year: string;
   month: string;
   day: string;
 }
 
-interface Meta {
-  [key: string]: string;
+export interface Object {
+  [key: string]: any;
 }
 
-export interface PostContent {
-  html: string;
-  markdown: string;
-  description: string;
-  meta: Meta;
+export interface Meta {
+  title: string;
+  author: string;
+  additionalMeta: Object;
+}
+
+export interface FrontMatterObject {
+  body: string;
+  attributes: Object;
 }
 
-export interface Post {
-  date: Date;
+export interface PostApiListEntry {
+  date: string;
+  author: string;
   title: string;
-  content: PostContent;
-  filePath: string;
-  route: string;
+  path: string;
+  description: string;
+  meta: Object;
+}
+
+export interface PostApiEntry extends PostApiListEntry {
+  content: string;
 }
diff --git a/lib/parsePost.ts b/lib/parsePost.ts
deleted file mode 100644
index 2f5f2b9..0000000
--- a/lib/parsePost.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import * as fs from "fs";
-import { JSDOM } from "jsdom";
-import { Post, PostContent } from "./interfaces";
-
-const frontmatter = require("front-matter");
-const md = require("markdown-it")();
-
-function getDescription(html: string): string {
-  const { document } = new JSDOM(`<div>${html}</div>`).window;
-  const elements = document.getElementsByTagName("p");
-
-  const description = elements[1].textContent;
-
-  return description || "";
-}
-
-function getPostContent(fileContent: string): PostContent {
-  const post = frontmatter(fileContent);
-
-  const markdown = post.body;
-  const html = `<div>${md.render(markdown)}</div>`;
-  const description = getDescription(html);
-
-  return {
-    html,
-    markdown,
-    description,
-    meta: post.attributes
-  };
-}
-
-export function parsePost(filePath: string): Post {
-  let [year, month, day, title] = filePath.split("/").splice(-4, 4);
-  title = title.substr(0, title.lastIndexOf("."));
-  const date = { year, month, day };
-  const fileContent = fs.readFileSync(filePath, "utf-8");
-  const content = getPostContent(fileContent);
-
-  return {
-    date,
-    title,
-    content,
-    filePath,
-    route: `/kronika/${year}/${month}/${day}/${title}`
-  };
-}
diff --git a/lib/utils.ts b/lib/utils.ts
index 23a3947..f75c0e4 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,4 +1,6 @@
 import * as fs from "fs";
+import { Date } from './interfaces';
+import Post from './Post'
 
 export function readDir(path: string): Array<string> {
   if (fs.existsSync(path)) {
@@ -7,3 +9,15 @@ export function readDir(path: string): Array<string> {
     throw `Path "${path}" doesn't exist`;
   }
 }
+
+export function sortPosts(posts: Array<Post>): Array<Post> {
+    return posts;
+}
+
+export function dateToString({ year, month, day }: Date): string {
+    return `${year}-${month}-${day}`
+}
+
+export function dateToPath({ year, month, day }: Date): string {
+    return `${year}/${month}/${day}`
+}