@@ -4,7 +4,7 @@ const copy = require('copy-template-dir')
44const { flags } = require ( '@oclif/command' )
55const Command = require ( '@netlify/cli-utils' )
66const inquirer = require ( 'inquirer' )
7- const readRepoURL = require ( '../../utils/readRepoURL' )
7+ const { readRepoURL, validateRepoURL } = require ( '../../utils/readRepoURL' )
88const { createSiteAddon } = require ( '../../utils/addons' )
99const http = require ( 'http' )
1010const fetch = require ( 'node-fetch' )
@@ -111,11 +111,22 @@ async function pickTemplate() {
111111 // show separators
112112 return [
113113 new inquirer . Separator ( `----[JS]----` ) ,
114- ...jsreg
114+ ...jsreg ,
115115 // new inquirer.Separator(`----[TS]----`),
116116 // ...tsreg,
117117 // new inquirer.Separator(`----[GO]----`),
118118 // ...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+ }
119130 ]
120131 } else {
121132 // only show filtered results sorted by score
@@ -144,7 +155,9 @@ async function pickTemplate() {
144155 } )
145156 }
146157 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' ) ) )
148161 . sort ( ( a , b ) => ( a . priority || 999 ) - ( b . priority || 999 ) )
149162 . map ( t => {
150163 t . lang = lang
@@ -208,61 +221,103 @@ async function downloadFromURL(flags, args, functionsDir) {
208221 cp . exec ( 'npm i' , { cwd : path . join ( functionsDir , nameToUse ) } , ( ) => {
209222 this . log ( `installing dependencies for ${ nameToUse } complete ` )
210223 } )
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+ }
211233}
212234
213235// no --url flag specified, pick from a provided template
214236async 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 )
238256 }
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+ )
245268 }
246269
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' ) )
253287 }
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 ` )
260295 } )
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
263310 }
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+ }
266321}
267322
268323// we used to allow for a --dir command,
0 commit comments