UNPKG

6.24 kBJavaScriptView Raw
1'use strict'
2
3const path = require('path')
4const fs = require('fs')
5const Mustache = require('mustache')
6const swaggerSpec = require('./swaggerSpec')
7const util = require('./util')
8
9exports.enableFile = enableFile
10exports.disableFile = disableFile
11exports.getFilePath = getFilePath
12exports.getTemplate = getTemplate
13exports.renderTemplate = renderTemplate
14exports.updateHeader = updateHeader
15exports.getDefaultTemplateView = getDefaultTemplateView
16exports.getDisabledFilePath = getDisabledFilePath
17exports.disableOldOperationFiles = disableOldOperationFiles
18exports.fileInfo = fileInfo
19
20function enableFile(id, data, type, headerLineRegex, options) {
21 const fileInfo = _enableFile(id, data, type, options)
22 if (options.maintainHeaders && !fileInfo.gen && !!headerLineRegex) {
23 updateHeader(fileInfo, data, type, headerLineRegex, options)
24 }
25 return fileInfo
26}
27
28function _enableFile(info, data, type, options) {
29 const filePath = getFilePath(info, type, options)
30 if (!util.existsSync(filePath)) {
31 const disabledPath = getDisabledFilePath(info, type, options)
32 if (util.existsSync(disabledPath)) {
33 return renameFile(disabledPath, filePath, options)
34 } else if (options[type].generate) {
35 return writeNewHandler(data, type, filePath, options)
36 }
37 }
38 return fileInfo(filePath, false, false, options)
39}
40
41function renameFile(oldPath, filePath, options) {
42 fs.renameSync(oldPath, filePath)
43 return fileInfo(filePath, false, false, options)
44}
45
46function writeNewHandler(data, type, filePath, options) {
47 const contents = renderTemplate(data, type, options)
48 util.writeFileSync(filePath, contents)
49 return fileInfo(filePath, true, false, options)
50}
51
52function renderTemplate(data, type, options) {
53 const view = options[type].getTemplateView(data)
54 return Mustache.render(options[type].template, view)
55}
56
57function disableFile(info, type, options) {
58 const filePath = getFilePath(info, type, options)
59 const disabledPath = getDisabledFilePath(info, type, options)
60 if (util.existsSync(filePath)) {
61 fs.renameSync(filePath, disabledPath)
62 }
63 return fileInfo(disabledPath, false, true, options)
64}
65
66function getTemplate(type, options) {
67 const template = options[type].template
68 if (template && typeof template === 'string') {
69 return fs.readFileSync(template, 'utf8')
70 } else {
71 return template
72 }
73}
74
75function getFilePath(info, type, options) {
76 const typeOptions = options[type]
77 return path.join(typeOptions.path, typeOptions.group ? info.pkg : '/', `${info.id}${options.fileType || '.js'}`)
78}
79
80function getDisabledFilePath(info, type, options) {
81 return path.join(options[type].path, `${options.unusedFilePrefix}${info.id}${options.fileType || '.js'}`)
82}
83
84function fileInfo(filePath, gen, old, options) {
85 return {
86 id: path.basename(filePath, options.fileType || '.js'),
87 path: path.resolve(filePath),
88 gen,
89 old
90 }
91}
92
93function disableOldOperationFiles(operations, type, options) {
94 const typeOptions = options[type]
95 if (!typeOptions.generate || typeOptions.create || typeOptions.group) return []
96
97 const fileType = options.fileType || '.js'
98 const filenames = fs.readdirSync(typeOptions.path)
99 const unknown = filenames.filter(name =>
100 name.endsWith(fileType) &&
101 !name.startsWith(options.unusedFilePrefix) &&
102 !operations.find(op => `${op.id}${fileType}` === name))
103
104 return unknown.map(name => {
105 name = path.basename(name, fileType)
106 const filePath = getFilePath({ id: name }, type, options)
107 const disabledPath = getDisabledFilePath({ id: name }, type, options)
108 fs.renameSync(filePath, disabledPath)
109 return fileInfo(disabledPath, false, true, options)
110 })
111}
112
113function getDefaultTemplateView(operation, commentMarker) {
114 const view = Object.assign({}, operation)
115 const groupsMap = operation.paramGroupSchemas
116
117 // Make sure we always use fwd slash
118 view.fullPath = (view.fullPath || '').split(path.sep).join('/')
119 view.method = view.method.toUpperCase()
120 view.summary = (view.summary || view.id).split('\n').join(`\n${commentMarker}`)
121 view.params = Object.keys(groupsMap)
122 .map(groupId => {
123 const propMap = groupsMap[groupId].properties
124 return {
125 groupId,
126 order: swaggerSpec.PARAM_GROUPS.indexOf(groupId),
127 properties: Object.keys(propMap).map(name => ({
128 details: formatParamPropertyDoc(name, propMap[name])
129 }))
130 }
131 })
132 .sort((a, b) => a.order - b.order)
133
134 return view
135}
136
137function formatParamPropertyDoc(name, info) {
138 const MAX_CHARS = 100
139 const type = info.format || info.type || ''
140 const desc = (info.description || '').split('\n')[0]
141 const match = desc.match(/(^.*?[a-z]{2,}[.!?])\s+\W*[A-Z]/)
142 const i = match ? Math.min(match.index, MAX_CHARS) : Math.min(desc.length, MAX_CHARS)
143 const shortDesc = desc.substr(0, i)
144
145 let dot = '.'
146 if (shortDesc.endsWith('.')) dot = ''
147 else if (shortDesc.length === MAX_CHARS) dot = '...'
148
149 let s = name
150 if (type) s += ` {${type}}`
151 if (shortDesc) s += ` ${shortDesc}${dot}`
152 return s
153}
154
155/**
156 * Keeps the header block of your templated file in sync with your Swagger api.
157 *
158 * @param {object} fileInfo info on the file to target
159 * @param {object} data the operation which to gen the header from
160 * @param {string} type the type of file being updated
161 * @param {regex} headerLineRegex regex to find if a line is part of the header
162 * @param {object} options options containing details on rendering the header
163 * @return {void}
164 */
165function updateHeader(fileInfo, data, type, headerLineRegex, options) {
166 const contents = fs.readFileSync(fileInfo.path, 'utf8') || ''
167 const body = contents.split('\n')
168 // assumes all comments at top of template are the header block
169
170 const preheader = []
171 while (body.length && !body[0].trim().match(headerLineRegex)) preheader.push(body.shift())
172 while (body.length && body[0].trim().match(headerLineRegex)) body.shift()
173
174 const template = renderTemplate(data, type, options)
175 const header = []
176 for (let hline of template.split('\n')) {
177 if (!hline.trim().match(headerLineRegex)) break
178 else header.push(hline)
179 }
180 const newContents = preheader.concat(header).concat(body).join('\n')
181 fs.writeFileSync(fileInfo.path, newContents)
182}