Skip to content

Commit

Permalink
Merge pull request #11 from jaredwray/migration-to-ecto
Browse files Browse the repository at this point in the history
Migration to Ecto
  • Loading branch information
jaredwray authored Feb 21, 2021
2 parents cb172c1 + fe31a2d commit 33418e1
Show file tree
Hide file tree
Showing 14 changed files with 1,092 additions and 302 deletions.
40 changes: 40 additions & 0 deletions blog_example/article-ejs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: 'Docula: EJS Style'
tags:
- Github
- Open Source
- Docula
date: 2017-03-07
layout: post3
featured_image: Docula_%20Persistent%20Links%20and%20Styles%201.jpeg
---

![](Images/Docula_%20Persistent%20Links%20and%20Styles%201.jpeg)

## Docula: Persistent Links and Styles!

In our last update, we simplified the modules down to two ([docula-ui](https://www.npmjs.com/package/docula-ui), and [docula-core](https://www.npmjs.com/package/docula-core)) and also added In-Memory data stores as the default. Now with this latest update, we made it so that you can do [persistent linking](https://docu.la/docs/article/configuration/deeplinks) and [customize the interface](https://docu.la/docs/article/configuration/customization) it very easily.

### Persistent Linking

There are many times a document can move around in a GitHub repository, but you want to keep to an URL that works. [Check out how to do it here!](https://docu.la/docs/article/configuration/deeplinks)

### Styling! Let’s Get Started

The first step is to see how we did this. Since we use [GitHub](https://github.com/) for our knowledge base repository, it made sense to extend the configuration for skinning there. Here is what the [Fons repository](https://github.com/fonsio/public-kb) looks like with the `style.css` and `navigation.html` file in the root.

![](Images/Docula_%20Persistent%20Links%20and%20Styles%202.png)

The configuration for your [docula-ui](https://www.npmjs.com/package/docula-ui) is easy to do the config. You will want to add in the style.css path and navigation.html path. Also, if you have a customized logo, you can do that. Here is an example that we use for Fons.

```
Docula.install(app, '/help', {git: 'https://github.com/fonsio/public-kb.git',pageTitle: 'Fons',logo: 'https://fons.io/n/img/fons-logo-300x85.png',redis: redisConfig(),elasticsearch: elasticConfig(),topNavigation: 'navigation.html',cssTheme: 'theme.scss'});
```

Yep, its as simple as that and you can see the style.css and navigation.html examples here: [https://github.com/fonsio/public-kb](https://github.com/fonsio/public-kb)

<div>
<p>foo</p>
</div>
***Happy Styling!***

7 changes: 7 additions & 0 deletions blog_example/templates/post3.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<h1>Post 3</h1>

<p><%= post.title %></p>
<p><%=post.author %></p>
<p><%- post.body %></p>

<p><%=post.matter.featured_image %></p>
18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "writr",
"version": "1.0.2",
"version": "1.5.0",
"description": "A Simple to Use Markdown Blog",
"main": "./dist/index",
"types": "./dist/index",
Expand All @@ -26,10 +26,11 @@
"scripts": {
"watch": "tsc -w -p .",
"clean-all": "yarn clean && rm -rf node_modules && rm -rf coverage",
"clean": "rm -rf ./dist && rm -rf ./blog_output",
"clean": "rm -rf ./dist && yarn clean-output",
"clean-output": "rm -rf ./blog_output",
"compile": "yarn clean && tsc -p .",
"build": "yarn test && yarn compile",
"test": "jest --coverage",
"test": "jest --coverage && yarn clean-output",
"test-output": "yarn compile && node ./bin/writr -p ./blog_example"
},
"bin": {
Expand All @@ -38,12 +39,13 @@
"dependencies": {
"array-sort": "^1.0.0",
"cheerio": "^1.0.0-rc.5",
"commander": "^7.0.0",
"commander": "^7.1.0",
"del": "^6.0.0",
"ecto": "^0.9.3",
"feed": "^4.2.2",
"fs-extra": "^9.1.0",
"gray-matter": "^4.0.2",
"handlebars": "4.7.6",
"handlebars": "4.7.7",
"helper-date": "^1.0.1",
"keyv": "^4.0.3",
"markdown-it": "^12.0.4",
Expand All @@ -56,19 +58,19 @@
"@types/array-sort": "^1.0.0",
"@types/express": "^4.17.11",
"@types/express-serve-static-core": "^4.17.18",
"@types/fs-extra": "^9.0.6",
"@types/fs-extra": "^9.0.7",
"@types/handlebars": "^4.0.37",
"@types/keyv": "^3.1.1",
"@types/markdown-it": "^12.0.1",
"@types/node": "^14.14.25",
"@types/node": "^14.14.31",
"@types/parse-json": "^4.0.0",
"@types/winston": "^2.3.8",
"codecov": "^3.8.1",
"jest": "^26.6.3",
"source-map-support": "^0.5.19",
"ts-jest": "^26.5.1",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
"typescript": "^4.1.5"
},
"files": [
"dist",
Expand Down
134 changes: 46 additions & 88 deletions src/render/htmRenderlProvider.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import * as handlebars from "handlebars";
import { createLogger, transports } from "winston";
import * as fs from "fs-extra";
import { DataService } from "../data/dataService";
import { Config } from "../config";
import { Post } from "../post";
import { Tag } from "../tag";
import { RenderProviderInterface } from "./renderProviderInterface";
import { StorageService } from "../storage/storageService";
import { Ecto } from "ecto";
import * as fs from "fs-extra";

const ecto = new Ecto({defaultEngine: "handlebars"});


export class HtmlRenderProvider implements RenderProviderInterface {
log: any;

constructor() {
this.log = createLogger({ transports: [new transports.Console()]});

ecto.handlebars.opts = { allowProtoPropertiesByDefault: true }
ecto.handlebars.engine.registerHelper('formatDate', require('helper-date'));
}

async render(data: DataService, config: Config): Promise<boolean | undefined> {
Expand All @@ -27,8 +29,6 @@ export class HtmlRenderProvider implements RenderProviderInterface {
let tags = await data.getPublishedTags();
let unpublishedTags = await data.getTags();

let storage = new StorageService(config);

//posts

let previousPost: Post;
Expand All @@ -49,11 +49,9 @@ export class HtmlRenderProvider implements RenderProviderInterface {
nextPost = posts[index+1]
}

let postHtml = await this.renderPost(post, tags, config, previousPost, nextPost);

let postPath = output + "/" + post.id;
let postPath = output + "/" + post.id + "/index.html";
await this.renderPost(post, tags, config, previousPost, nextPost, postPath);

await storage.set(postPath + "/index.html", postHtml);
});

//unpublished posts
Expand All @@ -72,129 +70,89 @@ export class HtmlRenderProvider implements RenderProviderInterface {
}

if(post.published === false) {
let postHtml = await this.renderPost(post, unpublishedTags, config, previousPost, nextPost);

let postPath = output + "/" + post.id;

storage.set(postPath + "/index.html", postHtml);
let postPath = output + "/" + post.id + "/index.html";
await this.renderPost(post, unpublishedTags, config, previousPost, nextPost, postPath);
}
});

//tags

tags.forEach(async tag => {
let tagHtml = await this.renderTag(tag, tags, config);

let tagPath = output + "/tags/" + tag.id;
let tagOutputPath = output + "/tags/" + tag.id + "/index.html";

await this.renderTag(tag, tags, config, tagOutputPath);

storage.set(tagPath + "/index.html", tagHtml);
});

//home
storage.set(output + "/index.html", await this.renderHome(data, config));
await this.renderHome(data, config, output + "/index.html");

return result;
}

//render
async renderHome(data: DataService, config:Config): Promise<string> {
async renderHome(data: DataService, config:Config, outputPath?:string): Promise<string> {
let result = "";

let postList = await data.getPublishedPostsByCount(config.indexCount);
let tagList = await data.getPublishedTags();
let dataObject = { tags: tagList, posts: postList };
let rootTemplatePath = config.path + "/templates/";

let templateName = await this.getTemplatePath(config.path + "/templates", "index");

let source = this.getHomeTemplate(config);
result = this.renderTemplate(source, { tags: tagList, posts: postList }, config);
let homeTemplatePath = rootTemplatePath + templateName;

result = await ecto.renderFromFile(homeTemplatePath, dataObject, rootTemplatePath, outputPath);

return result;
}

async renderTag(tag: Tag, tags: Array<Tag>, config:Config): Promise<string> {
async renderTag(tag: Tag, tags: Array<Tag>, config:Config, outputPath?:string): Promise<string> {
let result = "";
if (tag) {
let source = this.getTagTemplate(config);
let dataObject = {tag: tag, tags: tags};
let rootTemplatePath = config.path + "/templates/";
let templateName = await this.getTemplatePath(config.path + "/templates", "tag");
let templatePath = rootTemplatePath + templateName;

result = this.renderTemplate(source, {tag: tag, tags: tags}, config);
result = await ecto.renderFromFile(templatePath, dataObject, rootTemplatePath, outputPath);
}
return result;
}

async renderPost(post: Post, tags: Array<Tag>, config:Config, previousPost?: Post, nextPost?: Post,): Promise<string> {
async renderPost(post: Post, tags: Array<Tag>, config:Config, previousPost?: Post, nextPost?: Post, outputPath?:string): Promise<string> {
let result = "";

if (post) {
let source: string = this.getPostTemplate(config);

let dataObject = { post: post, previousPost: previousPost, nextPost: nextPost, tags: tags };
let rootTemplatePath = config.path + "/templates/";
let templateID = "post";
if(post.matter.layout) {
source = this.getTemplate(config, post.matter.layout);
templateID = post.matter.layout;
}
result = this.renderTemplate(source, { post: post, previousPost: previousPost, nextPost: nextPost, tags: tags }, config);
}

return result;
}

renderTemplate(source: string, data: any, config: Config): string {
let result = "";

data.writr = config;
let templateName = await this.getTemplatePath(config.path + "/templates", templateID);
let templatePath = rootTemplatePath + templateName;

this.registerPartials(config);

handlebars.registerHelper('formatDate', require('helper-date'));
let template: handlebars.Template = handlebars.compile(source);
result = template(data, {
allowProtoPropertiesByDefault: true
});

return result;
}

registerPartials(config: Config) {
let result = false;
let path = config.path + "/templates/partials";
if(fs.pathExistsSync(path)) {
let partials = fs.readdirSync(path);

partials.forEach(p => {
let source = fs.readFileSync(path + "/" + p).toString();
let name = p.split(".hjs")[0];

if(handlebars.partials[name] === undefined) {
handlebars.registerPartial(name, handlebars.compile(source));
}

});
result = true;
}

return result;
}

//Templates
getTemplate(config: Config, layout:string ): string {
return fs.readFileSync(config.path + "/templates/" + layout + ".hjs").toString();
}

getPostTemplate(config: Config): string {
let result = "";

result = this.getTemplate(config, "post");
result = await ecto.renderFromFile(templatePath, dataObject, rootTemplatePath, outputPath);
}

return result;
}

getTagTemplate(config: Config): string {
async getTemplatePath(templatesPath:string, templateName:string): Promise<string> {
let result = "";

result = this.getTemplate(config, "tag");

return result;
}
let templates = await fs.readdir(templatesPath);

getHomeTemplate(config: Config): string {
let result = "";
templates.forEach((file) => {

result = this.getTemplate(config, "index");
if(file.split(".")[0].toLowerCase() === templateName.toLowerCase()) {
result = file;
}
});

return result;
}
Expand Down
2 changes: 1 addition & 1 deletion src/storage/fileStorageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class FileStorageProvider implements StorageProviderInterface {
});
}

private async ensureFilePath(path:string) {
async ensureFilePath(path:string) {
let pathList = path.split("/");
pathList.pop();

Expand Down
8 changes: 4 additions & 4 deletions test/data/dataService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe("Data Service", () => {
it("get published posts", async () => {
let posts = await ds.getPublishedPosts();

expect(posts.length).toBe(6);
expect(posts.length).toBe(7);
});

it("get posts by count", async () => {
Expand All @@ -30,7 +30,7 @@ describe("Data Service", () => {
it("get published posts by count", async () => {
let posts = await ds.getPublishedPostsByCount(7);

expect(posts.length).toBe(6);
expect(posts.length).toBe(7);
});

it("get published posts by count maximum", async () => {
Expand All @@ -42,15 +42,15 @@ describe("Data Service", () => {
it("get posts", async () => {
let posts = await ds.getPosts();

expect(posts.length).toBe(7);
expect(posts.length).toBe(8);
});

it("get posts from cache", async () => {
await ds.getPosts();

let posts2 = await ds.getPosts();

expect(posts2.length).toBe(7);
expect(posts2.length).toBe(8);
});

it("get posts with a miss", async () => {
Expand Down
Loading

0 comments on commit 33418e1

Please sign in to comment.