Module: Jazzy::DocBuilder

Defined in:
lib/jazzy/doc_builder.rb,
lib/jazzy/docset_builder.rb

Overview

This module handles HTML generation, file writing, asset copying, and generally building docs given sourcekitten output

Defined Under Namespace

Classes: DocsetBuilder

Class Method Summary collapse

Class Method Details

.build(options) ⇒ SourceModule

Build documentation from the given options

Parameters:

Returns:



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/jazzy/doc_builder.rb', line 44

def self.build(options)
  if options.sourcekitten_sourcefile
    stdout = options.sourcekitten_sourcefile.read
  else
    if podspec = options.podspec
      stdout = PodspecDocumenter.new(podspec).sourcekitten_output
    else
      stdout = Dir.chdir(Config.instance.source_directory) do
        arguments = ['doc'] + options.xcodebuild_arguments
        SourceKitten.run_sourcekitten(arguments)
      end
    end
    unless $?.success?
      warn 'Please pass in xcodebuild arguments using -x'
      warn 'If build arguments are correct, please file an issue on ' \
        'https://github.com/realm/jazzy/issues'
      exit $?.exitstatus || 1
    end
  end
  warn 'building site'
  build_docs_for_sourcekitten_output(stdout, options)
end

.build_docs(output_dir, docs, source_module) ⇒ Object

Build & write HTML docs to disk from structured docs array

Parameters:

  • output_dir (String)

    Root directory to write docs

  • docs (Array)

    Array of structured docs

  • options (Config)

    Build options

  • doc_structure (Array)

    @see #doc_structure_for_docs



72
73
74
75
76
77
78
79
80
# File 'lib/jazzy/doc_builder.rb', line 72

def self.build_docs(output_dir, docs, source_module)
  each_doc(output_dir, docs) do |doc, path, depth|
    prepare_output_dir(path.parent, false)
    path_to_root = ['../'].cycle(depth).to_a.join('')
    path.open('w') do |file|
      file.write(document(source_module, doc, path_to_root))
    end
  end
end

.build_docs_for_sourcekitten_output(sourcekitten_output, options) ⇒ SourceModule

Build docs given sourcekitten output

Parameters:

  • sourcekitten_output (String)

    Output of sourcekitten command

  • options (Config)

    Build options

Returns:



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/jazzy/doc_builder.rb', line 101

def self.build_docs_for_sourcekitten_output(sourcekitten_output, options)
  output_dir = options.output
  prepare_output_dir(output_dir, options.clean)

  (docs, coverage, undocumented) = SourceKitten.parse(
    sourcekitten_output,
    options.min_acl,
    options.skip_undocumented,
  )

  structure = doc_structure_for_docs(docs)

  docs << SourceDeclaration.new.tap do |sd|
    sd.name = 'index'
    sd.children = []
  end

  source_module = SourceModule.new(options, docs, structure, coverage)
  build_docs(output_dir, source_module.docs, source_module)

  write_undocumented_file(undocumented, output_dir)

  copy_assets(output_dir)

  DocsetBuilder.new(output_dir, source_module).build!

  puts "jam out ♪♫ to your fresh new docs in `#{output_dir}`"

  source_module
end

.copy_assets(destination) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
# File 'lib/jazzy/doc_builder.rb', line 162

def self.copy_assets(destination)
  origin = Pathname(__FILE__).parent + '../../lib/jazzy/assets/.'
  FileUtils.cp_r(origin, destination)
  Pathname.glob(destination + 'css/**/*.scss').each do |scss|
    contents = scss.read
    css = Sass::Engine.new(contents, syntax: :scss).render
    css_filename = scss.sub(/\.scss$/, '')
    css_filename.open('w') { |f| f.write(css) }
    FileUtils.rm scss
  end
end

.decl_for_token(token) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/jazzy/doc_builder.rb', line 132

def self.decl_for_token(token)
  if token['key.parsed_declaration']
    token['key.parsed_declaration']
  elsif token['key.annotated_decl']
    token['key.annotated_decl'].gsub(/<[^>]+>/, '')
  elsif token['key.name']
    token['key.name']
  else
    'unknown declaration'
  end
end

.doc_structure_for_docs(docs) ⇒ Array

Generate doc structure to be used in sidebar navigation

Returns:

  • (Array)

    doc structure comprised of section names & child names & URLs



30
31
32
33
34
35
36
37
38
39
# File 'lib/jazzy/doc_builder.rb', line 30

def self.doc_structure_for_docs(docs)
  docs.map do |doc|
    {
      section: doc.name,
      children: doc.children.sort_by(&:name).map do |child|
        { name: child.name, url: child.url }
      end,
    }
  end
end

.document(source_module, doc_model, path_to_root) ⇒ Object

Build Mustache document from single parsed doc

Parameters:

  • options (Config)

    Build options

  • doc_model (Hash)

    Parsed doc. @see SourceKitten.parse

  • path_to_root (String)
  • doc_structure (Array)

    doc structure comprised of section names and child names and URLs. @see doc_structure_for_docs



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/jazzy/doc_builder.rb', line 275

def self.document(source_module, doc_model, path_to_root)
  if doc_model.name == 'index'
    return document_index(source_module, path_to_root)
  end

  doc = Doc.new # Mustache model instance
  doc[:doc_coverage] = source_module.doc_coverage
  doc[:name] = doc_model.name
  doc[:kind] = doc_model.type.name
  doc[:dash_type] = doc_model.type.dash_type
  doc[:overview] = Jazzy.markdown.render(doc_model.overview)
  doc[:structure] = source_module.doc_structure
  doc[:tasks] = render_tasks(source_module, doc_model.children)
  doc[:module_name] = source_module.name
  doc[:author_name] = source_module.author_name
  doc[:author_website] = source_module.author_url.to_s
  doc[:github_url] = source_module.github_url.to_s
  doc[:dash_url] = source_module.dash_url
  doc[:path_to_root] = path_to_root
  doc.render
