feat: regularize front-matter, add new-post command
This commit is contained in:
parent
ad67a86ef9
commit
767d351bd5
|
@ -7,7 +7,8 @@
|
||||||
"start": "astro dev",
|
"start": "astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro"
|
"astro": "astro",
|
||||||
|
"new-post": "node scripts/new-post.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.2.0",
|
"@astrojs/check": "^0.2.0",
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 877 KiB |
|
@ -0,0 +1,52 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
function getDate() {
|
||||||
|
const today = new Date();
|
||||||
|
const year = today.getFullYear();
|
||||||
|
const month = String(today.getMonth() + 1).padStart(2, '0'); //月份从0开始,所以要加1
|
||||||
|
const day = String(today.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.length === 0) {
|
||||||
|
console.error(`Error: No filename argument provided
|
||||||
|
Usage: npm run new-post -- <filename>`);
|
||||||
|
process.exit(1); // Terminate the script and return error code 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileName = args[0];
|
||||||
|
|
||||||
|
// Add .md extension if not present
|
||||||
|
const fileExtensionRegex = /\.(md|mdx)$/i;
|
||||||
|
if (!fileExtensionRegex.test(fileName)) {
|
||||||
|
fileName += '.md';
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetDir = './src/content/posts/';
|
||||||
|
const fullPath = path.join(targetDir, fileName);
|
||||||
|
|
||||||
|
if (fs.existsSync(fullPath)) {
|
||||||
|
console.error(`Error:File ${fullPath} already exists `);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const content =
|
||||||
|
`---
|
||||||
|
title: ${args[0]}
|
||||||
|
published: ${getDate()}
|
||||||
|
description:
|
||||||
|
cover:
|
||||||
|
url:
|
||||||
|
alt:
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
---
|
||||||
|
`;
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(targetDir, fileName), content);
|
||||||
|
|
||||||
|
console.log(`Post ${fullPath} created`);
|
|
@ -26,7 +26,7 @@ if (Array.isArray(categories) && categories.length > 0) {
|
||||||
|
|
||||||
const groups = function () {
|
const groups = function () {
|
||||||
const groupedPosts = posts.reduce((grouped, post) => {
|
const groupedPosts = posts.reduce((grouped, post) => {
|
||||||
const year = post.data.pubDate.getFullYear()
|
const year = post.data.published.getFullYear()
|
||||||
if (!grouped[year]) {
|
if (!grouped[year]) {
|
||||||
grouped[year] = []
|
grouped[year] = []
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ function formatDate(date: Date) {
|
||||||
<Button light height="40px" class="w-full rounded-lg hover:text-[initial]">
|
<Button light height="40px" class="w-full rounded-lg hover:text-[initial]">
|
||||||
<div class="flex flex-row justify-start items-center h-full">
|
<div class="flex flex-row justify-start items-center h-full">
|
||||||
<!-- date -->
|
<!-- date -->
|
||||||
<div class="w-[10%] transition text-sm text-right text-black/50 dark:text-white/50">{formatDate(post.data.pubDate)}</div>
|
<div class="w-[10%] transition text-sm text-right text-black/50 dark:text-white/50">{formatDate(post.data.published)}</div>
|
||||||
<!-- dot and line -->
|
<!-- dot and line -->
|
||||||
<div class="w-[10%] relative dash-line h-full flex items-center">
|
<div class="w-[10%] relative dash-line h-full flex items-center">
|
||||||
<div class="transition-all mx-auto w-1 h-1 rounded group-hover:h-5
|
<div class="transition-all mx-auto w-1 h-1 rounded group-hover:h-5
|
||||||
|
|
|
@ -6,11 +6,11 @@ import I18nKey from "../i18n/i18nKey";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
class: string;
|
class: string;
|
||||||
pubDate: Date;
|
published: Date;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
categories: string[];
|
categories: string[];
|
||||||
}
|
}
|
||||||
const {pubDate, tags, categories} = Astro.props;
|
const {published, tags, categories} = Astro.props;
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ const className = Astro.props.class;
|
||||||
>
|
>
|
||||||
<Icon name="material-symbols:calendar-today-outline-rounded" class="text-xl"></Icon>
|
<Icon name="material-symbols:calendar-today-outline-rounded" class="text-xl"></Icon>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-black/50 dark:text-white/50 text-sm font-medium">{formatDateToYYYYMMDD(pubDate)}</span>
|
<span class="text-black/50 dark:text-white/50 text-sm font-medium">{formatDateToYYYYMMDD(published)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- categories -->
|
<!-- categories -->
|
||||||
|
@ -31,7 +31,7 @@ const className = Astro.props.class;
|
||||||
<Icon name="material-symbols:menu-rounded" class="text-xl"></Icon>
|
<Icon name="material-symbols:menu-rounded" class="text-xl"></Icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap">
|
||||||
{categories && categories.map(category => <div
|
{(categories && categories.length > 0) && categories.map(category => <div
|
||||||
class="with-divider"
|
class="with-divider"
|
||||||
>
|
>
|
||||||
<a href=`/archive/category/${category}`
|
<a href=`/archive/category/${category}`
|
||||||
|
@ -40,7 +40,7 @@ const className = Astro.props.class;
|
||||||
{category}
|
{category}
|
||||||
</a>
|
</a>
|
||||||
</div>)}
|
</div>)}
|
||||||
{!categories && <div class="transition text-black/50 dark:text-white/50 text-sm font-medium">{i18n(I18nKey.uncategorized)}</div>}
|
{!(categories && categories.length > 0) && <div class="transition text-black/50 dark:text-white/50 text-sm font-medium">{i18n(I18nKey.uncategorized)}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ const className = Astro.props.class;
|
||||||
<Icon name="material-symbols:tag-rounded" class="text-xl"></Icon>
|
<Icon name="material-symbols:tag-rounded" class="text-xl"></Icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap">
|
||||||
{tags && tags.map(tag => <div
|
{(tags && tags.length > 0) && tags.map(tag => <div
|
||||||
class="with-divider"
|
class="with-divider"
|
||||||
>
|
>
|
||||||
<a href=`/archive/tag/${tag}`
|
<a href=`/archive/tag/${tag}`
|
||||||
|
@ -60,7 +60,7 @@ const className = Astro.props.class;
|
||||||
{tag}
|
{tag}
|
||||||
</a>
|
</a>
|
||||||
</div>)}
|
</div>)}
|
||||||
{!tags && <div class="transition text-black/50 dark:text-white/50 text-sm font-medium">{i18n(I18nKey.noTags)}</div>}
|
{!(tags && tags.length > 0) && <div class="transition text-black/50 dark:text-white/50 text-sm font-medium">{i18n(I18nKey.noTags)}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,13 +3,13 @@ import {formatDateToYYYYMMDD} from "../utils/date-utils";
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
url: string;
|
url: string;
|
||||||
pubDate: Date;
|
published: Date;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
cover: string;
|
cover: string;
|
||||||
description: string;
|
description: string;
|
||||||
words: number;
|
words: number;
|
||||||
}
|
}
|
||||||
const { title, url, pubDate, tags, cover, description, words } = Astro.props;
|
const { title, url, published, tags, cover, description, words } = Astro.props;
|
||||||
// console.log(Astro.props);
|
// console.log(Astro.props);
|
||||||
import ImageBox from "./misc/ImageBox.astro";
|
import ImageBox from "./misc/ImageBox.astro";
|
||||||
import ButtonTag from "./control/ButtonTag.astro";
|
import ButtonTag from "./control/ButtonTag.astro";
|
||||||
|
@ -40,7 +40,7 @@ const hasCover = cover !== undefined && cover !== null && cover !== '';
|
||||||
This is a very long title
|
This is a very long title
|
||||||
</a>
|
</a>
|
||||||
<div class="flex text-neutral-500 dark:text-neutral-400 items-center mb-1">
|
<div class="flex text-neutral-500 dark:text-neutral-400 items-center mb-1">
|
||||||
<div>{formatDateToYYYYMMDD(pubDate)}</div>
|
<div>{formatDateToYYYYMMDD(published)}</div>
|
||||||
<div class="transition h-1 w-1 rounded-sm bg-neutral-400 dark:bg-neutral-600 mx-3"></div>
|
<div class="transition h-1 w-1 rounded-sm bg-neutral-400 dark:bg-neutral-600 mx-3"></div>
|
||||||
<div>Uncategorized</div>
|
<div>Uncategorized</div>
|
||||||
<div class="transition h-1 w-1 rounded-sm bg-neutral-400 dark:bg-neutral-600 mx-3"></div>
|
<div class="transition h-1 w-1 rounded-sm bg-neutral-400 dark:bg-neutral-600 mx-3"></div>
|
||||||
|
|
|
@ -5,14 +5,14 @@ interface Props {
|
||||||
entry: any;
|
entry: any;
|
||||||
title: string;
|
title: string;
|
||||||
url: string;
|
url: string;
|
||||||
pubDate: Date;
|
published: Date;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
categories: string[];
|
categories: string[];
|
||||||
cover: string;
|
cover: string;
|
||||||
description: string;
|
description: string;
|
||||||
words: number;
|
words: number;
|
||||||
}
|
}
|
||||||
const { entry, title, url, pubDate, tags, categories, cover, description, words } = Astro.props;
|
const { entry, title, url, published, tags, categories, cover, description, words } = Astro.props;
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class;
|
||||||
// console.log(Astro.props);
|
// console.log(Astro.props);
|
||||||
import ImageBox from "./misc/ImageBox.astro";
|
import ImageBox from "./misc/ImageBox.astro";
|
||||||
|
@ -46,7 +46,7 @@ const { remarkPluginFrontmatter } = await entry.render();
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- metadata -->
|
<!-- metadata -->
|
||||||
<PostMetadata pubDate={pubDate} tags={tags} categories={categories} class:list={{"mb-4": description, "mb-6": !description}}></PostMetadata>
|
<PostMetadata published={published} tags={tags} categories={categories} class:list={{"mb-4": description, "mb-6": !description}}></PostMetadata>
|
||||||
|
|
||||||
<div class="transition text-black/75 dark:text-white/75 mb-4">
|
<div class="transition text-black/75 dark:text-white/75 mb-4">
|
||||||
{ description }
|
{ description }
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
---
|
---
|
||||||
title: 'My First Blog Post'
|
title: 'My First Blog Post'
|
||||||
pubDate: 2022-10-01
|
published: 2022-10-01
|
||||||
description: 'This is the first post of my new Astro blog.'
|
description: 'This is the first post of my new Astro blog.'
|
||||||
author: 'Astro Learner'
|
author: 'Astro Learner'
|
||||||
image:
|
cover:
|
||||||
url: 'https://docs.astro.build/assets/full-logo-light.png'
|
url:
|
||||||
alt: 'The full Astro logo.'
|
alt:
|
||||||
tags: ["astro", "blogging", "learning in public"]
|
tags: ["astro", "blogging", "learning in public"]
|
||||||
categories: ['Foo', 'Bar']
|
categories: ['Foo', 'Bar']
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
---
|
---
|
||||||
title: 'My Second Blog Post'
|
title: 'My Second Blog Post'
|
||||||
pubDate: 2021-07-01
|
published: 2021-07-01
|
||||||
description: 'This is the first post of my new Astro blog.'
|
description: 'This is the first post of my new Astro blog.'
|
||||||
author: 'Astro Learner'
|
author: 'Astro Learner'
|
||||||
image:
|
cover:
|
||||||
url: 'https://docs.astro.build/assets/full-logo-light.png'
|
url: 'https://saicaca.github.io/vivia-preview/assets/79905307_p0.jpg'
|
||||||
alt: 'The full Astro logo.'
|
alt:
|
||||||
tags: ["astro", "blogging", "learning in public"]
|
tags: ["astro", "blogging", "learning in public"]
|
||||||
cover: 'https://saicaca.github.io/vivia-preview/assets/79905307_p0.jpg'
|
|
||||||
---
|
---
|
||||||
# My First Blog Post
|
# My First Blog Post
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
---
|
---
|
||||||
title: 'My Third Blog Post'
|
title: 'My Third Blog Post'
|
||||||
pubDate: 2020-07-01
|
published: 2020-07-01
|
||||||
description: 'This is the first post of my new Astro blog.'
|
description: 'This is the first post of my new Astro blog.'
|
||||||
author: 'Astro Learner'
|
author: 'Astro Learner'
|
||||||
image:
|
cover:
|
||||||
url: 'https://docs.astro.build/assets/full-logo-light.png'
|
url:
|
||||||
alt: 'The full Astro logo.'
|
alt:
|
||||||
tags: ["astro", "blogging", "learning in public"]
|
tags: ["astro", "blogging", "learning in public"]
|
||||||
---
|
---
|
||||||
# My First Blog Post
|
# My First Blog Post
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
---
|
---
|
||||||
title: 'My Fourth Blog Post'
|
title: 'My Fourth Blog Post'
|
||||||
pubDate: 2022-07-01
|
published: 2022-07-01
|
||||||
description: 'This is the first post of my new Astro blog.'
|
description: 'This is the first post of my new Astro blog.'
|
||||||
author: 'Astro Learner'
|
author: 'Astro Learner'
|
||||||
image:
|
cover:
|
||||||
url: 'https://docs.astro.build/assets/full-logo-light.png'
|
url:
|
||||||
alt: 'The full Astro logo.'
|
alt:
|
||||||
tags: ["astro", "blogging", "learning in public"]
|
tags: ["astro", "blogging", "learning in public"]
|
||||||
---
|
---
|
||||||
# My First Blog Post
|
# My First Blog Post
|
||||||
|
|
|
@ -23,9 +23,9 @@ const {page} = Astro.props;
|
||||||
title={entry.data.title}
|
title={entry.data.title}
|
||||||
tags={entry.data.tags}
|
tags={entry.data.tags}
|
||||||
categories={entry.data.categories}
|
categories={entry.data.categories}
|
||||||
pubDate={entry.data.pubDate}
|
published={entry.data.published}
|
||||||
url={getPostUrlBySlug(entry.slug)}
|
url={getPostUrlBySlug(entry.slug)}
|
||||||
cover={entry.data.cover}
|
cover={entry.data.cover.url}
|
||||||
description={entry.data.description}
|
description={entry.data.description}
|
||||||
></TitleCard>
|
></TitleCard>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -24,7 +24,7 @@ const { Content } = await entry.render();
|
||||||
const { remarkPluginFrontmatter } = await entry.render();
|
const { remarkPluginFrontmatter } = await entry.render();
|
||||||
|
|
||||||
---
|
---
|
||||||
<MainGridLayout banner={entry.data.cover} title={entry.data.title}>
|
<MainGridLayout banner={entry.data.cover.url} title={entry.data.title}>
|
||||||
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4">
|
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4">
|
||||||
<div class:list={["card-base z-10 px-4 md:px-9 py-6 relative w-full ",
|
<div class:list={["card-base z-10 px-4 md:px-9 py-6 relative w-full ",
|
||||||
{}
|
{}
|
||||||
|
@ -61,18 +61,18 @@ const { remarkPluginFrontmatter } = await entry.render();
|
||||||
<!-- metadata -->
|
<!-- metadata -->
|
||||||
<PostMetadata
|
<PostMetadata
|
||||||
class="mb-5"
|
class="mb-5"
|
||||||
pubDate={entry.data.pubDate}
|
published={entry.data.published}
|
||||||
tags={entry.data.tags}
|
tags={entry.data.tags}
|
||||||
categories={entry.data.categories}
|
categories={entry.data.categories}
|
||||||
></PostMetadata>
|
></PostMetadata>
|
||||||
|
|
||||||
<!-- always show cover as long as it has one -->
|
<!-- always show cover as long as it has one -->
|
||||||
|
|
||||||
{entry.data.cover &&
|
{entry.data.cover.url &&
|
||||||
<ImageBox src={entry.data.cover} class="mb-8 rounded-xl"/>
|
<ImageBox src={entry.data.cover.url} class="mb-8 rounded-xl"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
{!entry.data.cover && <div class="border-[var(--line-divider)] border-dashed border-b-[1px] mb-5"></div>}
|
{!entry.data.cover.url && <div class="border-[var(--line-divider)] border-dashed border-b-[1px] mb-5"></div>}
|
||||||
|
|
||||||
<div class="prose dark:prose-invert prose-sm md:prose-base max-w-none custom-md
|
<div class="prose dark:prose-invert prose-sm md:prose-base max-w-none custom-md
|
||||||
">
|
">
|
||||||
|
|
|
@ -3,8 +3,8 @@ import {CollectionEntry, getCollection} from "astro:content";
|
||||||
export async function getSortedPosts() {
|
export async function getSortedPosts() {
|
||||||
const allBlogPosts = await getCollection("posts");
|
const allBlogPosts = await getCollection("posts");
|
||||||
const sorted = allBlogPosts.sort((a, b) => {
|
const sorted = allBlogPosts.sort((a, b) => {
|
||||||
const dateA = new Date(a.data.pubDate);
|
const dateA = new Date(a.data.published);
|
||||||
const dateB = new Date(b.data.pubDate);
|
const dateB = new Date(b.data.published);
|
||||||
return dateA > dateB ? -1 : 1;
|
return dateA > dateB ? -1 : 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ lang: en
|
||||||
|
|
||||||
banner:
|
banner:
|
||||||
enable: true
|
enable: true
|
||||||
url: https://saicaca.github.io/vivia-preview/assets/banner.jpg
|
url: /images/demo-banner.jpg
|
||||||
|
|
||||||
profile:
|
profile:
|
||||||
avatar: https://saicaca.github.io/vivia-preview/assets/avatar.jpg
|
avatar: https://saicaca.github.io/vivia-preview/assets/avatar.jpg
|
||||||
|
|
Loading…
Reference in New Issue