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