Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

commitlint,docs: start using FP patterns #174

Merged
merged 10 commits into from
Sep 10, 2024
112 changes: 53 additions & 59 deletions commitlint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,19 @@ let bodyMaxLineLength = 64;
let headerMaxLineLength = 50;
let footerMaxLineLength = 150;

function notNullStringErrorMessage(stringType: string): string {
return `This is unexpected because ${stringType} should never be null`;
function notStringErrorMessage(variableName: string): string {
return `This is unexpected because ${variableName} should have been a string`;
}

function extractStringFromCommitlintParam(
paramName: string,
variable: any
): string {
let str = Helpers.assertNotNone(
Helpers.convertAnyToString(variable),
notStringErrorMessage(paramName)
);
return str;
}

export default {
Expand Down Expand Up @@ -64,27 +75,21 @@ export default {
{
rules: {
"body-prose": ({ raw }: { raw: any }) => {
let rawStr = Helpers.assertNotNull(
Helpers.convertAnyToString(raw, "raw"),
notNullStringErrorMessage("raw")
);
let rawStr = extractStringFromCommitlintParam("raw", raw);

return Plugins.bodyProse(rawStr);
},

"commit-hash-alone": ({ raw }: { raw: any }) => {
let rawStr = Helpers.assertNotNull(
Helpers.convertAnyToString(raw, "raw"),
notNullStringErrorMessage("raw")
);
let rawStr = extractStringFromCommitlintParam("raw", raw);

return Plugins.commitHashAlone(rawStr);
},

"empty-wip": ({ header }: { header: any }) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = extractStringFromCommitlintParam(
"header",
header
);

return Plugins.emptyWip(headerStr);
Expand All @@ -95,9 +100,9 @@ export default {
_: any,
maxLineLength: number
) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = extractStringFromCommitlintParam(
"header",
header
);

return Plugins.headerMaxLengthWithSuggestions(
Expand All @@ -107,15 +112,12 @@ export default {
},

"footer-notes-misplacement": ({ body }: { body: any }) => {
let bodyStr = Helpers.convertAnyToString(body, "body");
return Plugins.footerNotesMisplacement(bodyStr);
let maybeBody = Helpers.convertAnyToString(body);
return Plugins.footerNotesMisplacement(maybeBody);
},

"footer-refs-validity": ({ raw }: { raw: any }) => {
let rawStr = Helpers.assertNotNull(
Helpers.convertAnyToString(raw, "raw"),
notNullStringErrorMessage("raw")
);
let rawStr = extractStringFromCommitlintParam("raw", raw);

return Plugins.footerRefsValidity(rawStr);
},
Expand All @@ -125,72 +127,67 @@ export default {
}: {
header: any;
}) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = extractStringFromCommitlintParam(
"header",
header
);

return Plugins.preferSlashOverBackslash(headerStr);
},

"proper-issue-refs": ({ raw }: { raw: any }) => {
let rawStr = Helpers.assertNotNull(
Helpers.convertAnyToString(raw, "raw"),
notNullStringErrorMessage("raw")
);
let rawStr = extractStringFromCommitlintParam("raw", raw);

return Plugins.properIssueRefs(rawStr);
},

"title-uppercase": ({ header }: { header: any }) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = extractStringFromCommitlintParam(
"header",
header
);

return Plugins.titleUppercase(headerStr);
},

"too-many-spaces": ({ raw }: { raw: any }) => {
let rawStr = Helpers.assertNotNull(
Helpers.convertAnyToString(raw, "raw"),
notNullStringErrorMessage("raw")
);
let rawStr = extractStringFromCommitlintParam("raw", raw);

return Plugins.tooManySpaces(rawStr);
},

"type-space-after-colon": ({ header }: { header: any }) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = extractStringFromCommitlintParam(
"header",
header
);

return Plugins.typeSpaceAfterColon(headerStr);
},

"type-with-square-brackets": ({ header }: { header: any }) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = extractStringFromCommitlintParam(
"header",
header
);

return Plugins.typeWithSquareBrackets(headerStr);
},

// NOTE: we use 'header' instead of 'subject' as a workaround to this bug: https://github.com/conventional-changelog/commitlint/issues/3404
"subject-lowercase": ({ header }: { header: any }) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = extractStringFromCommitlintParam(
"header",
header
);

return Plugins.subjectLowercase(headerStr);
},

"type-space-after-comma": ({ header }: { header: any }) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = Helpers.assertNotNone(
Helpers.convertAnyToString(header),
notStringErrorMessage("header")
);

