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