Module: Esp::DocsGenerator

Defined in:
lib/esp/docs_generator.rb

Overview

Renders the data from Esp::Introspection into markdown files under docs/reference/. Each file is split into two regions: an ‘esp:auto` block (regenerated by `esp docs build`, enforced fresh by lefthook so it can’t drift) and a hand-written tail below it that survives rebuilds. write_doc rewrites only the auto block and preserves the tail verbatim, so an unchanged source reproduces the file byte-for-byte — that idempotence is what lets the freshness hook keep diffing the whole file.

The marker literal kept the ‘mw:auto` token for one release so existing generated files migrate cleanly: manual_tail accepts both the new `esp:auto` marker and the legacy `mw:auto` one. After the next regen, every file carries the new marker and the fallback is dead code.

Constant Summary collapse

AUTO_OPEN =
'<!-- esp:auto — regenerated by `esp docs build`; edits here are overwritten -->'.freeze
AUTO_CLOSE =
'<!-- /esp:auto — write durable docs below this line; they survive rebuilds -->'.freeze
LEGACY_AUTO_CLOSE =
'<!-- /mw:auto — write durable docs below this line; they survive rebuilds -->'.freeze

Class Method Summary collapse

Class Method Details

.build(output_dir:) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/esp/docs_generator.rb', line 23

def build(output_dir:)
  api_dir = File.join(output_dir, 'api')
  FileUtils.mkdir_p(api_dir)

  write_doc(File.join(output_dir, 'commands.md'), render_commands)
  module_docs = Esp::Introspection.module_docs
  module_docs.each do |mod|
    write_doc(File.join(api_dir, "#{slug_for(mod[:name])}.md"), render_module(mod))
  end
  write_doc(File.join(api_dir, 'index.md'), render_api_index(module_docs))

  { commands: File.join(output_dir, 'commands.md'),
    api_index: File.join(api_dir, 'index.md'),
    api_modules: module_docs.map { |m| File.join(api_dir, "#{slug_for(m[:name])}.md") } }
end

.render_api_index(modules) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/esp/docs_generator.rb', line 71

def render_api_index(modules)
  out = StringIO.new
  out.puts '# API reference'
  out.puts
  out.puts 'Core library modules. Shell modules are `Esp::<Name>`;'
  out.puts 'Morrowind-plugin modules are `Esp::Mw::<Name>`.'
  out.puts
  modules.each do |mod|
    out.puts "- [`#{mod[:name]}`](#{slug_for(mod[:name])}.md)"
  end
  out.string
end

.render_commandsObject



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/esp/docs_generator.rb', line 46

def render_commands
  out = StringIO.new
  tree = Esp::Introspection.command_tree
  out.puts '# Command reference'
  out.puts
  out.puts 'Every command accepts `--json` for structured output to stdout.'
  out.puts 'Errors print as `{"error": "..."}` to stderr with a non-zero exit.'
  out.puts
  out.puts '## Top-level commands'
  out.puts
  tree[:commands].each { |cmd| render_command(out, cmd) }
  tree[:subcommands].each { |sub| render_subcommand_section(out, sub) }
  out.string
end

.render_module(mod) ⇒ Object



61
62
63
64
65
66
67
68
69
# File 'lib/esp/docs_generator.rb', line 61

def render_module(mod)
  <<~MD
    # #{mod[:name]}

    **Source:** `#{mod[:source]}`

    #{mod[:description]}
  MD
end

.write_doc(path, body) ⇒ Object

Write the regenerated auto block, then re-attach the file’s existing hand-written tail (everything after AUTO_CLOSE) verbatim — or scaffold one if the file is new.



42
43
44
# File 'lib/esp/docs_generator.rb', line 42

def write_doc(path, body)
  File.write(path, "#{AUTO_OPEN}\n\n#{body.strip}\n\n#{AUTO_CLOSE}#{manual_tail(path)}")
end