return Plugins.typeSpaceAfterComma(headerStr);
Expand All @@ -201,35 +198,32 @@ export default {
_: any,
maxLineLength: number
) => {
let bodyStr = Helpers.convertAnyToString(body, "body");
let maybeBody = Helpers.convertAnyToString(body);
return Plugins.bodySoftMaxLineLength(
bodyStr,
maybeBody,
maxLineLength
);
},

"body-paragraph-line-min-length": ({ body }: { body: any }) => {
let bodyStr = Helpers.convertAnyToString(body, "body");
let maybeBody = Helpers.convertAnyToString(body);
return Plugins.bodyParagraphLineMinLength(
bodyStr,
maybeBody,
headerMaxLineLength,
bodyMaxLineLength
);
},

"trailing-whitespace": ({ raw }: { raw: any }) => {
let rawStr = Helpers.assertNotNull(
Helpers.convertAnyToString(raw, "raw"),
notNullStringErrorMessage("raw")
);
let rawStr = extractStringFromCommitlintParam("raw", raw);

return Plugins.trailingWhitespace(rawStr);
},

"type-space-before-paren": ({ header }: { header: any }) => {
let headerStr = Helpers.assertNotNull(
Helpers.convertAnyToString(header, "header"),
notNullStringErrorMessage("header")
let headerStr = extractStringFromCommitlintParam(
"header",
header
);

return Plugins.typeSpaceBeforeParen(headerStr);
Expand Down
79 changes: 79 additions & 0 deletions commitlint/fpHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
interface IOption {
/**
* @deprecated it is better to use `if (foo instanceof None)` so that you can access the .value in the `else` case
**/
IsNone(): boolean;
/**
* @deprecated it is better to use `if (!(foo instanceof None))` so that you can access the .value inside the `if` block
**/
IsSome(): boolean;
}

export class None {
public IsNone(): boolean {
return true;
}
public IsSome(): boolean {
return false;
}

/**
* @deprecated it is better to use `OptionStatic.None`
**/
constructor() {}
}
export class Some<T> {
value: T;

constructor(val: NonNullable<T>) {
this.value = val;
}

public IsNone(): boolean {
return false;
}
public IsSome(): boolean {
return true;
}
}

export type Option<T> = (None | Some<NonNullable<T>>) & IOption;

export class OptionStatic {
public static None = new None();
public static OfObj<T>(obj: T | null | undefined): Option<NonNullable<T>> {
if (obj === null || obj === undefined) {
return OptionStatic.None;
} else {
return new Some(obj);
}
}
}

export class TypeHelpers {
public static IsNullOrUndefined(variable: any) {
return variable === null || variable === undefined;
}

// because instanceof doesn't work with primitive types (e.g. String), taken from https://stackoverflow.com/a/58184883/544947
public static IsInstanceOf(variable: any, type: any) {
if (TypeHelpers.IsNullOrUndefined(variable)) {
throw new Error(
"Invalid 'variable' parameter passed in: null or undefined"
);
}
if (TypeHelpers.IsNullOrUndefined(type)) {
throw new Error(
"Invalid 'type' parameter passed in: null or undefined"
);
}

let res: boolean = false;
if (typeof type == "string") {
res = typeof variable == type.toLowerCase();
} else {
res = variable.constructor == type;
}
return res;
}
}
29 changes: 22 additions & 7 deletions commitlint/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { None, Some, Option, OptionStatic, TypeHelpers } from "./fpHelpers.js";

export abstract class Helpers {
public static errMessageSuffix =
"\nFor reference, these are the guidelines that include our commit message conventions: https://github.com/nblockchain/conventions/blob/master/docs/WorkflowGuidelines.md";
Expand All @@ -16,12 +18,25 @@ export abstract class Helpers {
}

// to convert from 'any' type
public static convertAnyToString(
potentialString: any,
paramName: string
): string | null {
// this null/undefined check is required, otherwise, String(null) might give us the stupid string "null"
return potentialString ? String(potentialString) : null;
public static convertAnyToString(potentialString: any): Option<string> {
if (TypeHelpers.IsNullOrUndefined(potentialString)) {
return OptionStatic.None;
}
// this type check is required, otherwise, String(null) would give us the stupid string "null"
if (TypeHelpers.IsInstanceOf(potentialString, String)) {
return new Some(String(potentialString));
}
return OptionStatic.None;
}

public static assertNotNone(
text: Option<string>,
errorMessage: string
): string {
if (text instanceof None) {
throw new Error(errorMessage);
}
return text.value;
}

public static assertNotNull(
Expand Down Expand Up @@ -75,7 +90,7 @@ export abstract class Helpers {

public static findUrls(text: string) {
var urlRegex = /(https?:\/\/[^\s]+)/g;
return text.match(urlRegex);
return OptionStatic.OfObj(text.match(urlRegex));
}

public static isCommitUrl(url: string) {
Expand Down
Loading