##
# Renders documentations from result of collector
# @module render
# @see Renderer
fs = require 'fs.extra'
pug = require 'pug'
{resolve, dirname} = require 'path'
##
# Renders documentations from result of collector
class Renderer
##
# Creates a Renderer instance
constructor: (@result, @options) ->
theme = 'default'
@resources_dir = resolve __dirname, '../themes', theme, 'resources'
@templates_dir = resolve __dirname, '../themes', theme, 'templates'
##
# @param {String} type
# @return {String}
_makeMissingLink: (type, place = '') ->
txt = if @result.ids[type]
"'#{type}' link is ambiguous"
else
"'#{type}' link does not exist"
console.log txt + " #{place}"
return "<span class='missing-link'>#{type}</span>"
##
# Makes links for given type
#
# * "String" -> "<a href='reference url for String'>String</a>"
# * "Array<Model>" -> "<a href='reference url for Array'>Array</a>&lt;<a href='internal url for Model'>Model</a>&gt;"
# @param {String} rel_path
# @param {String} type
# @return {String}
_makeTypeLink: (rel_path, type, place = '') ->
return type if not type
getlink = (type) =>
if @options.types[type]
link = @options.types[type]
else if @result.ids[type] and @result.ids[type] isnt 'DUPLICATED ENTRY'
filename = @result.ids[type].filename + '.html'
html_id = @result.ids[type].html_id or ''
link = "#{rel_path}#{filename}##{html_id}"
else
return @_makeMissingLink type, place
return "<a href='#{link}'>#{type}</a>"
if res = type.match(/\[(.*)\]\((.*)\)/)
@options.types[res[1]] = res[2]
return "<a href='#{res[2]}'>#{res[1]}</a>"
if res = type.match /(.*?)<(.*)>/
return "#{@_makeTypeLink rel_path, res[1]}<#{@_makeTypeLink rel_path, res[2]}>"
else
return getlink type
##
# @param {String} rel_path
# @param {String} str
# @return {String}
_makeSeeLink: (rel_path, str) ->
if @result.ids[str]
filename = @result.ids[str].filename + '.html'
html_id = @result.ids[str].html_id or ''
str = "<a href='#{rel_path}#{filename}##{html_id}'>#{str}</a>"
return str
##
# Converts link markups to HTML links in the description
# @param {String} rel_path
# @param {String} str
# @return {String}
_convertLink: (rel_path, str) ->
return '' if not str
str = str.replace /\[\[#([^\[\]]+)\]\]/g, (_, $1) =>
if @result.ids[$1] and @result.ids[$1] isnt 'DUPLICATED ENTRY'
filename = @result.ids[$1].filename + '.html'
html_id = @result.ids[$1].html_id or ''
return "<a href='#{rel_path}#{filename}##{html_id}'>#{$1}</a>"
else
return @_makeMissingLink $1
return str
##
# @param {String} source
# @param {String} target
# @param {Function} callback
_copyResources: (source, target, callback) ->
try
files = fs.readdirSync target
catch
files = []
for file in files
if file[0] isnt '.'
fs.rmrfSync resolve target, file
try fs.mkdirSync target
fs.copyRecursive source, target, ->
callback()
##
# Renders one template
_renderOne: (pug_options, template, output) ->
pug_options.result = @result
pug_options.makeTypeLink = @_makeTypeLink.bind(@) if not pug_options.makeTypeLink
pug_options.makeSeeLink = @_makeSeeLink.bind(@)
pug_options.convertLink = @_convertLink.bind(@)
pug_options.github = @options.github
pug_options.cache = true
pug_options.self = true
pug.renderFile "#{@templates_dir}/#{template}.pug", pug_options, (error, result) =>
return console.error error.stack if error
output_file = "#{@options.output_dir}/#{output}.html"
fs.writeFile output_file, result, (error) =>
return console.error 'failed to create '+output_file if error
console.log output_file + ' is created' if not @options.quiet
##
# Renders the README
_renderReadme: ->
pug_options =
rel_path: './'
name: 'README'
content: @result.readme
type: 'home'
@_renderOne pug_options, 'extra', 'index'
##
# Renders guides
_renderGuides: ->
return if @result.guides.length is 0
try fs.mkdirSync "#{@options.output_dir}/guides"
@result.guides.forEach (guide) =>
pug_options =
rel_path: '../'
name: guide.name
content: guide.content
type: 'guides'
@_renderOne pug_options, 'extra', guide.filename
##
# Renders pages
_renderPages: ->
if @result.pages.length > 0
pug_options =
rel_path: './'
name: 'Pages'
type: 'pages'
@_renderOne pug_options, 'pages', 'pages'
##
# Renders REST apis
_renderRESTApis: ->
if @result.restapis.length > 0
pug_options =
rel_path: './'
name: 'REST APIs'
type: 'restapis'
@_renderOne pug_options, 'restapis', 'restapis'
##
# Renders classes
_renderClasses: ->
return if @result.classes.length is 0
try fs.mkdirSync "#{@options.output_dir}/classes"
pug_options =
rel_path: '../'
type: 'classes'
@_renderOne pug_options, 'class-toc', 'classes/index'
@result.classes.forEach (klass) =>
pug_options =
rel_path: '../'
name: klass.ctx.name
klass: klass
properties: klass.properties
type: 'classes'
_makeTypeLink: (path, type) =>
@_makeTypeLink path, type, "(in #{klass.full_path})"
@_renderOne pug_options, 'class', klass.filename
##
# Renders modules
_renderModules: ->
return if @result.modules.length is 0
try fs.mkdirSync "#{@options.output_dir}/modules"
pug_options =
rel_path: '../'
type: 'modules'
@_renderOne pug_options, 'module-toc', 'modules/index'
@result.modules.forEach (module) =>
pug_options =
rel_path: '../'
name: module.ctx.name
module_data: module
properties: module.properties
type: 'modules'
@_renderOne pug_options, 'module', module.filename
##
# Renders features
_renderFeatures: ->
return if @result.features.length is 0
try fs.mkdirSync "#{@options.output_dir}/features"
@result.features.forEach (feature) =>
pug_options =
rel_path: '../'
name: feature.name
feature: feature
type: 'features'
@_renderOne pug_options, 'feature', feature.filename
##
# Renders files
_renderFiles: ->
return if @result.files.length is 0
try fs.mkdirSync "#{@options.output_dir}/files"
@result.files.forEach (file) =>
pug_options =
rel_path: '../'
name: file.name
file: file
type: 'files'
@_renderOne pug_options, 'file', file.filename
##
# Groups items by namespaces
_groupByNamespaces: (items) ->
if items.length is 0
return []
current_group = []
grouped_items = [current_group]
current_namespace = items[0].namespace
items.forEach (item) ->
if current_namespace isnt item.namespace
current_group = []
grouped_items.push current_group
current_namespace = item.namespace
current_group.push item
return grouped_items
##
# Runs
run: ->
@result.ns_pages = @_groupByNamespaces @result.pages
@result.ns_restapis = @_groupByNamespaces @result.restapis
@result.ns_classes = @_groupByNamespaces @result.classes
@result.ns_modules = @_groupByNamespaces @result.modules
@result.ns_features = @_groupByNamespaces @result.features
@result.ns_files = @_groupByNamespaces @result.files
@_copyResources @resources_dir, @options.output_dir, =>
@_renderReadme()
@_renderGuides()
@_renderPages()
@_renderRESTApis()
@_renderClasses()
@_renderModules()
@_renderFeatures()
@_renderFiles()
##
# Renders
# @param {Result} result
# @param {Options} options
# @memberOf render
render = (result, options) ->
renderer = new Renderer result, options
renderer.run()
module.exports = render