love/source/scripts/pages.ts

189 lines
5.7 KiB
TypeScript

import fs, {promises as fsp} from 'fs';
import {join} from 'path';
import cpy from 'cpy';
// @ts-expect-error
import htmlclean from 'htmlclean';
import marked from 'marked';
import nunjucks from 'nunjucks';
import refractor from 'refractor';
import rehype from 'rehype';
import sass from 'sass';
import {generateLove, LoveVariant} from './love';
import {getVersions, Versions} from './version';
export async function main(): Promise<void> {
// Create all required directories for the website.
await fsp.mkdir(join(__dirname, '../../public/css/'), {recursive: true});
await fsp.copyFile(
join(__dirname, '../../node_modules/modern-normalize/modern-normalize.css'),
join(__dirname, '../../public/css/modern-normalize.css')
);
await fsp.mkdir(join(__dirname, '../../public/images/'), {recursive: true});
// Configure Nunjucks to use the templates for `source/pages/`.
nunjucks.configure(join(__dirname, '../pages/'), {
lstripBlocks: true,
trimBlocks: true,
throwOnUndefined: true
});
const love: LoveVariant[] = generateLove();
const versions: Versions = await getVersions();
// Write the colors to file.
await writeJSON(join(__dirname, '../../public/love.json'), love);
// Render the Sass to CSS.
let css: string = sass
.renderSync({
file: join(__dirname, '../pages/scss/style.scss'),
sourceMap: false
})
.css.toString();
// Generate the CSS custom properties.
const cssProperties = `:root {
--foreground-1: ${love[0].colors.foreground1};
--foreground-2: ${love[0].colors.foreground2};
--background-1: ${love[0].colors.background1};
--background-2: ${love[0].colors.background2};
${love[0].colors.accents
.map((value, index) => ` --dark-accent-${index + 1}: ${value};`)
.join('\n')}
${love[0].colors.grays
.map((value, index) => ` --dark-gray-${index + 1}: ${value};`)
.join('\n')}
${love[1].colors.accents
.map((value, index) => ` --light-accent-${index + 1}: ${value};`)
.join('\n')}
${love[1].colors.grays
.map((value, index) => ` --light-gray-${index + 1}: ${value};`)
.join('\n')}
}\n`;
// Replace the predefined `:root` location with our properties.
css = css.replace(/\/\* :root-insert \*\//, cssProperties);
// Write the CSS to file.
await fsp.writeFile(join(__dirname, '../../public/css/style.css'), css);
// Render and write the `index.html` file.
await fsp.writeFile(
join(__dirname, '../../public/index.html'),
htmlclean(nunjucks.render('index.html', {love}))
);
const renderer: marked.Renderer = new marked.Renderer();
renderer.code = (code: string, language: string | undefined): string => {
if (language === undefined) {
throw new Error('Markdown code block with no language detected');
}
const codeHTML: string = rehype()
.stringify({type: 'root', children: refractor.highlight(code, language)})
.toString();
return `<pre><code class="language-${language}">${codeHTML}</code></pre>`;
};
await fsp.mkdir(join(__dirname, '../../public/get/'), {recursive: true});
const pagesToCreate: string[] = (
await fsp.readdir(join(__dirname, '../'))
).filter((value) => !['pages', 'scripts'].includes(value));
for (const page of pagesToCreate) {
const markdown: string = marked(
nunjucks.renderString(
await fsp.readFile(
join(__dirname, `../${page}/SITE README.md`),
'utf8'
),
{versions}
),
{renderer}
);
await fsp.mkdir(join(__dirname, `../../public/images/${page}/`), {
recursive: true
});
if (fs.existsSync(join(__dirname, `../${page}/images/`))) {
await cpy(
join(__dirname, `../${page}/images/*`),
join(__dirname, `../../public/images/${page}/`)
);
}
let title = `Love for ${page
.replace(/-/g, ' ')
.split(' ')
.map((value) => capitalize(value))
.join(' ')}`;
switch (page) {
case 'vscode':
title = 'Love for VS Code';
break;
default:
break;
}
const directory = join(__dirname, `../../public/get/${page}/`);
await fsp.mkdir(directory, {recursive: true});
await fsp.writeFile(
join(directory, 'index.html'),
htmlclean(nunjucks.render('get.html', {love, markdown, title}))
);
}
await writeCustomProperties(love, versions['css-custom-properties']);
}
export function capitalize(word: string): string {
return `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`;
}
// Utility helper function to write some data to file as JSON.
export async function writeJSON(path: string, data: unknown): Promise<void> {
await fsp.writeFile(path, JSON.stringify(data, null, 2));
}
export async function writeCustomProperties(
love: LoveVariant[],
version: string
): Promise<void> {
let css = `/*
The Love Theme CSS Custom Properties
https://love.holllo.cc - version ${version}
MIT license
*/
.love {\n`;
for (const variant of love) {
const prefix = variant.name === 'dark' ? 'd' : 'l';
css += ` /* Love ${capitalize(variant.name)} */\n`;
css += ` --${prefix}f-1: ${variant.colors.foreground1};\n`;
css += ` --${prefix}f-2: ${variant.colors.foreground2};\n`;
css += ` --${prefix}b-1: ${variant.colors.background1};\n`;
css += ` --${prefix}b-2: ${variant.colors.background2};\n`;
for (const [index, color] of variant.colors.accents.entries()) {
css += ` --${prefix}a-${index + 1}: ${color};\n`;
}
for (const [index, color] of variant.colors.grays.entries()) {
css += ` --${prefix}g-${index + 1}: ${color};\n`;
}
css += '\n';
}
css = css.trim();
css += '\n}\n';
await fsp.writeFile(join(__dirname, '../../public/love.css'), css);
}
if (require.main === module) {
void main();
}