1
+ import inquirer from 'inquirer' ;
2
+ import path from 'path' ;
3
+ import { getGithubFolderNames } from './get-github-folder-names' ;
4
+ import { ACCEPTED_EXAMPLE_FOLDERS , ACCEPTED_LANGUAGES , BANNED_FUNCTION_NAMES } from './constants' ;
5
+ import { GenerateFunctionSettings , AcceptedFunctionExamples , SourceName , GenerateFunctionOptions , Language } from '../types' ;
6
+ import ora from 'ora' ;
7
+ import chalk from 'chalk' ;
8
+ import { warn } from './logger' ;
9
+
10
+ export async function buildGenerateFunctionSettings ( ) : Promise < GenerateFunctionSettings > {
11
+ const baseSettings = await inquirer . prompt < GenerateFunctionSettings > ( [
12
+ {
13
+ name : 'name' ,
14
+ message : `Function name (${ path . basename ( process . cwd ( ) ) } ):` ,
15
+ } ,
16
+ {
17
+ name : 'sourceType' ,
18
+ message : 'Do you want to start with a blank template or use one of our examples?' ,
19
+ type : 'list' ,
20
+ choices : [
21
+ { name : 'Template' , value : 'template' } ,
22
+ { name : 'Example' , value : 'example' } ,
23
+ ] ,
24
+ default : 'template' ,
25
+ }
26
+ ] ) ;
27
+ if ( BANNED_FUNCTION_NAMES . includes ( baseSettings . name ) ) {
28
+ throw new Error ( `Invalid function name: ${ baseSettings . name } ` ) ;
29
+ }
30
+ let sourceSpecificSettings : GenerateFunctionSettings ;
31
+ if ( baseSettings . sourceType === 'template' ) {
32
+ sourceSpecificSettings = await inquirer . prompt < GenerateFunctionSettings > ( [
33
+ {
34
+ name : 'language' ,
35
+ message : 'Pick a template' ,
36
+ type : 'list' ,
37
+ choices : [
38
+ { name : 'TypeScript' , value : 'typescript' } ,
39
+ { name : 'JavaScript' , value : 'javascript' } ,
40
+ ] ,
41
+ default : 'typescript' ,
42
+ }
43
+ ] )
44
+ sourceSpecificSettings . sourceName = sourceSpecificSettings . language . toLowerCase ( ) as SourceName
45
+ } else {
46
+ const availableExamples = await getGithubFolderNames ( ) ;
47
+ const filteredExamples = availableExamples . filter (
48
+ ( template ) =>
49
+ ACCEPTED_EXAMPLE_FOLDERS . includes ( template as ( typeof ACCEPTED_EXAMPLE_FOLDERS ) [ number ] )
50
+ ) ;
51
+
52
+ sourceSpecificSettings = await inquirer . prompt < GenerateFunctionSettings > ( [
53
+ {
54
+ name : 'sourceName' ,
55
+ message : 'Select an example:' ,
56
+ type : 'list' ,
57
+ choices : filteredExamples ,
58
+ } ,
59
+ {
60
+ name : 'language' ,
61
+ message : 'Pick a template' ,
62
+ type : 'list' ,
63
+ choices : [
64
+ { name : 'TypeScript' , value : 'typescript' } ,
65
+ { name : 'JavaScript' , value : 'javascript' } ,
66
+ ] ,
67
+ default : 'typescript' ,
68
+ }
69
+ ] )
70
+ }
71
+ baseSettings . sourceName = sourceSpecificSettings . sourceName
72
+ baseSettings . language = sourceSpecificSettings . language
73
+ return baseSettings
74
+ }
75
+
76
+ function validateArguments ( options : GenerateFunctionOptions ) {
77
+ const templateRequired = [ 'name' , 'template' ] ;
78
+ const exampleRequired = [ 'name' , 'example' , 'language' ] ;
79
+ if ( BANNED_FUNCTION_NAMES . includes ( options . name ) ) {
80
+ throw new Error ( `Invalid function name: ${ options . name } ` ) ;
81
+ }
82
+ if ( 'template' in options ) {
83
+ if ( ! templateRequired . every ( ( key ) => key in options ) ) {
84
+ throw new Error ( 'You must specify a function name and a template' ) ;
85
+ }
86
+ } else if ( 'example' in options ) {
87
+ if ( ! exampleRequired . every ( ( key ) => key in options ) ) {
88
+ throw new Error ( 'You must specify a function name, an example, and a language' ) ;
89
+ }
90
+ } else {
91
+ throw new Error ( 'You must specify either --template or --example' ) ;
92
+ }
93
+ }
94
+
95
+ export async function buildGenerateFunctionSettingsFromOptions ( options : GenerateFunctionOptions ) : Promise < GenerateFunctionSettings > {
96
+ const validateSpinner = ora ( 'Validating your input\n' ) . start ( ) ;
97
+ const settings : GenerateFunctionSettings = { } as GenerateFunctionSettings ;
98
+ try {
99
+ validateArguments ( options ) ;
100
+ for ( const key in options ) { // convert all options to lowercase and trim
101
+ const optionKey = key as keyof GenerateFunctionOptions ;
102
+ options [ optionKey ] = options [ optionKey ] . toLowerCase ( ) . trim ( ) ;
103
+ }
104
+
105
+ if ( 'example' in options ) {
106
+ if ( 'template' in options ) {
107
+ throw new Error ( 'Cannot specify both --template and --example' ) ;
108
+ }
109
+
110
+ if ( ! ACCEPTED_EXAMPLE_FOLDERS . includes ( options . example as AcceptedFunctionExamples ) ) {
111
+ throw new Error ( `Invalid example name: ${ options . example } ` ) ;
112
+ }
113
+
114
+ if ( ! ACCEPTED_LANGUAGES . includes ( options . language ) ) {
115
+ warn ( `Invalid language: ${ options . language } . Defaulting to TypeScript.` ) ;
116
+ settings . language = 'typescript' ;
117
+ } else {
118
+ settings . language = options . language ;
119
+ }
120
+ settings . sourceType = 'example' ;
121
+ settings . sourceName = options . example ;
122
+ settings . name = options . name ;
123
+
124
+ } else if ( 'template' in options ) {
125
+ if ( 'language' in options && options . language && options . language != options . template ) {
126
+ console . warn ( `Ignoring language option: ${ options . language } . Defaulting to ${ options . template } .` ) ;
127
+ }
128
+ if ( ! ACCEPTED_LANGUAGES . includes ( options . template as Language ) ) {
129
+ console . warn ( `Invalid language: ${ options . template } . Defaulting to TypeScript.` ) ;
130
+ settings . language = 'typescript' ;
131
+ settings . sourceName = 'typescript' ;
132
+ } else {
133
+ settings . language = options . template as Language ;
134
+ settings . sourceName = options . template ;
135
+ }
136
+ settings . sourceType = 'template' ;
137
+ settings . name = options . name ;
138
+ }
139
+
140
+ return settings ;
141
+ } catch ( err : any ) {
142
+ console . log ( `
143
+ ${ chalk . red ( 'Validation failed' ) }
144
+ ${ err . message }
145
+ ` ) ;
146
+ // eslint-disable-next-line no-process-exit
147
+ process . exit ( 1 ) ;
148
+ } finally {
149
+ validateSpinner . stop ( ) ;
150
+ }
151
+ }
0 commit comments