新增 new-post 脚本与操作指南文档
- scripts/new-post.mjs + pnpm new-post:按标题建文章,支持 --category/--tags/ --description/--image/--file/--pinned/--draft,标题里的冒号引号会正确转义 - docs/操作指南.md:写文章、发偶得、加相册、预览、检查、部署的完整流程 - 修复 astro check 报出的 2 个类型错误(gallery 索引页 querySelector 未指定 HTMLElement,导致 dataset 不可用)。现在 pnpm check 为 0 error
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
/* 创建一篇新文章
|
||||
*
|
||||
* 用法:
|
||||
* pnpm new-post "文章标题"
|
||||
* pnpm new-post "树莓派装 Docker" --category 教程 --tags Docker,树莓派
|
||||
* pnpm new-post "读《XX》有感" --category 思考 --description "摘要" --pinned
|
||||
* pnpm new-post "标题" --file my-custom-slug --category 建站
|
||||
*
|
||||
* 不加 --category 时文件放在 src/content/posts/ 根目录;
|
||||
* 加了则同时写进 frontmatter 的 category 并放进同名子目录,和现有文章结构一致。
|
||||
*/
|
||||
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
const POSTS_DIR = "./src/content/posts"
|
||||
|
||||
function today() {
|
||||
const d = new Date()
|
||||
const p = (n) => String(n).padStart(2, "0")
|
||||
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
||||
console.log(`创建一篇新文章
|
||||
|
||||
用法:
|
||||
pnpm new-post "文章标题" [选项]
|
||||
|
||||
选项:
|
||||
--category <分类> 分类名,同时决定存放的子目录
|
||||
--file <文件名> 自定义文件名(默认用标题)
|
||||
--tags <标签,标签> 标签,逗号分隔
|
||||
--description <摘要> 摘要,用于 SEO 和列表卡片
|
||||
--image <路径> 封面图,public 下的绝对路径
|
||||
--pinned 置顶
|
||||
--draft 存为草稿(构建时不会输出)
|
||||
|
||||
示例:
|
||||
pnpm new-post "树莓派装 Docker" --category 教程 --tags Docker,树莓派`)
|
||||
process.exit(args.length === 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
const title = args[0]
|
||||
|
||||
const opts = {
|
||||
category: "",
|
||||
file: "",
|
||||
tags: "",
|
||||
description: "",
|
||||
image: "",
|
||||
pinned: false,
|
||||
draft: false,
|
||||
}
|
||||
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
const arg = args[i]
|
||||
|
||||
if (arg === "--pinned") {
|
||||
opts.pinned = true
|
||||
continue
|
||||
}
|
||||
if (arg === "--draft") {
|
||||
opts.draft = true
|
||||
continue
|
||||
}
|
||||
if (!arg.startsWith("--")) {
|
||||
console.error(`错误:无法识别的参数 "${arg}"`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const key = arg.slice(2)
|
||||
if (!(key in opts)) {
|
||||
console.error(`错误:未知选项 --${key}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const value = args[i + 1]
|
||||
if (value === undefined || value.startsWith("--")) {
|
||||
console.error(`错误:--${key} 缺少值`)
|
||||
process.exit(1)
|
||||
}
|
||||
opts[key] = value
|
||||
i++
|
||||
}
|
||||
|
||||
/** 文件名里不能出现 / : 等字符,统一替换掉 */
|
||||
const safeName = (s) =>
|
||||
s
|
||||
.replace(/[/\\:*?"<>|]/g, "")
|
||||
.replace(/\s+/g, "-")
|
||||
.trim()
|
||||
|
||||
const fileName = `${safeName(opts.file || title)}.md`
|
||||
const dir = opts.category ? path.join(POSTS_DIR, safeName(opts.category)) : POSTS_DIR
|
||||
const fullPath = path.join(dir, fileName)
|
||||
|
||||
if (fs.existsSync(fullPath)) {
|
||||
console.error(`错误:${fullPath} 已存在`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
|
||||
const list = (v) =>
|
||||
v
|
||||
? v
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: []
|
||||
|
||||
const tags = list(opts.tags)
|
||||
|
||||
/** 双引号包裹并转义,避免标题里的冒号、引号破坏 YAML */
|
||||
const q = (v) => `"${String(v).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`
|
||||
|
||||
const frontmatter = [
|
||||
`title: ${q(title)}`,
|
||||
`published: ${today()}`, // 只写日期,YAML 按 UTC 零点解析,不会有时区偏移
|
||||
opts.description ? `description: ${q(opts.description)}` : "",
|
||||
opts.image ? `image: ${q(opts.image)}` : "",
|
||||
tags.length > 0 ? `tags:\n${tags.map((t) => ` - ${q(t)}`).join("\n")}` : "tags: []",
|
||||
`category: ${q(opts.category)}`,
|
||||
opts.pinned ? "pinned: true" : "",
|
||||
opts.draft ? "draft: true" : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
|
||||
fs.writeFileSync(fullPath, `---\n${frontmatter}\n---\n\n在这里写正文。\n`, "utf8")
|
||||
|
||||
console.log(`已创建文章: ${fullPath}`)
|
||||
if (opts.draft) console.log("状态:草稿(draft: true),构建时不会输出。写完记得删掉这一行。")
|
||||
console.log("运行 pnpm dev 可以在浏览器里实时预览。")
|
||||
Reference in New Issue
Block a user