Skip to content

Commit

Permalink
Add a script to generate unreleased changelog (#10988)
Browse files Browse the repository at this point in the history
* Add script to generate changelog

* Fix by prettier

* Use CHANGELOG_CATEGORIES from utils

* Fix filename

* Fix typo

* Add comments

* Replace codeblock

* Fix typo

* Avoid to use top-level await

* Use enquirer

* Address review

* Use meriyah for mjs

* Add comment

* Update usase comment

* Update CONTRIBUTING.md
  • Loading branch information
sosukesuzuki committed Aug 13, 2021
1 parent c5e33a3 commit 83e5ebe
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 22 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ To debug Prettier locally, you can either debug it in Node (recommended) or the

The project uses ESLint for linting and Prettier for formatting. If your editor isn't set up to work with them, you can lint and format all files from the command line using `yarn fix`.

After opening a PR, describe your changes in a file in the `changelog_unreleased` directory following the template [`changelog_unreleased/TEMPLATE.md`](changelog_unreleased/TEMPLATE.md) and commit this file to your PR.
After opening a PR, describe your changes in a file in the `changelog_unreleased` directory following the template [`changelog_unreleased/TEMPLATE.md`](changelog_unreleased/TEMPLATE.md) and commit this file to your PR. You can use `./scripts/generate-changelog.mjs` to create changelog file. Please see comments of the script file for usage.

Take a look at [`commands.md`](commands.md) and, if you know Haskell, check out [Wadler's paper](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf) to understand how Prettier works.

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"core-js": "3.16.0",
"cross-env": "7.0.3",
"cspell": "5.6.6",
"enquirer": "2.3.6",
"eslint": "7.32.0",
"eslint-config-prettier": "8.3.0",
"eslint-formatter-friendly": "7.0.0",
Expand All @@ -121,6 +122,7 @@
"jest-snapshot-serializer-raw": "1.2.0",
"jest-watch-typeahead": "0.6.4",
"node-actionlint": "1.1.0",
"node-fetch": "2.6.1",
"npm-run-all": "4.1.5",
"path-browserify": "1.0.1",
"prettier": "2.3.2",
Expand Down
191 changes: 191 additions & 0 deletions scripts/generate-changelog.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#!/usr/bin/env node

/**
* When you run the script, enter the number and category of the Pull Request at the prompt.
* Get the PR title and author name via the GitHub API and create a file in ./changelog_unreleased
*
* $ node ./scripts/generate-changelog.mjs
* ✔ Input your Pull Request number: · 10961
* ✔ Input category of your Pull Request: · typescript
*/

import fs from "node:fs/promises";
import path from "node:path";
import fetch from "node-fetch";
import createEsmUtils from "esm-utils";
import enquirer from "enquirer";
import { CHANGELOG_CATEGORIES } from "./utils/changelog-categories.mjs";

const { __dirname } = createEsmUtils(import.meta);

(async () => {
const prNumberPrompt = new enquirer.NumberPrompt({
message: "Input your Pull Request number:",
});
const prNumber = await prNumberPrompt.run();

const categoryPrompt = new enquirer.AutoComplete({
message: "Input category of your Pull Request:",
limit: CHANGELOG_CATEGORIES.length,
// The array passed to `choices` will be broken, so copy it.
choices: [...CHANGELOG_CATEGORIES],
});
const category = (await categoryPrompt.run()).trim();

if (!prNumber || !category) {
throw new Error("Two args are required.");
}
assertCategory(category);

const { title, user } = await getPr(prNumber);

const newChangelog = await createChangelog(title, user, prNumber, category);

await addNewChangelog(prNumber, category, newChangelog);

console.log("Done");
})();

/**
* @param {number} prNumber
* @returns {Promise<{ title: string; user: string }>}
*/
async function getPr(prNumber) {
// https://docs.github.com/en/rest/reference/pulls#get-a-pull-request
const url = `https://api.github.com/repos/prettier/prettier/pulls/${prNumber}`;
const response = await fetch(url, {
Headers: {
"Content-Type": "application/json",
Accept: "application/vnd.github.v3+json",
},
});
if (!response.ok) {
if (response.status === 404) {
throw new Error(`Pull Request #${prNumber} not found.`);
}
throw new Error(response);
}
const { title, user } = await response.json();
return {
title,
user: user.login,
};
}

/**
* @param {number} prNumber
* @param {string} category
* @param {string} newChangelog
* @returns {Promise<void>}
*/
async function addNewChangelog(prNumber, category, newChangelog) {
const newChangelogPath = path.resolve(
__dirname,
`../changelog_unreleased/${category}/${prNumber}.md`
);
await fs.writeFile(newChangelogPath, newChangelog);
}

/**
* @param {string} title
* @param {string} user
* @param {number} prNumber
* @param {string} string
* @returns {string}
*/
async function createChangelog(title, user, prNumber, category) {
const changelogTemplatePath = path.resolve(
__dirname,
"../changelog_unreleased/TEMPLATE.md"
);
const changelogTemplate = await fs.readFile(changelogTemplatePath, "utf-8");

const titlePart = "Title";
const prNumberPart = "#XXXX";
const userPart = "@user";
const codeBlockPart = "```jsx\n";
const inputCommentPart = "// Input\n";
const stableCommentPart = "// Prettier stable\n";
const mainCommentPart = "// Prettier main\n";

const syntax = getSyntaxFromCategory(category);

return changelogTemplate
.replace(titlePart, title)
.replace(prNumberPart, `#${prNumber}`)
.replace(userPart, `@${user}`)
.replace(codeBlockPart, `\`\`\`${syntax}\n`)
.replace(inputCommentPart, getCommentForSyntax(syntax, "Input") + "\n")
.replace(
stableCommentPart,
getCommentForSyntax(syntax, "Prettier stable") + "\n"
)
.replace(
mainCommentPart,
getCommentForSyntax(syntax, "Prettier main") + "\n"
);
}

/**
* @param {string} category
* @returns {string}
*/
function getSyntaxFromCategory(category) {
switch (category) {
case "angular":
case "html":
case "lwc":
return "html";
case "cli":
return "sh";
case "graphql":
return "gql";
case "handlebars":
return "hbs";
case "json":
return "jsonc";
case "markdown":
return "md";
case "mdx":
return "mdx";
case "flow":
case "javascript":
case "api":
return "jsx";
case "typescript":
return "tsx";
default:
return category;
}
}

/**
* @param {string} syntax
* @param {string} comment
* @returns {string}
*/
function getCommentForSyntax(syntax, comment) {
switch (syntax) {
case "md":
case "mdx":
case "html":
return `<!-- ${comment} -->`;
case "sh":
case "gql":
return `# ${comment}`;
case "hbs":
return `{{! ${comment} }}`;
default:
return `// ${comment}`;
}
}

/**
* @param {unknown}
* @returns {void}
*/
function assertCategory(category) {
if (!CHANGELOG_CATEGORIES.includes(category)) {
throw new Error(`${category} is invalid category`);
}
}
21 changes: 1 addition & 20 deletions scripts/lint-changelog.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,13 @@ import path from "node:path";
import fs from "node:fs";
import { outdent } from "outdent";
import createEsmUtils from "esm-utils";
import { CHANGELOG_CATEGORIES } from "./utils/changelog-categories.mjs";

const { __dirname } = createEsmUtils(import.meta);
const CHANGELOG_DIR = "changelog_unreleased";
const TEMPLATE_FILE = "TEMPLATE.md";
const BLOG_POST_INTRO_TEMPLATE_FILE = "BLOG_POST_INTRO_TEMPLATE.md";
const BLOG_POST_INTRO_FILE = "blog-post-intro.md";
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);
Expand Down
20 changes: 20 additions & 0 deletions scripts/utils/changelog-categories.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const CHANGELOG_CATEGORIES = [
"angular",
"api",
"cli",
"css",
"flow",
"graphql",
"handlebars",
"html",
"javascript",
"json",
"less",
"lwc",
"markdown",
"mdx",
"scss",
"typescript",
"vue",
"yaml",
];
7 changes: 6 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2865,7 +2865,7 @@ enhanced-resolve@^5.8.0:
graceful-fs "^4.2.4"
tapable "^2.2.0"

enquirer@^2.3.5:
enquirer@2.3.6, enquirer@^2.3.5:
version "2.3.6"
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
Expand Down Expand Up @@ -4969,6 +4969,11 @@ node-actionlint@1.1.0:
chalk "^4.1.1"
fast-glob "^3.2.7"

node-fetch@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==

node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
Expand Down

0 comments on commit 83e5ebe

Please sign in to comment.