1 | 'use strict'
|
2 |
|
3 | const path = require('path')
|
4 | const fs = require('fs')
|
5 | const Mustache = require('mustache')
|
6 | const swaggerSpec = require('./swaggerSpec')
|
7 | const util = require('./util')
|
8 |
|
9 | exports.enableFile = enableFile
|
10 | exports.disableFile = disableFile
|
11 | exports.getFilePath = getFilePath
|
12 | exports.getTemplate = getTemplate
|
13 | exports.renderTemplate = renderTemplate
|
14 | exports.updateHeader = updateHeader
|
15 | exports.getDefaultTemplateView = getDefaultTemplateView
|
16 | exports.getDisabledFilePath = getDisabledFilePath
|
17 | exports.disableOldOperationFiles = disableOldOperationFiles
|
18 | exports.fileInfo = fileInfo
|
19 |
|
20 | function 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 |
|
28 | function _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 |
|
41 | function renameFile(oldPath, filePath, options) {
|
42 | fs.renameSync(oldPath, filePath)
|
43 | return fileInfo(filePath, false, false, options)
|
44 | }
|
45 |
|
46 | function 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 |
|
52 | function renderTemplate(data, type, options) {
|
53 | const view = options[type].getTemplateView(data)
|
54 | return Mustache.render(options[type].template, view)
|
55 | }
|
56 |
|
57 | function 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 |
|
66 | function 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 |
|
75 | function 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 |
|
80 | function getDisabledFilePath(info, type, options) {
|
81 | return path.join(options[type].path, `${options.unusedFilePrefix}${info.id}${options.fileType || '.js'}`)
|
82 | }
|
83 |
|
84 | function 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 |
|
93 | function 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 |
|
113 | function getDefaultTemplateView(operation, commentMarker) {
|
114 | const view = Object.assign({}, operation)
|
115 | const groupsMap = operation.paramGroupSchemas
|
116 |
|
117 |
|
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 |
|
137 | function 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 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | function updateHeader(fileInfo, data, type, headerLineRegex, options) {
|
166 | const contents = fs.readFileSync(fileInfo.path, 'utf8') || ''
|
167 | const body = contents.split('\n')
|
168 |
|
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 | }
|