/* 创建一条新的「偶得」(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") { // 无参数进来时先说清楚原因:下面 pnpm 还会补一句 // "[ELIFECYCLE] Command failed with exit code 1",不点明的话很像脚本崩了 if (args.length === 0) { console.log('缺少正文内容,没有创建任何文件。\n正确写法:pnpm new-thought "今天把树莓派的散热换掉了。"\n') } 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 会自动刷新。")