techdoc
Version:
a cli tool to generate documentation for developers, users, and others also.
307 lines (257 loc) β’ 15.5 kB
JavaScript
const dree = require('dree');
var clc = require("cli-color");
const inquirer = require('inquirer');
const fs = require('fs');
const path = require('path');
const openai = require("openai").default
const { development_sm, development_md, development_lg } = require('./md/development');
const { readme_sm, readme_md, readme_lg } = require('./md/readme');
const { research_sm, research_lg, research_md } = require('./md/research');
const { contributing_lg, contributing_sm, contributing_md } = require('./md/contributing');
const [, , ...arg] = process.argv;
const dirPath = process.cwd();
function getDependencies(packageJsonPath) {
try {
// Read the content of the package.json file
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
// Parse the JSON content
const packageJson = JSON.parse(packageJsonContent);
// Extract dependencies
const dependencies = packageJson.dependencies || {};
// Extract devDependencies
const devDependencies = packageJson.devDependencies || {};
// Merge dependencies and devDependencies
const allDependencies = { ...dependencies, ...devDependencies };
// Convert the dependencies object to a string
const dependenciesString = Object.entries(allDependencies)
.map(([package, version]) => `${package}: ${version}`)
.join('\n');
return dependenciesString;
} catch (error) {
console.error('Error reading or parsing package.json:', error.message);
return null;
}
}
function readJsonFile(filePath) {
try {
// Read the content of the JSON file
const fileContent = fs.readFileSync(filePath, 'utf8');
// Parse the JSON content
const jsonData = JSON.parse(fileContent);
return jsonData;
} catch (error) {
console.error('Error reading or parsing JSON file:', error.message);
return null;
}
}
function getDotStartingFolder(dirPath) {
try {
// Read the contents of the directory
const contents = fs.readdirSync(dirPath);
// Filter out folders that start with a dot
const dotStartingFolders = contents.filter(item => fs.statSync(path.join(dirPath, item)).isDirectory() && item.startsWith('.'));
return dotStartingFolders.map(folder => `/${folder}/`);
} catch (error) {
console.error('Error reading directory:', error.message);
return null;
}
}
function createMarkdownFile(fileName, mdContent, filePath) {
try {
// Combine the filePath and fileName to create the complete file path
const fullPath = `${filePath}/${fileName}.md`;
// Write the markdown content to the file
fs.writeFileSync(fullPath, mdContent, 'utf8');
console.log(clc.bold.green(`File "${fileName}.md" created successfully - now change accordingly"`));
} catch (error) {
console.error('Error creating Markdown file:', error.message);
}
}
async function generateMarkdownWithAI(description, mdTemplate, api_key, fileName) {
console.log(clc.yellow("Loading..."))
const prompt = mdTemplate + "\n\n" + `Populate the full markdown template given above with the description - ${description}`
const OpenAI = new openai({ baseURL: 'https://api.openai.com/v1/chat', apiKey: api_key })
OpenAI.completions.create({ model: 'gpt-3.5-turbo', messages: [{ role: "user", content: prompt }], max_tokens: prompt.length, temperature: 0.2 }).then(response => {
const generatedContent = response.choices[0].message.content;
createMarkdownFile(fileName, generatedContent, dirPath);
}).catch(error => {
console.error(clc.red('Error from OpenAI API:', error.message));
})
}
const StartProcess = async () => {
const prompt = inquirer.createPromptModule();
const dependencies = getDependencies("./package.json")
if (fs.existsSync(dirPath + "/techdoc.json")) {
let type = await prompt({
type: "list",
name: "type",
loop: false,
message: "β Which Markdown You Want",
choices: ["π Development", "π Readme", "π§ͺ Research", "π Development AI", "π Readme AI", "π§ͺ Research AI", "π€ Contributing", "β Exit"]
})
type = type.type
const prevSavedData = readJsonFile("techdoc.json")
const folders = prevSavedData.excludeFolders
const tree = folders.length ? dree.parse(dirPath, {
depth: 3,
exclude: [/node_modules/, ...folders.map(str => new RegExp(str.slice(1, -1))), , ...getDotStartingFolder(dirPath).map(str => new RegExp(str.slice(1, -1)))]
}) : dree.parse(dirPath, {
depth: 3,
exclude: [/node_modules/, ...getDotStartingFolder(dirPath).map(str => new RegExp(str.slice(1, -1)))]
})
if (type == "β Exit") {
process.exit();
}
else if (type == "π Development") {
createMarkdownFile("DEVELOPMENT", prevSavedData.size == "π± sm" ? development_sm(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π² md" ? development_md(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π lg" ? development_lg(prevSavedData.desc, tree, dependencies) : ``, dirPath)
}
else if (type == "π Readme") {
createMarkdownFile("README", prevSavedData.size == "π± sm" ? readme_sm(prevSavedData.desc) : prevSavedData.size == "π² md" ? readme_md(prevSavedData.desc) : prevSavedData.size == "π lg" ? readme_lg(prevSavedData.desc) : ``, dirPath)
}
else if (type == "π§ͺ Research") {
createMarkdownFile("RESEARCH", prevSavedData.size == "π± sm" ? research_sm() : prevSavedData.size == "π² md" ? research_md() : prevSavedData.size == "π lg" ? research_lg() : ``, dirPath)
}
else if (type == "π€ Contributing") {
createMarkdownFile("CONTRIBUTION", prevSavedData.size == "π± sm" ? contributing_sm() : prevSavedData.size == "π² md" ? contributing_md() : prevSavedData.size == "π lg" ? contributing_lg() : ``, dirPath)
}
// AI
else if (type == "π Development AI") {
if ('api_key' in prevSavedData && prevSavedData.desc != "") {
generateMarkdownWithAI(prevSavedData.desc, prevSavedData.size == "π± sm" ? development_sm(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π² md" ? development_md(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π lg" ? development_lg(prevSavedData.desc, tree, dependencies) : ``, prevSavedData.api_key, "DEVELOPMENT")
} else {
let api_key = await prompt({
type: "password",
name: "api_key",
message: "π Enter Your API Key:"
})
fs.writeFileSync(dirPath + '/techdoc.json', JSON.stringify({ ...prevSavedData, ...api_key }, null, 2))
generateMarkdownWithAI(prevSavedData.desc, prevSavedData.size == "π± sm" ? development_sm(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π² md" ? development_md(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π lg" ? development_lg(prevSavedData.desc, tree, dependencies) : ``, api_key, "DEVELOPMENT")
}
}
else if (type == "π Readme AI") {
if ('api_key' in prevSavedData && prevSavedData.desc != "") {
generateMarkdownWithAI(prevSavedData.desc, prevSavedData.size == "π± sm" ? readme_sm(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π² md" ? readme_md(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π lg" ? readme_lg(prevSavedData.desc, tree, dependencies) : ``, prevSavedData.api_key, "README")
} else {
let api_key = await prompt({
type: "password",
name: "api_key",
message: "π Enter Your API Key:"
})
fs.writeFileSync(dirPath + '/techdoc.json', JSON.stringify({ ...prevSavedData, ...api_key }, null, 2))
generateMarkdownWithAI(prevSavedData.desc, prevSavedData.size == "π± sm" ? readme_sm(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π² md" ? readme_md(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π lg" ? readme_lg(prevSavedData.desc, tree, dependencies) : ``, api_key, "README")
}
}
else if (type == "π§ͺ Research AI") {
if ('api_key' in prevSavedData && prevSavedData.desc != "") {
generateMarkdownWithAI(prevSavedData.desc, prevSavedData.size == "π± sm" ? research_sm(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π² md" ? research_md(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π lg" ? research_lg(prevSavedData.desc, tree, dependencies) : ``, prevSavedData.api_key, "RESEARCH")
} else {
let api_key = await prompt({
type: "password",
name: "api_key",
message: "π Enter Your API Key:"
})
fs.writeFileSync(dirPath + '/techdoc.json', JSON.stringify({ ...prevSavedData, ...api_key }, null, 2))
generateMarkdownWithAI(prevSavedData.desc, prevSavedData.size == "π± sm" ? research_sm(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π² md" ? research_md(prevSavedData.desc, tree, dependencies) : prevSavedData.size == "π lg" ? research_lg(prevSavedData.desc, tree, dependencies) : ``, api_key, "RESEARCH")
}
}
} else {
let desc = await prompt({
type: "input",
name: "desc",
message: "π What Is The Project Description(You May Edit It Later)"
})
let excludeFolders = await prompt({
type: "input",
name: "excludeFolders",
message: "π Excluded Folders (like .git - write by space)"
})
let size = await prompt({
type: "list",
name: "size",
default: "./src",
choices: ["π± sm", "π² md", "π lg"],
message: "πWhat Is Your Project Size"
})
let type = await prompt({
type: "list",
name: "type",
loop: false,
message: "β Which Markdown You Want",
choices: ["π Development", "π Readme", "π§ͺ Research", "π Development AI", "π Readme AI", "π§ͺ Research AI", "π€ Contributing", "β Exit"]
})
fs.writeFileSync(dirPath + '/techdoc.json', JSON.stringify({ ...desc, ...size, excludeFolders: excludeFolders.excludeFolders ? excludeFolders.excludeFolders.split(' ') : [] }, null, 2))
type = type.type
size = size.size
desc=desc.desc
const prevSavedData = readJsonFile("techdoc.json")
const folders = prevSavedData.excludeFolders
const tree = folders.length ? dree.parse(dirPath, {
depth: 3,
exclude: [/node_modules/, ...folders.map(str => new RegExp(str.slice(1, -1))), , ...getDotStartingFolder(dirPath).map(str => new RegExp(str.slice(1, -1)))]
}) : dree.parse(dirPath, {
depth: 3,
exclude: [/node_modules/, ...getDotStartingFolder(dirPath).map(str => new RegExp(str.slice(1, -1)))]
})
if (type == "β Exit") {
process.exit();
}
else if (type == "π Development") {
createMarkdownFile("DEVELOPMENT", size == "π± sm" ? development_sm(desc, tree, dependencies) : size == "π² md" ? development_md(desc, tree, dependencies) : size == "π lg" ? development_lg(desc, tree, dependencies) : ``, dirPath)
}
else if (type == "π Readme") {
createMarkdownFile("README", size == "π± sm" ? readme_sm(desc) : size == "π² md" ? readme_md(desc) : size == "π lg" ? readme_lg(desc) : ``, dirPath)
}
else if (type == "π§ͺ Research") {
createMarkdownFile("RESEARCH", size == "π± sm" ? research_sm() : size == "π² md" ? research_md() : size == "π lg" ? research_lg() : ``, dirPath)
}
else if (type == "π€ Contributing") {
createMarkdownFile("CONTRIBUTION", size == "π± sm" ? contributing_sm() : size == "π² md" ? contributing_md() : size == "π lg" ? contributing_lg() : ``, dirPath)
}
// Ai
else if (type == "π Development AI") {
let api_key = await prompt({
type: "password",
name: "api_key",
message: "π Enter Your API Key:"
})
fs.writeFileSync(dirPath + '/techdoc.json', JSON.stringify({ ...prevSavedData, ...api_key }, null, 2))
generateMarkdownWithAI(prevSavedData.desc, size == "π± sm" ? development_sm(desc, tree, dependencies) : size == "π² md" ? development_md(desc, tree, dependencies) : size == "π lg" ? development_lg(desc, tree, dependencies) : ``, api_key.api_key, "DEVELOPMENT")
}
else if (type == "π Readme AI") {
let api_key = await prompt({
type: "password",
name: "api_key",
message: "π Enter Your API Key:"
})
fs.writeFileSync(dirPath + '/techdoc.json', JSON.stringify({ ...prevSavedData, ...api_key }, null, 2))
generateMarkdownWithAI(prevSavedData.desc, size == "π± sm" ? readme_sm(desc, tree, dependencies) : size == "π² md" ? readme_md(desc, tree, dependencies) : size == "π lg" ? readme_lg(desc, tree, dependencies) : ``, api_key.api_key, "README")
}
else if (type == "π§ͺ Research AI") {
let api_key = await prompt({
type: "password",
name: "api_key",
message: "π Enter Your API Key:"
})
fs.writeFileSync(dirPath + '/techdoc.json', JSON.stringify({ ...prevSavedData, ...api_key }, null, 2))
generateMarkdownWithAI(prevSavedData.desc, size == "π± sm" ? research_sm(desc, tree, dependencies) : size == "π² md" ? research_md(desc, tree, dependencies) : size == "π lg" ? research_lg(desc, tree, dependencies) : ``, api_key.api_key, "RESEARCH")
}
}
}
const main = () => {
if (arg.length == 1) {
if (arg[0].startsWith('--help')) {
console.log("Commands:\n\t" + clc.yellowBright(" init") + " - Initializes the project.\n\t" + clc.blackBright(" --help") + " - Show The Commands And About\n\t")
} else if (arg[0] == 'init') {
try {
StartProcess()
} catch (error) {
console.log(clc.red(error.message))
}
}
} else {
console.log(clc.red("Error: Please provide a command."))
console.log("Commands:\n\t" + clc.yellowBright(" init") + " - Initializes the project.\n\t" + clc.blackBright(" --help") + " - Show The Commands And About\n\t")
}
}
main()