11#!/usr/bin/env node
22
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' )
77
8- const parseArgs = require ( 'minimist' ) ;
8+ const parseArgs = require ( 'minimist' )
99
10- const { Parser } = require ( 'node-sql-parser' ) ;
10+ const { Parser } = require ( 'node-sql-parser' )
1111
12- const endpointsFile = 'endpoints.yaml' ;
12+ const endpointsFile = 'endpoints.yaml'
1313
1414const parseCommandLineArgs = ( args ) => {
1515 const opts = {
@@ -22,10 +22,10 @@ const parseCommandLineArgs = (args) => {
2222 'dest-dir' : '.' ,
2323 'overwrite' : false ,
2424 }
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
2929}
3030
3131// Restructure YAML configuration to simplify downstream code.
@@ -44,34 +44,34 @@ const parseCommandLineArgs = (args) => {
4444// }
4545const restructureConfiguration = ( config ) => {
4646 for ( const endpoint of config ) {
47- endpoint . methods = [ ] ;
47+ endpoint . methods = [ ] ; // this semicolon is really needed
4848 [ 'get' , 'get_list' , 'post' , 'put' , 'delete' ] . forEach ( method => {
4949 if ( ! endpoint . hasOwnProperty ( method ) ) {
50- return ;
50+ return
5151 }
5252 endpoint . methods . push ( {
5353 'name' : method ,
5454 'verb' : method !== 'get_list' ? method : 'get' ,
5555 ...endpoint [ method ] ,
56- } ) ;
57- delete endpoint [ method ] ;
58- } ) ;
56+ } )
57+ delete endpoint [ method ]
58+ } )
5959 }
60- } ;
60+ }
6161
6262const loadConfig = ( endpointsFile ) => {
63- console . log ( 'Read' , endpointsFile ) ;
63+ console . log ( 'Read' , endpointsFile )
6464 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
7070 } 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
7373 }
74- } ;
74+ }
7575
7676const lang2extension = ( lang ) => {
7777 switch ( lang ) {
@@ -115,8 +115,8 @@ const fileExistsHandler = (err) => {
115115const createApp = async ( destDir , { lang, overwrite } ) => {
116116 const ext = lang2extension ( lang )
117117 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 )
120120 const customRouters = findFileNamesEndWith ( destDir , `_routes.${ ext } ` )
121121 if ( customRouters . length > 0 ) {
122122 customRouters . forEach ( filename => console . log ( `Include a custom router from ${ filename } ` ) )
@@ -133,44 +133,44 @@ const createApp = async (destDir, { lang, overwrite }) => {
133133
134134 const fsFlags = overwrite ? 'w' : 'wx'
135135 await fs . writeFile ( resultFile , resultedCode , { 'flag' : fsFlags } , fileExistsHandler )
136- } ;
136+ }
137137
138138const createDb = async ( destDir , { lang, overwrite } ) => {
139139 if ( lang !== 'python' ) {
140140 return
141141 }
142142 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 )
145145
146146 const mode = overwrite ? 0 : fs . constants . COPYFILE_EXCL
147147 await fs . copyFile ( `${ __dirname } /templates/${ fileName } ` , resultFile , mode , fileExistsHandler )
148148}
149149
150150// "-- comment\nSELECT * FROM foo" => "SELECT * FROM foo"
151- const removeComments = ( query ) => query . replace ( / - - .* \n / g, '' ) ;
151+ const removeComments = ( query ) => query . replace ( / - - .* \n / g, '' )
152152
153153// "SELECT *\n FROM foo" => "SELECT * FROM foo"
154- const flattenQuery = ( query ) => query . replace ( / \n [ ] * / g, ' ' ) ;
154+ const flattenQuery = ( query ) => query . replace ( / \n [ ] * / g, ' ' )
155155
156156// "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, '' )
158158
159159// "/categories/:id" => "/categories/{id}"
160160// (used only with Golang's go-chi)
161- const convertPathPlaceholders = ( path ) => path . replace ( / : ( [ ^ \/ ] + ) / g, '{$1}' ) ;
161+ const convertPathPlaceholders = ( path ) => path . replace ( / : ( [ ^ \/ ] + ) / g, '{$1}' )
162162
163163// "name_ru" => "nameRu"
164164// (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 ( ) )
166166
167167// "categoryId" => "category_id"
168168// (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 ( ) )
170170
171171// "nameRu" => "NameRu"
172172// (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 )
174174
175175// ["a", "bb", "ccc"] => 3
176176// (used only with Golang's go-chi)
@@ -179,22 +179,22 @@ const lengthOfLongestString = (arr) => arr
179179 . reduce (
180180 ( acc , val ) => val > acc ? val : acc ,
181181 0 /* initial value */
182- ) ;
182+ )
183183
184184const createEndpoints = async ( destDir , { lang, overwrite } , config ) => {
185185 const ext = lang2extension ( lang )
186186 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 )
189189
190190 for ( let endpoint of config ) {
191- let path = endpoint . path ;
191+ let path = endpoint . path
192192 if ( lang === 'go' ) {
193193 path = convertPathPlaceholders ( path )
194194 }
195195 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 } ` )
198198
199199 let queries = [ ]
200200 if ( method . query ) {
@@ -203,10 +203,10 @@ const createEndpoints = async (destDir, { lang, overwrite }, config) => {
203203 queries = Object . values ( method . aggregated_queries )
204204 }
205205 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 } ` )
208208 } )
209- } ) ;
209+ } )
210210 }
211211
212212 const placeholdersMap = {
@@ -220,15 +220,15 @@ const createEndpoints = async (destDir, { lang, overwrite }, config) => {
220220 return `chi.URLParam(r, "${ param } ")`
221221 } ,
222222 'b' : function ( param ) {
223- return 'dto.' + capitalize ( snake2camelCase ( param ) ) ;
223+ return 'dto.' + capitalize ( snake2camelCase ( param ) )
224224 } ,
225225 'q' : function ( param ) {
226226 return `r.URL.Query().Get("${ param } ")`
227227 } ,
228228 }
229229 }
230230
231- const parser = new Parser ( ) ;
231+ const parser = new Parser ( )
232232
233233 const resultedCode = await ejs . renderFile (
234234 `${ __dirname } /templates/routes.${ ext } .ejs` ,
@@ -250,22 +250,22 @@ const createEndpoints = async (destDir, { lang, overwrite }, config) => {
250250 // (used only with Express)
251251 "formatParamsAsJavaScriptObject" : ( params ) => {
252252 if ( params . length === 0 ) {
253- return params ;
253+ return params
254254 }
255255 return Array . from (
256256 new Set ( params ) ,
257257 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 ]
261261 return `"${ paramName } ": ${ prefix } .${ paramName } `
262262 }
263- ) . join ( ', ' ) ;
263+ ) . join ( ', ' )
264264 } ,
265265
266266 // "SELECT *\n FROM foo WHERE id = :p.id" => "SELECT * FROM foo WHERE id = :id"
267267 "formatQuery" : ( query ) => {
268- return removePlaceholders ( flattenQuery ( removeComments ( query ) ) ) ;
268+ return removePlaceholders ( flattenQuery ( removeComments ( query ) ) )
269269 } ,
270270
271271 // (used only with Golang)
@@ -283,34 +283,34 @@ const createEndpoints = async (destDir, { lang, overwrite }, config) => {
283283 // (used only with Golang's go-chi)
284284 "formatParamsAsGolangMap" : ( params ) => {
285285 if ( params . length === 0 ) {
286- return params ;
286+ return params
287287 }
288- const maxParamNameLength = lengthOfLongestString ( params ) ;
288+ const maxParamNameLength = lengthOfLongestString ( params )
289289 return Array . from (
290290 new Set ( params ) ,
291291 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 + '":'
296296 // We don't count quotes and colon because they are compensated by "p." prefix.
297297 // We do +1 because the longest parameter will also have an extra space as a delimiter.
298298 return `${ quotedParam . padEnd ( maxParamNameLength + 1 ) } ${ formatFunc ( paramName ) } ,`
299299 }
300- ) . join ( '\n\t\t\t' ) ;
300+ ) . join ( '\n\t\t\t' )
301301 } ,
302302
303303 "placeholdersMap" : placeholdersMap ,
304304 "removeComments" : removeComments ,
305305 }
306- ) ;
306+ )
307307
308308 const fsFlags = overwrite ? 'w' : 'wx'
309309 await fs . writeFile ( resultFile , resultedCode , { 'flag' : fsFlags } , fileExistsHandler )
310- } ;
310+ }
311311
312312const createDependenciesDescriptor = async ( destDir , { lang, overwrite } ) => {
313- let fileName ;
313+ let fileName
314314 if ( lang === 'js' ) {
315315 fileName = 'package.json'
316316
@@ -321,16 +321,16 @@ const createDependenciesDescriptor = async (destDir, { lang, overwrite }) => {
321321 fileName = 'requirements.txt'
322322
323323 } else {
324- return ;
324+ return
325325 }
326326
327- console . log ( 'Generate' , fileName ) ;
327+ console . log ( 'Generate' , fileName )
328328
329- const resultFile = path . join ( destDir , fileName ) ;
329+ const resultFile = path . join ( destDir , fileName )
330330 // @todo #24 [js] Possibly incorrect project name with --dest-dir option
331- const projectName = path . basename ( destDir ) ;
331+ const projectName = path . basename ( destDir )
332332 if ( lang === 'js' ) {
333- console . log ( 'Project name:' , projectName ) ;
333+ console . log ( 'Project name:' , projectName )
334334 }
335335
336336 const minimalPackageJson = await ejs . renderFile (
@@ -339,11 +339,11 @@ const createDependenciesDescriptor = async (destDir, { lang, overwrite }) => {
339339 // project name is being used only for package.json
340340 projectName
341341 }
342- ) ;
342+ )
343343
344344 const fsFlags = overwrite ? 'w' : 'wx'
345345 await fs . writeFile ( resultFile , minimalPackageJson , { 'flag' : fsFlags } , fileExistsHandler )
346- } ;
346+ }
347347
348348const showInstructions = ( lang ) => {
349349 console . info ( 'The application has been generated!' )
@@ -353,7 +353,7 @@ const showInstructions = (lang) => {
353353to install its dependencies and
354354 export DB_NAME=db DB_USER=user DB_PASSWORD=secret
355355 npm start
356- afteward to run` ) ;
356+ afteward to run` )
357357 } else if ( lang === 'go' ) {
358358 console . info ( `Use
359359 export DB_NAME=db DB_USER=user DB_PASSWORD=secret
@@ -371,27 +371,27 @@ to install its dependencies and
371371 uvicorn app:app
372372afteward to run` )
373373 }
374- } ;
374+ }
375375
376376const absolutePathToDestDir = ( argv ) => {
377377 const relativeDestDir = argv . _ . length > 0 ? argv . _ [ 0 ] : argv [ 'dest-dir' ]
378378 return path . resolve ( process . cwd ( ) , relativeDestDir )
379379}
380380
381- const argv = parseCommandLineArgs ( process . argv . slice ( 2 ) ) ;
381+ const argv = parseCommandLineArgs ( process . argv . slice ( 2 ) )
382382
383- const config = loadConfig ( endpointsFile ) ;
383+ const config = loadConfig ( endpointsFile )
384384
385385const destDir = absolutePathToDestDir ( argv )
386386console . log ( 'Destination directory:' , destDir )
387387
388388if ( ! fs . existsSync ( destDir ) ) {
389389 console . log ( 'Create' , destDir )
390- fs . mkdirSync ( destDir , { recursive : true } ) ;
390+ fs . mkdirSync ( destDir , { recursive : true } )
391391}
392392
393- createApp ( destDir , argv ) ;
393+ createApp ( destDir , argv )
394394createDb ( 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