import fs from "fs"; import path from "path"; import {IMongooseDocsSchema} from "../types"; import writeFile from "./writeFile"; /** * Escape HTML special characters like quotes and brackets. * @param string */ function escapeHtml(string: string): string { const entityMap: { [entity: string]: string } = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }; return string.replace(/[&<>"'`=\/]/g, function (s) { return entityMap[s]; }); } /** * Generate anchor tag for a schema type. * @param fieldType */ function schemaTypeAnchor(fieldType: string): string { const officialSchemaTypeUrl = "https://mongoosejs.com/docs/schematypes.html"; let schemaTypeUrl: string = ""; switch (fieldType) { case "String": case "Number": case "Date": case "Buffer": case "Boolean": case "ObjectID": case "Array": case "Map": schemaTypeUrl = `${officialSchemaTypeUrl}#${fieldType.toLowerCase()}s`; break; case "Mixed": schemaTypeUrl = `${officialSchemaTypeUrl}#${fieldType.toLowerCase()}`; break; case "Decimal128": schemaTypeUrl = `${officialSchemaTypeUrl}#mongoose_Mongoose-Decimal128`; break; } if (schemaTypeUrl) { return `${fieldType}`; } else { return fieldType; } } /** * Generate HTML code for bootstrap navigation to other schemas. * @param mongooseSchemas * @param modelName */ function generateNavigation(mongooseSchemas: IMongooseDocsSchema[], modelName?: string): string { const anchorLinks: string[] = mongooseSchemas.map((schema) => { const title = (schema.comment) ? escapeHtml(schema.comment) : ""; const classes = `nav-link mb-3 border ${(schema.name === modelName) ? "active" : ""}` return ` ${schema.name} `; }); return `

Models

`; } /** * Generate HTML code for a schema table. * @param schema */ function generateSchemaTable(schema: IMongooseDocsSchema): string { const fieldRows: string[] = Object.keys(schema.fields).map((fieldName: string) => { const field = schema.fields[fieldName]; let nestedSchema: string = ""; if (field.nestedSchema !== undefined) { nestedSchema = (typeof field.nestedSchema === "string") ? field.nestedSchema : JSON.stringify(field.nestedSchema); } return ` ${fieldName} ${schemaTypeAnchor(field.type)} ${(field.comment !== undefined) ? escapeHtml(field.comment) : ""} ${(field.required) ? "True" : "False"} ${(field.default !== undefined) ? field.default : ""} ${(field.min !== undefined) ? field.min : ""} ${(field.max !== undefined) ? field.max : ""} ${nestedSchema} `; }); return `
${fieldRows.join("")}
Field Name Field Type Comment Required Default Min Max Nested Schema
`; } /** * Generate HTML code for documentation index. * @param mongooseSchemas */ function mongooseDocsGenerateIndexHTML(mongooseSchemas: IMongooseDocsSchema[]): string { return ` Mongoose Docs
${generateNavigation(mongooseSchemas)}

Mongoose Docs

`; } /** * Generate HTML code for a single schema. * @param mongooseSchemas * @param modelName */ function mongooseDocsGenerateSchemaHTML(mongooseSchemas: IMongooseDocsSchema[], modelName: string): string { const currentSchema: IMongooseDocsSchema = mongooseSchemas.find((schema) => schema.name === modelName) as IMongooseDocsSchema; if (!currentSchema) return ""; return ` Mongoose Docs for ${modelName} model
${generateNavigation(mongooseSchemas, modelName)}

Mongoose Docs for ${modelName} model

${currentSchema.comment}
${generateSchemaTable(currentSchema)}
`; } /** * Write documentation as HTML files for all available schemas. * @param mongooseSchemas Schema structure returned from `mongooseDocsJSON` function. * @param fileDirectory Directory to write the documentation HTML files. */ export function mongooseDocsOutputHTML(mongooseSchemas: IMongooseDocsSchema[], fileDirectory: string): void { const lastChar: string = fileDirectory.substr(-1); // Selects the last character if (lastChar != '/') { // If the last character is not a slash fileDirectory = fileDirectory + '/'; // Append a slash to it. } // Make directory if it does not exist if (!fs.existsSync(fileDirectory)) { try { fs.mkdirSync(fileDirectory, {recursive: true}); } catch (err) { console.error(`Error creating directory "${fileDirectory}"`, err); } } // Write out HTML files writeFile(mongooseDocsGenerateIndexHTML(mongooseSchemas), path.join(fileDirectory, "index.html")); mongooseSchemas.forEach((schema) => { writeFile(mongooseDocsGenerateSchemaHTML(mongooseSchemas, schema.name), path.join(fileDirectory, `${schema.name}.html`)); }); console.log(`documentation files written to ${fileDirectory}`); }