refactor: ♻️ move and refactor code to the package

Moved and refactored most of the banner creation code to the
@marknow/banner package.
This commit is contained in:
Guz013
2023-06-22 15:23:11 -03:00
parent 00af431a9e
commit 23908cdc5b
12 changed files with 215 additions and 77 deletions

View File

@@ -226,6 +226,7 @@ module.exports = {
'duotone',
'tsconfig',
'workspace',
'woff',
],
minLength: 4,
}],

View File

@@ -1,5 +1,3 @@
/src/lib/components.d.ts
/src/lib/imports.d.ts
/.eslint-auto-import.json
*.woff
*.woff2

View File

@@ -1,33 +1,12 @@
import type { RequestHandler } from '@sveltejs/kit';
import satori from 'satori';
import { html as satoriHtml } from 'satori-html';
import Banner from './Banner.html?raw';
import font400 from '$lib/assets/Mona-Sans-Regular.woff?url';
import font600 from '$lib/assets/Mona-Sans-SemiBold.woff?url';
import newBanner from '@marknow/banners';
export const GET = (async ({ fetch }): Promise<Response> => {
const html = satoriHtml(Banner);
const banner = await satori(html,
{
width: 1000,
height: 180,
fonts: [
{
name: 'Mona Sans',
weight: 400,
style: 'normal',
data: await (await fetch(font400)).arrayBuffer(),
},
{
name: 'Mona Sans',
weight: 600,
style: 'normal',
data: await (await fetch(font600)).arrayBuffer(),
}],
export const GET = (async (): Promise<Response> => {
const banner = await newBanner({
title: 'Hello world',
});
return new Response(banner, {
return new Response(`${banner.toString()}`, {
status: 200,
headers: {
'Content-type': 'image/svg+xml',

View File

@@ -1,48 +0,0 @@
<div style="
display: flex;
justify-items: center;
align-items: center;
width: 1000px;
height: 180px;
">
<div style="
box-shadow: 0 5px 12px #00000040;
position: relative;
font-family: 'Mona Sans';
background-color: white;
margin: auto;
border-radius: 1em;
padding: 1.2em 2.5em;
display: flex;
min-width: 98%;
min-height: 20%;
gap: 1em;
">
<div style="
align-items: center;
display: flex;
margin: 1.5em 0;
">
<svg xmlns="http://www.w3.org/2000/svg" width="3.5em" height="3.5em" viewBox="0 0 24 24">
<g fill="currentColor">
<path
d="M15.75 2a.75.75 0 0 0-1.5 0v20a.75.75 0 0 0 1.5 0v-2.006c2.636-.027 4.104-.191 5.078-1.166C22 17.657 22 15.771 22 12c0-3.771 0-5.657-1.172-6.828c-.974-.975-2.442-1.139-5.078-1.166V2Z" />
<path fill-rule="evenodd"
d="M10 20c-3.771 0-5.657 0-6.828-1.172C2 17.657 2 15.771 2 12c0-3.771 0-5.657 1.172-6.828C4.343 4 6.229 4 10 4h3v16h-3ZM6.818 7.787c.3-.037.666-.037 1.066-.037h2.232c.4 0 .766 0 1.066.037c.329.041.68.137.98.405c.052.046.1.094.146.146c.268.3.364.651.405.98c.037.3.037.666.037 1.066v.041a.75.75 0 0 1-1.5 0c0-.455-.001-.726-.026-.922c-.024-.195-.228-.227-.228-.227c-.195-.025-.466-.026-.921-.026H9.75v5.5H11a.75.75 0 0 1 0 1.5H7a.75.75 0 0 1 0-1.5h1.25v-5.5h-.325c-.455 0-.726.001-.922.026c0 0-.203.032-.227.227c-.025.196-.026.467-.026.922a.75.75 0 0 1-1.5 0v-.041c0-.4 0-.766.037-1.066c.041-.329.137-.68.405-.98c.046-.052.094-.1.146-.146c.3-.268.651-.364.98-.405Z"
clip-rule="evenodd" />
</g>
</svg>
</div>
<div style="
align-items: center;
display: flex;
">
<div style="display: flex; flex-direction: column;">
<h1 style="margin: 0; font-weight: 600;">Marknow</h1>
<sub style="font-size: medium; font-weight: 400;">
Create beautiful markdown for your projects with ease
</sub>
</div>
</div>
</div>
</div>

View File

@@ -1 +1,3 @@
/dist
*.woff
*.woff2

View File

@@ -0,0 +1,29 @@
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
/**
* @param {import('./types').Reader | undefined} reader
* @typedef {import('satori').SatoriOptions['fonts'][0]} Font
* @returns {Promise<{regular: Font, bold: Font}>}
*/
export async function getMonaSansFonts(reader) {
reader ||= (await import('node:fs/promises')).readFile;
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
return {
regular: {
name: 'Mona Sans',
weight: 400,
style: 'normal',
data: await reader(join(__dirname, './assets/Mona-Sans-Regular.woff')),
},
bold: {
name: 'Mona Sans',
weight: 600,
style: 'normal',
data: await reader(join(__dirname, './assets/Mona-Sans-SemiBold.woff')),
},
};
}

View File

@@ -0,0 +1,85 @@
/* eslint-disable complexity */
/* eslint-disable @typescript-eslint/indent */
/**
* Returns the html string of the banner to be used by satori.
* Use the params to customize and complete it.
*
* @param {'vertical' | 'horizontal'} layout
* @param {{width: number, height: number}} dimensions
*
* @return {string}
*/
export function generateBannerHtml(layout, dimensions) {
/** @type {boolean} */
const horizontal = layout === 'horizontal';
return `
<div style="
display: flex;
justify-items: center;
align-items: center;
width: ${dimensions.width}px;
height: ${dimensions.height}px;
">
<div style="
box-shadow: 0 5px 12px #00000040;
position: relative;
font-family: 'Mona Sans';
background-color: white;
margin: auto;
border-radius: 1em;
padding: ${horizontal ? '1.2' : '2.5'}em 2.5em;
display: flex;
${horizontal
? 'flex-direction: row;'
: 'flex-direction: column;'
}
align-items: center;
min-width: 98%;
min-height: 20%;
gap: 1em;
">
<div style="
align-items: center;
display: flex;
margin: ${horizontal ? '1.5' : '0'}em 0;
">
<svg xmlns="http://www.w3.org/2000/svg" width="3.5em" height="3.5em" viewBox="0 0 24 24">
<g fill="currentColor">
<path
d="M15.75 2a.75.75 0 0 0-1.5 0v20a.75.75 0 0 0 1.5 0v-2.006c2.636-.027 4.104-.191 5.078-1.166C22 17.657 22 15.771 22 12c0-3.771 0-5.657-1.172-6.828c-.974-.975-2.442-1.139-5.078-1.166V2Z" />
<path fill-rule="evenodd"
d="M10 20c-3.771 0-5.657 0-6.828-1.172C2 17.657 2 15.771 2 12c0-3.771 0-5.657 1.172-6.828C4.343 4 6.229 4 10 4h3v16h-3ZM6.818 7.787c.3-.037.666-.037 1.066-.037h2.232c.4 0 .766 0 1.066.037c.329.041.68.137.98.405c.052.046.1.094.146.146c.268.3.364.651.405.98c.037.3.037.666.037 1.066v.041a.75.75 0 0 1-1.5 0c0-.455-.001-.726-.026-.922c-.024-.195-.228-.227-.228-.227c-.195-.025-.466-.026-.921-.026H9.75v5.5H11a.75.75 0 0 1 0 1.5H7a.75.75 0 0 1 0-1.5h1.25v-5.5h-.325c-.455 0-.726.001-.922.026c0 0-.203.032-.227.227c-.025.196-.026.467-.026.922a.75.75 0 0 1-1.5 0v-.041c0-.4 0-.766.037-1.066c.041-.329.137-.68.405-.98c.046-.052.094-.1.146-.146c.3-.268.651-.364.98-.405Z"
clip-rule="evenodd" />
</g>
</svg>
</div>
<div style="
align-items: center;
display: flex;
">
<div style="display: flex; flex-direction: column;">
<h1 style="
margin: ${horizontal ? '0' : '0 0 1em 0'};
font-weight: 600;
text-overflow: ellipsis;
max-width: 50em;
${horizontal ? 'text-align: start;' : 'text-align: center;'}
">
%%MARKNOW-PLACEHOLDER-TITLE%%
</h1>
<sub style="
font-size: medium;
font-weight: 400;
text-overflow: ellipsis;
max-width: 50em;
${horizontal ? 'text-align: start;' : 'text-align: center;'}
">
%%MARKNOW-PLACEHOLDER-SUBTILE%%
</sub>
</div>
</div>
</div>
</div>
`;
}

3
packages/banners/src/index.d.ts vendored Normal file
View File

@@ -0,0 +1,3 @@
import type { BannerOptions, Banner } from "./types";
export default async function banner(options: BannerOptions): Promise<Banner>;

View File

@@ -0,0 +1,43 @@
import { html as htmlToVNodes } from 'satori-html';
import satori from 'satori';
import { generateBannerHtml } from './html';
import { getMonaSansFonts } from './fonts';
/**
* @param {import('./types').BannerOptions} options
* @returns {Promise<import('./types').Banner>}
*/
export default async function banner({
title,
subtitle = '',
layout = 'horizontal',
config,
}) {
const dimensions = {
width: 1000,
height: layout === 'horizontal' ? 180 : 680,
};
const bannerFonts = await getMonaSansFonts(config?.reader);
const html = generateBannerHtml(layout, dimensions)
.replace('%%MARKNOW-PLACEHOLDER-TITLE%%', title)
.replace('%%MARKNOW-PLACEHOLDER-SUBTILE%%', subtitle);
const vNodes = htmlToVNodes(html);
const svg = await satori(vNodes, {
...dimensions,
fonts: [
bannerFonts.bold,
bannerFonts.regular,
],
});
return {
html,
vNodes,
svg,
toString() { return svg; },
};
}

46
packages/banners/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,46 @@
import type { Abortable } from "node:events";
import type { OpenMode, PathLike } from "node:fs";
import type { FileHandle } from "node:fs/promises";
export type Reader = (
path: PathLike | FileHandle,
) => Promise<Buffer | ArrayBuffer>
/**
* Options object for creating a banner.
*
* @package `@marknow/banners`
*/
export interface BannerOptions {
title: string,
subtitle?: string,
layout?: 'horizontal' | 'vertical' = 'horizontal',
config?: {
reader?: Reader,
}
}
/**
*
*/
export interface Banner {
toString(): string,
html: string,
svg: string,
vNodes: VNode,
}
/**
* **Copied from the satori-html package,**
* React-element-like objects / VDOM object used in satori.
*
* @package `satori-html`
*/
export interface VNode {
type: string;
props: {
style?: Record<string, any>;
children?: string | VNode | VNode[];
[prop: string]: any;
};
}