Files
Blog/scripts/new-post.mjs
T
2026-08-11 10:25:27 +08:00

72 lines
2.2 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
const root = process.cwd();
const now = new Date();
const year = String(now.getFullYear());
const month = String(now.getMonth() + 1).padStart(2, '0');
const postsDir = path.join(root, 'src', 'content', 'posts', year, month);
const rl = readline.createInterface({ input, output });
async function main() {
try {
let answers;
if (input.isTTY) {
answers = [
await rl.question('文章标题: '),
await rl.question('文章类名: '),
await rl.question('文章封面(可选,直接回车跳过): ')
];
} else {
answers = [];
for await (const line of rl) answers.push(line);
}
const [rawTitle = '', rawSlug = '', rawCover = ''] = answers;
const title = rawTitle.trim();
const slug = rawSlug.trim();
const cover = rawCover.trim();
if (!title) throw new Error('文章标题不能为空');
if (!slug) throw new Error('文章类名不能为空');
if (!/^[^\\/:*?"<>|]+$/.test(slug) || slug === '.' || slug === '..') {
throw new Error('文章类名只能是单个文件名,不能包含路径或 Windows 保留字符');
}
const target = path.join(postsDir, `${slug}.md`);
if (fs.existsSync(target)) throw new Error(`文章已存在: ${path.relative(root, target)}`);
const pubDate = now.toISOString();
const frontmatter = [
'---',
`title: ${JSON.stringify(title)}`,
`pubDate: ${JSON.stringify(pubDate)}`,
...(cover ? [`cover: ${JSON.stringify(cover)}`] : []),
'categories: []',
'tags: []',
'draft: true',
'---',
'',
''
].join('\n');
fs.mkdirSync(postsDir, { recursive: true });
fs.writeFileSync(target, frontmatter, 'utf8');
console.log(`已新建文章: ${path.relative(root, target)}`);
} catch (error) {
console.error(`新建文章失败: ${error.message}`);
process.exitCode = 1;
} finally {
rl.close();
}
}
main().catch((error) => {
console.error(`新建文章失败: ${error.message}`);
process.exitCode = 1;
});