dce-dev-wizard
Version:
Wizard for managing development apps at Harvard DCE.
234 lines • 9.85 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
// Import constants
const TOP_LEVEL_DIRECTORY_1 = __importDefault(require("./constants/TOP_LEVEL_DIRECTORY"));
const APP_DIRECTORY_1 = __importDefault(require("./constants/APP_DIRECTORY"));
// Error code regex
const errorCodeRegex = /new ErrorWithCode\(\s*(.+),\s*(.+)\s*\)(;|,)/;
/*------------------------------------------------------------------------*/
/* ------------------------------- Helpers ------------------------------ */
/*------------------------------------------------------------------------*/
/**
* Get all paths to the error code types within a folder
* @author Gabe Abrams
* @param parentFolderPath - the path to the parent folder
* @returns an array of paths to the error code types
*/
const getErrorCodesPaths = (parentFolderPath) => {
// Get all files in the folder
const files = fs_1.default.readdirSync(parentFolderPath);
// For each item in the folder, recurse if folder, analyze if file
const paths = [];
files.forEach((file) => {
// Get the full path
const fullPath = path_1.default.join(parentFolderPath, file);
// If folder, recurse
const isFolder = (fs_1.default
.lstatSync(fullPath)
.isDirectory());
if (isFolder) {
// Skip if the "from-server" folder
if (file !== 'from-server') {
// Recurse
paths.push(...getErrorCodesPaths(fullPath));
}
}
else {
// Not folder. Analyze
if (file.includes('ErrorCode') && (file.endsWith('.ts') || file.endsWith('.tsx'))) {
// Found an error code file
paths.push(fullPath);
}
}
});
// Return all accumulated paths
return paths;
};
/**
* Read an error code file and return the error codes within it
* @author Gabe Abrams
* @param filePath - the path to the error code file
* @returns an array of error codes
*/
const readErrorCodes = (filePath) => {
// Read the file
const fileContents = fs_1.default.readFileSync(filePath, 'utf8');
// Get the name of the error code file
const parent = ((filePath.split('/').pop() || 'UnknownErrorCodeParent')
.replace('.tsx', '')
.replace('.ts', ''));
// Split the file into lines
const lines = fileContents.split('\n');
// Find all error codes
const errorCodes = [];
lines.forEach((line, lineNumber) => {
// Format of an error code:
// DescriptionOfErrorCode = 'CODE',
// Check if this line is an error code
const matches = line.trim().match(/(\w+) = '(\w+)',/);
if (matches) {
// Found an error code
const name = matches[1];
const code = matches[2];
// Add to error codes
errorCodes.push({
parent,
code,
found: false,
prop: name,
appearances: [],
});
}
});
// Return all error codes
return errorCodes;
};
/*------------------------------------------------------------------------*/
/* -------------------------------- Main -------------------------------- */
/*------------------------------------------------------------------------*/
/*----------------------------------------*/
/* ---------- Parse Error Codes --------- */
/*----------------------------------------*/
console.log('\nUpdating error docs...');
// Get error code file paths
const serverPaths = getErrorCodesPaths(path_1.default.join(APP_DIRECTORY_1.default, '/server/src'));
const clientPaths = getErrorCodesPaths(path_1.default.join(APP_DIRECTORY_1.default, '/client/src'));
const errorCodePaths = serverPaths.concat(clientPaths);
// Read each and process the enums
const errorCodes = [];
errorCodePaths.forEach((filePath) => {
errorCodes.push(...readErrorCodes(filePath));
});
/*----------------------------------------*/
/* -------- Validate Error Codes -------- */
/*----------------------------------------*/
// Display fatal error if duplicate error codes exist
const errorCodeSet = new Set();
errorCodes.forEach((errorCode) => {
if (errorCodeSet.has(errorCode.code)) {
console.error(`Duplicate error code defined: ${errorCode.code} in ${errorCode.parent}. Fatal error. Exiting!`);
process.exit(1);
}
});
/*----------------------------------------*/
/* ---------- Find Appearances ---------- */
/*----------------------------------------*/
/**
* Recursively look for appearances of error codes
* @author Gabe Abrams
* @param folderPath - the path to the folder to search
*/
const findAppearances = (folderPath) => {
// Format of an error appearance:
// new ErrorWithCode(
// `Message template with ${templateVariables} in it perhaps`,
// ErrorCodeParent.ErrorCodePropName,
// );
// Get all files in the folder
const files = fs_1.default.readdirSync(folderPath);
// For each item in the folder, recurse if folder, analyze if file
files.forEach((file) => {
// Get the full path
const fullPath = path_1.default.join(folderPath, file);
// If folder, recurse
const isFolder = (fs_1.default
.lstatSync(fullPath)
.isDirectory());
if (isFolder) {
// Recurse
findAppearances(fullPath);
}
else {
// Not folder. Analyze
if ((file.endsWith('.ts') || file.endsWith('.tsx'))
&& !errorCodePaths.includes(fullPath)) {
// Read the file
let fileContents = fs_1.default.readFileSync(fullPath, 'utf8');
// Check if found
errorCodes.find((errorCode) => {
if (fileContents.includes(`${errorCode.parent}.${errorCode.prop},`)
|| fileContents.includes(`${errorCode.parent}.${errorCode.prop};`)
|| fileContents.includes(`${errorCode.parent}.${errorCode.prop} `)
|| fileContents.includes(`${errorCode.parent}.${errorCode.prop})`)
|| fileContents.includes(`${errorCode.parent}.${errorCode.prop}\n`)) {
errorCode.found = true;
}
});
// Check if this line is an error appearance
let matches = fileContents.trim().match(errorCodeRegex);
while (matches) {
// Found an error appearance
const messageTemplate = matches[1];
const parentAndProp = matches[2];
// Find the error code
const errorCode = errorCodes.find((errorCode) => {
return (`${errorCode.parent}.${errorCode.prop},` === parentAndProp.trim());
});
if (errorCode) {
// Add the appearance
errorCode.appearances.push({
messageTemplate,
filePath: fullPath,
});
}
// Remove this match
fileContents = fileContents.replace(matches[0], '');
matches = fileContents.trim().match(errorCodeRegex);
}
}
}
});
};
// Add appearances in each folder
findAppearances(path_1.default.join(APP_DIRECTORY_1.default, '/server/src'));
findAppearances(path_1.default.join(APP_DIRECTORY_1.default, '/client/src'));
/*------------------------------------------------------------------------*/
/* ----------------------------- Build Docs ----------------------------- */
/*------------------------------------------------------------------------*/
let docsMD = '';
/*----------------------------------------*/
/* -------------- Metadata -------------- */
/*----------------------------------------*/
const pkg = require(path_1.default.join(APP_DIRECTORY_1.default, 'package.json'));
if (!pkg) {
console.log('package.json not found');
process.exit(1);
}
const projectName = pkg.name || 'This Project';
/*----------------------------------------*/
/* ---------------- Intro --------------- */
/*----------------------------------------*/
docsMD = `# Error Cheatsheet for ${projectName}\n`;
docsMD += 'This is an auto-generated document that outlines the friendly error codes that users may encounter when using this app.\n\n';
/*----------------------------------------*/
/* ---------------- Table --------------- */
/*----------------------------------------*/
// Header
docsMD += '\n| Code | Internal Name | Associated Message |\n';
docsMD += '| -------- | -------- | -------- |\n';
// Error codes
errorCodes.forEach((err) => {
// Skip ones that are not in use
if (!err.found) {
return;
}
let associatedMessage = (err
.appearances
.map((appearance) => {
return `<div>${appearance.messageTemplate.replace('`', '\'')}<div style="font-size: 80%">${appearance.filePath.replace(APP_DIRECTORY_1.default, '')}</div></div>`;
})
.join('<br>'));
if (err.appearances.length === 0) {
associatedMessage = 'No additional information';
}
docsMD += `| ${err.code} | ${err.prop} | ${associatedMessage} |\n`;
});
// Finish
fs_1.default.writeFileSync(path_1.default.join(TOP_LEVEL_DIRECTORY_1.default, 'docs/ErrorCodeDocs.md'), docsMD, 'utf-8');
console.log('Success! Written to docs/ErrorCodeDocs.md');
//# sourceMappingURL=buildErrorDocs.js.map