初始提交:时之刃的小站
基于 VermilionVoid(朱墨留白)主题的个人博客。 - 站点配置集中在 src/data/site.ts 与 src/data/profile.ts - 30 篇文章、8 条偶得、相册 - 评论使用自托管 twikoo - 部署走 deploy.sh(rsync 到树莓派 + 云服务器)
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
/* 创建一条新的「偶得」(thought)
|
||||
*
|
||||
* 用法:
|
||||
* pnpm new-thought "今天把树莓派的散热换掉了,温度降了 15 度。"
|
||||
* pnpm new-thought "标题" --title 换散热 --location 张掖 --weather "晴 27°C" \
|
||||
* --images /gallery/GalleryFav/xxx.png --tags 生活,硬件 --pinned
|
||||
*
|
||||
* 只提供正文,其余字段都是可选的。
|
||||
*/
|
||||
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
const TARGET_DIR = "./src/content/thoughts"
|
||||
|
||||
/**
|
||||
* 用带时区的 ISO 时间。若写成不带时区的 `2026-09-14 21:30`,
|
||||
* YAML 会按 UTC 解析,显示时会偏移。
|
||||
*/
|
||||
function nowIso() {
|
||||
return new Date().toISOString()
|
||||
}
|
||||
|
||||
function fileStamp() {
|
||||
const now = new Date()
|
||||
const pad = (n) => String(n).padStart(2, "0")
|
||||
return (
|
||||
`${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}` +
|
||||
`-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`
|
||||
)
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
||||
console.log(`创建一条新的「偶得」
|
||||
|
||||
用法:
|
||||
pnpm new-thought "正文内容" [选项]
|
||||
|
||||
选项:
|
||||
--title <标题> 可选标题
|
||||
--location <地点> 地点,显示在日期旁
|
||||
--weather <天气> 天气,与地点一起显示
|
||||
--images <路径,路径> 配图,public 下的绝对路径,逗号分隔
|
||||
--tags <标签,标签> 标签,逗号分隔
|
||||
--pinned 置顶
|
||||
|
||||
示例:
|
||||
pnpm new-thought "换了散热,温度降了 15 度。" --location 张掖 --tags 硬件`)
|
||||
process.exit(args.length === 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
const content = args[0]
|
||||
|
||||
const options = {
|
||||
title: "",
|
||||
location: "",
|
||||
weather: "",
|
||||
images: "",
|
||||
tags: "",
|
||||
pinned: false,
|
||||
}
|
||||
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
const arg = args[i]
|
||||
|
||||
if (arg === "--pinned") {
|
||||
options.pinned = true
|
||||
continue
|
||||
}
|
||||
|
||||
if (!arg.startsWith("--")) {
|
||||
console.error(`错误:无法识别的参数 "${arg}"`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const key = arg.slice(2)
|
||||
if (!(key in options)) {
|
||||
console.error(`错误:未知选项 --${key}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const value = args[i + 1]
|
||||
if (value === undefined || value.startsWith("--")) {
|
||||
console.error(`错误:--${key} 缺少值`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
options[key] = value
|
||||
i++
|
||||
}
|
||||
|
||||
const list = (value) =>
|
||||
value
|
||||
? value
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: []
|
||||
|
||||
const images = list(options.images)
|
||||
const tags = list(options.tags)
|
||||
|
||||
if (!fs.existsSync(TARGET_DIR)) {
|
||||
fs.mkdirSync(TARGET_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
const fullPath = path.join(TARGET_DIR, `${fileStamp()}.md`)
|
||||
if (fs.existsSync(fullPath)) {
|
||||
console.error(`错误:${fullPath} 已存在,请稍后重试`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// 字符串统一加引号,避免标题里的冒号、井号等破坏 YAML
|
||||
const quote = (value) => `"${String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`
|
||||
|
||||
const frontmatter = [
|
||||
`published: ${nowIso()}`,
|
||||
options.title ? `title: ${quote(options.title)}` : "",
|
||||
options.location ? `location: ${quote(options.location)}` : "",
|
||||
options.weather ? `weather: ${quote(options.weather)}` : "",
|
||||
images.length > 0 ? `images:\n${images.map((img) => ` - ${quote(img)}`).join("\n")}` : "",
|
||||
tags.length > 0 ? `tags:\n${tags.map((tag) => ` - ${quote(tag)}`).join("\n")}` : "",
|
||||
options.pinned ? "pinned: true" : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
|
||||
fs.writeFileSync(fullPath, `---\n${frontmatter}\n---\n\n${content}\n`, "utf8")
|
||||
|
||||
console.log(`已创建偶得: ${fullPath}`)
|
||||
console.log("写入正文后,dev server 会自动刷新。")
|
||||
Reference in New Issue
Block a user