mirror of
https://github.com/mat-1/matdoesdev.git
synced 2025-08-02 14:46:04 +00:00
Merge branch 'main' into metasearch-post
This commit is contained in:
commit
40f275649a
14 changed files with 593 additions and 392 deletions
Binary file not shown.
25
package.json
25
package.json
|
@ -14,37 +14,38 @@
|
|||
"postinstall": "patch-package"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@img/sharp-linux-x64": "^0.33.0",
|
||||
"@img/sharp-linux-x64": "^0.33.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.1",
|
||||
"@types/cookie": "^0.6.0",
|
||||
"@types/html-minifier": "^4.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||
"@typescript-eslint/parser": "^6.13.2",
|
||||
"eslint": "^8.55.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.18.1",
|
||||
"@typescript-eslint/parser": "^6.18.1",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"mdsvex": "^0.11.0",
|
||||
"prettier": "^3.1.0",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"sharp": "^0.33.0",
|
||||
"sharp": "^0.33.1",
|
||||
"svelte": "4.2.8",
|
||||
"svelte-check": "^3.6.2",
|
||||
"svelte-preprocess": "^5.1.1",
|
||||
"svelte-preprocess": "^5.1.3",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@lukeed/uuid": "^2.0.1",
|
||||
"@sveltejs/adapter-node": "1.3.1",
|
||||
"@sveltejs/adapter-static": "^2.0.3",
|
||||
"@sveltejs/kit": "1.27.7",
|
||||
"@sveltejs/adapter-node": "^3.0.0",
|
||||
"@sveltejs/adapter-static": "^3.0.1",
|
||||
"@sveltejs/kit": "^2.3.0",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"cbor-x": "^1.5.6",
|
||||
"cbor-x": "^1.5.7",
|
||||
"cookie": "^0.6.0",
|
||||
"html-minifier": "^4.0.0",
|
||||
"patch-package": "^8.0.0",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"svelte-body": "^1.4.0",
|
||||
"vite": "5.0.6"
|
||||
"vite": "5.0.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
|
|
|
@ -19,12 +19,11 @@
|
|||
<nav>
|
||||
<BackAnchor href="/blog" />
|
||||
</nav>
|
||||
<div class="article-header">
|
||||
<h1>{title}</h1>
|
||||
<time>{new Date(published).toLocaleDateString()}</time>
|
||||
</div>
|
||||
<article>
|
||||
<div class="article-header">
|
||||
<h1>{title}</h1>
|
||||
<time>{new Date(published).toLocaleDateString()}</time>
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
</article>
|
||||
</div>
|
||||
|
@ -56,6 +55,7 @@
|
|||
}
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.article-header {
|
||||
margin-bottom: 1em;
|
||||
|
|
|
@ -24,7 +24,7 @@ export async function listBlogPostSlugs(): Promise<string[]> {
|
|||
)
|
||||
}
|
||||
|
||||
interface BlogPost {
|
||||
export interface BlogPost {
|
||||
title: string
|
||||
published: string
|
||||
html: string
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/blog.rss" />
|
||||
<link rel="alternate" type="application/atom+xml" title="Atom" href="/blog.atom" />
|
||||
</svelte:head>
|
||||
|
||||
<Head />
|
||||
|
||||
<div class="section-container">
|
||||
|
|
|
@ -10,7 +10,7 @@ export const load: Load = async ({ params }) => {
|
|||
try {
|
||||
page = await import(`../../(blog)/${slug}/index.svx`)
|
||||
} catch (e) {
|
||||
throw error(404, 'Not found')
|
||||
error(404, 'Not found');
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -21,7 +21,7 @@ export const GET: RequestHandler = async ({ params }) => {
|
|||
if (!postSlug) throw new Error('No slug')
|
||||
if (!assetName) throw new Error('No asset')
|
||||
|
||||
if (!(await doesAssetExist(postSlug, assetName))) throw error(404, 'Not found')
|
||||
if (!(await doesAssetExist(postSlug, assetName))) error(404, 'Not found');
|
||||
|
||||
const file = await fs.promises.readFile(path.join(postsDir, postSlug, assetName))
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ export const GET: RequestHandler = async ({ params }) => {
|
|||
|
||||
const post = await getPost(slug)
|
||||
|
||||
if (post === null) throw error(404, 'Not found')
|
||||
if (post === null) error(404, 'Not found');
|
||||
|
||||
return json({
|
||||
title: post.title,
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import type { RequestHandler } from '@sveltejs/kit'
|
||||
import type { BlogPostPreview } from '../blog.json/+server'
|
||||
import type { BlogPost } from '$lib/blog'
|
||||
import { getPostsUntrimmed } from '../blog.json/preview'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export const GET: RequestHandler = async ({ fetch }) => {
|
||||
const posts = (await fetch('/blog.json').then((r) => r.json())) as BlogPostPreview[]
|
||||
function item(post: BlogPostPreview) {
|
||||
const posts = await getPostsUntrimmed()
|
||||
function item(post: BlogPost) {
|
||||
const escapedPostHtml = post.html
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
return `
|
||||
<entry>
|
||||
<title>${post.title}</title>
|
||||
|
@ -13,6 +18,7 @@ export const GET: RequestHandler = async ({ fetch }) => {
|
|||
<id>https://matdoes.dev/${post.slug}</id>
|
||||
<published>${post.published}</published>
|
||||
<updated>${post.published}</updated>
|
||||
<content type="xhtml">${escapedPostHtml}</content>
|
||||
</entry>
|
||||
`
|
||||
}
|
||||
|
|
|
@ -1,80 +1,10 @@
|
|||
import { getPost, listBlogPostSlugs } from '$lib/blog'
|
||||
import { json, type RequestHandler } from '@sveltejs/kit'
|
||||
import { getPostsUntrimmed } from './preview'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export interface BlogPostPreview {
|
||||
title: string
|
||||
published: string
|
||||
html: string
|
||||
css: string
|
||||
slug: string
|
||||
}
|
||||
|
||||
function cutOffAtLine(text: string, line: number) {
|
||||
let row = 0
|
||||
let column = 0
|
||||
|
||||
let inHtmlTag = false
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
if (text[i] === '<') {
|
||||
inHtmlTag = true
|
||||
} else if (text[i] === '>') {
|
||||
inHtmlTag = false
|
||||
continue
|
||||
}
|
||||
if (text[i] === '\n') {
|
||||
row++
|
||||
column = 0
|
||||
} else {
|
||||
column++
|
||||
}
|
||||
if (column > 128 && !inHtmlTag) {
|
||||
row++
|
||||
column = 0
|
||||
}
|
||||
if (row >= line && !inHtmlTag) {
|
||||
return text.slice(0, i) + '...'
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
async function getPosts() {
|
||||
const existingPosts: string[] = await listBlogPostSlugs()
|
||||
|
||||
const posts = (
|
||||
await Promise.all(
|
||||
existingPosts.map(async (slug): Promise<BlogPostPreview | null> => {
|
||||
const blogPost = await getPost(slug)
|
||||
|
||||
// theoretically it's possible a blog post was deleted while we were reading the directory, so just ignore it if it's null
|
||||
if (blogPost === null) return null
|
||||
|
||||
return {
|
||||
title: blogPost.title,
|
||||
published: blogPost.published,
|
||||
// HACK: remove images, i WILL parse html with regex and you won't stop me
|
||||
html: cutOffAtLine(
|
||||
blogPost.html.replace(/<(img|iframe).+?\/?>|<\/?(img|iframe)>/g, ''),
|
||||
6
|
||||
),
|
||||
css: blogPost.css,
|
||||
slug: blogPost.slug,
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
.filter((p) => p)
|
||||
.sort((a, b) => (new Date(a!.published) > new Date(b!.published) ? -1 : 1))
|
||||
|
||||
// typescript thinks posts is (BlogPostPreview | null)[] but it's not because of the .filter
|
||||
return posts as BlogPostPreview[]
|
||||
}
|
||||
|
||||
export const GET: RequestHandler = async () => {
|
||||
const posts = await getPosts()
|
||||
const posts = await getPostsUntrimmed()
|
||||
|
||||
return json(posts)
|
||||
}
|
||||
|
|
68
src/routes/blog.json/preview.ts
Normal file
68
src/routes/blog.json/preview.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
import { listBlogPostSlugs, type BlogPost, getPost } from '$lib/blog'
|
||||
|
||||
export interface BlogPostPreview {
|
||||
title: string
|
||||
published: string
|
||||
html: string
|
||||
css: string
|
||||
slug: string
|
||||
}
|
||||
|
||||
function cutOffAtLine(text: string, line: number) {
|
||||
let row = 0
|
||||
let column = 0
|
||||
|
||||
let inHtmlTag = false
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
if (text[i] === '<') {
|
||||
inHtmlTag = true
|
||||
} else if (text[i] === '>') {
|
||||
inHtmlTag = false
|
||||
continue
|
||||
}
|
||||
if (text[i] === '\n') {
|
||||
row++
|
||||
column = 0
|
||||
} else {
|
||||
column++
|
||||
}
|
||||
if (column > 128 && !inHtmlTag) {
|
||||
row++
|
||||
column = 0
|
||||
}
|
||||
if (row >= line && !inHtmlTag) {
|
||||
return text.slice(0, i) + '...'
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
export async function getPostsUntrimmed(): Promise<BlogPost[]> {
|
||||
const existingPosts: string[] = await listBlogPostSlugs()
|
||||
|
||||
const posts = (
|
||||
await Promise.all(
|
||||
existingPosts.map(async (slug): Promise<BlogPost | null> => {
|
||||
return await getPost(slug)
|
||||
})
|
||||
)
|
||||
)
|
||||
.filter((p) => p)
|
||||
.sort((a, b) => (new Date(a!.published) > new Date(b!.published) ? -1 : 1))
|
||||
|
||||
// typescript thinks posts is (BlogPost | null)[] but it's not because of the .filter
|
||||
return posts as BlogPost[]
|
||||
}
|
||||
|
||||
export async function getPosts() {
|
||||
const posts = await getPostsUntrimmed()
|
||||
return posts.map((p) => ({
|
||||
title: p.title,
|
||||
published: p.published,
|
||||
// HACK: remove images, i WILL parse html with regex and you won't stop me
|
||||
html: cutOffAtLine(p.html.replace(/<(img|iframe).+?\/?>|<\/?(img|iframe)>/g, ''), 6),
|
||||
css: p.css,
|
||||
slug: p.slug,
|
||||
}))
|
||||
}
|
|
@ -1,16 +1,27 @@
|
|||
import type { RequestHandler } from '@sveltejs/kit'
|
||||
import type { BlogPostPreview } from '../blog.json/+server'
|
||||
import { getPostsUntrimmed } from '../blog.json/preview'
|
||||
import type { BlogPost } from '$lib/blog'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export const GET: RequestHandler = async ({ fetch }) => {
|
||||
const posts = (await fetch('/blog.json').then((r) => r.json())) as BlogPostPreview[]
|
||||
function item(post: BlogPostPreview) {
|
||||
const posts = await getPostsUntrimmed()
|
||||
function item(post: BlogPost) {
|
||||
const escapedPostHtml = post.html
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
return `
|
||||
<item>
|
||||
<title>${post.title}</title>
|
||||
<link>https://matdoes.dev/${post.slug}</link>
|
||||
<pubDate>${post.published}</pubDate>
|
||||
<description>
|
||||
${escapedPostHtml}
|
||||
<style>
|
||||
${post.css}
|
||||
</style>
|
||||
</description>
|
||||
</item>
|
||||
`
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"module": "es2022",
|
||||
"lib": ["es2022", "DOM", "WebWorker"],
|
||||
"target": "es2020",
|
||||
|
@ -8,7 +8,6 @@
|
|||
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
|
||||
to enforce using \`import type\` instead of \`import\` for Types.
|
||||
*/
|
||||
"importsNotUsedAsValues": "error",
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue