Skip to content

Instantly share code, notes, and snippets.

@sulmanweb
Last active November 5, 2024 22:06
Show Gist options
  • Save sulmanweb/70f8886ddcdef1fddb96f0b365946991 to your computer and use it in GitHub Desktop.
Save sulmanweb/70f8886ddcdef1fddb96f0b365946991 to your computer and use it in GitHub Desktop.
Generating a Markdown Documentation for Your Codebase with Ruby
require 'fileutils'
ALWAYS_IGNORE = ['.git', 'tmp', 'log', '.ruby-lsp', '.github', '.devcontainer'].freeze
IGNORED_EXTENSIONS = %w[.jpg .jpeg .png .gif .bmp .svg .webp .ico .pdf .tiff .raw].freeze
def read_gitignore(directory_path)
gitignore_path = File.join(directory_path, '.gitignore')
return [] unless File.exist?(gitignore_path)
File.readlines(gitignore_path).map(&:chomp).reject(&:empty?)
end
def ignored?(path, base_path, ignore_patterns)
relative_path = path.sub("#{base_path}/", '')
# Check if the path starts with any of the ALWAYS_IGNORE directories
return true if ALWAYS_IGNORE.any? { |dir| relative_path.start_with?(dir + '/') || relative_path == dir }
# Check if the file has an ignored extension
return true if IGNORED_EXTENSIONS.include?(File.extname(path).downcase)
ignore_patterns.any? do |pattern|
File.fnmatch?(pattern, relative_path, File::FNM_PATHNAME | File::FNM_DOTMATCH) ||
File.fnmatch?(File.join('**', pattern), relative_path, File::FNM_PATHNAME | File::FNM_DOTMATCH)
end
end
def convert_to_markdown(file_path)
extension = File.extname(file_path).downcase[1..]
format = extension.nil? || extension.empty? ? 'text' : extension
begin
content = File.read(file_path, encoding: 'UTF-8')
"# #{File.basename(file_path)}\n\n```#{format}\n#{content}\n```\n\n"
rescue StandardError => e
"# #{File.basename(file_path)}\n\n[File content not displayed: #{e.message}]\n\n"
end
end
def sanitize_anchor(text)
text.gsub(/[^a-zA-Z0-9\-_]/, '-').gsub(/-+/, '-').downcase
end
def process_directory(directory_path, output_file)
ignore_patterns = read_gitignore(directory_path)
markdown_content = "# File Documentation\n\n## Table of Contents\n\n"
file_contents = []
Dir.glob("#{directory_path}/**/*", File::FNM_DOTMATCH).each do |file_path|
next if File.directory?(file_path)
next if ['.', '..'].include?(File.basename(file_path))
next if ignored?(file_path, directory_path, ignore_patterns)
relative_path = file_path.sub("#{directory_path}/", '')
anchor = sanitize_anchor(relative_path)
markdown_content += "- [#{relative_path}](##{anchor})\n"
file_contents << "## #{relative_path}\n\n#{convert_to_markdown(file_path)}"
end
markdown_content += "\n---\n\n" + file_contents.join("\n---\n\n")
File.write(output_file, markdown_content)
puts "Markdown file created: #{output_file}"
end
# Check if correct number of arguments are provided
if ARGV.length != 2
puts "Usage: ruby ruby_to_md.rb <input_directory> <output_file>"
exit 1
end
input_directory = ARGV[0]
output_file = ARGV[1]
process_directory(input_directory, output_file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment