commit
95f2b6b499
@ -0,0 +1,11 @@ |
|||||||
|
.vscode |
||||||
|
.DS_Store |
||||||
|
|
||||||
|
## allow .gitkeep files |
||||||
|
!.gitkeep |
||||||
|
|
||||||
|
include/* |
||||||
|
!include/.gitkeep |
||||||
|
|
||||||
|
output/* |
||||||
|
!output/.gitkeep |
@ -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); |
@ -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" |
||||||
|
} |
Loading…
Reference in new issue