-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a script to generate unreleased changelog (#10988)
* 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
1 parent
c5e33a3
commit 83e5ebe
Showing
6 changed files
with
221 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters