Socket
Socket
Sign inDemoInstall

bob-group-frontend-coding-standards

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bob-group-frontend-coding-standards - npm Package Compare versions

Comparing version 1.0.2 to 1.0.3

202

index.js

@@ -29,2 +29,3 @@ "use strict";

incorrectComponentFileNames: [],
missingFontawesomeIconImports: [],
};

@@ -34,2 +35,4 @@ let warnings = {

incorrectlyNamedVariables: [],
incorrectlyNamedStateVariables: [],
incorrectlyNamedShowModalVariables: [],
incorrectTruthy: [],

@@ -42,2 +45,21 @@ classComponents: [],

const upperSnakeCaseRegex = /^[A-Z0-9_]+$/;
let allImportNames = [];
let setupIconsContent;
function keyToHumanReadable(key) {
if (!key)
return "";
// @ts-ignore
let keyHumanReadable = key.replaceAll("_", " ");
keyHumanReadable = keyHumanReadable.replaceAll("sender", "collection");
keyHumanReadable = keyHumanReadable.replaceAll("receiver", "delivery");
keyHumanReadable = keyHumanReadable.replaceAll("-", " ");
// camel case to sentence case
keyHumanReadable = keyHumanReadable.replace(/([A-Z])/g, " $1").trim();
let sentenceCaseKey = keyHumanReadable.charAt(0).toUpperCase() +
keyHumanReadable.slice(1).toLowerCase();
sentenceCaseKey = sentenceCaseKey.replaceAll("Bob box", "Bob Box");
sentenceCaseKey = sentenceCaseKey.replaceAll("Bob pay", "Bob Pay");
sentenceCaseKey = sentenceCaseKey.replaceAll("Bob go", "Bob Go");
return sentenceCaseKey;
}
function writeOutput(type, content) {

@@ -81,5 +103,9 @@ let colors = {

data = addRenderMethodsComment(data, filePath);
// data = makeCommentsSentenceCase(data); // todo needs more testing
// Checks for files
data = fixLodashImports(data, filePath);
data = listMissingFontawesomeImports(data);
// // data = makeCommentsSentenceCase(data); // todo needs more testing
// // Checks for files
checkStateVariableNamingConventions(data, file, filePath);
checkVariableNamingConventions(data, file, filePath);
// // checkShowModalNamingConventions(data, file, filePath); // todo needs to be defined better
checkForRenderFunction(data, filePath);

@@ -195,2 +221,21 @@ checkForBooleanTruthyDetection(data, filePath);

}
function checkStateVariableNamingConventions(data, file, filePath) {
// CRITERIA: State variables should be camel case
const stateVariableRegex = /(?<=\[\s*)(\w+)(?=\s*,\s*set\w+\s*\])/gm;
const variableNames = [];
let match;
while ((match = stateVariableRegex.exec(data)) !== null) {
variableNames.push(match[1]);
}
variableNames.forEach((variableName) => {
if (!camelCaseRegex.test(variableName) &&
variableName !== file.split(".tsx").join("") &&
!(filePath.includes("/Routes") && variableName.includes("Page"))) {
warnings.incorrectlyNamedStateVariables.push({
file: filePath,
error: variableName,
});
}
});
}
function checkVariableNamingConventions(data, file, filePath) {

@@ -216,2 +261,19 @@ // CRITERIA: Variables should be camel case or upper snake case

}
function checkShowModalNamingConventions(data, file, filePath) {
// CRITERIA: When naming state variables to show/hide modals, make use of const [modalToShow, setModalToShow] = useState<string>("") or const [shouldShowXModal, setShouldShowXModal] = useState<boolean>(false)
const stateVariableRegex = /(?<=\[\s*)(\w+)(?=\s*,\s*set\w+\s*\])/gm;
let match;
const shouldShowXModalRegex = /^shouldShow[A-Z][a-zA-Z]*Modal$/;
while ((match = stateVariableRegex.exec(data)) !== null) {
let stateVariableName = match[1];
if (stateVariableName.toLowerCase().includes("modal") &&
!shouldShowXModalRegex.test(stateVariableName) &&
stateVariableName !== "modalToShow") {
warnings.incorrectlyNamedShowModalVariables.push({
file: filePath,
error: stateVariableName,
});
}
}
}
function addRenderMethodsComment(data, filePath) {

@@ -234,2 +296,48 @@ // CRITERIA: All components should have a comment indicating where the render methods section starts

}
function fixLodashImports(data, filePath) {
let importAll = 'import _ from "lodash";';
if (data.includes(importAll)) {
const lodashFunctionRegex = /_\.\w+[A-Za-z]*\(/g;
const lodashFunctions = data.match(lodashFunctionRegex);
let functionNames = [];
lodashFunctions === null || lodashFunctions === void 0 ? void 0 : lodashFunctions.forEach((functionString) => {
let functionName = functionString.substring(2, functionString.length - 1);
functionNames.push(functionName);
data = data.replace(functionString, functionName + "(");
let newImport = `import ${functionName} from "lodash/${functionName}";`;
if (!data.includes(newImport)) {
// prevent newImport from being added multiple times
data = data.replace(importAll, `${importAll}\n${newImport}`);
}
});
data = data.replace(importAll, "");
}
return data;
}
function kebabToUpperCase(str) {
// Split the string into individual words
const words = str.split("-");
// Capitalize each word (except the first one)
const upperCamelCaseWords = words.map((word) => {
return word.charAt(0).toUpperCase() + word.slice(1);
});
// Join the words and return the upper camel case string
return upperCamelCaseWords.join("");
}
function listMissingFontawesomeImports(data) {
let regex = /icon="[^"]*"/g;
let matches = data.match(regex);
matches === null || matches === void 0 ? void 0 : matches.forEach((match) => {
let iconString = match.replace('icon="', "").replace('"', "");
let importName = "fa" + kebabToUpperCase(iconString);
if (!allImportNames.includes(importName) &&
!setupIconsContent.includes(importName)) {
errors.missingFontawesomeIconImports.push({
error: `Missing import for ${importName}`,
});
allImportNames.push(importName);
}
});
return data;
}
function makeCommentsSentenceCase(data) {

@@ -273,5 +381,25 @@ // CRITERIA: All comments should be sentence case

errors.forEach((err) => {
console.log("\t" + err.file + (err.error ? ` - ${err.error}` : ""));
var _a;
console.log("\t" +
((_a = err.file) !== null && _a !== void 0 ? _a : "") +
(err.error && err.file ? " - " : "") +
(err.error ? `${err.error}` : ""));
});
}
function getSetupIconsContent() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
let filePath = folderPath + "/setupIcons.ts";
fs.readFile(filePath, "utf8", (err, data) => {
if (err) {
reject(`Error reading file: ${filePath}`);
}
else {
setupIconsContent = data;
resolve(true);
}
});
});
});
}
function run() {

@@ -284,43 +412,35 @@ return __awaiter(this, void 0, void 0, function* () {

writeOutput("info", "Running code checker...");
return readTSXFilesRecursively(folderPath)
.then(() => {
errors.incorrectInterfaceFileNames.length > 0 &&
logErrors("error", "Interface files named incorrectly", errors.incorrectInterfaceFileNames);
errors.incorrectInterfaceNames.length > 0 &&
logErrors("error", "Interfaces named incorrectly", errors.incorrectInterfaceNames);
errors.incorrectComponentFileNames.length > 0 &&
logErrors("error", "Component files named incorrectly", errors.incorrectComponentFileNames);
errors.incorrectComponentNames.length > 0 &&
logErrors("error", "Components named incorrectly", errors.incorrectComponentNames);
warnings.filesMissingRenderFunction.length > 0 &&
logErrors("warning", "Missing render function", warnings.filesMissingRenderFunction);
warnings.incorrectlyNamedVariables.length > 0 &&
logErrors("warning", "Variables that are not camel case or upper snake case", warnings.incorrectlyNamedVariables);
warnings.incorrectTruthy.length > 0 &&
logErrors("warning", "Prefer boolean truthy detection Boolean(x) over double !!", warnings.incorrectTruthy);
warnings.classComponents.length > 0 &&
logErrors("warning", "Class components should be functional components", warnings.classComponents);
warnings.forgottenTodos.length > 0 &&
logErrors("warning", "Forgotten Todos", warnings.forgottenTodos);
let errorCount = 0;
Object.keys(errors).forEach((key) => {
var _a;
// @ts-ignore
errorCount += (_a = errors[key]) === null || _a === void 0 ? void 0 : _a.length;
return getSetupIconsContent().then(() => __awaiter(this, void 0, void 0, function* () {
return readTSXFilesRecursively(folderPath)
.then(() => {
let errorCount = 0;
Object.keys(errors).forEach((key) => {
// @ts-ignore
let errorArray = errors[key];
errorCount += errorArray === null || errorArray === void 0 ? void 0 : errorArray.length;
errorArray.length > 0 &&
logErrors("error", keyToHumanReadable(key), errorArray);
});
Object.keys(warnings).forEach((key) => {
// @ts-ignore
let warningArray = warnings[key];
warningArray.length > 0 &&
logErrors("warning", keyToHumanReadable(key), warningArray);
});
if (errorCount === 0) {
writeOutput("info", "Done running code checker.");
process.exit(0);
}
else {
writeOutput("error", "Done running code checker.");
process.exit(1);
}
})
.catch((err) => {
writeOutput("error", err);
process.exit(1);
});
if (errorCount === 0) {
writeOutput("info", "Done running code checker.");
process.exit(0);
}
else {
writeOutput("error", "Done running code checker.");
process.exit(1);
}
})
.catch((err) => {
writeOutput("error", err);
process.exit(1);
});
}));
});
}
run();

@@ -18,3 +18,3 @@ const fs = require("fs");

interface IErrorObject {
file: string;
file?: string;
error?: any;

@@ -28,2 +28,3 @@ }

incorrectComponentFileNames: IErrorObject[];
missingFontawesomeIconImports: IErrorObject[];
} = {

@@ -34,2 +35,3 @@ incorrectInterfaceNames: [],

incorrectComponentFileNames: [],
missingFontawesomeIconImports: [],
};

@@ -39,2 +41,4 @@ let warnings: {

incorrectlyNamedVariables: IErrorObject[];
incorrectlyNamedStateVariables: IErrorObject[];
incorrectlyNamedShowModalVariables: IErrorObject[];
incorrectTruthy: IErrorObject[];

@@ -46,2 +50,4 @@ classComponents: IErrorObject[];

incorrectlyNamedVariables: [],
incorrectlyNamedStateVariables: [],
incorrectlyNamedShowModalVariables: [],
incorrectTruthy: [],

@@ -56,2 +62,27 @@ classComponents: [],

let allImportNames: string[] = [];
let setupIconsContent: any;
function keyToHumanReadable(key: string | undefined): string {
if (!key) return "";
// @ts-ignore
let keyHumanReadable = key.replaceAll("_", " ");
keyHumanReadable = keyHumanReadable.replaceAll("sender", "collection");
keyHumanReadable = keyHumanReadable.replaceAll("receiver", "delivery");
keyHumanReadable = keyHumanReadable.replaceAll("-", " ");
// camel case to sentence case
keyHumanReadable = keyHumanReadable.replace(/([A-Z])/g, " $1").trim();
let sentenceCaseKey =
keyHumanReadable.charAt(0).toUpperCase() +
keyHumanReadable.slice(1).toLowerCase();
sentenceCaseKey = sentenceCaseKey.replaceAll("Bob box", "Bob Box");
sentenceCaseKey = sentenceCaseKey.replaceAll("Bob pay", "Bob Pay");
sentenceCaseKey = sentenceCaseKey.replaceAll("Bob go", "Bob Go");
return sentenceCaseKey;
}
function writeOutput(

@@ -104,6 +135,9 @@ type: "success" | "error" | "warning" | "info",

data = addRenderMethodsComment(data, filePath);
// data = makeCommentsSentenceCase(data); // todo needs more testing
// Checks for files
data = fixLodashImports(data, filePath);
data = listMissingFontawesomeImports(data);
// // data = makeCommentsSentenceCase(data); // todo needs more testing
// // Checks for files
checkStateVariableNamingConventions(data, file, filePath);
checkVariableNamingConventions(data, file, filePath);
// // checkShowModalNamingConventions(data, file, filePath); // todo needs to be defined better
checkForRenderFunction(data, filePath);

@@ -116,7 +150,5 @@ checkForBooleanTruthyDetection(data, filePath);

}
if (isComponentFile(data, filePath)) {
checkComponentNamingConventions(data, file, filePath);
}
fs.writeFile(filePath, data, "utf8", (err: any) => {

@@ -240,2 +272,31 @@ if (err) {

function checkStateVariableNamingConventions(
data: string,
file: string,
filePath: string
) {
// CRITERIA: State variables should be camel case
const stateVariableRegex = /(?<=\[\s*)(\w+)(?=\s*,\s*set\w+\s*\])/gm;
const variableNames = [];
let match;
while ((match = stateVariableRegex.exec(data)) !== null) {
variableNames.push(match[1]);
}
variableNames.forEach((variableName) => {
if (
!camelCaseRegex.test(variableName) &&
variableName !== file.split(".tsx").join("") &&
!(filePath.includes("/Routes") && variableName.includes("Page"))
) {
warnings.incorrectlyNamedStateVariables.push({
file: filePath,
error: variableName,
});
}
});
}
function checkVariableNamingConventions(

@@ -271,2 +332,29 @@ data: string,

function checkShowModalNamingConventions(
data: string,
file: string,
filePath: string
) {
// CRITERIA: When naming state variables to show/hide modals, make use of const [modalToShow, setModalToShow] = useState<string>("") or const [shouldShowXModal, setShouldShowXModal] = useState<boolean>(false)
const stateVariableRegex = /(?<=\[\s*)(\w+)(?=\s*,\s*set\w+\s*\])/gm;
let match;
const shouldShowXModalRegex = /^shouldShow[A-Z][a-zA-Z]*Modal$/;
while ((match = stateVariableRegex.exec(data)) !== null) {
let stateVariableName = match[1];
if (
stateVariableName.toLowerCase().includes("modal") &&
!shouldShowXModalRegex.test(stateVariableName) &&
stateVariableName !== "modalToShow"
) {
warnings.incorrectlyNamedShowModalVariables.push({
file: filePath,
error: stateVariableName,
});
}
}
}
function addRenderMethodsComment(data: string, filePath: string) {

@@ -292,2 +380,62 @@ // CRITERIA: All components should have a comment indicating where the render methods section starts

function fixLodashImports(data: string, filePath: string) {
let importAll = 'import _ from "lodash";';
if (data.includes(importAll)) {
const lodashFunctionRegex = /_\.\w+[A-Za-z]*\(/g;
const lodashFunctions = data.match(lodashFunctionRegex);
let functionNames: string[] = [];
lodashFunctions?.forEach((functionString: string) => {
let functionName = functionString.substring(2, functionString.length - 1);
functionNames.push(functionName);
data = data.replace(functionString, functionName + "(");
let newImport = `import ${functionName} from "lodash/${functionName}";`;
if (!data.includes(newImport)) {
// prevent newImport from being added multiple times
data = data.replace(importAll, `${importAll}\n${newImport}`);
}
});
data = data.replace(importAll, "");
}
return data;
}
function kebabToUpperCase(str: string) {
// Split the string into individual words
const words = str.split("-");
// Capitalize each word (except the first one)
const upperCamelCaseWords = words.map((word) => {
return word.charAt(0).toUpperCase() + word.slice(1);
});
// Join the words and return the upper camel case string
return upperCamelCaseWords.join("");
}
function listMissingFontawesomeImports(data: string) {
let regex = /icon="[^"]*"/g;
let matches = data.match(regex);
matches?.forEach((match: string) => {
let iconString = match.replace('icon="', "").replace('"', "");
let importName = "fa" + kebabToUpperCase(iconString);
if (
!allImportNames.includes(importName) &&
!setupIconsContent.includes(importName)
) {
errors.missingFontawesomeIconImports.push({
error: `Missing import for ${importName}`,
});
allImportNames.push(importName);
}
});
return data;
}
function makeCommentsSentenceCase(data: string) {

@@ -344,6 +492,25 @@ // CRITERIA: All comments should be sentence case

errors.forEach((err) => {
console.log("\t" + err.file + (err.error ? ` - ${err.error}` : ""));
console.log(
"\t" +
(err.file ?? "") +
(err.error && err.file ? " - " : "") +
(err.error ? `${err.error}` : "")
);
});
}
async function getSetupIconsContent() {
return new Promise((resolve, reject) => {
let filePath = folderPath + "/setupIcons.ts";
fs.readFile(filePath, "utf8", (err: any, data: string) => {
if (err) {
reject(`Error reading file: ${filePath}`);
} else {
setupIconsContent = data;
resolve(true);
}
});
});
}
async function run() {

@@ -355,82 +522,37 @@ if (!args.folderPath) {

writeOutput("info", "Running code checker...");
return readTSXFilesRecursively(folderPath)
.then(() => {
errors.incorrectInterfaceFileNames.length > 0 &&
logErrors(
"error",
"Interface files named incorrectly",
errors.incorrectInterfaceFileNames
);
return getSetupIconsContent().then(async () => {
return readTSXFilesRecursively(folderPath)
.then(() => {
let errorCount: number = 0;
errors.incorrectInterfaceNames.length > 0 &&
logErrors(
"error",
"Interfaces named incorrectly",
errors.incorrectInterfaceNames
);
Object.keys(errors).forEach((key) => {
// @ts-ignore
let errorArray = errors[key];
errorCount += errorArray?.length;
errors.incorrectComponentFileNames.length > 0 &&
logErrors(
"error",
"Component files named incorrectly",
errors.incorrectComponentFileNames
);
errorArray.length > 0 &&
logErrors("error", keyToHumanReadable(key), errorArray);
});
Object.keys(warnings).forEach((key) => {
// @ts-ignore
let warningArray = warnings[key];
warningArray.length > 0 &&
logErrors("warning", keyToHumanReadable(key), warningArray);
});
errors.incorrectComponentNames.length > 0 &&
logErrors(
"error",
"Components named incorrectly",
errors.incorrectComponentNames
);
warnings.filesMissingRenderFunction.length > 0 &&
logErrors(
"warning",
"Missing render function",
warnings.filesMissingRenderFunction
);
warnings.incorrectlyNamedVariables.length > 0 &&
logErrors(
"warning",
"Variables that are not camel case or upper snake case",
warnings.incorrectlyNamedVariables
);
warnings.incorrectTruthy.length > 0 &&
logErrors(
"warning",
"Prefer boolean truthy detection Boolean(x) over double !!",
warnings.incorrectTruthy
);
warnings.classComponents.length > 0 &&
logErrors(
"warning",
"Class components should be functional components",
warnings.classComponents
);
warnings.forgottenTodos.length > 0 &&
logErrors("warning", "Forgotten Todos", warnings.forgottenTodos);
let errorCount: number = 0;
Object.keys(errors).forEach((key) => {
// @ts-ignore
errorCount += errors[key]?.length;
if (errorCount === 0) {
writeOutput("info", "Done running code checker.");
process.exit(0);
} else {
writeOutput("error", "Done running code checker.");
process.exit(1);
}
})
.catch((err) => {
writeOutput("error", err);
process.exit(1);
});
if (errorCount === 0) {
writeOutput("info", "Done running code checker.");
process.exit(0);
} else {
writeOutput("error", "Done running code checker.");
process.exit(1);
}
})
.catch((err) => {
writeOutput("error", err);
process.exit(1);
});
});
}
run();
{
"name": "bob-group-frontend-coding-standards",
"version": "1.0.2",
"version": "1.0.3",
"description": "Script to check that frontend code follows coding standards",

@@ -5,0 +5,0 @@ "main": "index.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc