1#!/usr/bin/env ruby 2 3# This script coalesces the AsciiDoc content from a document master into a 4# single output file. It does so by resolving all preprocessor directives in 5# the document, and in any files which are included. The resolving of include 6# directives is likely of most interest to users of this script. 7# 8# This script works by using Asciidoctor's PreprocessorReader to read and 9# resolve all the lines in the specified input file. The script then writes the 10# result to the output. 11# 12# The script only recognizes attributes passed in as options or those defined 13# in the document header. It does not currently process attributes defined in 14# other, arbitrary locations within the document. 15# 16# You can find a similar extension written against AsciidoctorJ here: 17# https://github.com/hibernate/hibernate-asciidoctor-extensions/blob/master/src/main/java/org/hibernate/infra/asciidoctor/extensions/savepreprocessed/SavePreprocessedOutputPreprocessor.java 18 19# TODO 20# - add cli option to write attributes passed to cli to header of document 21# - escape all preprocessor directives after lines are processed (these are preprocessor directives that were escaped in the input) 22# - wrap in a custom converter so it can be used as an extension 23 24require 'asciidoctor' 25require 'optparse' 26 27options = { attributes: [], output: '-' } 28OptionParser.new do |opts| 29 opts.banner = 'Usage: ruby asciidoc-coalescer.rb [OPTIONS] FILE' 30 opts.on('-a', '--attribute key[=value]', 'A document attribute to set in the form of key[=value]') do |a| 31 options[:attributes] << a 32 end 33 opts.on('-o', '--output FILE', 'Write output to FILE instead of stdout.') do |o| 34 options[:output] = o 35 end 36end.parse! 37 38unless (source_file = ARGV.shift) 39 warn 'Please specify an AsciiDoc source file to coalesce.' 40 exit 1 41end 42 43unless (output_file = options[:output]) == '-' 44 if (output_file = File.expand_path output_file) == (File.expand_path source_file) 45 warn 'Source and output cannot be the same file.' 46 exit 1 47 end 48end 49 50# NOTE first, resolve attributes defined at the end of the document header 51# QUESTION can we do this in a single load? 52doc = Asciidoctor.load_file source_file, safe: :unsafe, header_only: true, attributes: options[:attributes] 53# NOTE quick and dirty way to get the attributes set or unset by the document header 54header_attr_names = (doc.instance_variable_get :@attributes_modified).to_a 55header_attr_names.each {|k| doc.attributes[%(#{k}!)] = '' unless doc.attr? k } 56 57doc = Asciidoctor.load_file source_file, safe: :unsafe, parse: false, attributes: doc.attributes 58# FIXME also escape ifdef, ifndef, ifeval and endif directives 59# FIXME do this more carefully by reading line by line; if input differs by output by leading backslash, restore original line 60lines = doc.reader.read.gsub(/^include::(?=.*\[\]$)/m, '\\include::') 61 62if output_file == '-' 63 puts lines 64else 65 File.open(output_file, 'w') {|f| f.write lines } 66end