静态网站博客文章生成器
如果您使用Gatsby或Gridsome之类的博客平台,则没有用于创建新博客文章的用户界面。创建过程通常首先根据文章格式创建一个或一系列目录,然后创建一个新的 Markdown 文件。
接下来,你需要添加一些用于描述文章的前置内容。这通常取决于你的博客需求,但也可能包含标题、slug、作者、日期等等。
这变得乏味,更糟糕的是,我不喜欢一遍又一遍地复制粘贴项目。事实上,每当我发现自己复制粘贴了好几次,可能就该找个解决办法了。
在本文中,我将向您介绍我编写的博客文章生成器。这个脚本已经迭代了好几次,而且我确实从其他做过类似事情的人那里学到了一些技巧。
创建并初始化脚本
你首先要决定的是这个脚本要放在哪里。这个问题其实没有绝对的正确或错误,所以我在根目录下创建了一个名为 scripts 的文件夹。我想这个文件夹可以用来存放我可能需要的脚本,如果以后找到更好的存放位置,我就可以重构它们。顺便说一句,每次我写代码时,我都会这样做,找到一种快速完成代码、使其运行良好并在之后使其更加美观的方法。
我要做的第一件事是newpost.js
在我的脚本文件夹中创建一个名为 的脚本。接下来,我们需要确定这个脚本的结构。在某些情况下,我们可以直接自上而下地编写,但在本例中,这种方法行不通。
将代码包装在一个函数中并执行该函数是很常见的做法,我们可以通过几种方式来实现。我们可以编写一个包含所有逻辑的普通函数,然后在脚本末尾调用该函数来启动。
function newPost() {
console.log("create new post...");
}
newPost();
但是,如果你只需要调用函数,那么有一个更好的方法。你可以编写所谓的“自执行函数”,也称为“立即调用函数表达式”(IIFE)。为了实现这一点
(function newPost() {
console.log("create new post...");
})();
你也可以使用箭头函数来写这个
(() => {
console.log("create new post...");
})();
和任何普通函数一样,如果要执行异步任务,可以使用async
关键字。在本例中,我们将引入一个库来帮助我们编写命令行应用程序,因此我们将从以下内容开始。
(async () => {
console.log("create new post...");
})();
在测试之前,你需要添加一个新脚本到你的package.json
"newpost": "node ./scripts/newpost.js"
此时,我会对脚本进行快速测试,以确保一切按预期运行。
npm run newpost
接受用户输入
现在脚本已经准备好了,是时候开始构建一些功能了。首先,你需要询问一些关于新帖子的详细信息。根据每个人的需求,这显然会有所不同,但以下是我想要询问的数据以及我可以推断的数据。
- 标题
- 摘抄
- 标签
这些是我可以根据用户上面输入的内容或脚本运行时间确定的项目列表。
- 蛞蝓
- 日期
- 作者
这就是我真正需要的。正如我之前所说,这对你来说可能有所不同,但你可以相应地进行调整。
询问者
为了方便用户输入,我们将安装 inquirer 软件包。Inquirer 是一系列常用的交互式命令行用户界面的集合。Inquirer 可以简化以下流程:
- 提供 错误反馈
- 提出问题
- 解析 输入
- 验证 答案
- 管理 分层提示
首先,您可以通过运行以下命令将其安装为开发依赖项:
npm install -D inquirer
并在你的脚本中要求它
const inquirer = require("inquirer");
这个包的功能远不止我们想用到的,所以如果有机会,可以查看一下它的文档。首先你需要做的就是获取进程参数。
const args = process.argv;
process.argv 属性返回一个数组,其中包含启动 Node.js 进程时传递的命令行参数。第一个元素是 process.execPath。如果需要访问 argv[0] 的原始值,请参阅 process.argv0。第二个元素是正在执行的 JavaScript 文件的路径。其余元素是任何其他命令行参数。
如果您愿意,您可以检查用户提供的参数是否存在并将其纳入,但在这个例子中,我要说的是,只要没有自定义参数,我们就会向用户询问一些数据。
if (args.length < 3) {
const { title, excerpt, tags } = await inquirer.prompt([
{
type: "input",
name: "title",
message: "Post Title:"
},
{
type: "input",
name: "excerpt",
message: "Post Excerpt:"
},
{
type: "input",
name: "tags",
message: "Tags (comma separated):"
}
]);
} else {
log(error("Please don't provide any arguments to the new post generator"));
}
我们稍后会讨论日志行,但现在我们先集中讨论一下 Inquirer。在设置脚本时,我说过需要将自执行函数标记为异步,这就是原因。它inquirer.prompt
返回一个 Promise,所以我们在这里使用 await。
我们要求用户提供 3 种不同的数据
- 标题
- 摘抄
- 标签
我们可以创建一个变量来保存响应,但我们将响应解构为 3 个变量。
const { title, excerpt, tags } = ...
prompt 方法传入的数组参数中的每个对象都是一个问题。在我们的示例中,我们要求输入一些简单的内容,定义问题的名称以及应该向用户显示的消息内容。同样,这些内容可能会更加复杂,因此如果您有更具体的需求,请查看文档。
现在我们已经获得了用户的答案,我们可以使用这些答案来构建我们的新帖子。
创建帖子目录
在我们开始创建任何文件夹或文件之前,您需要做一些额外的设置。
邮政弹头
现在我已经有了帖子的标题,我需要创建一个 slug。slug 是标题的 URL 友好版本,有助于 SEO。如果我的帖子标题是“我的第一篇帖子”,那么 slug 应该是“my-first-post”。
在这个简单的例子中,我们可能可以自己处理,但实际操作起来可能会相当复杂。为此,我将安装一个名为 slugify 的包,并引入它,然后创建一个 slug。
const slugify = require("slugify");
const slug = slugify(title);
文件夹和 URL 格式
我的每篇博客文章都采用以下格式
https://www.danvega.dev/{year}/{month}/{day}/{slug}
到目前为止,我们已经有了 slug,但现在我需要提取日期的一些部分。由于我们现在正在运行生成器,我假设我们想在今天发布这个,并以此作为日期的基础。你可能会认为这会更容易,但处理日期在任何语言中都是非常不容易的事情之一。
const createdOn = new Date();
const year = createdOn.getFullYear();
const month = `${createdOn.getMonth() + 1 < 10 ? "0" : ""}${createdOn.getMonth() + 1}`;
const day = `${createdOn.getDate() < 10 ? "0" : ""}${createdOn.getDate()}`;
现在我们有了日期部分,我们可以创建一个名为的变量,blogPostFolder
该变量将是创建新 markdown 文件的文件夹的路径。
const blogPostFolder = `./blog/${year}/${month}/${day}`;
最后,我要清理标签并将它们变成列表。
const tagsList = tags.split(",").map(t => t.trim());
创建文件和文件夹
现在你已经设置好了所有变量,是时候开始创建一些文件和文件夹了。为此,你需要引入文件系统模块。
const fs = require("fs");
在节点中创建递归目录
我们已经为博客文章文件夹位置创建了一个变量,所以就从这里开始吧。首先要检查它是否已经存在,因为如果存在,我们就不需要创建它了。对我来说,这种情况几乎永远不会发生,因为每周完成一篇博文对我来说已经够难的了,但为了万一哪天我突然变得雄心勃勃,我们还是谨慎行事吧。
if (!fs.existsSync(blogPostFolder)) {
// create directory
}
这是比较棘手的部分,可能会让一些人犯难,我第一次就遇到了。如果你只是创建一个目录,那么mkdirSync不带任何选项就可以正常工作。我的意思是,假设你已经blog/2019/04/
创建了文件夹,只需要创建 day24
文件夹,那么这个方法就可以正常工作。如果你需要递归(超过 1 层)创建文件夹,则需要为该方法传递一个选项mkdirSync
。如果你感兴趣的话,我写了一篇文章对此进行了更深入的介绍。
if (!fs.existsSync(blogPostFolder)) {
fs.mkdirSync(blogPostFolder, {
recursive: true
});
}
前言
在每个 Markdown 文件中,我们使用一种叫做 front matter 的东西来定义博客文章。这些是 YAML 声明块内的变量。
---
key: value
---
为了帮助我们创建前言,我们将引入一个名为json-to-pretty-yaml的包。
const jsToYaml = require("json-to-pretty-yaml");
const yaml = jsToYaml.stringify({
slug,
title,
date: createdOn.toISOString(),
published: false,
excerpt: excerpt,
author: "Dan Vega",
tags: tagsList,
cover: ""
});
Markdown
有了 front matter,现在就可以创建 Markdown 文件了。我会引入一个名为 prettier 的包来格式化 Markdown 文件,让它看起来更漂亮 ☺️
const prettier = require("prettier");
const markdown = prettier.format(`---\n${yaml}\n---\n`, {
parser: "markdown",
singleQuote: true
});
现在您已经有了文件的内容,剩下要做的就是创建文件。您将再次使用文件系统模块,但这次您将使用writeFileSync
方法。您将把此文件写入之前创建的博客文章文件夹,slug 将是文件的名称,文件扩展名为md
。
fs.writeFileSync(`${blogPostFolder}/${slug}.md`, markdown);
log(success(`Post ${title} was created successfully`));
日志记录
为了给我的终端日志添加一些样式,我使用了一个名为 chalk 的包。要在本地安装它,请运行以下命令:
npm install -D chalk
然后将以下变量声明添加到脚本的顶部。
const chalk = require("chalk");
const log = console.log;
const error = chalk.bold.red;
const success = chalk.bold.green.inverse;
这使得我在想要记录错误或成功时可以编写以下语句,并拥有一些时尚的日志语句。
log(success(`Post ${title} was created successfully`));
log(error("Please don't provide any arguments to the new post generator"));
结论
这里的计划是向你展示如何创建你自己的博客文章生成器,但我希望你在这里学到更多。当你在构建类似的项目时,如果你发现需要某些东西,你可以自己创建。
如果您写过类似的代码,或者解决了项目中遇到的问题,我很乐意听听您的分享。如果您想查看我的博客文章生成器的源码以及整个网站的代码,可以在这里查看。
仅供参考 - 我使用这个脚本创建了你正在阅读的帖子🤯
一如既往……
快乐编码
丹
本文最初发布在我的博客https://www.danvega.dev/blog上。如果您觉得这篇文章有趣,请考虑订阅我的新闻通讯或在Twitter上关注我。
鏂囩珷鏉ユ簮锛�https://dev.to/therealdanvega/static-site-blog-post-generator-3ma6