end

.document_index(source_module, path_to_root) ⇒ Object

Build index Mustache document

Parameters:

  • options (Config)

    Build options

  • path_to_root (String)
  • doc_structure (Array)

    doc structure comprised of section names and child names and URLs. @see doc_structure_for_docs



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/jazzy/doc_builder.rb', line 179

def self.document_index(source_module, path_to_root)
  doc = Doc.new # Mustache model instance
  doc[:name] = source_module.name
  doc[:overview] = ReadmeGenerator.generate(source_module)
  doc[:doc_coverage] = source_module.doc_coverage
  doc[:structure] = source_module.doc_structure
  doc[:module_name] = source_module.name
  doc[:author_name] = source_module.author_name
  doc[:author_website] = source_module.author_url.to_s
  doc[:github_url] = source_module.github_url.to_s
  doc[:dash_url] = source_module.dash_url
  doc[:path_to_root] = path_to_root
  doc[:hide_name] = true
  doc.render
end

.each_doc(output_dir, docs, depth = 0, &block) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/jazzy/doc_builder.rb', line 82

def self.each_doc(output_dir, docs, depth = 0, &block)
  docs.each do |doc|
    next if doc.name != 'index' && doc.children.count == 0
    path = output_dir + "#{doc.name}.html"
    block.call(doc, path, depth)
    next if doc.name == 'index'
    each_doc(
      output_dir + doc.name,
      doc.children,
      depth + 1,
      &block
    )
  end
end

.gh_token_url(item, source_module) ⇒ Object

Construct Github token URL

Parameters:

  • item (Hash)

    Parsed doc child item

  • options (Config)

    Build options



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/jazzy/doc_builder.rb', line 204

def self.gh_token_url(item, source_module)
  if source_module.github_file_prefix && should_link_to_github(item.file)
    relative_file_path = item.file.gsub(`pwd`.strip, '')
    if item.start_line && (item.start_line != item.end_line)
      gh_line = "#L#{item.start_line}-L#{item.end_line}"
    else
      gh_line = "#L#{item.line}"
    end
    source_module.github_file_prefix + relative_file_path + gh_line
  end
end

.make_task(mark, uid, items) ⇒ Object



239
240
241
242
243
244
245
246
247
# File 'lib/jazzy/doc_builder.rb', line 239

def self.make_task(mark, uid, items)
  {
    name: mark.name,
    uid: URI.encode(uid),
    items: items,
    pre_separator: mark.has_start_dash,
    post_separator: mark.has_end_dash,
  }
end

.prepare_output_dir(output_dir, clean) ⇒ Object

mkdir -p output directory and clean if option is set



22
23
24
25
# File 'lib/jazzy/doc_builder.rb', line 22

def self.prepare_output_dir(output_dir, clean)
  FileUtils.rm_r output_dir if clean && output_dir.directory?
  FileUtils.mkdir_p output_dir
end

.render_item(item, source_module) ⇒ Object

Build mustache item for a top-level doc

Parameters:

  • item (Hash)

    Parsed doc child item

  • options (Config)

    Build options



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/jazzy/doc_builder.rb', line 219

def self.render_item(item, source_module)
  # Combine abstract and discussion into abstract
  abstract = (item.abstract || '') + (item.discussion || '')
  item_render = {
    name: item.name,
    abstract: Jazzy.markdown.render(abstract),
    declaration: item.declaration,
    usr: item.usr,
    dash_type: item.type.dash_type,
  }
  gh_token_url = gh_token_url(item, source_module)
  item_render[:github_token_url] = gh_token_url
  item_render[:return] = Jazzy.markdown.render(item.return) if item.return
  item_render[:parameters] = item.parameters if item.parameters.any?
  item_render[:url] = item.url if item.children.any?
  item_render[:start_line] = item.start_line
  item_render[:end_line] = item.end_line
  item_render.reject { |_, v| v.nil? }
end

.render_tasks(source_module, children) ⇒ Object

Render tasks for Mustache document

Parameters:

  • options (Config)

    Build options

  • doc_model (Hash)

    Parsed doc. @see SourceKitten.parse



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/jazzy/doc_builder.rb', line 252

def self.render_tasks(source_module, children)
  marks = children.map(&:mark).uniq
  mark_names_counts = {}
  marks.map do |mark|
    mark_children = children.select { |child| child.mark == mark }
    items = mark_children.map { |child| render_item(child, source_module) }
    uid = "#{mark.name || 'Unnamed'}"
    if mark_names_counts.key?(uid)
      mark_names_counts[uid] += 1
      uid += "#{mark_names_counts[uid]}"
    else
      mark_names_counts[uid] = 1
    end
    make_task(mark, uid, items)
  end
end


195
196
197
198
199
# File 'lib/jazzy/doc_builder.rb', line 195

def self.should_link_to_github(file)
  developer_directory = SourceKitten.xcode_developer_directory
  return unless developer_directory && file
  !file.start_with?(developer_directory.realpath.to_s)
end

.write_undocumented_file(undocumented, output_dir) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/jazzy/doc_builder.rb', line 144

def self.write_undocumented_file(undocumented, output_dir)
  (output_dir + 'undocumented.txt').open('w') do |f|
    tokens_by_file = undocumented.group_by do |d|
      if d['key.filepath']
        Pathname.new(d['key.filepath']).basename.to_s
      else
        d['key.modulename'] || ''
      end
    end
    tokens_by_file.each_key do |file|
      f.write(file + "\n")
      tokens_by_file[file].each do |token|
        f.write("\t" + decl_for_token(token) + "\n")
      end
    end
  end
end