-
-
Notifications
You must be signed in to change notification settings - Fork 21
/
index.js
181 lines (162 loc) · 6.51 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
'use strict'
let fs = require('fs')
const path = require('path')
const markdown = require('nunjucks-markdown')
const marked = require('marked')
const Minimize = require('minimize')
const nunjucks = require('nunjucks')
const stylus = require('stylus')
const getMimeType = require(path.join(__dirname, 'lib/utils.js')).getMimeType
const readFile = require(path.join(__dirname, 'lib/utils.js')).readFile
const logAndExit = require(path.join(__dirname, 'lib/utils.js')).logAndExit.bind(null, 'raml2html')
const getCurlStatement = require(path.join(__dirname, 'lib/stylus-globals.js')).getCurlStatement
const getLanguage = require(path.join(__dirname, 'lib/stylus-globals.js')).getLanguage
const getResponseHeaders = require(path.join(__dirname, 'lib/stylus-globals.js')).getResponseHeaders
const getSafeId = require(path.join(__dirname, 'lib/stylus-globals.js')).getSafeId
const hasExamples = require(path.join(__dirname, 'lib/stylus-globals.js')).hasExamples
const getTypeDefinitions = require(path.join(__dirname, 'lib/stylus-globals.js')).getTypeDefinitions
const hasType = require(path.join(__dirname, 'lib/stylus-globals.js')).hasType
const getType = require(path.join(__dirname, 'lib/stylus-globals.js')).getType
let minimize = new Minimize({quotes: true})
const templatesPath = path.join(__dirname, 'templates')
const DEFAULT_LOGO = path.join(templatesPath, 'images', 'logo.png')
const DEFAULT_COLOR_THEME = path.join(templatesPath, 'css', '_variables.styl.default')
const DEFAULT_LANGUAGE_TABS = ['json']
let mdRenderer = new marked.Renderer()
mdRenderer.hr = () => `</div><div class="set-examples">`
mdRenderer.code = (text, language) => `<pre><code class="lang-${language} hljs">${text}</code></pre>`
marked.setOptions({renderer: mdRenderer})
/**
* Renders a parsed RAML object into the templatesPath
* @param {object} ramlObj A parsed RAML object as produced by raml2obj
* @param {object} config A map with raml2html configuration
* (create a typedef for this and validate)
* @return {Promise<string>} A Promise resolving to html as a string
*/
function processRamlObj (ramlObj, config) {
return Promise.all([
renderCss(templatesPath, config.colorThemePath),
loadLogo(config.logoPath)
])
.then((data) => {
ramlObj.css = data[0]
ramlObj.logo = data[1]
ramlObj.logoMime = getMimeType(config.logoPath)
ramlObj.languageTabs = config.languageTabs.length > 1 ? config.languageTabs : undefined
ramlObj.search = true
return renderHtml(templatesPath, ramlObj)
})
}
/**
* Renders the stylus sheets along with the supplied theme into a css string
* @param {string} basePath The folder in which stylus references are resolved
* @param {string} colorThemePath The path to _variables.styl
* @return {Promise<string>} A Promise resolving to the css stylesheet as a string
*/
function renderCss (basePath, colorThemePath) {
// TODO: figure out how we can get load this from user config options
const stylusPath = path.join(basePath, 'css', 'style.styl')
return Promise.all([readFile(colorThemePath), readFile(stylusPath)])
.then((stylusFiles) => {
return new Promise((resolve, reject) => {
stylus.render(
stylusFiles.map((item) => item.toString('utf8')).join(''),
{paths: [path.join(basePath, 'css')]},
(err, css) => err ? reject(err) : resolve(css)
)
})
})
}
/**
* Read the logo at logoPath and return the contents as a base64 encoded string
* @param {string} logoPath The path of the logo
* @return {Promise<string>} A Promise resolving to the base64 encoded content of the logo
*/
function loadLogo (logoPath) {
return readFile(logoPath).then((buffer) => buffer.toString('base64'))
}
/**
* Render the ramlObj into the nunjucks template and return the resulting
* HTML as a string
* @param {string} basePath The directory in which nunjucks references are resolved
* @param {object} ramlObj A ramlObj with some additional properties for logo and css
* @return {string} The final HTML
*/
function renderHtml (basePath, ramlObj) {
const template = path.join(basePath, 'root.nunjucks')
const env = nunjucks
.configure(basePath, {autoescape: false})
.addGlobal('getSafeId', getSafeId)
.addGlobal('getLanguage', getLanguage)
.addGlobal('getResponseHeaders', getResponseHeaders)
.addGlobal('getCurlStatement', getCurlStatement.bind(null, ramlObj.securitySchemes, ramlObj.baseUri))
.addGlobal('hasExamples', hasExamples)
.addGlobal('getTypeDefinitions', getTypeDefinitions)
.addGlobal('hasType', hasType)
.addGlobal('getType', getType)
markdown.register(env, marked)
return env.render(template, ramlObj)
}
/**
* Minimize the HTML
* @param {string} data The HTML generated by the theme
* @return {Promise<string>} A Promise resolving to the minimized HTML
*/
function postProcessHtml (data) {
return new Promise((resolve, reject) => {
minimize.parse(data, (err, html) => err ? reject(err) : resolve(html))
})
}
function configureTheme (args) {
args = args || {}
if (args['generate-color-theme']) {
let defaultTheme = fs.readFileSync(DEFAULT_COLOR_THEME, {encoding: 'utf8'})
fs.writeSync(1, defaultTheme)
fs.fsyncSync(1)
process.exit(0)
}
const logoPath = args['logo'] || DEFAULT_LOGO
const colorThemePath = args['color-theme'] || DEFAULT_COLOR_THEME
const languageTabs = validateLanguageTabs(args['language-tabs'] || DEFAULT_LANGUAGE_TABS)
return {
colorThemePath,
languageTabs,
logoPath,
processRamlObj,
postProcessHtml
}
}
/**
* TODO: add tests
* Convert the `language-tabs` commandline argument into an Array of strings
* @param {string|array} arg The argument as passed on the commandline or in the config object
* @return {array<string>} An array of strings for the language tabs
* @throws {TypeError} Throws a TypeError if the argument does not pass input validation
*/
function validateLanguageTabs (arg) {
let languageTabs
if (typeof arg === 'string') {
arg = arg === '' ? '[]' : arg
try {
languageTabs = JSON.parse(arg)
} catch (e) {
if (e instanceof SyntaxError) {
logAndExit(arg)
} else {
throw e
}
}
} else {
languageTabs = arg
}
if (!Array.isArray(languageTabs)) {
logAndExit(arg)
}
languageTabs.forEach((item) => {
if (typeof item !== 'string') {
logAndExit(arg)
}
})
return languageTabs
}
module.exports = configureTheme