mirror of
https://github.com/Ad-closeNN/blog-fuwari.git
synced 2026-05-31 01:20:06 -04:00
feat(upgrade): 迁移到 Astro V6
This commit is contained in:
@@ -27,3 +27,5 @@ bun.lockb
|
|||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
||||||
src/content/.obsidian
|
src/content/.obsidian
|
||||||
|
|
||||||
|
.playwright-mcp
|
||||||
+26
-26
@@ -16,32 +16,32 @@
|
|||||||
"preinstall": "npx only-allow pnpm"
|
"preinstall": "npx only-allow pnpm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.8",
|
||||||
"@astrojs/rss": "^4.0.12",
|
"@astrojs/rss": "^4.0.18",
|
||||||
"@astrojs/sitemap": "^3.4.2",
|
"@astrojs/sitemap": "^3.7.2",
|
||||||
"@astrojs/svelte": "7.1.0",
|
"@astrojs/svelte": "8.0.5",
|
||||||
"@astrojs/tailwind": "^6.0.2",
|
"@astrojs/tailwind": "^6.0.2",
|
||||||
"@expressive-code/core": "^0.41.3",
|
"@expressive-code/core": "^0.41.7",
|
||||||
"@expressive-code/plugin-collapsible-sections": "^0.41.3",
|
"@expressive-code/plugin-collapsible-sections": "^0.41.7",
|
||||||
"@expressive-code/plugin-line-numbers": "^0.41.3",
|
"@expressive-code/plugin-line-numbers": "^0.41.7",
|
||||||
"@iconify-json/fa6-brands": "^1.2.6",
|
"@iconify-json/fa6-brands": "^1.2.6",
|
||||||
"@iconify-json/fa6-regular": "^1.2.4",
|
"@iconify-json/fa6-regular": "^1.2.4",
|
||||||
"@iconify-json/fa6-solid": "^1.2.4",
|
"@iconify-json/fa6-solid": "^1.2.4",
|
||||||
"@iconify-json/ic": "^1.2.2",
|
"@iconify-json/ic": "^1.2.4",
|
||||||
"@iconify-json/material-symbols": "^1.2.30",
|
"@iconify-json/material-symbols": "^1.2.67",
|
||||||
"@iconify/svelte": "^4.2.0",
|
"@iconify/svelte": "^4.2.0",
|
||||||
"@swup/astro": "^1.7.0",
|
"@swup/astro": "^1.8.0",
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"astro": "5.12.8",
|
"astro": "6.1.8",
|
||||||
"astro-expressive-code": "^0.41.3",
|
"astro-expressive-code": "^0.41.7",
|
||||||
"astro-icon": "^1.1.5",
|
"astro-icon": "^1.1.5",
|
||||||
"hastscript": "^9.0.1",
|
"hastscript": "^9.0.1",
|
||||||
"katex": "^0.16.22",
|
"katex": "^0.16.45",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.1",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"node-html-parser": "^7.0.1",
|
"node-html-parser": "^7.1.0",
|
||||||
"overlayscrollbars": "^2.11.4",
|
"overlayscrollbars": "^2.15.1",
|
||||||
"pagefind": "^1.3.0",
|
"pagefind": "^1.5.2",
|
||||||
"photoswipe": "^5.4.4",
|
"photoswipe": "^5.4.4",
|
||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
"rehype-autolink-headings": "^7.1.0",
|
"rehype-autolink-headings": "^7.1.0",
|
||||||
@@ -54,22 +54,22 @@
|
|||||||
"remark-github-admonitions-to-directives": "^1.0.5",
|
"remark-github-admonitions-to-directives": "^1.0.5",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"remark-sectionize": "^2.1.0",
|
"remark-sectionize": "^2.1.0",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.3",
|
||||||
"sharp": "^0.34.3",
|
"sharp": "^0.34.5",
|
||||||
"stylus": "^0.64.0",
|
"stylus": "^0.64.0",
|
||||||
"svelte": "^5.37.3",
|
"svelte": "^5.55.4",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.19",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.3",
|
||||||
"unist-util-visit": "^5.0.0"
|
"unist-util-visit": "^5.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/ts-plugin": "^1.10.4",
|
"@astrojs/ts-plugin": "^1.10.7",
|
||||||
"@biomejs/biome": "2.1.3",
|
"@biomejs/biome": "2.1.3",
|
||||||
"@rollup/plugin-yaml": "^4.1.2",
|
"@rollup/plugin-yaml": "^4.1.2",
|
||||||
"@types/hast": "^3.0.4",
|
"@types/hast": "^3.0.4",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"@types/sanitize-html": "^2.16.0",
|
"@types/sanitize-html": "^2.16.1",
|
||||||
"postcss-import": "^16.1.1",
|
"postcss-import": "^16.1.1",
|
||||||
"postcss-nesting": "^13.0.2"
|
"postcss-nesting": "^13.0.2"
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+2851
-3355
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ interface Post {
|
|||||||
data: {
|
data: {
|
||||||
title: string;
|
title: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
category?: string;
|
category?: string | null;
|
||||||
published: Date;
|
published: Date;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { i18n } from "../i18n/translation";
|
|||||||
import { getDir } from "../utils/url-utils";
|
import { getDir } from "../utils/url-utils";
|
||||||
import ImageWrapper from "./misc/ImageWrapper.astro";
|
import ImageWrapper from "./misc/ImageWrapper.astro";
|
||||||
import PostMetadata from "./PostMeta.astro";
|
import PostMetadata from "./PostMeta.astro";
|
||||||
|
import { render } from 'astro:content';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
class?: string;
|
class?: string;
|
||||||
@@ -20,7 +21,7 @@ interface Props {
|
|||||||
image: string;
|
image: string;
|
||||||
description: string;
|
description: string;
|
||||||
draft: boolean;
|
draft: boolean;
|
||||||
style: string;
|
style?: string;
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
entry,
|
entry,
|
||||||
@@ -32,7 +33,7 @@ const {
|
|||||||
category,
|
category,
|
||||||
image,
|
image,
|
||||||
description,
|
description,
|
||||||
style,
|
style = "",
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class;
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ const hasCover = image !== undefined && image !== null && image !== "";
|
|||||||
|
|
||||||
const coverWidth = "28%";
|
const coverWidth = "28%";
|
||||||
|
|
||||||
const { remarkPluginFrontmatter } = await entry.render();
|
const { remarkPluginFrontmatter } = await render(entry);
|
||||||
---
|
---
|
||||||
<div class:list={["card-base flex flex-col-reverse md:flex-col w-full rounded-[var(--radius-large)] overflow-hidden relative border border-black/20 dark:border-white/20 hover:scale-[1.02] hover:shadow-xl transition-all duration-[300ms]", className]} style={style}>
|
<div class:list={["card-base flex flex-col-reverse md:flex-col w-full rounded-[var(--radius-large)] overflow-hidden relative border border-black/20 dark:border-white/20 hover:scale-[1.02] hover:shadow-xl transition-all duration-[300ms]", className]} style={style}>
|
||||||
<div class:list={["pl-6 md:pl-9 pr-6 md:pr-2 pt-6 md:pt-7 pb-6 relative", {"w-full md:w-[calc(100%_-_52px_-_12px)]": !hasCover, "w-full md:w-[calc(100%_-_var(--coverWidth)_-_12px)]": hasCover}]}>
|
<div class:list={["pl-6 md:pl-9 pr-6 md:pr-2 pt-6 md:pt-7 pb-6 relative", {"w-full md:w-[calc(100%_-_52px_-_12px)]": !hasCover, "w-full md:w-[calc(100%_-_var(--coverWidth)_-_12px)]": hasCover}]}>
|
||||||
@@ -72,7 +73,7 @@ const { remarkPluginFrontmatter } = await entry.render();
|
|||||||
<div>{remarkPluginFrontmatter.minutes} {" " + i18n(I18nKey.minutesCount)}</div>
|
<div>{remarkPluginFrontmatter.minutes} {" " + i18n(I18nKey.minutesCount)}</div>
|
||||||
<div>|</div>
|
<div>|</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="text-50 text-sm font-medium" id={`page-views-${entry.slug}`}>加载中...</span>
|
<span class="text-50 text-sm font-medium" id={`page-views-${entry.id}`}>加载中...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ const { remarkPluginFrontmatter } = await entry.render();
|
|||||||
|
|
||||||
<!-- https://github.com/afoim/fuwari/blob/81f22decb17ff7ee1dd480c10773f7ba8f4df296/src/components/PostCard.astro -->
|
<!-- https://github.com/afoim/fuwari/blob/81f22decb17ff7ee1dd480c10773f7ba8f4df296/src/components/PostCard.astro -->
|
||||||
|
|
||||||
<script define:vars={{ slug: entry.slug }}>
|
<script define:vars={{ slug: entry.id }}>
|
||||||
// 获取文章浏览量统计
|
// 获取文章浏览量统计
|
||||||
async function fetchPostCardViews(slug) {
|
async function fetchPostCardViews(slug) {
|
||||||
const displayElement = document.getElementById(`page-views-${slug}`);
|
const displayElement = document.getElementById(`page-views-${slug}`);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const interval = 50;
|
|||||||
category={entry.data.category}
|
category={entry.data.category}
|
||||||
published={entry.data.published}
|
published={entry.data.published}
|
||||||
updated={entry.data.updated}
|
updated={entry.data.updated}
|
||||||
url={getPostUrlBySlug(entry.slug)}
|
url={getPostUrlBySlug(entry.id)}
|
||||||
image={entry.data.image}
|
image={entry.data.image}
|
||||||
description={entry.data.description}
|
description={entry.data.description}
|
||||||
draft={entry.data.draft}
|
draft={entry.data.draft}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { defineCollection } from "astro:content";
|
||||||
|
import { z } from "astro/zod";
|
||||||
|
import { glob } from "astro/loaders";
|
||||||
|
|
||||||
|
const postsCollection = defineCollection({
|
||||||
|
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/posts" }),
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
published: z.date(),
|
||||||
|
updated: z.date().optional(),
|
||||||
|
draft: z.boolean().optional().default(false),
|
||||||
|
description: z.string().optional().default(""),
|
||||||
|
image: z.string().optional().default(""),
|
||||||
|
tags: z.array(z.string()).optional().default([]),
|
||||||
|
category: z.string().optional().nullable().default(""),
|
||||||
|
showcover: z.boolean().optional().default(true),
|
||||||
|
customcover: z.string().optional().default(""),
|
||||||
|
lang: z.string().optional().default(""),
|
||||||
|
prevTitle: z.string().default(""),
|
||||||
|
prevSlug: z.string().default(""),
|
||||||
|
nextTitle: z.string().default(""),
|
||||||
|
nextSlug: z.string().default(""),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const specCollection = defineCollection({
|
||||||
|
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/spec" }),
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string().optional(),
|
||||||
|
published: z.date().optional(),
|
||||||
|
updated: z.date().optional(),
|
||||||
|
draft: z.boolean().optional().default(false),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = {
|
||||||
|
posts: postsCollection,
|
||||||
|
spec: specCollection,
|
||||||
|
};
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { defineCollection, z } from "astro:content";
|
|
||||||
|
|
||||||
const postsCollection = defineCollection({
|
|
||||||
schema: z.object({
|
|
||||||
title: z.string(),
|
|
||||||
published: z.date(),
|
|
||||||
updated: z.date().optional(),
|
|
||||||
draft: z.boolean().optional().default(false),
|
|
||||||
description: z.string().optional().default(""),
|
|
||||||
image: z.string().optional().default(""),
|
|
||||||
tags: z.array(z.string()).optional().default([]),
|
|
||||||
category: z.string().optional().nullable().default(""),
|
|
||||||
showcover: z.boolean().optional().default(true),
|
|
||||||
customcover: z.string().optional().default(""),
|
|
||||||
|
|
||||||
lang: z.string().optional().default(""),
|
|
||||||
|
|
||||||
/* For internal use */
|
|
||||||
prevTitle: z.string().default(""),
|
|
||||||
prevSlug: z.string().default(""),
|
|
||||||
nextTitle: z.string().default(""),
|
|
||||||
nextSlug: z.string().default(""),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
/* https://github.com/afoim/fuwari/commit/8b651d5d4e666c520d8fc95e89bf9d0ecf307644#diff-544dcd1cb4d05890db2dcf497052df475216a57683c346216e43133407b7ea58 */
|
|
||||||
const specCollection = defineCollection({
|
|
||||||
schema: z.object({
|
|
||||||
title: z.string().optional(),
|
|
||||||
published: z.date().optional(),
|
|
||||||
updated: z.date().optional(),
|
|
||||||
draft: z.boolean().optional().default(false),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
export const collections = {
|
|
||||||
posts: postsCollection,
|
|
||||||
spec: specCollection,
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
import { render, type CollectionEntry } from "astro:content";
|
||||||
import License from "@components/misc/License.astro";
|
import License from "@components/misc/License.astro";
|
||||||
import Markdown from "@components/misc/Markdown.astro";
|
import Markdown from "@components/misc/Markdown.astro";
|
||||||
import I18nKey from "@i18n/i18nKey";
|
import I18nKey from "@i18n/i18nKey";
|
||||||
@@ -17,15 +18,17 @@ import { formatDateToYYYYMMDD } from "../../utils/date-utils";
|
|||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const blogEntries = await getSortedPosts();
|
const blogEntries = await getSortedPosts();
|
||||||
return blogEntries.map((entry) => ({
|
return blogEntries.map((entry) => ({
|
||||||
params: { slug: entry.slug },
|
params: { slug: entry.id },
|
||||||
props: { entry },
|
props: { entry },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const { entry } = Astro.props;
|
interface Props {
|
||||||
const { Content, headings } = await entry.render();
|
entry: CollectionEntry<"posts">;
|
||||||
|
}
|
||||||
|
|
||||||
const { remarkPluginFrontmatter } = await entry.render();
|
const { entry }: Props = Astro.props;
|
||||||
|
const { Content, headings, remarkPluginFrontmatter } = await render(entry);
|
||||||
|
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
@@ -134,7 +137,7 @@ const customcover = entry.data.customcover;
|
|||||||
<Content />
|
<Content />
|
||||||
</Markdown>
|
</Markdown>
|
||||||
|
|
||||||
{licenseConfig.enable && <License title={entry.data.title} slug={entry.slug} pubDate={entry.data.published} class="mb-6 rounded-xl license-container onload-animation"></License>}
|
{licenseConfig.enable && <License title={entry.data.title} slug={entry.id} pubDate={entry.data.published} class="mb-6 rounded-xl license-container onload-animation"></License>}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import rss from '@astrojs/rss';
|
import rss from '@astrojs/rss';
|
||||||
import sanitizeHtml from 'sanitize-html';
|
import sanitizeHtml from 'sanitize-html';
|
||||||
import MarkdownIt from 'markdown-it';
|
import MarkdownIt from 'markdown-it';
|
||||||
import { getCollection } from 'astro:content';
|
|
||||||
import { siteConfig } from '@/config';
|
import { siteConfig } from '@/config';
|
||||||
import { parse as htmlParser } from 'node-html-parser';
|
import { parse as htmlParser } from 'node-html-parser';
|
||||||
import { getImage } from 'astro:assets';
|
import { getImage } from 'astro:assets';
|
||||||
@@ -27,7 +26,7 @@ export async function GET(context: APIContext) {
|
|||||||
|
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
// convert markdown to html string
|
// convert markdown to html string
|
||||||
const body = markdownParser.render(post.body);
|
const body = markdownParser.render(post.body ?? '');
|
||||||
// convert html string to DOM-like structure
|
// convert html string to DOM-like structure
|
||||||
const html = htmlParser.parse(body);
|
const html = htmlParser.parse(body);
|
||||||
// hold all img tags in variable images
|
// hold all img tags in variable images
|
||||||
@@ -66,7 +65,7 @@ export async function GET(context: APIContext) {
|
|||||||
title: post.data.title,
|
title: post.data.title,
|
||||||
description: post.data.description,
|
description: post.data.description,
|
||||||
pubDate: post.data.published,
|
pubDate: post.data.published,
|
||||||
link: `/posts/${post.slug}/`,
|
link: `/posts/${post.id}/`,
|
||||||
// sanitize the new html string with corrected image paths
|
// sanitize the new html string with corrected image paths
|
||||||
content: sanitizeHtml(html.toString(), {
|
content: sanitizeHtml(html.toString(), {
|
||||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
|
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ export async function getSortedPosts() {
|
|||||||
const sorted = await getRawSortedPosts();
|
const sorted = await getRawSortedPosts();
|
||||||
|
|
||||||
for (let i = 1; i < sorted.length; i++) {
|
for (let i = 1; i < sorted.length; i++) {
|
||||||
sorted[i].data.nextSlug = sorted[i - 1].slug;
|
sorted[i].data.nextSlug = sorted[i - 1].id;
|
||||||
sorted[i].data.nextTitle = sorted[i - 1].data.title;
|
sorted[i].data.nextTitle = sorted[i - 1].data.title;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < sorted.length - 1; i++) {
|
for (let i = 0; i < sorted.length - 1; i++) {
|
||||||
sorted[i].data.prevSlug = sorted[i + 1].slug;
|
sorted[i].data.prevSlug = sorted[i + 1].id;
|
||||||
sorted[i].data.prevTitle = sorted[i + 1].data.title;
|
sorted[i].data.prevTitle = sorted[i + 1].data.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export async function getSortedPostsList(): Promise<PostForList[]> {
|
|||||||
|
|
||||||
// delete post.body
|
// delete post.body
|
||||||
const sortedPostsList = sortedFullPosts.map((post) => ({
|
const sortedPostsList = sortedFullPosts.map((post) => ({
|
||||||
slug: post.slug,
|
slug: post.id,
|
||||||
data: post.data,
|
data: post.data,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user