1.1.2 • Published 1 year ago

anah v1.1.2

Weekly downloads
3
License
MIT
Repository
github
Last release
1 year ago

Anah

Handlebars to HTML compiler that accept HTML and markdown source.

Install

$ npm install anah

Features

  • Allow for markdown as sources for layouts, pages and partials. (converted with showdown)
  • Use front-matter on both HTML and markdown sources. (parsed with gray-matter)
  • Possibility to pass data in options of anah function. This can be useful to add data from database, external API, headless CMS...
  • Possibility to pass pages in options of anah function. This can be useful to add computed pages based on database, external API, headless CMS...
  • Handlebar's features also useable in markdown files.
  • Allow for json and yaml files for data.
  • Output an array of objects representing each compiled pages with their destination path, content and data.
  • Save the compiled pages in destination folder.
  • Processed page's global data accessible from template files : path from root, sub-folders depth from root, root prefix for menus.

Quick example

const anah = require("anah");

const options = {
  pages: "./html/pages",
  partials: "./html/partials",
  helpers: "./html/helpers",
  layouts: "./html/layouts",
  data: "./html/data",
  output: "tmp",
};

const compile = async (options) => {
  await anah(options).catch((error) => console.error(error));
};

compile(options);

Advanced example

const options = {
  pages: "./html/pages",
  partials: "./html/partials",
  helpers: "./html/helpers",
  layouts: "./html/layouts",
  data: "./html/data",
  output: "tmp",
  writeOutput: true, // true by default.
  helpersLibraries: [
    myHelperLibrary,
    anOtherHelperLibrary,
    {
      awesomeHelper: awesomeHelperFunction,
      anotherAwesomeHelper: anotherAwesomeHelperFunction,
    },
  ],
  directData: {
    // computed data, external API data, headless cms data or others can be specified here
    someData: {
      myFirstData: "Hello",
      mySecondData: "world!",
    },
    anotherData: 42,
  },
  directPages: [
    // computed pages, external API pages, headless cms pages or others can be specified here
    {
      content: "test page!",
      data: {
        title: "test page",
        layout: "myAwsomeLayout",
      },
      path: "testfolder/test",
    },
    {
      content: "hello {{you}}",
      data: {
        you: "John Doe",
      },
      path: "people/john",
    },
  ],
  showdownOptions: {
    noHeaderId: true, // showdown options can be specified here.
  },
};

const compile = async (options) => {
  const compiled = await anah(options).catch((error) => console.error(error));
  console.log(compiled);
};

compile(options);

API

anah(options)

Returns a Promise with an Array of Objects representing the pages.

With theses template files :

layouts/default.html :

<body>
  <h1>{{ title }}</h1>
  {{> body }}
</body>

layouts/myAwesomeLayout.html :

<title>{{title}}</title>

{{> body }}

pages/subFolder/hello.hbs :

---
title: I'm the title provided in front-matter of hello page!
---

{{> hello}}
path to holla : {{ global.root }}{{ my_data.pages_link.holla }}

pages/holla.md :

---
title: I'm the title provided in front-matter of holla page!
---

## Holla !

partials/hello.md :

## Hello {{ who }} !

{{ myData }}

data/my_data.json :

{
  "who": "John Doe",
  "pages_link": {
    "hello": "subFolder/hello.html",
    "holla": "holla.html"
  },
  "persons": [
    {
      "name": "Nelson",
      "age": 50
    },
    {
      "name": "Jim",
      "age": 25
    }
  ],
  "dogs": [
    {
      "name": "Rex",
      "age": 3
    }
  ]
}

data/my_data.yml :

persons:
  - name: "Joe"
    age: 25
  - name: "Frank"
    age: 15

index.js :

const adjective = "awesome";
const options = {
  pages: "./html/pages",
  partials: "./html/partials",
  helpers: "./html/helpers",
  layouts: "./html/layouts",
  data: "./html/data",
  output: "tmp",
  directData: {
    myData: `This is an ${adjective} text!`,
  },
  directPages: [
    {
      content: '<div class="awesome">{{myData}}</div>',
      data: { title: "awesome page", layout: "myAwesomeLayout" },
      path: "subFolder/awesome",
    },
  ],
};

const compile = async (options) => {
  const compiled = await anah(options).catch((error) => console.error(error));
  console.log(compiled);
};

compile(options);

Writes in tmp/subFolder/hello.html :

<body>
  <h1>I'm the title provided in front-matter of hello page!</h1>
  <h2>Hello John Doe !</h2>
  path to holla : ../holla.html
</body>

Writes in tmp/holla.html :

<body>
  <h1>I'm the title provided in front-matter of holla page!</h1>
  <h2>Holla</h2>
  This is an awesome text!
</body>

Write in tmp/subFolder/awesome.html :

<title>awesome page</title>
<div class="awesome">This is an awesome text!</div>

Returned output from anah(options)

