1
1
#!/usr/bin/env node
2
2
3
- const yaml = require ( 'js-yaml' ) ;
4
- const ejs = require ( 'ejs' ) ;
5
- const fs = require ( 'fs' ) ;
6
- const path = require ( 'path' ) ;
3
+ const yaml = require ( 'js-yaml' )
4
+ const ejs = require ( 'ejs' )
5
+ const fs = require ( 'fs' )
6
+ const path = require ( 'path' )
7
7
8
- const parseArgs = require ( 'minimist' ) ;
8
+ const parseArgs = require ( 'minimist' )
9
9
10
- const { Parser } = require ( 'node-sql-parser' ) ;
10
+ const { Parser } = require ( 'node-sql-parser' )
11
11
12
- const endpointsFile = 'endpoints.yaml' ;
12
+ const endpointsFile = 'endpoints.yaml'
13
13
14
14
const parseCommandLineArgs = ( args ) => {
15
15
const opts = {
@@ -22,10 +22,10 @@ const parseCommandLineArgs = (args) => {
22
22
'dest-dir' : '.' ,
23
23
'overwrite' : false ,
24
24
}
25
- } ;
26
- const argv = parseArgs ( args , opts ) ;
27
- //console.debug('argv:', argv);
28
- return argv ;
25
+ }
26
+ const argv = parseArgs ( args , opts )
27
+ //console.debug('argv:', argv)
28
+ return argv
29
29
}
30
30
31
31
// Restructure YAML configuration to simplify downstream code.
@@ -44,34 +44,34 @@ const parseCommandLineArgs = (args) => {
44
44
// }
45
45
const restructureConfiguration = ( config ) => {
46
46
for ( const endpoint of config ) {
47
- endpoint . methods = [ ] ;
47
+ endpoint . methods = [ ] ; // this semicolon is really needed
48
48
[ 'get' , 'get_list' , 'post' , 'put' , 'delete' ] . forEach ( method => {
49
49
if ( ! endpoint . hasOwnProperty ( method ) ) {
50
- return ;
50
+ return
51
51
}
52
52
endpoint . methods . push ( {
53
53
'name' : method ,
54
54
'verb' : method !== 'get_list' ? method : 'get' ,
55
55
...endpoint [ method ] ,
56
- } ) ;
57
- delete endpoint [ method ] ;
58
- } ) ;
56
+ } )
57
+ delete endpoint [ method ]
58
+ } )
59
59
}
60
- } ;
60
+ }
61
61
62
62
const loadConfig = ( endpointsFile ) => {
63
- console . log ( 'Read' , endpointsFile ) ;
63
+ console . log ( 'Read' , endpointsFile )
64
64
try {
65
- const content = fs . readFileSync ( endpointsFile , 'utf8' ) ;
66
- const config = yaml . safeLoad ( content ) ;
67
- restructureConfiguration ( config ) ;
68
- //console.debug(config);
69
- return config ;
65
+ const content = fs . readFileSync ( endpointsFile , 'utf8' )
66
+ const config = yaml . safeLoad ( content )
67
+ restructureConfiguration ( config )
68
+ //console.debug(config)
69
+ return config
70
70
} catch ( ex ) {
71
- console . error ( `Failed to parse ${ endpointsFile } : ${ ex . message } ` ) ;
72
- throw ex ;
71
+ console . error ( `Failed to parse ${ endpointsFile } : ${ ex . message } ` )
72
+ throw ex
73
73
}
74
- } ;
74
+ }
75
75
76
76
const lang2extension = ( lang ) => {
77
77
switch ( lang ) {
@@ -115,8 +115,8 @@ const fileExistsHandler = (err) => {
115
115
const createApp = async ( destDir , { lang, overwrite } ) => {
116
116
const ext = lang2extension ( lang )
117
117
const fileName = `app.${ ext } `
118
- console . log ( 'Generate' , fileName ) ;
119
- const resultFile = path . join ( destDir , fileName ) ;
118
+ console . log ( 'Generate' , fileName )
119
+ const resultFile = path . join ( destDir , fileName )
120
120
const customRouters = findFileNamesEndWith ( destDir , `_routes.${ ext } ` )
121
121
if ( customRouters . length > 0 ) {
122
122
customRouters . forEach ( filename => console . log ( `Include a custom router from ${ filename } ` ) )
@@ -133,44 +133,44 @@ const createApp = async (destDir, { lang, overwrite }) => {
133
133
134
134
const fsFlags = overwrite ? 'w' : 'wx'
135
135
await fs . writeFile ( resultFile , resultedCode , { 'flag' : fsFlags } , fileExistsHandler )
136
- } ;
136
+ }
137
137
138
138
const createDb = async ( destDir , { lang, overwrite } ) => {
139
139
if ( lang !== 'python' ) {
140
140
return
141
141
}
142
142
const fileName = 'db.py'
143
- console . log ( 'Generate' , fileName ) ;
144
- const resultFile = path . join ( destDir , fileName ) ;
143
+ console . log ( 'Generate' , fileName )
144
+ const resultFile = path . join ( destDir , fileName )
145
145
146
146
const mode = overwrite ? 0 : fs . constants . COPYFILE_EXCL
147
147
await fs . copyFile ( `${ __dirname } /templates/${ fileName } ` , resultFile , mode , fileExistsHandler )
148
148
}
149
149
150
150
// "-- comment\nSELECT * FROM foo" => "SELECT * FROM foo"
151
- const removeComments = ( query ) => query . replace ( / - - .* \n / g, '' ) ;
151
+ const removeComments = ( query ) => query . replace ( / - - .* \n / g, '' )
152
152
153
153
// "SELECT *\n FROM foo" => "SELECT * FROM foo"
154
- const flattenQuery = ( query ) => query . replace ( / \n [ ] * / g, ' ' ) ;
154
+ const flattenQuery = ( query ) => query . replace ( / \n [ ] * / g, ' ' )
155
155
156
156
// "WHERE id = :p.categoryId OR id = :b.id LIMIT :q.limit" => "WHERE id = :categoryId OR id = :id LIMIT :limit"
157
- const removePlaceholders = ( query ) => query . replace ( / (?< = : ) [ p b q ] \. / g, '' ) ;
157
+ const removePlaceholders = ( query ) => query . replace ( / (?< = : ) [ p b q ] \. / g, '' )
158
158
159
159
// "/categories/:id" => "/categories/{id}"
160
160
// (used only with Golang's go-chi)
161
- const convertPathPlaceholders = ( path ) => path . replace ( / : ( [ ^ \/ ] + ) / g, '{$1}' ) ;
161
+ const convertPathPlaceholders = ( path ) => path . replace ( / : ( [ ^ \/ ] + ) / g, '{$1}' )
162
162
163
163
// "name_ru" => "nameRu"
164
164
// (used only with Golang's go-chi)
165
- const snake2camelCase = ( str ) => str . replace ( / _ ( [ a - z ] ) / g, ( match , group ) => group . toUpperCase ( ) ) ;
165
+ const snake2camelCase = ( str ) => str . replace ( / _ ( [ a - z ] ) / g, ( match , group ) => group . toUpperCase ( ) )
166
166
167
167
// "categoryId" => "category_id"
168
168
// (used only with Python's FastAPI)
169
- const camel2snakeCase = ( str ) => str . replace ( / ( [ A - Z ] ) / g, ( match , group ) => '_' + group . toLowerCase ( ) ) ;
169
+ const camel2snakeCase = ( str ) => str . replace ( / ( [ A - Z ] ) / g, ( match , group ) => '_' + group . toLowerCase ( ) )
170
170
171
171
// "nameRu" => "NameRu"
172
172
// (used only with Golang's go-chi)
173
- const capitalize = ( str ) => str [ 0 ] . toUpperCase ( ) + str . slice ( 1 ) ;
173
+ const capitalize = ( str ) => str [ 0 ] . toUpperCase ( ) + str . slice ( 1 )
174
174
175
175
// ["a", "bb", "ccc"] => 3
176
176
// (used only with Golang's go-chi)
@@ -179,22 +179,22 @@ const lengthOfLongestString = (arr) => arr
179
179
. reduce (
180
180
( acc , val ) => val > acc ? val : acc ,
181
181
0 /* initial value */
182
- ) ;
182
+ )
183
183
184
184
const createEndpoints = async ( destDir , { lang, overwrite } , config ) => {
185
185
const ext = lang2extension ( lang )
186
186
const fileName = `routes.${ ext } `
187
- console . log ( 'Generate' , fileName ) ;
188
- const resultFile = path . join ( destDir , fileName ) ;
187
+ console . log ( 'Generate' , fileName )
188
+ const resultFile = path . join ( destDir , fileName )
189
189
190
190
for ( let endpoint of config ) {
191
- let path = endpoint . path ;
191
+ let path = endpoint . path
192
192
if ( lang === 'go' ) {
193
193
path = convertPathPlaceholders ( path )
194
194
}
195
195
endpoint . methods . forEach ( method => {
196
- const verb = method . verb . toUpperCase ( ) ;
197
- console . log ( `${ verb } ${ path } ` ) ;
196
+ const verb = method . verb . toUpperCase ( )
197
+ console . log ( `${ verb } ${ path } ` )
198
198
199
199
let queries = [ ]
200
200
if ( method . query ) {
@@ -203,10 +203,10 @@ const createEndpoints = async (destDir, { lang, overwrite }, config) => {
203
203
queries = Object . values ( method . aggregated_queries )
204
204
}
205
205
queries . forEach ( query => {
206
- const sql = removePlaceholders ( flattenQuery ( removeComments ( query ) ) ) ;
207
- console . log ( `\t${ sql } ` ) ;
206
+ const sql = removePlaceholders ( flattenQuery ( removeComments ( query ) ) )
207
+ console . log ( `\t${ sql } ` )
208
208
} )
209
- } ) ;
209
+ } )
210
210
}
211
211
212
212
const placeholdersMap = {
@@ -220,15 +220,15 @@ const createEndpoints = async (destDir, { lang, overwrite }, config) => {
220
220
return `chi.URLParam(r, "${ param } ")`
221
221
} ,
222
222
'b' : function ( param ) {
223
- return 'dto.' + capitalize ( snake2camelCase ( param ) ) ;
223
+ return 'dto.' + capitalize ( snake2camelCase ( param ) )
224
224
} ,
225
225
'q' : function ( param ) {
226
226
return `r.URL.Query().Get("${ param } ")`
227
227
} ,
228
228
}
229
229
}
230
230
231
- const parser = new Parser ( ) ;
231
+ const parser = new Parser ( )
232
232
233
233
const resultedCode = await ejs . renderFile (
234
234
`${ __dirname } /templates/routes.${ ext } .ejs` ,
@@ -250,22 +250,22 @@ const createEndpoints = async (destDir, { lang, overwrite }, config) => {
250
250
// (used only with Express)
251
251
"formatParamsAsJavaScriptObject" : ( params ) => {
252
252
if ( params . length === 0 ) {
253
- return params ;
253
+ return params
254
254
}
255
255
return Array . from (
256
256
new Set ( params ) ,
257
257
p => {
258
- const bindTarget = p . substring ( 0 , 1 ) ;
259
- const paramName = p . substring ( 2 ) ;
260
- const prefix = placeholdersMap [ 'js' ] [ bindTarget ] ;
258
+ const bindTarget = p . substring ( 0 , 1 )
259
+ const paramName = p . substring ( 2 )
260
+ const prefix = placeholdersMap [ 'js' ] [ bindTarget ]
261
261
return `"${ paramName } ": ${ prefix } .${ paramName } `
262
262
}
263
- ) . join ( ', ' ) ;
263
+ ) . join ( ', ' )
264
264
} ,
265
265
266
266
// "SELECT *\n FROM foo WHERE id = :p.id" => "SELECT * FROM foo WHERE id = :id"
267
267
"formatQuery" : ( query ) => {
268
- return removePlaceholders ( flattenQuery ( removeComments ( query ) ) ) ;
268
+ return removePlaceholders ( flattenQuery ( removeComments ( query ) ) )
269
269
} ,
270
270
271
271
// (used only with Golang)
@@ -283,34 +283,34 @@ const createEndpoints = async (destDir, { lang, overwrite }, config) => {
283
283
// (used only with Golang's go-chi)
284
284
"formatParamsAsGolangMap" : ( params ) => {
285
285
if ( params . length === 0 ) {
286
- return params ;
286
+ return params
287
287
}
288
- const maxParamNameLength = lengthOfLongestString ( params ) ;
288
+ const maxParamNameLength = lengthOfLongestString ( params )
289
289
return Array . from (
290
290
new Set ( params ) ,
291
291
p => {
292
- const bindTarget = p . substring ( 0 , 1 ) ;
293
- const paramName = p . substring ( 2 ) ;
294
- const formatFunc = placeholdersMap [ 'go' ] [ bindTarget ] ;
295
- const quotedParam = '"' + paramName + '":' ;
292
+ const bindTarget = p . substring ( 0 , 1 )
293
+ const paramName = p . substring ( 2 )
294
+ const formatFunc = placeholdersMap [ 'go' ] [ bindTarget ]
295
+ const quotedParam = '"' + paramName + '":'
296
296
// We don't count quotes and colon because they are compensated by "p." prefix.
297
297
// We do +1 because the longest parameter will also have an extra space as a delimiter.
298
298
return `${ quotedParam . padEnd ( maxParamNameLength + 1 ) } ${ formatFunc ( paramName ) } ,`
299
299
}
300
- ) . join ( '\n\t\t\t' ) ;
300
+ ) . join ( '\n\t\t\t' )
301
301
} ,
302
302
303
303
"placeholdersMap" : placeholdersMap ,
304
304
"removeComments" : removeComments ,
305
305
}
306
- ) ;
306
+ )
307
307
308
308
const fsFlags = overwrite ? 'w' : 'wx'
309
309
await fs . writeFile ( resultFile , resultedCode , { 'flag' : fsFlags } , fileExistsHandler )
310
- } ;
310
+ }
311
311
312
312
const createDependenciesDescriptor = async ( destDir , { lang, overwrite } ) => {
313
- let fileName ;
313
+ let fileName
314
314
if ( lang === 'js' ) {
315
315
fileName = 'package.json'
316
316
@@ -321,16 +321,16 @@ const createDependenciesDescriptor = async (destDir, { lang, overwrite }) => {
321
321
fileName = 'requirements.txt'
322
322
323
323
} else {
324
- return ;
324
+ return
325
325
}
326
326
327
- console . log ( 'Generate' , fileName ) ;
327
+ console . log ( 'Generate' , fileName )
328
328
329
- const resultFile = path . join ( destDir , fileName ) ;
329
+ const resultFile = path . join ( destDir , fileName )
330
330
// @todo #24 [js] Possibly incorrect project name with --dest-dir option
331
- const projectName = path . basename ( destDir ) ;
331
+ const projectName = path . basename ( destDir )
332
332
if ( lang === 'js' ) {
333
- console . log ( 'Project name:' , projectName ) ;
333
+ console . log ( 'Project name:' , projectName )
334
334
}
335
335
336
336
const minimalPackageJson = await ejs . renderFile (
@@ -339,11 +339,11 @@ const createDependenciesDescriptor = async (destDir, { lang, overwrite }) => {
339
339
// project name is being used only for package.json
340
340
projectName
341
341
}
342
- ) ;
342
+ )
343
343
344
344
const fsFlags = overwrite ? 'w' : 'wx'
345
345
await fs . writeFile ( resultFile , minimalPackageJson , { 'flag' : fsFlags } , fileExistsHandler )
346
- } ;
346
+ }
347
347
348
348
const showInstructions = ( lang ) => {
349
349
console . info ( 'The application has been generated!' )
@@ -353,7 +353,7 @@ const showInstructions = (lang) => {
353
353
to install its dependencies and
354
354
export DB_NAME=db DB_USER=user DB_PASSWORD=secret
355
355
npm start
356
- afteward to run` ) ;
356
+ afteward to run` )
357
357
} else if ( lang === 'go' ) {
358
358
console . info ( `Use
359
359
export DB_NAME=db DB_USER=user DB_PASSWORD=secret
@@ -371,27 +371,27 @@ to install its dependencies and
371
371
uvicorn app:app
372
372
afteward to run` )
373
373
}
374
- } ;
374
+ }
375
375
376
376
const absolutePathToDestDir = ( argv ) => {
377
377
const relativeDestDir = argv . _ . length > 0 ? argv . _ [ 0 ] : argv [ 'dest-dir' ]
378
378
return path . resolve ( process . cwd ( ) , relativeDestDir )
379
379
}
380
380
381
- const argv = parseCommandLineArgs ( process . argv . slice ( 2 ) ) ;
381
+ const argv = parseCommandLineArgs ( process . argv . slice ( 2 ) )
382
382
383
- const config = loadConfig ( endpointsFile ) ;
383
+ const config = loadConfig ( endpointsFile )
384
384
385
385
const destDir = absolutePathToDestDir ( argv )
386
386
console . log ( 'Destination directory:' , destDir )
387
387
388
388
if ( ! fs . existsSync ( destDir ) ) {
389
389
console . log ( 'Create' , destDir )
390
- fs . mkdirSync ( destDir , { recursive : true } ) ;
390
+ fs . mkdirSync ( destDir , { recursive : true } )
391
391
}
392
392
393
- createApp ( destDir , argv ) ;
393
+ createApp ( destDir , argv )
394
394
createDb ( destDir , argv )
395
- createEndpoints ( destDir , argv , config ) ;
396
- createDependenciesDescriptor ( destDir , argv ) ;
397
- showInstructions ( argv . lang ) ;
395
+ createEndpoints ( destDir , argv , config )
396
+ createDependenciesDescriptor ( destDir , argv )
397
+ showInstructions ( argv . lang )
0 commit comments