Skip to content

Commit

Permalink
Update ESLint configuration and packages (#118)
Browse files Browse the repository at this point in the history
The ESLint configuration and all related dependencies have been
updated. Most changes are related to the JSDoc lint rules; many JSDoc
blocks were added or adjusted. All other lint changes were applied
automatically using `yarn lint:fix`.
  • Loading branch information
Gudahtt authored Oct 28, 2022
1 parent 391806b commit 53a6d42
Show file tree
Hide file tree
Showing 12 changed files with 976 additions and 582 deletions.
2 changes: 0 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ module.exports = {
files: ['*.ts'],
extends: ['@metamask/eslint-config-typescript'],
rules: {
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34960
'node/prefer-global/url': 'off',
},
Expand Down
23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,24 @@
},
"devDependencies": {
"@lavamoat/allow-scripts": "^1.0.6",
"@metamask/eslint-config": "^7.0.1",
"@metamask/eslint-config-jest": "^7.0.0",
"@metamask/eslint-config-nodejs": "^7.0.1",
"@metamask/eslint-config-typescript": "^7.0.1",
"@metamask/eslint-config": "^10.0.0",
"@metamask/eslint-config-jest": "^10.0.0",
"@metamask/eslint-config-nodejs": "^10.0.0",
"@metamask/eslint-config-typescript": "^10.0.0",
"@types/cross-spawn": "^6.0.2",
"@types/diff": "^5.0.0",
"@types/jest": "^26.0.23",
"@types/semver": "^7.3.6",
"@types/yargs": "^16.0.1",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"eslint": "^7.23.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.3.4",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/parser": "^5.41.0",
"eslint": "^8.26.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.8.2",
"eslint-plugin-jsdoc": "^39.3.25",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^26.4.2",
"outdent": "^0.8.0",
"prettier": "^2.2.1",
Expand Down
84 changes: 70 additions & 14 deletions src/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const changelogDescription = `All notable changes to this project will be docume
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).`;

interface ReleaseMetadata {
type ReleaseMetadata = {
/**
* The version of the current release.
*/
Expand All @@ -29,7 +29,7 @@ interface ReleaseMetadata {
* The status of the release (e.g. 'WITHDRAWN', 'DEPRECATED')
*/
status?: string;
}
};

/**
* Release changes, organized by category.
Expand All @@ -45,6 +45,13 @@ type ChangelogChanges = Record<Version, ReleaseChanges> & {

// Stringification helpers

/**
* Stringify a changelog category section.
*
* @param category - The title of the changelog category.
* @param changes - The changes included in this category.
* @returns The stringified category section.
*/
function stringifyCategory(category: ChangeCategory, changes: string[]) {
const categoryHeader = `### ${category}`;
if (changes.length === 0) {
Expand All @@ -56,6 +63,16 @@ function stringifyCategory(category: ChangeCategory, changes: string[]) {
return `${categoryHeader}\n${changeDescriptions}`;
}

/**
* Stringify a changelog release section.
*
* @param version - The release version.
* @param categories - The categories of changes included in this release.
* @param options - Additional release options.
* @param options.date - The date of the release.
* @param options.status - The status of the release (e.g., "DEPRECATED").
* @returns The stringified release section.
*/
function stringifyRelease(
version: Version | typeof unreleased,
categories: ReleaseChanges,
Expand All @@ -77,6 +94,13 @@ function stringifyRelease(
return `${releaseHeader}\n${categorizedChanges}`;
}

/**
* Stringify a set of changelog release sections.
*
* @param releases - The releases to stringify.
* @param changes - The set of changes to include, organized by release.
* @returns The stringified set of release sections.
*/
function stringifyReleases(
releases: ReleaseMetadata[],
changes: ChangelogChanges,
Expand All @@ -93,18 +117,49 @@ function stringifyReleases(
return [stringifiedUnreleased, ...stringifiedReleases].join('\n\n');
}

/**
* Return the given URL with a trailing slash. It is returned unaltered if it
* already has a trailing slash.
*
* @param url - The URL string.
* @returns The URL string with a trailing slash.
*/
function withTrailingSlash(url: string) {
return url.endsWith('/') ? url : `${url}/`;
}

/**
* Get the GitHub URL for comparing two git commits.
*
* @param repoUrl - The URL for the GitHub repository.
* @param firstRef - A reference (e.g., commit hash, tag, etc.) to the first commit to compare.
* @param secondRef - A reference (e.g., commit hash, tag, etc.) to the second commit to compare.
* @returns The comparison URL for the two given commits.
*/
function getCompareUrl(repoUrl: string, firstRef: string, secondRef: string) {
return `${withTrailingSlash(repoUrl)}compare/${firstRef}...${secondRef}`;
}

/**
* Get a GitHub tag URL.
*
* @param repoUrl - The URL for the GitHub repository.
* @param tag - The tag name.
* @returns The URL for the given tag.
*/
function getTagUrl(repoUrl: string, tag: string) {
return `${withTrailingSlash(repoUrl)}releases/tag/${tag}`;
}

/**
* Get a stringified list of link definitions for the given set of releases. The first release is
* linked to the corresponding tag, and each subsequent release is linked to a comparison with the
* previous release.
*
* @param repoUrl - The URL for the GitHub repository.
* @param releases - The releases to generate link definitions for.
* @returns The stringified release link definitions.
*/
function stringifyLinkReferenceDefinitions(
repoUrl: string,
releases: ReleaseMetadata[],
Expand Down Expand Up @@ -164,19 +219,19 @@ function stringifyLinkReferenceDefinitions(
}`;
}

interface AddReleaseOptions {
type AddReleaseOptions = {
addToStart?: boolean;
date?: string;
status?: string;
version: Version;
}
};

interface AddChangeOptions {
type AddChangeOptions = {
addToStart?: boolean;
category: ChangeCategory;
description: string;
version?: Version;
}
};

/**
* A changelog that complies with the
Expand All @@ -195,10 +250,10 @@ export default class Changelog {
private _repoUrl: string;

/**
* Construct an empty changelog
* Construct an empty changelog.
*
* @param options
* @param options.repoUrl - The GitHub repository URL for the current project
* @param options - Changelog options.
* @param options.repoUrl - The GitHub repository URL for the current project.
*/
constructor({ repoUrl }: { repoUrl: string }) {
this._releases = [];
Expand All @@ -209,15 +264,15 @@ export default class Changelog {
/**
* Add a release to the changelog.
*
* @param options
* @param options - Release options.
* @param options.addToStart - Determines whether the change is added to the
* top or bottom of the list of changes in this category. This defaults to
* `true` because changes should be in reverse-chronological order. This
* should be set to `false` when parsing a changelog top-to-bottom.
* @param options.date - An ISO-8601 formatted date, representing the release
* date.
* @param options.status - The status of the release (e.g. 'WITHDRAWN',
* 'DEPRECATED')
* @param options.status - The status of the release (e.g., 'WITHDRAWN',
* 'DEPRECATED').
* @param options.version - The version of the current release, which should
* be a [SemVer](https://semver.org/spec/v2.0.0.html)-compatible version.
*/
Expand All @@ -242,7 +297,7 @@ export default class Changelog {
/**
* Add a change to the changelog.
*
* @param options
* @param options - Change options.
* @param options.addToStart - Determines whether the change is added to the
* top or bottom of the list of changes in this category. This defaults to
* `true` because changes should be in reverse-chronological order. This
Expand Down Expand Up @@ -275,6 +330,7 @@ export default class Changelog {
if (!release[category]) {
release[category] = [];
}

if (addToStart) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
release[category]!.unshift(description);
Expand Down Expand Up @@ -361,7 +417,7 @@ export default class Changelog {
}

/**
* Gets all changes that have not yet been released
* Gets all changes that have not yet been released.
*
* @returns The changes that have not yet been released.
*/
Expand Down
76 changes: 67 additions & 9 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ formatting is correct. Verification of the contents is left for manual review.`;
// eslint-disable-next-line node/no-process-env
const npmPackageVersion = process.env.npm_package_version;

/**
* Determine whether the given URL is valid.
*
* @param proposedUrl - The URL to validate.
* @returns True if the URL is valid, false otherwise.
*/
function isValidUrl(proposedUrl: string) {
try {
// eslint-disable-next-line no-new
Expand All @@ -50,32 +56,59 @@ function isValidUrl(proposedUrl: string) {
}
}

/**
* Exit the process with the given error.
*
* @param errorMessage - The error message to exit with.
*/
function exitWithError(errorMessage: string) {
console.error(errorMessage);
process.exitCode = 1;
}

/**
* Read the changelog contents from the filesystem.
*
* @param changelogPath - The path to the changelog file.
* @returns The changelog contents.
*/
async function readChangelog(changelogPath: string) {
return await fs.readFile(changelogPath, {
encoding: 'utf8',
});
}

/**
* Save the changelog to the filesystem.
*
* @param changelogPath - The path to the changelog file.
* @param newChangelogContent - The new changelog contents to save.
*/
async function saveChangelog(
changelogPath: string,
newChangelogContent: string,
) {
await fs.writeFile(changelogPath, newChangelogContent);
}

interface UpdateOptions {
type UpdateOptions = {
changelogPath: string;
currentVersion?: Version;
repoUrl: string;
isReleaseCandidate: boolean;
projectRootDirectory?: string;
}

};

/**
* Update the changelog.
*
* @param options - Update options.
* @param options.changelogPath - The path to the changelog file.
* @param options.currentVersion - The current project version.
* @param options.isReleaseCandidate - Whether the current branch is a release candidate or not.
* @param options.repoUrl - The GitHub repository URL for the current project.
* @param options.projectRootDirectory - The root project directory.
*/
async function update({
changelogPath,
currentVersion,
Expand All @@ -101,13 +134,22 @@ async function update({
}
}

interface ValidateOptions {
type ValidateOptions = {
changelogPath: string;
currentVersion?: Version;
isReleaseCandidate: boolean;
repoUrl: string;
}

};

/**
* Validate the changelog.
*
* @param options - Validation options.
* @param options.changelogPath - The path to the changelog file.
* @param options.currentVersion - The current project version.
* @param options.isReleaseCandidate - Whether the current branch is a release candidate or not.
* @param options.repoUrl - The GitHub repository URL for the current project.
*/
async function validate({
changelogPath,
currentVersion,
Expand Down Expand Up @@ -137,11 +179,18 @@ async function validate({
}
}

interface InitOptions {
type InitOptions = {
changelogPath: string;
repoUrl: string;
}

};

/**
* Create a new empty changelog.
*
* @param options - Initialization options.
* @param options.changelogPath - The path to the changelog file.
* @param options.repoUrl - The GitHub repository URL for the current project.
*/
async function init({ changelogPath, repoUrl }: InitOptions) {
const changelogContent = await createEmptyChangelog({ repoUrl });
await saveChangelog(changelogPath, changelogContent);
Expand All @@ -152,6 +201,12 @@ look for changes since the last release (defaults to the entire repository at \
the current working directory), and where the changelog path is resolved from \
(defaults to the current working directory).`;

/**
* Configure options that are common to all commands.
*
* @param _yargs - The yargs instance to configure.
* @returns A Yargs instance configured with all common commands.
*/
function configureCommonCommandOptions(_yargs: Argv) {
return _yargs
.option('file', {
Expand All @@ -170,6 +225,9 @@ function configureCommonCommandOptions(_yargs: Argv) {
});
}

/**
* The entrypoint for the auto-changelog CLI.
*/
async function main() {
const { argv } = yargs(hideBin(process.argv))
.command(
Expand Down
Loading

0 comments on commit 53a6d42

Please sign in to comment.