A CLI for publishing standard.site documents to ATProto
sequoia.pub
standard
site
lexicon
cli
publishing
1#!/usr/bin/env bun
2
3import { readdirSync, statSync } from "node:fs";
4import { join } from "node:path";
5
6const distDir = "./docs/dist";
7const ogImageUrl = "https://sequoia.pub/og.png";
8
9// Function to recursively find all HTML files
10function findHtmlFiles(dir: string): string[] {
11 const files: string[] = [];
12 const entries = readdirSync(dir);
13
14 for (const entry of entries) {
15 const fullPath = join(dir, entry);
16 const stat = statSync(fullPath);
17
18 if (stat.isDirectory()) {
19 files.push(...findHtmlFiles(fullPath));
20 } else if (entry.endsWith(".html")) {
21 files.push(fullPath);
22 }
23 }
24
25 return files;
26}
27
28// Function to inject OG image meta tags
29async function injectOgImageTags(filePath: string) {
30 const file = Bun.file(filePath);
31 let content = await file.text();
32
33 // Check if og:image already exists
34 if (content.includes('property="og:image"')) {
35 console.log(`⏭️ Skipping ${filePath} - og:image already exists`);
36 return;
37 }
38
39 // Find the position to inject the meta tag
40 // We'll insert it after og:description if it exists, or before twitter:card
41 const ogDescriptionMatch = content.match(
42 /<meta property="og:description"[^>]*>/,
43 );
44 const twitterCardMatch = content.match(/<meta name="twitter:card"[^>]*>/);
45
46 let insertPosition: number;
47 if (ogDescriptionMatch && ogDescriptionMatch.index !== undefined) {
48 insertPosition = ogDescriptionMatch.index + ogDescriptionMatch[0].length;
49 } else if (twitterCardMatch && twitterCardMatch.index !== undefined) {
50 insertPosition = twitterCardMatch.index;
51 } else {
52 // Fallback: insert before </head>
53 const headCloseMatch = content.indexOf("</head>");
54 if (headCloseMatch === -1) {
55 console.log(`⚠️ Warning: Could not find insertion point in ${filePath}`);
56 return;
57 }
58 insertPosition = headCloseMatch;
59 }
60
61 // Inject the og:image and twitter:image meta tags
62 const ogImageTag = `<meta property="og:image" content="${ogImageUrl}"/>`;
63 const twitterImageTag = `<meta name="twitter:image" content="${ogImageUrl}"/>`;
64 const newContent =
65 content.slice(0, insertPosition) +
66 ogImageTag +
67 twitterImageTag +
68 content.slice(insertPosition);
69
70 // Write the modified content back to the file
71 await Bun.write(filePath, newContent);
72 console.log(`✅ Injected og:image tags into ${filePath}`);
73}
74
75// Main execution
76async function main() {
77 console.log("🔍 Finding HTML files in dist directory...");
78 const htmlFiles = findHtmlFiles(distDir);
79 console.log(`📄 Found ${htmlFiles.length} HTML files`);
80
81 for (const file of htmlFiles) {
82 await injectOgImageTags(file);
83 }
84
85 console.log("\n✨ Done! All HTML files have been processed.");
86}
87
88main();