refactor: convert to bun workspaces monorepo
- Move web app into apps/web/ - Create packages/shared/ with shared types - Create plugins/decky-vault/ scaffold - Root package.json manages workspaces only
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import matter from "gray-matter"
|
||||
import { remark } from "remark"
|
||||
import remarkRehype from "remark-rehype"
|
||||
import rehypeSlug from "rehype-slug"
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings"
|
||||
import rehypeStringify from "rehype-stringify"
|
||||
|
||||
export interface UpdateMeta {
|
||||
slug: string
|
||||
title: string
|
||||
date: string
|
||||
version: string
|
||||
summary: string
|
||||
}
|
||||
|
||||
export interface UpdateHeading {
|
||||
id: string
|
||||
text: string
|
||||
level: number
|
||||
}
|
||||
|
||||
export interface UpdateContent {
|
||||
meta: UpdateMeta
|
||||
html: string
|
||||
headings: UpdateHeading[]
|
||||
}
|
||||
|
||||
const UPDATES_DIR = path.join(process.cwd(), "content", "updates")
|
||||
|
||||
function getSlugs(): string[] {
|
||||
if (!fs.existsSync(UPDATES_DIR)) return []
|
||||
return fs
|
||||
.readdirSync(UPDATES_DIR)
|
||||
.filter((file) => file.endsWith(".md"))
|
||||
.map((file) => file.replace(/\.md$/, ""))
|
||||
}
|
||||
|
||||
function compareVersion(a: string, b: string): number {
|
||||
const pa = a.split(".").map(Number)
|
||||
const pb = b.split(".").map(Number)
|
||||
const len = Math.max(pa.length, pb.length)
|
||||
for (let i = 0; i < len; i++) {
|
||||
const na = pa[i] ?? 0
|
||||
const nb = pb[i] ?? 0
|
||||
if (na !== nb) return nb - na // descending
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
export function getAllUpdates(): UpdateMeta[] {
|
||||
const slugs = getSlugs()
|
||||
const updates = slugs.map((slug) => {
|
||||
const { meta } = getUpdateMeta(slug)
|
||||
return meta
|
||||
})
|
||||
// Sort by date descending, then by version descending (newest first)
|
||||
return updates.sort((a, b) => {
|
||||
const dateDiff = new Date(b.date).getTime() - new Date(a.date).getTime()
|
||||
if (dateDiff !== 0) return dateDiff
|
||||
return compareVersion(a.version, b.version)
|
||||
})
|
||||
}
|
||||
|
||||
function getUpdateMeta(slug: string): { meta: UpdateMeta } {
|
||||
const filePath = path.join(UPDATES_DIR, `${slug}.md`)
|
||||
const fileContents = fs.readFileSync(filePath, "utf8")
|
||||
const { data } = matter(fileContents)
|
||||
|
||||
return {
|
||||
meta: {
|
||||
slug,
|
||||
title: data.title ?? "",
|
||||
date: data.date ?? "",
|
||||
version: data.version ?? "",
|
||||
summary: data.summary ?? "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUpdateBySlug(slug: string): Promise<UpdateContent> {
|
||||
const filePath = path.join(UPDATES_DIR, `${slug}.md`)
|
||||
const fileContents = fs.readFileSync(filePath, "utf8")
|
||||
const { data, content } = matter(fileContents)
|
||||
|
||||
const processedContent = await remark()
|
||||
.use(remarkRehype)
|
||||
.use(rehypeSlug)
|
||||
.use(rehypeAutolinkHeadings)
|
||||
.use(rehypeStringify)
|
||||
.process(content)
|
||||
|
||||
const html = processedContent.toString()
|
||||
|
||||
// Extract headings from the rendered HTML
|
||||
const headingRegex = /<h([1-6])[^>]*id=["']([^"']+)["'][^>]*>(.*?)<\/h[1-6]>/g
|
||||
const headings: UpdateHeading[] = []
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = headingRegex.exec(html)) !== null) {
|
||||
headings.push({
|
||||
level: parseInt(match[1], 10),
|
||||
text: match[3].replace(/<[^>]*>/g, ""), // Strip any inner HTML tags
|
||||
id: match[2],
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
meta: {
|
||||
slug,
|
||||
title: data.title ?? "",
|
||||
date: data.date ?? "",
|
||||
version: data.version ?? "",
|
||||
summary: data.summary ?? "",
|
||||
},
|
||||
html,
|
||||
headings,
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllUpdateSlugs(): string[] {
|
||||
return getSlugs()
|
||||
}
|
||||
Reference in New Issue
Block a user