From 910967819539b644e9340d626e619e990a365163 Mon Sep 17 00:00:00 2001 From: klemek Date: Fri, 24 Apr 2026 23:03:02 +0200 Subject: [PATCH] feat: better article parsing + html dom base --- README.md | 12 +++++++ articles | 2 +- package.json | 1 + src/interfaces.ts | 23 ++++++++----- src/lib/articles.ts | 67 +++++++++++++++++++++++--------------- src/lib/dates.ts | 17 ++++++++++ src/lib/meta.ts | 5 +++ src/views/ArticleView.vue | 55 +++++++++++++++++++++---------- src/views/HomeView.vue | 27 ++++++++++----- src/views/NotFoundView.vue | 5 ++- tsconfig.app.json | 6 +++- 11 files changed, 157 insertions(+), 63 deletions(-) create mode 100644 src/lib/dates.ts create mode 100644 src/lib/meta.ts diff --git a/README.md b/README.md index d11d553..71ee6a4 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,18 @@ This template should help get you started developing with Vue 3 in Vite. +## TODO + +- [ ] render code highlight +- [ ] render mathjax +- [ ] render plantuml +- [ ] custom css in sub repo +- [ ] custom layout in sub repo ? +- [ ] build RSS feed +- [ ] link to home +- [ ] link to previous/next article +- [ ] set page title + ## Recommended IDE Setup [VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). diff --git a/articles b/articles index 6c87d62..ba3fd65 160000 --- a/articles +++ b/articles @@ -1 +1 @@ -Subproject commit 6c87d62e2af9959e4b06cd32a1d8fff44292551e +Subproject commit ba3fd659165856d70c1ca24503f9c239675a6c32 diff --git a/package.json b/package.json index f9456ee..30f5d11 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "type": "module", + "repository": "https://github.com/klemek/md-blog", "scripts": { "dev": "vite", "build": "run-p type-check \"build-only {@}\" --", diff --git a/src/interfaces.ts b/src/interfaces.ts index 1add3a9..835755d 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,11 +1,18 @@ -export interface MarkdownAttributes { - title?: string - draft?: string - thumbnail?: string - path?: string -} - export interface MarkdownData { - attributes: MarkdownAttributes + attributes: Record + html: string +} + +export interface ArticleMetadata { + path: string + title: string + date: Date + author: string + thumbnail: string + draft: boolean +} + +export interface Article { + metadata: ArticleMetadata html: string } diff --git a/src/lib/articles.ts b/src/lib/articles.ts index 9753ce5..1e7d6b7 100644 --- a/src/lib/articles.ts +++ b/src/lib/articles.ts @@ -1,44 +1,59 @@ -import type { ArticleList, MarkdownAttributes } from '@/interfaces' -import type { MarkdownData } from '@interfaces' +import type { MarkdownData, Article, ArticleMetadata } from '@interfaces' +import { dateFromParts } from './dates' -function completeAttributes( - srcAttributes: MarkdownAttributes, +function parseMetadata( + srcAttributes: Record, pathPrefix: string, -): MarkdownAttributes { + date: Date, +): ArticleMetadata { return { - draft: srcAttributes.draft ?? 'false', - thumbnail: srcAttributes.thumbnail - ? pathPrefix + srcAttributes.thumbnail - : pathPrefix + '/thumbnail.jpg', - title: srcAttributes.title ?? 'Untitled', path: pathPrefix, + title: (srcAttributes.title as string) ?? 'Untitled', + date: date, + author: (srcAttributes.author as string) ?? '', + thumbnail: srcAttributes.thumbnail + ? (srcAttributes.thumbnail as string).replaceAll('./', pathPrefix + '/') + : pathPrefix + '/thumbnail.jpg', + draft: !!srcAttributes.draft, } } -export async function loadArticle(year: number, month: number, day: number): MarkdownData | null { - const path = `${year}/${month.toString().padStart(2, '0')}/${day.toString().padStart(2, '0')}` - console.log(path) +function transformHtml(srcHtml: string, pathPrefix: string): string { + return srcHtml.replaceAll('./', pathPrefix + '/') +} + +export async function loadArticle(date: Date): Promise
{ + const year = date.getFullYear().toString() + const month = (date.getMonth() + 1).toString().padStart(2, '0') + const day = date.getDate().toString().padStart(2, '0') + const path = `./articles/${year}/${month}/${day}` try { - const data = (await import( - `@articles/${year}/${month.toString().padStart(2, '0')}/${day.toString().padStart(2, '0')}/index.md` - )) as MarkdownData + const data = (await import(`@articles/${year}/${month}/${day}/index.md`)) as MarkdownData return { - attributes: completeAttributes(data.attributes, `./articles/${path}`), - html: data.html.replaceAll('./', `./articles/${path}/`), + metadata: parseMetadata(data.attributes, path, date), + html: transformHtml(data.html, path), } } catch { return null } } -export async function listArticles(): Promise { +export async function listArticles(): Promise { const raw_articles = import.meta.glob('@articles/**/index.md') - const articles: MarkdownAttributes[] = await Promise.all( - Object.keys(raw_articles).map(async (key) => { - const data = (await raw_articles[key]()) as MarkdownData - return completeAttributes(data.attributes, key.replace('/index.md', '')) - }), - ) - articles.sort((article1, article2) => article1.path?.localeCompare(article2.path ?? '') ?? 0) + const articles: ArticleMetadata[] = ( + await Promise.all( + Object.keys(raw_articles).map(async (key) => { + if (!raw_articles[key]) return null + const data = (await raw_articles[key]()) as MarkdownData + const match = key.match(/\/(\d+)\/(\d+)\/(\d+)\//) + if (match === null) { + return null + } + const date = dateFromParts(match[1], match[2], match[3]) + return parseMetadata(data.attributes, key.replace('/index.md', ''), date) + }), + ) + ).filter((item) => item !== null) + articles.sort((article1, article2) => article2.date.valueOf() - article1.date.valueOf()) return articles } diff --git a/src/lib/dates.ts b/src/lib/dates.ts new file mode 100644 index 0000000..12ae286 --- /dev/null +++ b/src/lib/dates.ts @@ -0,0 +1,17 @@ +export function dateFromParts( + year: string | undefined, + month: string | undefined, + day: string | undefined, +): Date { + return new Date(parseInt(year ?? ''), parseInt(month ?? '') - 1, parseInt(day ?? '')) +} + +export function simpleDateFormat(date: Date): string { + return ( + date.getFullYear() + + '-' + + (date.getMonth() + 1).toString().padStart(2, '0') + + '-' + + date.getDate().toString().padStart(2, '0') + ) +} diff --git a/src/lib/meta.ts b/src/lib/meta.ts new file mode 100644 index 0000000..a17dec5 --- /dev/null +++ b/src/lib/meta.ts @@ -0,0 +1,5 @@ +import packageJson from '@/../package.json' + +export const NAME = packageJson.name +export const VERSION = packageJson.version +export const REPOSITORY = packageJson.repository diff --git a/src/views/ArticleView.vue b/src/views/ArticleView.vue index c4b5054..a31ef74 100644 --- a/src/views/ArticleView.vue +++ b/src/views/ArticleView.vue @@ -1,39 +1,60 @@