Skip to content

Commit

Permalink
Use async generator
Browse files Browse the repository at this point in the history
  • Loading branch information
NotWoods committed Sep 20, 2023
1 parent 0d93168 commit cc8c0b2
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 44 deletions.
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"imports": {
"std/": "https://deno.land/std@0.202.0/",
"@notionhq/client": "npm:@notionhq/client@2.2.13",
"@notionhq/client": "npm:@notionhq/client@2.2.3",
"vhtml": "https://esm.sh/vhtml@2.2.0"
},
"tasks": {
Expand Down
28 changes: 22 additions & 6 deletions src/book.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ function changeCaffeineCharacters(level: string) {

/**
* Creates an HTML table to display the given tea information.
* @param caption The caption to display above the table.
* @param teaList List of tea information to use. Each item will be a row in the table.
* @param props.caption The caption to display above the table.
* @param props.teaList List of tea information to use. Each item will be a row in the table.
*/
export function generateTeaTableHtml(
caption: string,
teaList: readonly FormattedTeaDatabasePage[],
startingIndex: number,
export function TeaTable(
props: {
caption: string;
teaList: readonly FormattedTeaDatabasePage[];
startingIndex: number;
},
) {
const { caption, teaList, startingIndex } = props;
return (
<table>
<caption>{caption}</caption>
Expand Down Expand Up @@ -66,6 +69,19 @@ export function generateTeaTableHtml(
);
}

/**
* Creates an HTML table to display the given tea information.
* @param caption The caption to display above the table.
* @param teaList List of tea information to use. Each item will be a row in the table.
*/
export function generateTeaTableHtml(
caption: string,
teaList: readonly FormattedTeaDatabasePage[],
startingIndex: number,
) {
return <TeaTable caption={caption} teaList={teaList} startingIndex={startingIndex} />;
}

const listFormat = new Intl.ListFormat("en", {
style: "long",
type: "conjunction",
Expand Down
86 changes: 55 additions & 31 deletions src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ import {
import type { FormattedTeaDatabasePage } from "./notion/types.ts";
import { generateSvg } from "./svg.tsx";

interface TeaCollection {
topDisplayTeas: readonly FormattedTeaDatabasePage[];
bottomDisplayTeas: readonly FormattedTeaDatabasePage[];
pantryTeas: readonly FormattedTeaDatabasePage[];
}

/**
* Downloads all the teas from Notion and formats them into lists.
* @returns A list of teas, grouped by location.
*/
async function getTeaList() {
async function getTeaCollection(): Promise<TeaCollection> {
const topDisplayTeas: FormattedTeaDatabasePage[] = [];
const bottomDisplayTeas: FormattedTeaDatabasePage[] = [];
const pantryTeas: FormattedTeaDatabasePage[] = [];
Expand All @@ -38,61 +44,79 @@ async function getTeaList() {
}

/**
* Generate markdown chapter content for a specific tea.
* Generate markdown chapter content for each specific tea.
* Chapters are signified by a heading 1 (`#`).
*/
async function generateChapterMarkdown(tea: FormattedTeaDatabasePage) {
const content: string[] = await fetchTeaPageContent(tea);
async function* generateChapterMarkdown(
teas: readonly FormattedTeaDatabasePage[],
) {
const pageContentArray = teas.map(fetchTeaPageContent);

for (const [index, tea] of teas.entries()) {
const content: string[] = await pageContentArray[index];

return `${generateChapterPropertiesMarkdown(tea)}
yield "\n\n";
yield `${generateChapterPropertiesMarkdown(tea)}
${content.length > 0 ? "---\n" : ""}
${content.join("\n\n")}
`;
}
}

const { topDisplayTeas, bottomDisplayTeas, pantryTeas } = await getTeaList();

// Generate the cover image SVG
const svg = await generateSvg(topDisplayTeas, bottomDisplayTeas);
await Deno.writeTextFile("tea.svg", svg);
async function writeTextFile(input: AsyncIterable<string>, fileName: string) {
const file = await Deno.open(fileName, { write: true, create: true });
await ReadableStream.from(input)
.pipeThrough(new TextEncoderStream())
.pipeTo(file.writable);
}

// Generate the markdown file that represents the ebook
const book = `---
/**
* Generate the markdown file that represents the ebook.
*/
async function* generateBookMarkdown(
{ topDisplayTeas, bottomDisplayTeas, pantryTeas }: TeaCollection,
): AsyncIterableIterator<string> {
// YAML frontmatter
yield `---
title: Tea
creator: Tiger x Daphne
date: ${formatDate(new Date(), "yyyy-MM-dd")}
lang: en-US
cover-image: tea.png
css: assets/epub.css
...
...`;

${generateTeaTableHtml("Display (top)", topDisplayTeas, 1)}
yield "\n\n";
yield* generateTeaTableHtml("Display (top)", topDisplayTeas, 1);

${
generateTeaTableHtml(
yield "\n\n";
yield* generateTeaTableHtml(
"Display (bottom)",
bottomDisplayTeas,
topDisplayTeas.length + 1,
)
}
);

${
generateTeaTableHtml(
yield "\n\n";
yield* generateTeaTableHtml(
"Pantry",
pantryTeas,
topDisplayTeas.length + bottomDisplayTeas.length + 1,
)
);

yield* generateChapterMarkdown([
...topDisplayTeas,
...bottomDisplayTeas,
...pantryTeas,
]);
}

${
(
await Promise.all(
[...topDisplayTeas, ...bottomDisplayTeas, ...pantryTeas].map(
generateChapterMarkdown,
),
)
).join("\n\n")
}`;
const teaCollection = await getTeaCollection();
const { topDisplayTeas, bottomDisplayTeas } = teaCollection;

// Generate the cover image SVG
const svg = await generateSvg(topDisplayTeas, bottomDisplayTeas);
await Deno.writeTextFile("tea.svg", svg);

// Write the book file for pandoc
await Deno.writeTextFile("tea-list.txt", book);
writeTextFile(generateBookMarkdown(teaCollection), "tea-list.txt");
15 changes: 9 additions & 6 deletions src/svg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,20 @@ export async function generateSvg(
new URL("../assets/cover.svg", import.meta.url),
);

const closingTagMatch = SVG_CLOSING_TAG.exec(svgTemplate);
if (!closingTagMatch) {
throw new Error("Could not find </svg> tag in template");
}
const templatePrefix = svgTemplate.slice(0, closingTagMatch.index);
const templateSuffix = svgTemplate.slice(closingTagMatch.index);

const generatedText: string = (
<g id="tea">
<TeaDisplayGroup teaList={topDisplayTeas} position="8 8" />
<TeaDisplayGroup teaList={bottomDisplayTeas} position="8 360" />
</g>
);

const svg = svgTemplate.replace(SVG_CLOSING_TAG, `${generatedText}</svg>`);
if (svg === svgTemplate) {
throw new Error("Could not find </svg> tag in template");
}

return svg;
// Insert the generated content just before the closing </svg> tag
return templatePrefix + generatedText + templateSuffix;
}

0 comments on commit cc8c0b2

Please sign in to comment.