Skip to content

Commit

Permalink
feat: add support for language-dependent template vars
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelcamargo committed Aug 21, 2021
1 parent 1b704e0 commit 17b094f
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"files": [ "src/**/*.test.js" ],
"rules": {
"no-global-assign": 0,
"max-lines": ["error", { "max": 300 }],
"max-lines": ["error", { "max": 320 }],
"max-statements": ["error", { "max": 15 },
{ "ignoreTopLevelFunctions": true }
]
Expand Down
48 changes: 29 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Triven
> A markdown-based blog generator
> A multi-language markdown-based blog generator.
[![CircleCI](https://circleci.com/gh/glorious-codes/glorious-triven/tree/master.svg?style=svg)](https://circleci.com/gh/glorious-codes/glorious-triven/tree/master)
[![Coverage Status](https://coveralls.io/repos/github/glorious-codes/glorious-triven/badge.svg?branch=master)](https://coveralls.io/github/glorious-codes/glorious-triven?branch=master)
Expand All @@ -22,7 +22,9 @@ After running this command, a directory called `triven` and a demo post will be

### Setup

To override the default values used to build your blog, you can write a file called `triven.config.js` in the root directory of your project containing the following options:
You don't need to setup anything to see Triven in action. By default, Triven will look for markdown files in the project directory (and sub-directories) and will generate a blog - ready to be published - inside a directory called *triven*.

However, you can override the default configuration values used to build your blog creating a file called `triven.config.js` in the root directory of your project containing the following options:

``` javascript
// triven.config.js
Expand All @@ -34,6 +36,9 @@ module.exports = {
lang: 'pt-BR',
// Used as default language for articles and homepage.
// Default: en-US.
url: 'https://rafaelcamargo.com/blog',
// Production URL where the blog will be deployed to.
// Used to build absolute URLs on RSS Feeds.
sourceDirectory: './posts',
// Directory where triven will look for markdown files.
// Default: Root directory of your project ('./').
Expand All @@ -49,7 +54,9 @@ module.exports = {
// You can optionally set variables in your templates
// to be replaced with custom values in build time:
someVar: 'someValue',
anotherVar: 'anotherValue'
// If a variable depends on the page language,
// you can set a function as value to handle any custom logic:
greet: lang => lang == 'pt-BR' ? 'Olá' : 'Hello!'
}
},
// You can optionally set custom translations for labels used by Triven:
Expand Down Expand Up @@ -93,19 +100,16 @@ You can prefix your Markdown articles with a header containing some metadata:
#### Markdown Example

```
title: Unconditional Inhotim
date: 2020-06-28
description: Inhotim is a wonderful mix of contemporary art museum and botanical garden located in Minas Gerais, Brasil. This article explores a bit of my experience in there and reflects about what the are is.
keywords: Inhotim, contemporary art, garden
externalUrl: https://rafaelcamargo.com/unconditional-inhotim
excerpt: We all can recognize beauty at the right moment we stand before it. That was the certainty that I had the day I visited the world's largest open-air contemporary art museum.
lang: en-US
// hello-world.md
---
title: Hello World!
date: 2021-08-20
description: Saying hello to the world.
keywords: hello, world
We all can recognize beauty at the right moment we stand before it. That was the certainty that I had the day I visited the world's largest open-air contemporary art museum. It's a garden that could be called paradise. Inhotim.
---
...
It's very easy to get started with Triven.
```

### Custom Templates
Expand All @@ -118,7 +122,10 @@ You can define your own HTML to be used as template for homepage and article. Ju
<!DOCTYPE html>
<html>
<head></head>
<body>{{ triven:posts }}</body>
<body>
{{ triven:posts }}
{{ triven:settings }}
</body>
</html>
```

Expand All @@ -128,7 +135,9 @@ You can define your own HTML to be used as template for homepage and article. Ju
<!DOCTYPE html>
<html>
<head></head>
<body>{{ triven:article }}</body>
<body>
{{ triven:article }}
</body>
</html>
```

Expand All @@ -140,9 +149,7 @@ You can optionally set variables in your templates to be replaced in build time.
const date = new Date();

module.exports = {
...
template: {
...
templates: {
vars: {
copywrite: `©${date.getFullYear()} Triven`
}
Expand All @@ -156,7 +163,10 @@ module.exports = {
<head></head>
<body>
{{ triven:posts }}
<footer>{{ copywrite }}</footer>
{{ triven:settings }}
<footer>
{{ copywrite }}
</footer>
</body>
</html>
```
Expand Down
8 changes: 8 additions & 0 deletions src/mocks/custom-lang-dependent-article-vars.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html dir="ltr">
<head></head>
<body>
{{ greet }}
{{ triven:article }}
</body>
</html>
9 changes: 9 additions & 0 deletions src/mocks/custom-lang-dependent-homepage-vars.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html dir="ltr">
<head></head>
<body>
{{ triven:posts }}
{{ triven:settings }}
{{ bye }}
</body>
</html>
2 changes: 1 addition & 1 deletion src/services/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ _public.build = ({ filepath, summary, markdownText }, languages = []) => {
return {
summary: { ...summary, excerpt: excerptService.extract(article, summary) },
article: fillTemplate(
templateService.getArticleTemplate(),
templateService.getArticleTemplate({ lang: summary.lang }),
article,
summary,
languages,
Expand Down
9 changes: 5 additions & 4 deletions src/services/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ const translationService = require('./translation');
const _public = {};

_public.build = (posts, { page, total, hrefPrefixes = {}, lang, availableLanguages }) => {
const body = buildPageBody(posts, page, total, hrefPrefixes.post, lang, availableLanguages);
return domService.minifyHTML(buildPage(body, hrefPrefixes.asset, lang));
const body = buildPageBody(posts, page, total, hrefPrefixes.post, lang);
const settings = settingsService.build(availableLanguages, { selectedLanguage: lang, hrefPrefix: hrefPrefixes.post });
const html = templateService.replaceVar(buildPage(body, hrefPrefixes.asset, lang), 'triven:settings', settings);
return domService.minifyHTML(html);
};

function buildPageBody(posts, page, total, postHrefPrefix, lang, availableLanguages){
function buildPageBody(posts, page, total, postHrefPrefix, lang){
return `
<main class="tn-main">
${buildPostList(posts, page, postHrefPrefix, lang)}
${handleFooter(page, total, translationService.get(lang))}
</main>
${settingsService.build(availableLanguages, { selectedLanguage: lang, hrefPrefix: postHrefPrefix })}
`;
}

Expand Down
40 changes: 25 additions & 15 deletions src/services/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@ const stylesService = require('./styles');

const _public = {};

_public.getArticleTemplate = () => getTemplateByName('article', '../');
_public.getArticleTemplate = ({ lang } = {}) => getTemplateByName('article', '../', lang);

_public.getHomepageTemplate = ({ assetsDirPrefix = '', customLang } = {}) => {
const { title, lang } = configService.get();
const template = setLang(getTemplateByName('homepage', assetsDirPrefix), customLang || lang);
const $ = parseHTMLString(template);
title && $('head').append(`<title>${title}</title>`);
return $.html();
const language = customLang || configService.get().lang;
const template = setLang(getTemplateByName('homepage', assetsDirPrefix, language), language);
return handleHomepageTitle(template);
};

_public.getDemoPostTemplate = () => getByFilename('introducing-triven.md');
Expand All @@ -24,33 +22,45 @@ _public.replaceVar = (htmlString, key, value) => {
return htmlString.replace(regex, value);
};

function getTemplateByName(name, assetsDirPrefix){
function handleHomepageTitle(template){
const { title } = configService.get();
const $ = parseHTMLString(template);
title && $('head').append(`<title>${title}</title>`);
return $.html();
}

function getTemplateByName(name, assetsDirPrefix, lang){
const filepath = configService.getCustomTemplateFilepath(name);
const template = filepath ?
buildBaseMetaTags(parseTemplate(fileService.readSync(filepath), path.dirname(filepath), assetsDirPrefix)) :
buildBaseMetaTags(parseTemplate(fileService.readSync(filepath), path.dirname(filepath), assetsDirPrefix, lang)) :
buildBaseMetaTags(getByFilename(`${name}.html`));
return includeBaseStylesheet(template, assetsDirPrefix);
}

function parseTemplate(htmlString, baseDir, assetsDirPrefix){
return assetsService.handleRelativeAssets(handleCustomVars(htmlString), { baseDir, assetsDirPrefix });
function parseTemplate(htmlString, baseDir, assetsDirPrefix, lang){
return assetsService.handleRelativeAssets(handleCustomVars(htmlString, lang), { baseDir, assetsDirPrefix });
}

function getByFilename(filename){
return fileService.readSync(path.join(__dirname, `../templates/${filename}`));
}

function handleCustomVars(template){
function handleCustomVars(template, lang){
const vars = configService.getCustomTemplateVars();
return vars ? replaceTemplateVars(template, vars) : template;
return vars ? replaceTemplateVars(template, vars, lang) : template;
}

function replaceTemplateVars(template, vars){
const { key, value } = vars.shift();
if(vars.length) return replaceTemplateVars(_public.replaceVar(template, key, value), vars);
function replaceTemplateVars(template, vars, lang){
const { key, value: rawValue } = vars.shift();
const value = parseTemplateVarValue(rawValue, lang);
if(vars.length) return replaceTemplateVars(_public.replaceVar(template, key, value), vars, lang);
return _public.replaceVar(template, key, value);
}

function parseTemplateVarValue(value, lang){
return typeof value == 'function' ? value(lang) : value;
}

function buildBaseMetaTags(htmlString){
const $ = parseHTMLString(htmlString);
if(!$('meta[name="viewport"]').length) $('head').prepend('<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">');
Expand Down
41 changes: 40 additions & 1 deletion src/services/template.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ describe('Template Service', () => {
<link rel="stylesheet" href="a/triven-${getExpectedTrivenStylesheetHash()}.css">
<title>Triven</title>
</head>
<body>{{ triven:posts }}</body>
<body>
{{ triven:posts }}
{{ triven:settings }}
</body>
</html>
`);
mount(() => {
Expand Down Expand Up @@ -182,6 +185,24 @@ describe('Template Service', () => {
});
});

it('should replace language-dependent custom article variables', done => {
mockTrivenConfig({
templates: {
article: './src/mocks/custom-lang-dependent-article-vars.html',
vars: {
other: 'var',
greet: lang => lang == 'pt-BR' ? '<p>Olá!</p>' : '<p>Hello!</p>',
another: 'var'
}
}
});
mount(() => {
expect(templateService.getArticleTemplate()).toContain('<p>Hello!</p>');
expect(templateService.getArticleTemplate({ lang: 'pt-BR' })).toContain('<p>Olá!</p>');
done();
});
});

it('should replace custom homepage template variables', done => {
mockTrivenConfig({
templates: {
Expand All @@ -202,6 +223,24 @@ describe('Template Service', () => {
});
});

it('should replace language-dependent custom homepage variables', done => {
mockTrivenConfig({
templates: {
homepage: './src/mocks/custom-lang-dependent-homepage-vars.html',
vars: {
other: 'var',
bye: lang => lang == 'pt-BR' ? '<p>Tchau!</p>' : '<p>Bye!</p>',
another: 'var'
}
}
});
mount(() => {
expect(templateService.getHomepageTemplate()).toContain('<p>Bye!</p>');
expect(templateService.getHomepageTemplate({ customLang: 'pt-BR' })).toContain('<p>Tchau!</p>');
done();
});
});

it('should copy relative images to assets directory and update its source in custom article template', done => {
mockTrivenConfig({
templates: {
Expand Down
5 changes: 4 additions & 1 deletion src/templates/homepage.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<!DOCTYPE html>
<html>
<head></head>
<body>{{ triven:posts }}</body>
<body>
{{ triven:posts }}
{{ triven:settings }}
</body>
</html>

0 comments on commit 17b094f

Please sign in to comment.