lib/asciidoctor/convert.rb in asciidoctor-2.0.10 vs lib/asciidoctor/convert.rb in asciidoctor-2.0.11

- old
+ new

@@ -1,193 +1,198 @@ module Asciidoctor - module_function + class << self + # Public: Parse the AsciiDoc source input into an Asciidoctor::Document and + # convert it to the specified backend format. + # + # Accepts input as an IO (or StringIO), String or String Array object. If the + # input is a File, the object is expected to be opened for reading and is not + # closed afterwards by this method. Information about the file (filename, + # directory name, etc) gets assigned to attributes on the Document object. + # + # If the :to_file option is true, and the input is a File, the output is + # written to a file adjacent to the input file, having an extension that + # corresponds to the backend format. Otherwise, if the :to_file option is + # specified, the file is written to that file. If :to_file is not an absolute + # path, it is resolved relative to :to_dir, if given, otherwise the + # Document#base_dir. If the target directory does not exist, it will not be + # created unless the :mkdirs option is set to true. If the file cannot be + # written because the target directory does not exist, or because it falls + # outside of the Document#base_dir in safe mode, an IOError is raised. + # + # If the output is going to be written to a file, the header and footer are + # included unless specified otherwise (writing to a file implies creating a + # standalone document). Otherwise, the header and footer are not included by + # default and the converted result is returned. + # + # input - the String AsciiDoc source filename + # options - a String, Array or Hash of options to control processing (default: {}) + # String and Array values are converted into a Hash. + # See Asciidoctor::Document#initialize for details about options. + # + # Returns the Document object if the converted String is written to a + # file, otherwise the converted String + def convert input, options = {} + (options = options.merge).delete :parse + to_dir = options.delete :to_dir + mkdirs = options.delete :mkdirs - # Public: Parse the AsciiDoc source input into an Asciidoctor::Document and - # convert it to the specified backend format. - # - # Accepts input as an IO (or StringIO), String or String Array object. If the - # input is a File, the object is expected to be opened for reading and is not - # closed afterwards by this method. Information about the file (filename, - # directory name, etc) gets assigned to attributes on the Document object. - # - # If the :to_file option is true, and the input is a File, the output is - # written to a file adjacent to the input file, having an extension that - # corresponds to the backend format. Otherwise, if the :to_file option is - # specified, the file is written to that file. If :to_file is not an absolute - # path, it is resolved relative to :to_dir, if given, otherwise the - # Document#base_dir. If the target directory does not exist, it will not be - # created unless the :mkdirs option is set to true. If the file cannot be - # written because the target directory does not exist, or because it falls - # outside of the Document#base_dir in safe mode, an IOError is raised. - # - # If the output is going to be written to a file, the header and footer are - # included unless specified otherwise (writing to a file implies creating a - # standalone document). Otherwise, the header and footer are not included by - # default and the converted result is returned. - # - # input - the String AsciiDoc source filename - # options - a String, Array or Hash of options to control processing (default: {}) - # String and Array values are converted into a Hash. - # See Asciidoctor::Document#initialize for details about options. - # - # Returns the Document object if the converted String is written to a - # file, otherwise the converted String - def convert input, options = {} - (options = options.merge).delete :parse - to_dir = options.delete :to_dir - mkdirs = options.delete :mkdirs - - case (to_file = options.delete :to_file) - when true, nil - unless (write_to_target = to_dir) - sibling_path = ::File.absolute_path input.path if ::File === input + case (to_file = options.delete :to_file) + when true, nil + unless (write_to_target = to_dir) + sibling_path = ::File.absolute_path input.path if ::File === input + end + to_file = nil + when false + to_file = nil + when '/dev/null' + return load input, options + else + options[:to_file] = write_to_target = to_file unless (stream_output = to_file.respond_to? :write) end - to_file = nil - when false - to_file = nil - when '/dev/null' - return load input, options - else - options[:to_file] = write_to_target = to_file unless (stream_output = to_file.respond_to? :write) - end - unless options.key? :standalone - if sibling_path || write_to_target - options[:standalone] = options.fetch :header_footer, true - elsif options.key? :header_footer - options[:standalone] = options[:header_footer] + unless options.key? :standalone + if sibling_path || write_to_target + options[:standalone] = options.fetch :header_footer, true + elsif options.key? :header_footer + options[:standalone] = options[:header_footer] + end end - end - # NOTE outfile may be controlled by document attributes, so resolve outfile after loading - if sibling_path - options[:to_dir] = outdir = ::File.dirname sibling_path - elsif write_to_target - if to_dir - if to_file - options[:to_dir] = ::File.dirname ::File.expand_path ::File.join to_dir, to_file - else - options[:to_dir] = ::File.expand_path to_dir + # NOTE outfile may be controlled by document attributes, so resolve outfile after loading + if sibling_path + options[:to_dir] = outdir = ::File.dirname sibling_path + elsif write_to_target + if to_dir + if to_file + options[:to_dir] = ::File.dirname ::File.expand_path to_file, to_dir + else + options[:to_dir] = ::File.expand_path to_dir + end + elsif to_file + options[:to_dir] = ::File.dirname ::File.expand_path to_file end - elsif to_file - options[:to_dir] = ::File.dirname ::File.expand_path to_file end - end - # NOTE :to_dir is always set when outputting to a file - # NOTE :to_file option only passed if assigned an explicit path - doc = load input, options + # NOTE :to_dir is always set when outputting to a file + # NOTE :to_file option only passed if assigned an explicit path + doc = load input, options - if sibling_path # write to file in same directory - outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix}) - raise ::IOError, %(input file and output file cannot be the same: #{outfile}) if outfile == sibling_path - elsif write_to_target # write to explicit file or directory - working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) : ::Dir.pwd - # QUESTION should the jail be the working_dir or doc.base_dir??? - jail = doc.safe >= SafeMode::SAFE ? working_dir : nil - if to_dir - outdir = doc.normalize_system_path(to_dir, working_dir, jail, target_name: 'to_dir', recover: false) - if to_file - outfile = doc.normalize_system_path(to_file, outdir, nil, target_name: 'to_dir', recover: false) - # reestablish outdir as the final target directory (in the case to_file had directory segments) + if sibling_path # write to file in same directory + outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix}) + raise ::IOError, %(input file and output file cannot be the same: #{outfile}) if outfile == sibling_path + elsif write_to_target # write to explicit file or directory + working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) : ::Dir.pwd + # QUESTION should the jail be the working_dir or doc.base_dir??? + jail = doc.safe >= SafeMode::SAFE ? working_dir : nil + if to_dir + outdir = doc.normalize_system_path(to_dir, working_dir, jail, target_name: 'to_dir', recover: false) + if to_file + outfile = doc.normalize_system_path(to_file, outdir, nil, target_name: 'to_dir', recover: false) + # reestablish outdir as the final target directory (in the case to_file had directory segments) + outdir = ::File.dirname outfile + else + outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix}) + end + elsif to_file + outfile = doc.normalize_system_path(to_file, working_dir, jail, target_name: 'to_dir', recover: false) + # establish outdir as the final target directory (in the case to_file had directory segments) outdir = ::File.dirname outfile + end + + if ::File === input && outfile == (::File.absolute_path input.path) + raise ::IOError, %(input file and output file cannot be the same: #{outfile}) + end + + if mkdirs + Helpers.mkdir_p outdir else - outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix}) + # NOTE we intentionally refer to the directory as it was passed to the API + raise ::IOError, %(target directory does not exist: #{to_dir} (hint: set :mkdirs option)) unless ::File.directory? outdir end - elsif to_file - outfile = doc.normalize_system_path(to_file, working_dir, jail, target_name: 'to_dir', recover: false) - # establish outdir as the final target directory (in the case to_file had directory segments) - outdir = ::File.dirname outfile + else # write to stream + outfile = to_file + outdir = nil end - if ::File === input && outfile == (::File.absolute_path input.path) - raise ::IOError, %(input file and output file cannot be the same: #{outfile}) - end - - if mkdirs - Helpers.mkdir_p outdir + if outfile && !stream_output + output = doc.convert 'outfile' => outfile, 'outdir' => outdir else - # NOTE we intentionally refer to the directory as it was passed to the API - raise ::IOError, %(target directory does not exist: #{to_dir} (hint: set :mkdirs option)) unless ::File.directory? outdir + output = doc.convert end - else # write to stream - outfile = to_file - outdir = nil - end - if outfile && !stream_output - output = doc.convert 'outfile' => outfile, 'outdir' => outdir - else - output = doc.convert - end + if outfile + doc.write output, outfile - if outfile - doc.write output, outfile - - # NOTE document cannot control this behavior if safe >= SafeMode::SERVER - # NOTE skip if stylesdir is a URI - if !stream_output && doc.safe < SafeMode::SECURE && (doc.attr? 'linkcss') && (doc.attr? 'copycss') && - (doc.basebackend? 'html') && !((stylesdir = (doc.attr 'stylesdir')) && (Helpers.uriish? stylesdir)) - if (stylesheet = doc.attr 'stylesheet') - if DEFAULT_STYLESHEET_KEYS.include? stylesheet - copy_asciidoctor_stylesheet = true - elsif !(Helpers.uriish? stylesheet) - copy_user_stylesheet = true + # NOTE document cannot control this behavior if safe >= SafeMode::SERVER + # NOTE skip if stylesdir is a URI + if !stream_output && doc.safe < SafeMode::SECURE && (doc.attr? 'linkcss') && (doc.attr? 'copycss') && + (doc.basebackend? 'html') && !((stylesdir = (doc.attr 'stylesdir')) && (Helpers.uriish? stylesdir)) + if (stylesheet = doc.attr 'stylesheet') + if DEFAULT_STYLESHEET_KEYS.include? stylesheet + copy_asciidoctor_stylesheet = true + elsif !(Helpers.uriish? stylesheet) + copy_user_stylesheet = true + end end - end - copy_syntax_hl_stylesheet = (syntax_hl = doc.syntax_highlighter) && (syntax_hl.write_stylesheet? doc) - if copy_asciidoctor_stylesheet || copy_user_stylesheet || copy_syntax_hl_stylesheet - stylesoutdir = doc.normalize_system_path(stylesdir, outdir, doc.safe >= SafeMode::SAFE ? outdir : nil) - if mkdirs - Helpers.mkdir_p stylesoutdir - else - raise ::IOError, %(target stylesheet directory does not exist: #{stylesoutdir} (hint: set :mkdirs option)) unless ::File.directory? stylesoutdir - end - - if copy_asciidoctor_stylesheet - Stylesheets.instance.write_primary_stylesheet stylesoutdir - # FIXME should Stylesheets also handle the user stylesheet? - elsif copy_user_stylesheet - if (stylesheet_src = doc.attr 'copycss').empty? - stylesheet_src = doc.normalize_system_path stylesheet + copy_syntax_hl_stylesheet = (syntax_hl = doc.syntax_highlighter) && (syntax_hl.write_stylesheet? doc) + if copy_asciidoctor_stylesheet || copy_user_stylesheet || copy_syntax_hl_stylesheet + stylesoutdir = doc.normalize_system_path(stylesdir, outdir, doc.safe >= SafeMode::SAFE ? outdir : nil) + if mkdirs + Helpers.mkdir_p stylesoutdir else - # NOTE in this case, copycss is a source location (but cannot be a URI) - stylesheet_src = doc.normalize_system_path stylesheet_src + raise ::IOError, %(target stylesheet directory does not exist: #{stylesoutdir} (hint: set :mkdirs option)) unless ::File.directory? stylesoutdir end - stylesheet_dest = doc.normalize_system_path stylesheet, stylesoutdir, (doc.safe >= SafeMode::SAFE ? outdir : nil) - # NOTE don't warn if src can't be read and dest already exists (see #2323) - if stylesheet_src != stylesheet_dest && (stylesheet_data = doc.read_asset stylesheet_src, - warn_on_failure: !(::File.file? stylesheet_dest), label: 'stylesheet') - ::File.write stylesheet_dest, stylesheet_data, mode: FILE_WRITE_MODE + + if copy_asciidoctor_stylesheet + Stylesheets.instance.write_primary_stylesheet stylesoutdir + # FIXME should Stylesheets also handle the user stylesheet? + elsif copy_user_stylesheet + if (stylesheet_src = doc.attr 'copycss') == '' || stylesheet_src == true + stylesheet_src = doc.normalize_system_path stylesheet + else + # NOTE in this case, copycss is a source location (but cannot be a URI) + stylesheet_src = doc.normalize_system_path stylesheet_src.to_s + end + stylesheet_dest = doc.normalize_system_path stylesheet, stylesoutdir, (doc.safe >= SafeMode::SAFE ? outdir : nil) + # NOTE don't warn if src can't be read and dest already exists (see #2323) + if stylesheet_src != stylesheet_dest && (stylesheet_data = doc.read_asset stylesheet_src, + warn_on_failure: !(::File.file? stylesheet_dest), label: 'stylesheet') + if (stylesheet_outdir = ::File.dirname stylesheet_dest) != stylesoutdir && !(::File.directory? stylesheet_outdir) + if mkdirs + Helpers.mkdir_p stylesheet_outdir + else + raise ::IOError, %(target stylesheet directory does not exist: #{stylesheet_outdir} (hint: set :mkdirs option)) + end + end + ::File.write stylesheet_dest, stylesheet_data, mode: FILE_WRITE_MODE + end end + syntax_hl.write_stylesheet doc, stylesoutdir if copy_syntax_hl_stylesheet end - syntax_hl.write_stylesheet doc, stylesoutdir if copy_syntax_hl_stylesheet end + doc + else + output end - doc - else - output end - end - # Public: Parse the contents of the AsciiDoc source file into an - # Asciidoctor::Document and convert it to the specified backend format. - # - # input - the String AsciiDoc source filename - # options - a String, Array or Hash of options to control processing (default: {}) - # String and Array values are converted into a Hash. - # See Asciidoctor::Document#initialize for details about options. - # - # Returns the Document object if the converted String is written to a - # file, otherwise the converted String - def convert_file filename, options = {} - ::File.open(filename, FILE_READ_MODE) {|file| convert file, options } - end + # Public: Parse the contents of the AsciiDoc source file into an + # Asciidoctor::Document and convert it to the specified backend format. + # + # input - the String AsciiDoc source filename + # options - a String, Array or Hash of options to control processing (default: {}) + # String and Array values are converted into a Hash. + # See Asciidoctor::Document#initialize for details about options. + # + # Returns the Document object if the converted String is written to a + # file, otherwise the converted String + def convert_file filename, options = {} + ::File.open(filename, FILE_READ_MODE) {|file| convert file, options } + end - # Deprecated: Use {Asciidoctor.convert} instead. - alias render convert - module_function :render + # Deprecated: Use {Asciidoctor.convert} instead. + alias render convert - # Deprecated: Use {Asciidoctor.convert_file} instead. - alias render_file convert_file - module_function :render_file + # Deprecated: Use {Asciidoctor.convert_file} instead. + alias render_file convert_file + end end