[
  {
    path: "./tmp/subFolder/hello.html",
    content:
      "<body>\r\n<h1>I'm the title provided in front-matter of hello page!</h1>\r\n<h2>Hello John Doe !</h2>\r\npath to holla : ../holla.html\r\n</body>\r\n",
    data: {
      // data accessible from template of hello
      title: "I'm the title provided in front-matter of hello page!",
      my_data: {
        who: "John Doe",
        pages_link: {
          hello: "subFolder/hello.html",
          holla: "holla.html",
        },
        persons: [
          { name: "Joe", age: 25 }, // yaml files override json datas.
          { name: "Frank", age: 15 }, // yaml files override json datas.
        ],
        dogs: [{ name: "Rex", age: 3 }],
      },
      myData: `This is an awesome text!`,
      global: { path: "subFolder/hello.html", depth: 1, root: "../" },
    },
  },
  {
    path: "./tmp/holla.html",
    content:
      "<body>\r\n<h1>I'm the title provided in front-matter of holla page!</h1>\r\n<h2>Holla !</h2>\r\n</body>\r\n",
    data: {
      // data accessible from template of holla
      title: "I'm the title provided in front-matter of holla page!",
      my_data: {
        who: "John Doe",
        pages_link: {
          hello: "subFolder/hello.html",
          holla: "holla.html",
        },
        persons: [
          { name: "Joe", age: 25 }, // yaml files override json datas.
          { name: "Frank", age: 15 }, // yaml files override json datas.
        ],
        dogs: [{ name: "Rex", age: 3 }],
      },
      myData: `This is an awesome text!`,
      global: { path: "holla.html", depth: 0, root: "" },
    },
  },
  {
    path: "./tmp/subFolder/awesome.html",
    content:
      '<title>awesome page</title>\r\n<div class="awesome">This is an awesome text!</div>\r\n',
    data: {
      title: "awesome page",
      layout: "myAwesomeLayout",
      pages_link: {
        hello: "hello.html",
        holla: "holla.html",
      },
      names: {
        persons: [
          { name: "Joe", age: 25 }, // yaml files override json datas.
          { name: "Frank", age: 15 }, // yaml files override json datas.
        ],
        dogs: [{ name: "Rex", age: 3 }],
      },
      myData: `This is an awesome text!`,
      global: { path: "subFolder/awesome.html", depth: 1, root: "../" },
    },
  },
  // other pages ...
];

The output path will reproduce the path found in pages folder. By default, the output will be written to the output folder provided in options. The datas are these found in the datas folder and directData merged with the frontmatter datas of the pages or these provided in directPages. Global datas are calculated by Anah and can be used in templates.

Options

pages

Type : string

Optional

The pages folder. The pages can be markdown or HTML.

Files with .md, .html and .hbs extension are accepted, others will be ignored.

Pages can content front-matter data. The layout used by default is the one named default in the layouts folder. The layout can be specified with layout value in front-matter :

---
layout: myLayout
---

Hello world!

partials

Type : string

The partials folder. The pages can be markdown or HTML.

Files with .md, .html and .hbs extensions are accepted, others will be ignored.

helpers

Type : string

The helpers folder.

Files with .js extensions are accepted, others will be ignored.

layouts

Type : string

The layouts folder. The pages can be markdown or HTML.

Files with .md, .html and .hbs extension are accepted, others will be ignored.

There must at least be one layout named default in the folder.

Layout can be specified in frontmatter of pages or in data from directPages.

data

Type : string

The data folder. The data can be json or yaml files. If files with same names but one in json and the other in yaml, they will be mixed, but values in yaml will override the json values.

Data files can't be named global ( E.g. : global.yml or global.json).

output

Type : string

The output folder.

The destination of the compiled pages.

writeOutput

Type: boolean

Default : true

If true, allow to write the files to the destination folder in addition to return the data. Or just return the data if false.

directData

Type: object

Optional

Here, you can provide some data. These will override the ones from data folder. This can be useful for adding some computed data, data from an external API or from an headless CMS.

The data provided in frontmatter will not be overridden.

directPages

Type object[]

Optional

Here, you can provide some pages. This can be useful for adds some computed pages based on external API, database or from an headless CMS.

It can be possible to make something like this :

const productPages = (productsFromApi) => {
  const output = [];
  for (const item of productsFromApi) {
    output.push(
      {
        content: '<div>product name = {{name}}</div>',
        data: {
          name: item.name
        },
        path: `products/${item.name}`
      }
    )
  }
  return output;
};

const options = {
  directPages = productPages,
  // other options...
};

const compile = async (options) => {
  await anah(options).catch((error) => console.error(error));
};

compile(options);
Object properties
content

Type string

The content in HTML Handlebars.

data

Type object

The data for this page. Same as frontmatter for pages in path.

path

Type string

The path of the destination file, relative to the provided output option.

helpersLibraries

Type: object[]

Optional

Here, you can add your helpers libraries modules. They can be accessed in templates the same way than your helpers from the helpers folder.

Example :

const myLibrary = require("my-library");
const anOtherLibrary = require("an-other-library");
options.helpersLibrary = [myLibrary, anOtherLibrary];

verbose

Type : boolean

Default : false

If true, show warnings and done operations.

showdownOptions

Type : object

Optional

The default options are the default options from showdown.

The showdown options can be found here.