lib/jazzy/config.rb in jazzy-0.14.4 vs lib/jazzy/config.rb in jazzy-0.15.0
- old
+ new
@@ -11,20 +11,21 @@
# rubocop:disable Metrics/ClassLength
class Config
# rubocop:disable Naming/AccessorMethodName
class Attribute
attr_reader :name, :description, :command_line, :config_file_key,
- :default, :parse
+ :default, :parse, :per_module
def initialize(name, description: nil, command_line: nil,
- default: nil, parse: ->(x) { x })
+ default: nil, parse: ->(x) { x }, per_module: false)
@name = name.to_s
@description = Array(description)
@command_line = Array(command_line)
@default = default
@parse = parse
@config_file_key = full_command_line_name || @name
+ @per_module = per_module
end
def get(config)
config.method(name).call
end
@@ -132,26 +133,30 @@
default: false
config_attr :objc_mode,
command_line: '--[no-]objc',
description: 'Generate docs for Objective-C.',
- default: false
+ default: false,
+ per_module: true
config_attr :umbrella_header,
command_line: '--umbrella-header PATH',
description: 'Umbrella header for your Objective-C framework.',
- parse: ->(uh) { expand_path(uh) }
+ parse: ->(uh) { expand_path(uh) },
+ per_module: true
config_attr :framework_root,
command_line: '--framework-root PATH',
description: 'The root path to your Objective-C framework.',
- parse: ->(fr) { expand_path(fr) }
+ parse: ->(fr) { expand_path(fr) },
+ per_module: true
config_attr :sdk,
command_line: '--sdk [iphone|watch|appletv][os|simulator]|macosx',
description: 'The SDK for which your code should be built.',
- default: 'macosx'
+ default: 'macosx',
+ per_module: true
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, ' \
@@ -173,33 +178,44 @@
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.',
+ default: [],
+ per_module: true
+
+ config_attr :modules,
+ command_line: ['--modules Mod1,Mod2,…ModN', Array],
+ description: 'List of modules to document. Use the config file to set per-module ' \
+ "build flags, see 'Documenting multiple modules' in the README.",
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.'
config_attr :sourcekitten_sourcefile,
command_line: ['-s', '--sourcekitten-sourcefile filepath1,…filepathN',
Array],
description: 'File(s) generated from sourcekitten output to parse',
- parse: ->(paths) { [paths].flatten.map { |path| expand_path(path) } }
+ parse: ->(paths) { [paths].flatten.map { |path| expand_path(path) } },
+ default: [],
+ per_module: true
config_attr :source_directory,
command_line: '--source-directory DIRPATH',
description: 'The directory that contains the source to be documented',
default: Pathname.pwd,
- parse: ->(sd) { expand_path(sd) }
+ parse: ->(sd) { expand_path(sd) },
+ per_module: true
config_attr :symbolgraph_directory,
command_line: '--symbolgraph-directory DIRPATH',
description: 'A directory containing a set of Swift Symbolgraph files ' \
'representing the module to be documented',
- parse: ->(sd) { expand_path(sd) }
+ parse: ->(sd) { expand_path(sd) },
+ per_module: true
config_attr :excluded_files,
command_line: ['-e', '--exclude filepath1,filepath2,…filepathN', Array],
description: 'Source file pathnames to be excluded from documentation. ' \
'Supports wildcards.',
@@ -259,11 +275,12 @@
parse: ->(u) { URI(u) }
config_attr :module_name,
command_line: ['-m', '--module MODULE_NAME'],
description: 'Name of module being documented. (e.g. RealmSwift)',
- default: ''
+ default: '',
+ per_module: true
config_attr :version,
command_line: '--module-version VERSION',
description: 'Version string to use as part of the default docs ' \
'title and inside the docset.',
@@ -282,10 +299,14 @@
config_attr :readme_path,
command_line: '--readme FILEPATH',
description: 'The path to a markdown README file',
parse: ->(rp) { expand_path(rp) }
+ config_attr :readme_title,
+ command_line: '--readme-title TITLE',
+ description: 'The title for the README in the generated documentation'
+
config_attr :documentation_glob,
command_line: '--documentation GLOB',
description: 'Glob that matches available documentation',
parse: ->(dg) { Pathname.glob(dg) }
@@ -315,10 +336,17 @@
config_attr :docset_path,
command_line: '--docset-path DIRPATH',
description: 'The relative path for the generated docset'
+ config_attr :docset_title,
+ command_line: '--docset-title TITLE',
+ description: 'The title of the generated docset. A simplified version ' \
+ 'is used for the filenames associated with the docset. If the ' \
+ 'option is not set then the name of the module being documented is ' \
+ 'used as the docset title.'
+
# ──────── URLs ────────
config_attr :root_url,
command_line: ['-r', '--root-url URL'],
description: 'Absolute URL root where these docs will be stored',
@@ -486,10 +514,27 @@
command_line: '--[no-]include-spi-declarations',
description: 'Include Swift declarations marked `@_spi` even if ' \
'--min-acl is set to `public` or `open`.',
default: false
+ MERGE_MODULES = %w[all extensions none].freeze
+
+ config_attr :merge_modules,
+ command_line: "--merge-modules #{MERGE_MODULES.join(' | ')}",
+ description: 'Control how to display declarations from multiple ' \
+ 'modules. `all`, the default, places all declarations of the ' \
+ "same kind together. `none` keeps each module's declarations " \
+ 'separate. `extensions` is like `none` but merges ' \
+ 'cross-module extensions into their extended type.',
+ default: 'all',
+ parse: ->(merge) do
+ return merge.to_sym if MERGE_MODULES.include?(merge)
+
+ raise "Unsupported merge_modules #{merge}, " \
+ "supported values: #{MERGE_MODULES.join(', ')}"
+ end
+
# rubocop:enable Layout/ArgumentAlignment
def initialize
self.class.all_config_attrs.each do |attr|
attr.set_to_default(self)
@@ -505,16 +550,11 @@
config = new
config.parse_command_line
config.parse_config_file
PodspecDocumenter.apply_config_defaults(config.podspec, config)
- if config.root_url
- config.dash_url ||= URI.join(
- config.root_url,
- "docsets/#{config.module_name}.xml",
- )
- end
+ config.set_module_configs
config.validate
config
end
@@ -567,56 +607,148 @@
self.base_path = config_path.parent
puts "Using config file #{config_path}"
config_file = read_config_file(config_path)
- attrs_by_conf_key, attrs_by_name = %i[config_file_key name].map do |prop|
- self.class.all_config_attrs.group_by(&prop)
- end
+ attrs_by_conf_key, attrs_by_name = grouped_attributes
- config_file.each do |key, value|
+ parse_config_hash(config_file, attrs_by_conf_key, attrs_by_name)
+ end
+
+ def parse_config_hash(hash, attrs_by_conf_key, attrs_by_name, override: false)
+ hash.each do |key, value|
unless attr = attrs_by_conf_key[key]
message = "Unknown config file attribute #{key.inspect}"
if matching_name = attrs_by_name[key]
message +=
" (Did you mean #{matching_name.first.config_file_key.inspect}?)"
end
warning message
next
end
-
- attr.first.set_if_unconfigured(self, value)
+ setter = override ? :set : :set_if_unconfigured
+ attr.first.method(setter).call(self, value)
end
+ end
- self.base_path = nil
+ # Find keyed versions of the attributes, by config file key and then name-in-code
+ # Optional block allows filtering/overriding of attribute list.
+ def grouped_attributes
+ attrs = self.class.all_config_attrs
+ attrs = yield attrs if block_given?
+ %i[config_file_key name].map do |property|
+ attrs.group_by(&property)
+ end
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
+ if modules_configured && module_name_configured
+ raise 'Options `modules` and `module` are both set which is not supported. ' \
+ 'To document multiple modules, use just `modules`.'
+ end
+
+ if modules_configured && podspec_configured
+ raise 'Options `modules` and `podspec` are both set which is not supported.'
+ end
+
+ module_configs.each(&:validate_module)
+ end
+
+ def validate_module
if objc_mode &&
build_tool_arguments_configured &&
(framework_root_configured || umbrella_header_configured)
warning 'Option `build_tool_arguments` is set: values passed to ' \
'`framework_root` or `umbrella_header` may be ignored.'
end
end
# rubocop:enable Metrics/MethodLength
+ # Module Configs
+ #
+ # The user can enter module information in three different ways. This
+ # consolidates them into one view for the rest of the code.
+ #
+ # 1) Single module, back-compatible
+ # --module Foo etc etc (or not given at all)
+ #
+ # 2) Multiple modules, simple, sharing build params
+ # --modules Foo,Bar,Baz --source-directory Xyz
+ #
+ # 3) Multiple modules, custom, different build params but
+ # inheriting others from the top level.
+ # This is config-file only.
+ # - modules
+ # - module: Foo
+ # source_directory: Xyz
+ # build_tool_arguments: [a, b, c]
+ #
+ # After this we're left with `config.module_configs` that is an
+ # array of `Config` objects.
+
+ attr_reader :module_configs
+ attr_reader :module_names
+
+ def set_module_configs
+ @module_configs = parse_module_configs
+ @module_names = module_configs.map(&:module_name)
+ @module_names_set = Set.new(module_names)
+ end
+
+ def module_name?(name)
+ @module_names_set.include?(name)
+ end
+
+ def multiple_modules?
+ @module_names.count > 1
+ end
+
+ def parse_module_configs
+ return [self] unless modules_configured
+
+ raise 'Config file key `modules` must be an array' unless modules.is_a?(Array)
+
+ if modules.first.is_a?(String)
+ # Massage format (2) into (3)
+ self.modules = modules.map { { 'module' => _1 } }
+ end
+
+ # Allow per-module overrides of only some config options
+ attrs_by_conf_key, attrs_by_name =
+ grouped_attributes { _1.select(&:per_module) }
+
+ modules.map do |module_hash|
+ mod_name = module_hash['module'] || ''
+ raise 'Missing `modules.module` config key' if mod_name.empty?
+
+ dup.tap do |module_config|
+ module_config.parse_config_hash(
+ module_hash, attrs_by_conf_key, attrs_by_name, override: true
+ )
+ end
+ end
+ end
+
+ # For podspec query
+ def module_name_known?
+ module_name_configured || modules_configured
+ end
+
def locate_config_file
return config_file if config_file
source_directory.ascend do |dir|
candidate = dir.join('.jazzy.yaml')
return candidate if candidate.exist?
end
-
nil
end
def read_config_file(file)
case File.extname(file)