Shayu

Static Site Generator

Shayu is a static site generator, based around MDX structured content, and supported by JSX layouts and components. Stylesheets are bundled through icssify, with configurable postcss modules.
This gives you the writability of markdown, and the flexibility of React elements to structure your output html.

Getting started

Shayu is packaged as a module on npm.
To get started, you'll need to have Node.js installed, and a package manager like yarn (preferred) or npm.
For your site, create a new folder, initialize the package.json, and install the shayu module
Optionally, also install any postcss modules, react components or other utilities you need.
With everything in place, you can write a simple index.js that defines your site's options, and calls shayu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"use strict";
const shayu = require("shayu");

const config = {
    pageDir: 'pages',
    componentDir: 'components',
    outputDir: 'build',
    assetDir: 'assets',
    postcssModules: [],
    defaultMeta: {
        layout: 'default',
        title: 'Untitled Page'
    },
    httpPort: 8080,
    livereloadPort: 35729,
    livereload: {
        persistent: true
    }
};

shayu(config);

You can then run a single build with node index.js, or start the livereloading development server with NODE_ENV=development node index.js Either of those will populate the 'build' directory with all the static assets, that can be uploaded to a webserver (or something like neocities.org).

Pages

Every page in a Shayu site comes from a .mdx file in your pageDir
Shayu will render it, and place the output to an html file in the build directory, based on the following logic:
$pageDir/index.mdx becomes / on your site, (build/index.html on the filesystem)
$pageDir/projects.mdx becomes /pages (build/pages/index.html on the filesystem)
There is an edge-case where /projects.mdx and /projects/index.mdx would render to the same output html. In this case, /projects/index.mdx takes priority, and /projects.mdx is ignored. Shayu will warn you in the console if this happens.

Meta

A page can export a meta object, where information about the page and how it should be rendered is stored. This is the meta object for this page:

1
2
3
4
export const meta = {
    title: "Shayu Documentation",
    layout: "default"
}

It defines what to fill in the <title> tag, and which layout JSX file the content should be wrapped in.
You can also store custom keys here, that you can use in your layout component.

Layouts

Layouts are React components that take a page's parsed MDX and meta object, and turn it into a normal HTML file. The default.jsx layout used to render this page looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
"use strict";

const React = require('react');
const ReactDOM = require('react-dom');

function Layout({elementWithProvider, meta}) {
    return (
        <html lang="en">
            <head>
                <meta charSet="UTF-8"/>
                <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
                <link rel="stylesheet" href="/assets/bundle.css" type="text/css" />
                <title>{meta.title}</title>
            </head>
            <body>
                <div className="header">
                    <h1>Scout Documentation</h1>
                    <h3>Static Site Generator</h3>
                </div>
                <div className="content">
                    {elementWithProvider}
                </div>
            </body>
        </html>
    );
}

module.exports = Layout;

It loads the bundled stylesheet, uses meta.title to fill in the <title>, adds a header element, and wraps the MDX output in a .content div.

Overriding MDX generated components

MDX has a table with it's mappings from markdown to plain html elements.
To provide extra functionality, a component in /components with the same name will override it.

This is the React component in /components/code.jsx, that overrides the 'code' html element, used for rendering code blocks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
"use strict";

const React = require('react');
const {default: SyntaxHighlighter} = require('react-syntax-highlighter');

function findLanguageClass(className='') {
    let lang;
    let classes = className.split(' ');
    classes.some((classStr) => {
        let match = classStr.match(/^language-(.+)/);
        if (match) {
            lang = match[1];
        }
        return match;
    });
    return lang
}

function code({className, children}) {
    return <SyntaxHighlighter language={findLanguageClass(className)} className={className}>
        {children}
    </SyntaxHighlighter>
}

module.exports = {code};

The source for this site is available here.