commit 95f2b6b49986c3db440ec07f4dfe706e9b501371 Author: 4n0n <4n0n@tinkr.site> Date: Fri Feb 3 12:44:39 2023 -0600 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6acabd2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.vscode +.DS_Store + +## allow .gitkeep files +!.gitkeep + +include/* +!include/.gitkeep + +output/* +!output/.gitkeep \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..2623d3d --- /dev/null +++ b/index.js @@ -0,0 +1,230 @@ +const fs = require("fs"); +const path = require("path"); + +// Extract emmy lua classes from a file +// "---@class Bastion" -> "Bastion" +function extractClasses(file) { + const classes = []; + const lines = file.split("\n"); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.startsWith("---@class ")) { + const className = line.replace("---@class ", ""); + classes.push(className); + } + } + return classes; +} + +// console.log( +// extractClasses(fs.readFileSync(path.join(__dirname, "Bastion.lua"), "utf8")) +// ); + +// Extract emmy lua functions from a file +/* +"---@param name string" -> "name", "string" +"---@param name2 string|nil" -> "name2", "string|nil" +"---@return string" -> "string" +"function Bastion:Test(name, name2)" -> "Bastion", "Test" + +Every function documentation starts with a ---@param line or a ---@return line and ends with a function line, we should ignore any +other lines in between these lines + +return { + name: "Bastion", + function: "Test", + params: [ + { + name: "name", + type: "string" + }, + { + name: "name2", + type: "string|nil" + }, + ], + returns: [ + { + type: "string" + } + ] +} +*/ +function extractFunctions(file) { + const functions = []; + const lines = file.split("\n"); + + let params = []; + let returns = []; + let description = ""; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + if (line.startsWith("---@param ")) { + const paramLine = line.replace("---@param ", ""); + + // get the name (everything before the first space) and the types (everything after the first space allowing for spaces in the types) + const name = paramLine.split(" ")[0]; + const type = paramLine.replace(name + " ", ""); + + params.push({ + name, + type, + }); + } else if (line.startsWith("---@return ")) { + const returnLine = line.replace("---@return ", ""); + const type = returnLine; + returns.push({ + type, + }); + } else if (line.startsWith("---") && !line.startsWith("---@")) { + // This is a description line + description += line.replace("---", "") + "\n"; + } else if (line.startsWith("function ")) { + // We have reached the end of the function documentation, now we can extract the function name and class name + + if (params.length === 0 && returns.length === 0) { + // There is no documentation for this function + continue; + } + + const functionLine = line.replace("function ", ""); + const [className, functionName] = functionLine.split(":"); + functions.push({ + className: className, + function: functionName, + params, + returns, + description: description, + }); + + // Reset the params and returns array + params = []; + returns = []; + description = ""; + } + } + return functions; +} + +let globalFunctions = []; + +// dump a file to markdown +function Dump(filePath) { + const fileName = path.basename(filePath); + + const functions = extractFunctions(fs.readFileSync(filePath, "utf8")); + globalFunctions = [...globalFunctions, ...functions]; + + const classes = extractClasses(fs.readFileSync(filePath, "utf8")).map( + (className) => { + return { + name: className, + functions: functions.filter((func) => func.className === className), + }; + } + ); + + // console.log(classes); + + // write the data to disk as a markdown file + const markdown = classes + .map((classData) => { + const classMarkdown = `# ${classData.name} + + + ${classData.functions + .map((func) => { + return `## ${func.className}:${func.function} + + ${func.description} + + ${func.params.length > 0 ? "### Parameters" : ""} + ${func.params + .map((param) => { + return `\`${param.name} (${param.type})\``; + }) + .join("\n")} + + ${func.returns.length > 0 ? "### Returns" : ""} + ${func.returns + .map((ret) => { + return `\`-> ${ret.type}\``; + }) + .join("\n")}`; + }) + .join("\n")}`; + + return classMarkdown; + }) + .join("\n"); + + const p = path.join(filePath.replace("input", "output")) + ".md"; + + // create the output directory if it doesn't exist + if (!fs.existsSync(path.dirname(p))) { + fs.mkdirSync(path.dirname(p), { + recursive: true, + }); + } + + fs.writeFileSync(p, markdown); +} + +// Dump(path.join(__dirname, "Bastion.lua")); + +function DumpDirectory(directory) { + // get all the files in the directory + const files = fs.readdirSync(directory); + + // filter out the files that are not lua files + const luaFiles = files.filter((file) => file.endsWith(".lua")); + + // dump each file + luaFiles.forEach((file) => { + Dump(path.join(directory, file)); + }); + + // dump the files in the subdirectories + const subDirectories = files.filter((file) => + fs.lstatSync(path.join(directory, file)).isDirectory() + ); + subDirectories.forEach((subDirectory) => { + DumpDirectory(path.join(directory, subDirectory)); + }); +} + +function DumpAPIFile(_funcs) { + // console.log(_funcs); + const lua = _funcs + .map((func) => { + let str = ""; + + if (func.description !== "") { + str += `--- ${func.description}\n`; + } + + func.params.map((param) => { + str += `---@param ${param.name} ${param.type}\n`; + }); + + func.returns.map((ret) => { + str += `---@return ${ret.type}\n`; + }); + + str += `function ${func.className}:${func.function} end`; + + return str; + }) + .join("\n\n"); + + fs.writeFileSync(path.join(__dirname, "output", "API.lua"), lua); +} + +// Wipe the output directory +fs.rmdirSync(path.join(__dirname, "output"), { recursive: true }); +fs.mkdirSync(path.join(__dirname, "output")); + +DumpDirectory(path.join(__dirname, "input")); +DumpAPIFile(globalFunctions); diff --git a/input/.gitkeep b/input/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/output/.gitkeep b/output/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..5bec612 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "emmy-lua-extractor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +}