@@ -4,7 +4,7 @@ const copy = require('copy-template-dir')
4
4
const { flags } = require ( '@oclif/command' )
5
5
const Command = require ( '@netlify/cli-utils' )
6
6
const inquirer = require ( 'inquirer' )
7
- const readRepoURL = require ( '../../utils/readRepoURL' )
7
+ const { readRepoURL, validateRepoURL } = require ( '../../utils/readRepoURL' )
8
8
const { createSiteAddon } = require ( '../../utils/addons' )
9
9
const http = require ( 'http' )
10
10
const fetch = require ( 'node-fetch' )
@@ -111,11 +111,22 @@ async function pickTemplate() {
111
111
// show separators
112
112
return [
113
113
new inquirer . Separator ( `----[JS]----` ) ,
114
- ...jsreg
114
+ ...jsreg ,
115
115
// new inquirer.Separator(`----[TS]----`),
116
116
// ...tsreg,
117
117
// new inquirer.Separator(`----[GO]----`),
118
118
// ...goreg
119
+ new inquirer . Separator ( `----[Special Commands]----` ) ,
120
+ {
121
+ name : `*** Clone template from Github URL ***` ,
122
+ value : 'url' ,
123
+ short : 'gh-url'
124
+ } ,
125
+ {
126
+ name : `*** Report issue with, or suggest a new template ***` ,
127
+ value : 'report' ,
128
+ short : 'gh-report'
129
+ }
119
130
]
120
131
} else {
121
132
// only show filtered results sorted by score
@@ -144,7 +155,9 @@ async function pickTemplate() {
144
155
} )
145
156
}
146
157
function formatRegistryArrayForInquirer ( lang ) {
147
- const registry = require ( path . join ( templatesDir , lang , 'template-registry.js' ) )
158
+ const folderNames = fs . readdirSync ( path . join ( templatesDir , lang ) )
159
+ const registry = folderNames
160
+ . map ( name => require ( path . join ( templatesDir , lang , name , '.netlify-function-template.js' ) ) )
148
161
. sort ( ( a , b ) => ( a . priority || 999 ) - ( b . priority || 999 ) )
149
162
. map ( t => {
150
163
t . lang = lang
@@ -208,61 +221,103 @@ async function downloadFromURL(flags, args, functionsDir) {
208
221
cp . exec ( 'npm i' , { cwd : path . join ( functionsDir , nameToUse ) } , ( ) => {
209
222
this . log ( `installing dependencies for ${ nameToUse } complete ` )
210
223
} )
224
+
225
+ // read, execute, and delete function template file if exists
226
+ const fnTemplateFile = path . join ( fnFolder , '.netlify-function-template.js' )
227
+ if ( fs . existsSync ( fnTemplateFile ) ) {
228
+ const { onComplete, addons = [ ] } = require ( fnTemplateFile )
229
+ installAddons . call ( this , addons )
230
+ if ( onComplete ) onComplete ( )
231
+ fs . unlinkSync ( fnTemplateFile ) // delete
232
+ }
211
233
}
212
234
213
235
// no --url flag specified, pick from a provided template
214
236
async function scaffoldFromTemplate ( flags , args , functionsDir ) {
215
- const { onComplete, name : templateName , lang, addons = [ ] } = await pickTemplate ( ) // pull the rest of the metadata from the template
216
- const pathToTemplate = path . join ( templatesDir , lang , templateName )
217
- if ( ! fs . existsSync ( pathToTemplate ) ) {
218
- throw new Error ( `there isnt a corresponding folder to the selected name, ${ templateName } template is misconfigured` )
219
- }
220
-
221
- const name = await getNameFromArgs ( args , flags , templateName )
222
- this . log ( `Creating function ${ name } ` )
223
- const functionPath = ensureFunctionPathIsOk ( functionsDir , flags , name )
224
-
225
- // // SWYX: note to future devs - useful for debugging source to output issues
226
- // this.log('from ', pathToTemplate, ' to ', functionPath)
227
- const vars = { NETLIFY_STUFF_TO_REPLACE : 'REPLACEMENT' } // SWYX: TODO
228
- let hasPackageJSON = false
229
- copy ( pathToTemplate , functionPath , vars , ( err , createdFiles ) => {
230
- if ( err ) throw err
231
- createdFiles . forEach ( filePath => {
232
- this . log ( `Created ${ filePath } ` )
233
- if ( filePath . includes ( 'package.json' ) ) hasPackageJSON = true
234
- } )
235
- // rename functions with different names from default
236
- if ( name !== templateName ) {
237
- fs . renameSync ( path . join ( functionPath , templateName + '.js' ) , path . join ( functionPath , name + '.js' ) )
237
+ const chosentemplate = await pickTemplate ( ) // pull the rest of the metadata from the template
238
+ if ( chosentemplate === 'url' ) {
239
+ const { chosenurl } = await inquirer . prompt ( [
240
+ {
241
+ name : 'chosenurl' ,
242
+ message : 'URL to clone: ' ,
243
+ type : 'input' ,
244
+ validate : val => ! ! validateRepoURL ( val )
245
+ // make sure it is not undefined and is a valid filename.
246
+ // this has some nuance i have ignored, eg crossenv and i18n concerns
247
+ }
248
+ ] )
249
+ flags . url = chosenurl . trim ( )
250
+ try {
251
+ await downloadFromURL . call ( this , flags , args , functionsDir )
252
+ } catch ( err ) {
253
+ console . error ( 'Error downloading from URL: ' + flags . url )
254
+ console . error ( err )
255
+ process . exit ( 1 )
238
256
}
239
- // npm install
240
- if ( hasPackageJSON ) {
241
- this . log ( `installing dependencies for ${ name } ...` )
242
- cp . exec ( 'npm i' , { cwd : path . join ( functionPath ) } , ( ) => {
243
- this . log ( `installing dependencies for ${ name } complete ` )
244
- } )
257
+ } else if ( chosentemplate === 'report' ) {
258
+ console . log ( 'opening in browser: https://github.com/netlify/netlify-dev-plugin/issues/new' )
259
+ require ( '../../utils/openBrowser.js' ) ( 'https://github.com/netlify/netlify-dev-plugin/issues/new' )
260
+ } else {
261
+ const { onComplete, name : templateName , lang, addons = [ ] } = chosentemplate
262
+
263
+ const pathToTemplate = path . join ( templatesDir , lang , templateName )
264
+ if ( ! fs . existsSync ( pathToTemplate ) ) {
265
+ throw new Error (
266
+ `there isnt a corresponding folder to the selected name, ${ templateName } template is misconfigured`
267
+ )
245
268
}
246
269
247
- if ( addons . length ) {
248
- const { api, site } = this . netlify
249
- const siteId = site . id
250
- if ( ! siteId ) {
251
- this . log ( 'No site id found, please run inside a site folder or `netlify link`' )
252
- return false
270
+ const name = await getNameFromArgs ( args , flags , templateName )
271
+ this . log ( `Creating function ${ name } ` )
272
+ const functionPath = ensureFunctionPathIsOk ( functionsDir , flags , name )
273
+
274
+ // // SWYX: note to future devs - useful for debugging source to output issues
275
+ // this.log('from ', pathToTemplate, ' to ', functionPath)
276
+ const vars = { NETLIFY_STUFF_TO_REPLACE : 'REPLACEMENT' } // SWYX: TODO
277
+ let hasPackageJSON = false
278
+ copy ( pathToTemplate , functionPath , vars , ( err , createdFiles ) => {
279
+ if ( err ) throw err
280
+ createdFiles . forEach ( filePath => {
281
+ this . log ( `Created ${ filePath } ` )
282
+ if ( filePath . includes ( 'package.json' ) ) hasPackageJSON = true
283
+ } )
284
+ // rename functions with different names from default
285
+ if ( name !== templateName ) {
286
+ fs . renameSync ( path . join ( functionPath , templateName + '.js' ) , path . join ( functionPath , name + '.js' ) )
253
287
}
254
- api . getSite ( { siteId } ) . then ( async siteData => {
255
- const accessToken = await this . authenticate ( )
256
- const arr = addons . map ( addonName => {
257
- this . log ( 'installing addon: ' + addonName )
258
- // will prompt for configs if not supplied - we do not yet allow for addon configs supplied by `netlify functions:create` command and may never do so
259
- return createSiteAddon ( accessToken , addonName , siteId , siteData , log )
288
+ // delete function template file
289
+ fs . unlinkSync ( path . join ( functionPath , '.netlify-function-template.js' ) )
290
+ // npm install
291
+ if ( hasPackageJSON ) {
292
+ this . log ( `installing dependencies for ${ name } ...` )
293
+ cp . exec ( 'npm i' , { cwd : path . join ( functionPath ) } , ( ) => {
294
+ this . log ( `installing dependencies for ${ name } complete ` )
260
295
} )
261
- return Promise . all ( arr )
262
- } )
296
+ }
297
+ installAddons . call ( this , addons )
298
+ if ( onComplete ) onComplete ( ) // do whatever the template wants to do after it is scaffolded
299
+ } )
300
+ }
301
+ }
302
+
303
+ async function installAddons ( addons = [ ] ) {
304
+ if ( addons . length ) {
305
+ const { api, site } = this . netlify
306
+ const siteId = site . id
307
+ if ( ! siteId ) {
308
+ this . log ( 'No site id found, please run inside a site folder or `netlify link`' )
309
+ return false
263
310
}
264
- if ( onComplete ) onComplete ( ) // do whatever the template wants to do after it is scaffolded
265
- } )
311
+ return api . getSite ( { siteId } ) . then ( async siteData => {
312
+ const accessToken = await this . authenticate ( )
313
+ const arr = addons . map ( addonName => {
314
+ this . log ( 'installing addon: ' + addonName )
315
+ // will prompt for configs if not supplied - we do not yet allow for addon configs supplied by `netlify functions:create` command and may never do so
316
+ return createSiteAddon ( accessToken , addonName , siteId , siteData , this . log )
317
+ } )
318
+ return Promise . all ( arr )
319
+ } )
320
+ }
266
321
}
267
322
268
323
// we used to allow for a --dir command,
0 commit comments