Dumps emmy lua to markdown documentation files and a single API file
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

230 lines
5.9 KiB

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);