Files
test-repo/scripts/lint-changelog.js
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

148 lines
4.0 KiB
JavaScript
Raw Normal View History

2020-03-21 22:24:23 +02:00
#!/usr/bin/env node
2020-01-16 01:25:55 +08:00
"use strict";
const fs = require("fs");
const path = require("path");
2020-11-14 12:45:30 +08:00
const { outdent } = require("outdent");
2020-01-16 01:25:55 +08:00
const CHANGELOG_DIR = "changelog_unreleased";
const TEMPLATE_FILE = "TEMPLATE.md";
const BLOG_POST_INTRO_TEMPLATE_FILE = "BLOG_POST_INTRO_TEMPLATE.md";
2020-03-21 22:24:23 +02:00
const BLOG_POST_INTRO_FILE = "blog-post-intro.md";
2020-01-16 01:25:55 +08:00
const CHANGELOG_CATEGORIES = [
"angular",
"api",
"cli",
"css",
"flow",
"graphql",
"handlebars",
"html",
"javascript",
"json",
"less",
"lwc",
"markdown",
"mdx",
"scss",
"typescript",
"vue",
"yaml",
];
const CHANGELOG_ROOT = path.join(__dirname, `../${CHANGELOG_DIR}`);
const showErrorMessage = (message) => {
console.error(message);
process.exitCode = 1;
};
const files = fs.readdirSync(CHANGELOG_ROOT);
for (const file of files) {
2020-03-21 22:24:23 +02:00
if (
file !== TEMPLATE_FILE &&
file !== BLOG_POST_INTRO_FILE &&
file !== BLOG_POST_INTRO_TEMPLATE_FILE &&
2020-03-21 22:24:23 +02:00
!CHANGELOG_CATEGORIES.includes(file)
) {
2020-01-16 01:25:55 +08:00
showErrorMessage(`Please remove "${file}" from "${CHANGELOG_DIR}".`);
}
}
2020-03-21 22:24:23 +02:00
for (const file of [
TEMPLATE_FILE,
BLOG_POST_INTRO_TEMPLATE_FILE,
2020-03-21 22:24:23 +02:00
...CHANGELOG_CATEGORIES,
]) {
2020-01-16 01:25:55 +08:00
if (!files.includes(file)) {
showErrorMessage(`Please don't remove "${file}" from "${CHANGELOG_DIR}".`);
}
}
const authorRegex = /by @[\w-]+|by \[@([\w-]+)]\(https:\/\/github\.com\/\1\)/;
const titleRegex = /^#{4} (.*?)\((#\d{4,}|\[#\d{4,}])/;
2020-01-16 01:25:55 +08:00
const template = fs.readFileSync(
path.join(CHANGELOG_ROOT, TEMPLATE_FILE),
"utf8"
);
2020-04-10 21:20:40 +08:00
const [templateComment] = template.match(/<!--[\S\s]*?-->/);
2020-01-16 01:25:55 +08:00
const [templateAuthorLink] = template.match(authorRegex);
2020-11-14 12:45:30 +08:00
const checkedFiles = new Map();
2020-01-16 01:25:55 +08:00
for (const category of CHANGELOG_CATEGORIES) {
const files = fs.readdirSync(path.join(CHANGELOG_ROOT, category));
if (!files.includes(".gitkeep")) {
showErrorMessage(
`Please don't remove ".gitkeep" from "${CHANGELOG_DIR}/${category}".`
);
}
for (const prFile of files) {
if (prFile === ".gitkeep") {
continue;
}
const match = prFile.match(/^pr-(\d{4,})\.md$/);
const displayPath = `${CHANGELOG_DIR}/${category}/${prFile}`;
if (!match) {
showErrorMessage(
`[${displayPath}]: Filename is not in form of "pr-{PR_NUMBER}.md".`
);
continue;
}
const [, prNumber] = match;
2020-11-14 12:45:30 +08:00
const prLink = `#${prNumber}`;
if (checkedFiles.has(prNumber)) {
showErrorMessage(
outdent`
Duplicate files for ${prLink} found.
- ${checkedFiles.get(prNumber)}
- ${displayPath}
`
);
}
checkedFiles.set(prNumber, displayPath);
2020-01-16 01:25:55 +08:00
const content = fs.readFileSync(
path.join(CHANGELOG_DIR, category, prFile),
"utf8"
);
if (!content.includes(prLink)) {
showErrorMessage(`[${displayPath}]: PR link "${prLink}" is missing.`);
}
if (!authorRegex.test(content)) {
showErrorMessage(`[${displayPath}]: Author link is missing.`);
}
if (content.includes(templateComment)) {
showErrorMessage(
`[${displayPath}]: Please remove template comments at top.`
);
}
if (content.includes(templateAuthorLink)) {
showErrorMessage(
`[${displayPath}]: Please change author link to your github account.`
);
}
if (!content.startsWith("#### ")) {
showErrorMessage(`[${displayPath}]: Please use h4 ("####") for title.`);
2020-01-16 01:25:55 +08:00
}
const titleMatch = content.match(titleRegex);
if (!titleMatch) {
showErrorMessage(`[${displayPath}]: Something wrong in title.`);
continue;
}
const [, title] = titleMatch;
const categoryInTitle = title.split(":").shift().trim();
if (
[...CHANGELOG_CATEGORIES, "js"].includes(categoryInTitle.toLowerCase())
) {
2020-01-16 01:25:55 +08:00
showErrorMessage(
`[${displayPath}]: Please remove "${categoryInTitle}:" in title.`
);
}
if (!title.endsWith(" ") || title.length - title.trimEnd().length !== 1) {
showErrorMessage(
`[${displayPath}]: Please put one space between title and PR link.`
);
}
}
}