Skip to content
This repository has been archived by the owner on Sep 12, 2019. It is now read-only.

Commit

Permalink
Merge pull request #22 from netlify/addInquirer
Browse files Browse the repository at this point in the history
add working basic templates for netlify functions:create
  • Loading branch information
swyxio authored Mar 15, 2019
2 parents 4d7cb28 + b3f4ff9 commit 1a9660d
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 65 deletions.
66 changes: 20 additions & 46 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"ascii-table": "0.0.9",
"get-port": "^4.1.0",
"http-proxy": "^1.17.0",
"inquirer": "^6.2.2",
"netlify": "ssh+git://git@github.com:netlify/js-client-private",
"netlify-rules-proxy": "git+ssh://git@github.com/netlify/netlify-rules-proxy.git",
"static-dev-server": "^1.0.0",
Expand Down
71 changes: 52 additions & 19 deletions src/commands/functions/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,34 @@ const fs = require('fs')
const path = require('path')
const { flags } = require('@oclif/command')
const Command = require('@netlify/cli-utils')

const template = `async function hello() {
return Promise.resolve('Hello, World')
}
exports.handler = async function(event, context) {
try {
const body = await hello()
return { statusCode: 200, body }
} catch (err) {
return { statusCode: 500, body: err.toString() }
}
}
`
const inquirer = require('inquirer')

class FunctionsCreateCommand extends Command {
async run() {
const { flags, args } = this.parse(FunctionsCreateCommand)
const { name } = args
const name = await getNameFromArgs(args)
const { config } = this.netlify
const templates = fs
.readdirSync(path.resolve(__dirname, '../../functions-templates'))
.filter(x => path.extname(x) === '.js') // only js templates for now
const { templatePath } = await inquirer.prompt([
{
name: 'templatePath',
message: 'pick a template',
type: 'list',
choices: templates.map(t => {
return require(path.resolve(__dirname, '../../functions-templates/', t)).metadata
// ({ name: path.basename(t, '.js') })
})
}
])

let template = fs
.readFileSync(path.resolve(__dirname, `../../functions-templates/${templatePath}.js`))
.toString()
.split('// --- Netlify Template Below -- //')
if (template.length !== 2) throw new Error('template ' + templatePath + ' badly formatted')
template = '// scaffolded from `netlify functions:create` \n' + template[1]

this.log(`Creating function ${name}`)

Expand Down Expand Up @@ -56,18 +64,23 @@ class FunctionsCreateCommand extends Command {
// Ignore
}
} else if (fs.existsSync(functionPath.replace(/\.js/, ''))) {
this.log(`A folder version of the function ${name} alreadt exists at ${functionPath.replace(/\.js/, '')}`)
this.log(`A folder version of the function ${name} already exists at ${functionPath.replace(/\.js/, '')}`)
process.exit(1)
}

fs.writeFileSync(functionPath, template)
}
}

FunctionsCreateCommand.args = [{ name: 'name' }]
FunctionsCreateCommand.args = [
{
name: 'name',
// required: true, // tried this but the error message is very ugly
description: 'name of your new function file inside your functions folder'
}
]

FunctionsCreateCommand.description = `create a new function locally
`
FunctionsCreateCommand.description = `create a new function locally`

FunctionsCreateCommand.examples = ['netlify functions:create hello-world']

Expand All @@ -79,3 +92,23 @@ FunctionsCreateCommand.flags = {
})
}
module.exports = FunctionsCreateCommand

// prompt for a name if name not supplied
// we tried using required:true in oclif args (see below) but the error msg was very ugly
async function getNameFromArgs(args) {
let { name } = args
if (!name) {
let responses = await inquirer.prompt([
{
name: 'name',
message: 'name your function: ',
type: 'input',
validate: val => !!val && /^[\w\-.]+$/i.test(val)
// make sure it is not undefined and is a valid filename.
// this has some nuance i have ignored, eg crossenv and i18n concerns
}
])
name = responses.name
}
return name
}
15 changes: 15 additions & 0 deletions src/functions-templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## note to devs

place new templates here and our CLI will pick it up. currently only works for single file `.js` templates.

## why place it in this separate folder

we dont colocate this inside `src/commands/functions` because oclif will think it's a new command.

## providing metadata (and other functionality)

we split the file based on the `// --- Netlify Template Below -- //` string. everything below it is cloned as the template. everything above it can be required and run as a module for configuring the template. for now we simply export a `metadata` object that fits [`inquirer's choices spec`](https://www.npmjs.com/package/inquirer#question). in future we can think about other options we may want to offer.

## future dev thoughts

we will want a way to scale this to TS and Go as well.
19 changes: 19 additions & 0 deletions src/functions-templates/hello-world.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// --- Netlify Template Metadata -- //
exports.metadata = {
name: 'Basic Hello World function: shows async/await usage, and proper formatting with statusCode and body',
value: 'hello-world',
short: 'hello-world'
}
// --- Netlify Template Below -- //
async function hello() {
return Promise.resolve('Hello, World')
}

exports.handler = async function(event, context) {
try {
const body = await hello()
return { statusCode: 200, body }
} catch (err) {
return { statusCode: 500, body: err.toString() }
}
}
29 changes: 29 additions & 0 deletions src/functions-templates/node-fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// --- Netlify Template Metadata -- //
exports.metadata = {
name: 'Fetch function: uses node-fetch to hit an external API without CORS issues',
value: 'node-fetch',
short: 'node-fetch'
}
// --- Netlify Template Below -- //
const fetch = require('node-fetch')
exports.handler = async function(event, context) {
try {
const response = await fetch('https://api.chucknorris.io/jokes/random')
if (!response.ok) {
// NOT res.status >= 200 && res.status < 300
return { statusCode: response.status, body: response.statusText }
}
const data = await response.json()

return {
statusCode: 200,
body: JSON.stringify({ msg: data.value })
}
} catch (err) {
console.log(err) // output to netlify function log
return {
statusCode: 500,
body: JSON.stringify({ msg: err.message }) // Could be a custom message or object i.e. JSON.stringify(err)
}
}
}

0 comments on commit 1a9660d

Please sign in to comment.