diff --git a/.lune/docsgen/init.luau b/.lune/docsgen/init.luau new file mode 100644 index 0000000..76c1f42 --- /dev/null +++ b/.lune/docsgen/init.luau @@ -0,0 +1,32 @@ +local process = require("@lune/process") +local serde = require("@lune/serde") + +local moonwave = require("./moonwave") +local logger = require("./log") +local writeMarkdown = require("./markdown") + +local function extract(input: string): (number, { moonwave.Item }?) + local res = process.spawn("moonwave-extractor", { "extract", input }, { + stdio = { + stderr = "forward" + } + }) + + if not res.ok then + print() + logger.log("error", "`moonwave-extractor` failed with exit code", res.code) + return res.code, nil + end + + local ok, items: { moonwave.Item } = pcall(serde.decode, "json" :: "json", res.stdout) + if not ok then + return 1, nil + end + + return 0, items +end + +local code, items = extract("lib/init.luau") +writeMarkdown("./docs/index.md", items :: { moonwave.Item }) + +process.exit(code) diff --git a/.lune/docsgen/log.luau b/.lune/docsgen/log.luau new file mode 100644 index 0000000..77726d4 --- /dev/null +++ b/.lune/docsgen/log.luau @@ -0,0 +1,24 @@ +local stdio = require("@lune/stdio") + +local base = stdio.style("bold") + +local STYLE_INFO = base .. `{stdio.color("green")}info{stdio.color("reset")}:` +local STYLE_WARN = base .. `{stdio.color("yellow")}warn{stdio.color("reset")}:` +local STYLE_ERROR = base .. `{stdio.color("red")}error{stdio.color("reset")}:` + +export type LogType = "info" | "warn" | "error" +local styleMappings: { [LogType]: string } = { + info = STYLE_INFO, + warn = STYLE_WARN, + error = STYLE_ERROR, +} + +return { + styles = styleMappings, + log = function(type: LogType, ...: T...): () + local writer: (string) -> () = if type == "info" then stdio.write else stdio.ewrite + local fmtMsg = stdio.format(styleMappings[type], ...) + + return writer(fmtMsg .. "\n") + end +} diff --git a/.lune/docsgen/markdown.luau b/.lune/docsgen/markdown.luau new file mode 100644 index 0000000..254811b --- /dev/null +++ b/.lune/docsgen/markdown.luau @@ -0,0 +1,172 @@ +local fs = require("@lune/fs") + +local moonwave = require("./moonwave") + +local function writeSectionHeader(buf: string, title: string) + buf ..= `## {title}\n` + return buf +end + +local function writeRef(buf: string, name: string, fragment: string?) + buf ..= `\n[{name}]: #{fragment or name}\n` + return buf +end + +local function writeClass(buf: string, name: string, desc: string) + buf ..= `# \`{name}\`\n` + buf ..= desc + buf ..= `\n\n` + return buf +end + +local function writeDeclaration(buf: string, name: string, fields: { moonwave.Property }) + buf ..= `\`\`\`luau\n` + buf ..= `export type {name} = \{\n` + for _, field in fields do + buf ..= `\t{field.name}: {field.lua_type},\n` + end + buf ..= "}\n" + buf ..= `\`\`\`\n` + return buf +end + +local function writeProperty(buf: string, name: string, desc: string, type: string) + -- buf ..= `- **\`{name}: {type}\`** - {desc}\n` + buf ..= `- **{name}** - {desc}\n` + return buf +end + +local function writeFunction( + buf: string, + class: string, + type: string, + name: string, + desc: string, + params: { moonwave.FunctionParam }, + returns: { moonwave.FunctionReturn }, + private: boolean +) + local sep = if type == "method" then ":" else "." + local declaredSignature = `{class}{sep}{name}` + buf ..= `### \`{name}\`\n` + + if private then + buf ..= `> [!IMPORTANT]\n` + buf ..= `> This is a private API. It may be exported publically, but try to avoid\n` + buf ..= `> using this API, since it can have breaking changes at any time without\n` + buf ..= `> warning.\n\n` + end + + buf ..= `{desc}\n` + buf ..= `\`\`\`luau\n` + buf ..= `{declaredSignature}(` + if #params > 0 then + buf ..= "\n" + for _, param in params do + buf ..= `\t{param.name}: {param.lua_type}, -- {param.desc}\n` + end + end + buf ..= `)` + + if #returns > 0 then + if #returns == 1 then + buf ..= `: {returns[1].lua_type}\n` + else + for pos, ret in returns do + buf ..= `({ret.lua_type}` + if pos ~= #returns then + buf ..= `, ` + end + end + buf ..= `)` + end + end + + buf ..= `\n\`\`\`\n` + buf = writeRef(buf, declaredSignature, name) + return buf +end + +local function writeType(buf: string, name: string, desc: string, type: string) + buf ..= `\`\`\`luau\n` + buf ..= `export type {name} = {type}\n` + buf ..= `\`\`\`\n` + return buf +end + +local function writeMarkdown(path: string, items: { moonwave.Item }) + local buf = "" + for _, item in items do + buf = writeClass(buf, item.name, item.desc) + + local props: { moonwave.Property } = {} + for pos, type in item.types do + if type.name == item.name then + table.remove(item.types, pos) + props = type.fields + end + end + + buf = writeDeclaration(buf, item.name, props) + buf = writeSectionHeader(buf, "Properties") + for _, prop in props do + if prop.ignore then + continue + end + + buf = writeProperty(buf, prop.name, prop.desc, prop.lua_type) + end + buf ..= "\n" + + buf = writeSectionHeader(buf, "API") + for _, func in item.functions do + if func.ignore then + continue + end + + buf = writeFunction( + buf, + item.name, + func.function_type, + func.name, + func.desc, + func.params, + func.returns, + func.private + ) + end + buf ..= "\n" + + buf = writeSectionHeader(buf, "Types") + for _, type in item.types do + if type.ignore then + continue + end + + buf ..= `### \`{type.name}\`\n` + if type.private then + buf ..= `> [!IMPORTANT]\n` + buf ..= `> This is a private type. It may be exported publically, but try to avoid\n` + buf ..= `> using it, since its definition can have a breaking change at any time\n` + buf ..= `> without warning.\n\n` + end + buf ..= `{type.desc}\n` + if type.lua_type ~= nil then + buf = writeType(buf, type.name, type.desc, type.lua_type) + else + local fields: { moonwave.Property } = type.fields or {} + buf = writeDeclaration(buf, type.name, fields) + for _, field in fields do + buf = writeProperty(buf, field.name, field.desc, field.lua_type) + end + end + buf = writeRef(buf, type.name) + end + + buf = writeRef(buf, item.name) + end + + fs.writeFile(path, buf) +end + +return writeMarkdown diff --git a/.lune/docsgen/moonwave.luau b/.lune/docsgen/moonwave.luau new file mode 100644 index 0000000..2f47e1f --- /dev/null +++ b/.lune/docsgen/moonwave.luau @@ -0,0 +1,59 @@ +--> Copied from https://github.com/lune-org/docs/blob/0a1e5a/.lune/moonwave.luau + +export type Source = { + path: string, + line: number, +} + +export type FunctionParam = { + name: string, + desc: string, + lua_type: string, +} + +export type FunctionReturn = { + desc: string, + lua_type: string, +} + +export type Function = { + name: string, + desc: string, + params: { FunctionParam }, + returns: { FunctionReturn }, + function_type: string, + tags: { string }?, + ignore: boolean, + private: boolean, + source: Source, +} + +export type Property = { + name: string, + desc: string, + lua_type: string, + tags: { string }?, + ignore: boolean, + source: Source, +} + +export type Type = { + name: string, + desc: string, + lua_type: string, + ignore: boolean, + private: boolean, + fields: { Property }, + source: Source, +} + +export type Item = { + name: string, + desc: string, + functions: { Function }, + properties: { Property }, + types: { Type }, + source: Source, +} + +return {} diff --git a/dev.nix b/dev.nix index b22af8f..0bba225 100644 --- a/dev.nix +++ b/dev.nix @@ -111,6 +111,13 @@ pkgs.mkShell { artifactName = "pesde-0.6.0-rc.8-linux-x86_64.zip"; sha256 = "xjY8yPTAD32xeQokHDSWBOiGALLxPOU89Xlxi2Jdnno="; }) + (fromGithubRelease { + name = "evaera/moonwave"; + exeName = "moonwave-extractor"; + version = "v1.2.1"; + artifactName = "moonwave-extractor-v1.2.1-linux.zip"; + sha256 = "UPZ5ZIazNNBcqny7srFIkHqX0t09r0F1M9q4KyjLNgQ="; + }) (fromPesdeManifest { name = "JohnnyMorganz/luau-lsp"; exeName = "luau-lsp";