lib/jazzy/config.rb in jazzy-0.13.7 vs lib/jazzy/config.rb in jazzy-0.14.0

- old
+ new

@@ -1,16 +1,18 @@ +# frozen_string_literal: true + require 'optparse' require 'pathname' require 'uri' require 'jazzy/podspec_documenter' require 'jazzy/source_declaration/access_control_level' module Jazzy # rubocop:disable Metrics/ClassLength class Config - # rubocop:disable Style/AccessorMethodName + # rubocop:disable Naming/AccessorMethodName class Attribute attr_reader :name, :description, :command_line, :config_file_key, :default, :parse def initialize(name, description: nil, command_line: nil, @@ -48,10 +50,11 @@ config.method("#{name}_configured").call end def attach_to_option_parser(config, opt) return if command_line.empty? + opt.on(*command_line, *description) do |val| set(config, val) end end @@ -68,15 +71,16 @@ if long_option_name = long_option_names.compact.first long_option_name.tr('-', '_') end end end - # rubocop:enable Style/AccessorMethodName + # rubocop:enable Naming/AccessorMethodName def self.config_attr(name, **opts) attr_accessor name attr_accessor "#{name}_configured" + @all_config_attrs ||= [] @all_config_attrs << Attribute.new(name, **opts) end def self.alias_config_attr(name, forward, **opts) @@ -110,11 +114,11 @@ hide_declarations == 'objc' end # ──────── Build ──────── - # rubocop:disable Layout/AlignParameters + # rubocop:disable Layout/ArgumentAlignment config_attr :output, description: 'Folder to output the HTML docs to', command_line: ['-o', '--output FOLDER'], default: 'docs', @@ -122,11 +126,11 @@ config_attr :clean, command_line: ['-c', '--[no-]clean'], description: ['Delete contents of output directory before running. ', 'WARNING: If --output is set to ~/Desktop, this will '\ - 'delete the ~/Desktop directory.'], + 'delete the ~/Desktop directory.'], default: false config_attr :objc_mode, command_line: '--[no-]objc', description: 'Generate docs for Objective-C.', @@ -148,14 +152,14 @@ default: 'macosx' config_attr :hide_declarations, command_line: '--hide-declarations [objc|swift] ', description: 'Hide declarations in the specified language. Given that ' \ - 'generating Swift docs only generates Swift declarations, ' \ - 'this is useful for hiding a specific interface for ' \ - 'either Objective-C or mixed Objective-C and Swift ' \ - 'projects.', + 'generating Swift docs only generates Swift declarations, ' \ + 'this is useful for hiding a specific interface for ' \ + 'either Objective-C or mixed Objective-C and Swift ' \ + 'projects.', default: '' config_attr :keep_property_attributes, command_line: '--[no-]keep-property-attributes', description: 'Include the default Objective-C property attributes.', @@ -168,11 +172,11 @@ parse: ->(cf) { expand_path(cf) } config_attr :build_tool_arguments, command_line: ['-b', '--build-tool-arguments arg1,arg2,…argN', Array], description: 'Arguments to forward to xcodebuild, swift build, or ' \ - 'sourcekitten.', + 'sourcekitten.', default: [] alias_config_attr :xcodebuild_arguments, :build_tool_arguments, command_line: ['-x', '--xcodebuild-arguments arg1,arg2,…argN', Array], description: 'Back-compatibility alias for build_tool_arguments.' @@ -190,20 +194,20 @@ parse: ->(sd) { expand_path(sd) } config_attr :excluded_files, command_line: ['-e', '--exclude filepath1,filepath2,…filepathN', Array], description: 'Source file pathnames to be excluded from documentation. '\ - 'Supports wildcards.', + 'Supports wildcards.', default: [], parse: ->(files) do Array(files).map { |f| expand_glob_path(f).to_s } end config_attr :included_files, command_line: ['-i', '--include filepath1,filepath2,…filepathN', Array], description: 'Source file pathnames to be included in documentation. '\ - 'Supports wildcards.', + 'Supports wildcards.', default: [], parse: ->(files) do Array(files).map { |f| expand_glob_path(f).to_s } end @@ -211,28 +215,30 @@ command_line: '--swift-version VERSION', default: nil, parse: ->(v) do if v.to_s.empty? nil + elsif v.to_f < 2 + raise 'jazzy only supports Swift 2.0 or later.' else - raise 'jazzy only supports Swift 2.0 or later.' if v.to_f < 2 v end end SWIFT_BUILD_TOOLS = %w[spm xcodebuild symbolgraph].freeze config_attr :swift_build_tool, command_line: "--swift-build-tool #{SWIFT_BUILD_TOOLS.join(' | ')}", description: 'Control whether Jazzy uses Swift Package Manager, '\ - 'xcodebuild, or swift-symbolgraph to build the module '\ - 'to be documented. By default it uses xcodebuild if '\ - 'there is a .xcodeproj file in the source directory.', + 'xcodebuild, or swift-symbolgraph to build the module '\ + 'to be documented. By default it uses xcodebuild if '\ + 'there is a .xcodeproj file in the source directory.', parse: ->(tool) do return tool.to_sym if SWIFT_BUILD_TOOLS.include?(tool) + raise "Unsupported swift_build_tool #{tool}, "\ - "supported values: #{SWIFT_BUILD_TOOLS.join(', ')}" + "supported values: #{SWIFT_BUILD_TOOLS.join(', ')}" end # ──────── Metadata ──────── config_attr :author_name, @@ -252,17 +258,17 @@ default: '' config_attr :version, command_line: '--module-version VERSION', description: 'Version string to use as part of the default docs '\ - 'title and inside the docset.', + 'title and inside the docset.', default: '1.0' config_attr :title, command_line: '--title TITLE', description: 'Title to display at the top of each page, overriding the '\ - 'default generated from module name and version.', + 'default generated from module name and version.', default: '' config_attr :copyright, command_line: '--copyright COPYRIGHT_MARKDOWN', description: 'copyright markdown rendered at the bottom of the docs pages' @@ -283,20 +289,20 @@ parse: ->(ag) { Pathname.glob(ag) } config_attr :podspec, command_line: '--podspec FILEPATH', description: 'A CocoaPods Podspec that describes the Swift library to '\ - 'document', + 'document', parse: ->(ps) { PodspecDocumenter.create_podspec(Pathname(ps)) if ps }, default: Dir['*.podspec{,.json}'].first config_attr :pod_sources, command_line: ['--pod-sources url1,url2,…urlN', Array], description: 'A list of sources to find pod dependencies. Used only '\ - 'with --podspec when the podspec contains references to '\ - 'privately hosted pods. You must include the default pod '\ - 'source if public pods are also used.', + 'with --podspec when the podspec contains references to '\ + 'privately hosted pods. You must include the default pod '\ + 'source if public pods are also used.', default: [] config_attr :docset_icon, command_line: '--docset-icon FILEPATH', parse: ->(di) { expand_path(di) } @@ -314,34 +320,60 @@ parse: ->(r) { URI(r.sub(%r{/?$}, '/')) } config_attr :dash_url, command_line: ['-d', '--dash_url URL'], description: 'Location of the dash XML feed '\ - 'e.g. https://realm.io/docsets/realm.xml)', + 'e.g. https://realm.io/docsets/realm.xml)', parse: ->(d) { URI(d) } - config_attr :github_url, - command_line: ['-g', '--github_url URL'], - description: 'GitHub URL of this project (e.g. '\ - 'https://github.com/realm/realm-cocoa)', + SOURCE_HOSTS = %w[github gitlab bitbucket].freeze + + config_attr :source_host, + command_line: "--source-host #{SOURCE_HOSTS.join(' | ')}", + description: ['The source-code hosting site to be linked from documentation.', + 'This setting affects the logo image and link format.', + "Default: 'github'"], + default: 'github', + parse: ->(host) do + return host.to_sym if SOURCE_HOSTS.include?(host) + + raise "Unsupported source_host '#{host}', "\ + "supported values: #{SOURCE_HOSTS.join(', ')}" + end + + config_attr :source_host_url, + command_line: ['--source-host-url URL'], + description: ["URL to link from the source host's logo.", + 'For example https://github.com/realm/realm-cocoa'], parse: ->(g) { URI(g) } - config_attr :github_file_prefix, + alias_config_attr :github_url, :source_host_url, + command_line: ['-g', '--github_url URL'], + description: 'Back-compatibility alias for source_host_url.' + + config_attr :source_host_files_url, + command_line: '--source-host-files-url PREFIX', + description: [ + "The base URL on the source host of the project's files, to link "\ + 'from individual declarations.', + 'For example https://github.com/realm/realm-cocoa/tree/v0.87.1', + ] + + alias_config_attr :github_file_prefix, :source_host_files_url, command_line: '--github-file-prefix PREFIX', - description: 'GitHub URL file prefix of this project (e.g. '\ - 'https://github.com/realm/realm-cocoa/tree/v0.87.1)' + description: 'Back-compatibility alias for source_host_files_url' config_attr :docset_playground_url, command_line: '--docset-playground-url URL', description: 'URL of an interactive playground to demonstrate the '\ - 'framework, linked to from the docset.' + 'framework, linked to from the docset.' # ──────── Doc generation options ──────── config_attr :disable_search, command_line: '--disable-search', - description: ['Avoid generating a search index. '\ - 'Search is available in some themes.'], + description: 'Avoid generating a search index. '\ + 'Search is available in some themes.', default: false config_attr :skip_documentation, command_line: '--skip-documentation', description: 'Will skip the documentation generation phase.', @@ -357,51 +389,51 @@ end config_attr :skip_undocumented, command_line: '--[no-]skip-undocumented', description: "Don't document declarations that have no documentation "\ - 'comments.', + 'comments.', default: false config_attr :hide_documentation_coverage, command_line: '--[no-]hide-documentation-coverage', description: 'Hide "(X% documented)" from the generated documents', default: false config_attr :custom_categories, - description: ['Custom navigation categories to replace the standard '\ - '“Classes, Protocols, etc.”', 'Types not explicitly named '\ - 'in a custom category appear in generic groups at the end.', - 'Example: https://git.io/v4Bcp'], + description: 'Custom navigation categories to replace the standard '\ + "'Classes', 'Protocols', etc. Types not explicitly named "\ + 'in a custom category appear in generic groups at the '\ + 'end. Example: https://git.io/v4Bcp', default: [] config_attr :custom_categories_unlisted_prefix, description: "Prefix for navigation section names that aren't "\ - 'explicitly listed in `custom_categories`.', + 'explicitly listed in `custom_categories`.', default: 'Other ' config_attr :hide_unlisted_documentation, command_line: '--[no-]hide-unlisted-documentation', description: "Don't include documentation in the sidebar from the "\ - "`documentation` config value that aren't explicitly "\ - 'listed in `custom_categories`.', + "`documentation` config value that aren't explicitly "\ + 'listed in `custom_categories`.', default: false config_attr :custom_head, command_line: '--head HTML', description: 'Custom HTML to inject into <head></head>.', default: '' - BUILTIN_THEME_DIR = Pathname(__FILE__).parent + 'themes' + BUILTIN_THEME_DIR = Pathname(__dir__) + 'themes' BUILTIN_THEMES = BUILTIN_THEME_DIR.children(false).map(&:to_s) config_attr :theme_directory, command_line: "--theme [#{BUILTIN_THEMES.join(' | ')} | DIRPATH]", description: "Which theme to use. Specify either 'apple' (default), "\ - 'one of the other built-in theme names, or the path to '\ - 'your mustache templates and other assets for a custom '\ - 'theme.', + 'one of the other built-in theme names, or the path to '\ + 'your mustache templates and other assets for a custom '\ + 'theme.', default: 'apple', parse: ->(t) do if BUILTIN_THEMES.include?(t) BUILTIN_THEME_DIR + t else @@ -410,13 +442,13 @@ end config_attr :use_safe_filenames, command_line: '--use-safe-filenames', description: 'Replace unsafe characters in filenames with an encoded '\ - 'representation. This will reduce human readability of '\ - 'some URLs, but may be necessary for projects that '\ - 'expose filename-unfriendly functions such as /(_:_:)', + 'representation. This will reduce human readability of '\ + 'some URLs, but may be necessary for projects that '\ + 'expose filename-unfriendly functions such as /(_:_:)', default: false config_attr :template_directory, command_line: ['-t', '--template-directory DIRPATH'], description: 'DEPRECATED: Use --theme instead.', @@ -432,21 +464,21 @@ end config_attr :undocumented_text, command_line: '--undocumented-text UNDOCUMENTED_TEXT', description: 'Default text for undocumented symbols. The default '\ - 'is "Undocumented", put "" if no text is required', + 'is "Undocumented", put "" if no text is required', default: 'Undocumented' config_attr :separate_global_declarations, command_line: '--[no-]separate-global-declarations', description: 'Create separate pages for all global declarations '\ - "(classes, structures, enums etc.) even if they don't "\ - 'have children.', + "(classes, structures, enums etc.) even if they don't "\ + 'have children.', default: false - # rubocop:enable Style/AlignParameter + # rubocop:enable Layout/ArgumentAlignment def initialize self.class.all_config_attrs.each do |attr| attr.set_to_default(self) end @@ -455,11 +487,10 @@ def theme_directory=(theme_directory) @theme_directory = theme_directory Doc.template_path = theme_directory + 'templates' end - # rubocop:disable Metrics/MethodLength def self.parse! config = new config.parse_command_line config.parse_config_file PodspecDocumenter.apply_config_defaults(config.podspec, config) @@ -469,13 +500,20 @@ config.root_url, "docsets/#{config.module_name}.xml", ) end + config.validate + config end + def warning(message) + warn "WARNING: #{message}" + end + + # rubocop:disable Metrics/MethodLength def parse_command_line OptionParser.new do |opt| opt.banner = 'Usage: jazzy' opt.separator '' opt.separator 'Options' @@ -483,11 +521,11 @@ self.class.all_config_attrs.each do |attr| attr.attach_to_option_parser(self, opt) end opt.on('-v', '--version', 'Print version number') do - puts 'jazzy version: ' + Jazzy::VERSION + puts "jazzy version: #{Jazzy::VERSION}" exit end opt.on('-h', '--help [TOPIC]', 'Available topics:', ' usage Command line options (this help message)', @@ -502,10 +540,14 @@ print_option_help(topic) end exit end end.parse! + + unless ARGV.empty? + warning "Leftover unused command-line text: #{ARGV}" + end end def parse_config_file config_path = locate_config_file return unless config_path @@ -519,26 +561,36 @@ self.class.all_config_attrs.group_by(&prop) end config_file.each do |key, value| unless attr = attrs_by_conf_key[key] - message = "WARNING: Unknown config file attribute #{key.inspect}" + message = "Unknown config file attribute #{key.inspect}" if matching_name = attrs_by_name[key] - message << ' (Did you mean ' - message << matching_name.first.config_file_key.inspect - message << '?)' + message += + " (Did you mean #{matching_name.first.config_file_key.inspect}?)" end - warn message + warning message next end attr.first.set_if_unconfigured(self, value) end self.base_path = nil end + def validate + if source_host_configured && + source_host_url.nil? && + source_host_files_url.nil? + warning 'Option `source_host` is set but has no effect without either '\ + '`source_host_url` or `source_host_files_url`.' + end + end + + # rubocop:enable Metrics/MethodLength + def locate_config_file return config_file if config_file source_directory.ascend do |dir| candidate = dir.join('.jazzy.yaml') @@ -548,19 +600,14 @@ nil end def read_config_file(file) case File.extname(file) - when '.json' then JSON.parse(File.read(file)) - when '.yaml', '.yml' then - if YAML.respond_to?('safe_load') # ruby >= 2.1.0 - YAML.safe_load(File.read(file)) - else - # rubocop:disable Security/YAMLLoad - YAML.load(File.read(file)) - # rubocop:enable Security/YAMLLoad - end + when '.json' + JSON.parse(File.read(file)) + when '.yaml', '.yml' + YAML.safe_load(File.read(file)) else raise "Config file must be .yaml or .json, but got #{file.inspect}" end end def print_config_file_help @@ -573,10 +620,10 @@ (The source directory is the current working directory by default. You can override that with --source-directory.) The config file can be in YAML or JSON format. Available options are: - _EOS_ + _EOS_ .gsub(/^ +/, '') print_option_help end