Skip to content

Commit

Permalink
feat: add TypeScript function template (#3127)
Browse files Browse the repository at this point in the history
* feat: add TypeScript function template

* chore: update tests

* refactor: adjust error message

* fix: rename main file for all languages
  • Loading branch information
eduardoboucas authored Aug 11, 2021
1 parent b6aa9c7 commit 86bcad1
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 138 deletions.
96 changes: 53 additions & 43 deletions src/commands/functions/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ const { readRepoURL, validateRepoURL } = require('../../utils/read-repo-url')

const templatesDir = path.resolve(__dirname, '../../functions-templates')

// Ensure that there's a sub-directory in `src/functions-templates` named after
// each `value` property in this list, and that it matches the extension of the
// files used by that language.
const languages = [
{ name: 'JavaScript', value: 'js' },
{ name: 'TypeScript', value: 'ts' },
]

/**
* Be very clear what is the SOURCE (templates dir) vs the DEST (functions dir)
*/
Expand Down Expand Up @@ -82,7 +90,7 @@ const getNameFromArgs = async function (args, flags, defaultName) {
const { name } = await inquirer.prompt([
{
name: 'name',
message: 'name your function: ',
message: 'Name your function:',
default: defaultName,
type: 'input',
validate: (val) => Boolean(val) && /^[\w.-]+$/i.test(val),
Expand Down Expand Up @@ -145,57 +153,47 @@ const formatRegistryArrayForInquirer = function (lang) {

// pick template from our existing templates
const pickTemplate = async function () {
inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt)
// doesnt scale but will be ok for now
const [
jsreg,
// tsreg, goreg
] = [
'js',
// 'ts', 'go'
].map(formatRegistryArrayForInquirer)
const specialCommands = [
new inquirer.Separator(`----[Special Commands]----`),
new inquirer.Separator(),
{
name: `*** Clone template from Github URL ***`,
name: `Clone template from GitHub URL`,
value: 'url',
short: 'gh-url',
},
{
name: `*** Report issue with, or suggest a new template ***`,
name: `Report issue with, or suggest a new template`,
value: 'report',
short: 'gh-report',
},
new inquirer.Separator(),
]
const { chosentemplate } = await inquirer.prompt({
name: 'chosentemplate',
const { language } = await inquirer.prompt({
choices: languages,
message: 'Select the language of your function',
name: 'language',
type: 'list',
})

inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt)

const templatesForLanguage = formatRegistryArrayForInquirer(language)
const { chosenTemplate } = await inquirer.prompt({
name: 'chosenTemplate',
message: 'Pick a template',
type: 'autocomplete',
// suggestOnly: true, // we can explore this for entering URL in future
source(answersSoFar, input) {
if (!input || input === '') {
// show separators
return [
new inquirer.Separator(`----[JS]----`),
...jsreg,
// new inquirer.Separator(`----[TS]----`),
// ...tsreg,
// new inquirer.Separator(`----[GO]----`),
// ...goreg
...specialCommands,
]
return [...templatesForLanguage, ...specialCommands]
}
// only show filtered results sorted by score
const answers = [
...filterRegistry(jsreg, input),
// ...filterRegistry(tsreg, input),
// ...filterRegistry(goreg, input)
...specialCommands,
].sort((answerA, answerB) => answerB.score - answerA.score)
const answers = [...filterRegistry(templatesForLanguage, input), ...specialCommands].sort(
(answerA, answerB) => answerB.score - answerA.score,
)
return answers
},
})
return chosentemplate
return chosenTemplate
}

const DEFAULT_PRIORITY = 999
Expand Down Expand Up @@ -358,40 +356,49 @@ const installDeps = async ({ functionPackageJson, functionPath, functionsDir })
// We installed the function's dependencies in the site-level `package.json`,
// so there's no reason to keep the one copied over from the template.
fs.unlinkSync(functionPackageJson)

// Similarly, if the template has a `package-lock.json` file, we delete it.
try {
const functionPackageLock = path.join(functionPath, 'package-lock.json')

fs.unlinkSync(functionPackageLock)
} catch (error) {
// no-op
}
}

// no --url flag specified, pick from a provided template
const scaffoldFromTemplate = async function (context, flags, args, functionsDir) {
// pull the rest of the metadata from the template
const chosentemplate = await pickTemplate()
if (chosentemplate === 'url') {
const { chosenurl } = await inquirer.prompt([
const chosenTemplate = await pickTemplate()
if (chosenTemplate === 'url') {
const { chosenUrl } = await inquirer.prompt([
{
name: 'chosenurl',
name: 'chosenUrl',
message: 'URL to clone: ',
type: 'input',
validate: (val) => Boolean(validateRepoURL(val)),
// make sure it is not undefined and is a valid filename.
// this has some nuance i have ignored, eg crossenv and i18n concerns
},
])
flags.url = chosenurl.trim()
flags.url = chosenUrl.trim()
try {
await downloadFromURL(context, flags, args, functionsDir)
} catch (error) {
context.error(`$${NETLIFYDEVERR} Error downloading from URL: ${flags.url}`)
context.error(error)
process.exit(1)
}
} else if (chosentemplate === 'report') {
} else if (chosenTemplate === 'report') {
log(`${NETLIFYDEVLOG} Open in browser: https://github.com/netlify/cli/issues/new`)
} else {
const { onComplete, name: templateName, lang, addons = [] } = chosentemplate
const { onComplete, name: templateName, lang, addons = [] } = chosenTemplate

const pathToTemplate = path.join(templatesDir, lang, templateName)
if (!fs.existsSync(pathToTemplate)) {
throw new Error(
`there isnt a corresponding directory to the selected name, ${templateName} template is misconfigured`,
`There isn't a corresponding directory to the selected name. Template '${templateName}' is misconfigured`,
)
}

Expand Down Expand Up @@ -419,16 +426,19 @@ const scaffoldFromTemplate = async function (context, flags, args, functionsDir)
fs.unlinkSync(path.join(functionPath, '.netlify-function-template.js'))
// rename the root function file if it has a different name from default
if (name !== templateName) {
fs.renameSync(path.join(functionPath, `${templateName}.js`), path.join(functionPath, `${name}.js`))
const oldPath = path.join(functionPath, `${templateName}.${lang}`)
const newPath = path.join(functionPath, `${name}.${lang}`)

fs.renameSync(oldPath, newPath)
}
// npm install
if (functionPackageJson !== undefined) {
const spinner = ora({
text: `installing dependencies for ${name}`,
text: `Installing dependencies for ${name}`,
spinner: 'moon',
}).start()
await installDeps({ functionPackageJson, functionPath, functionsDir })
spinner.succeed(`installed dependencies for ${name}`)
spinner.succeed(`Installed dependencies for ${name}`)
}

await installAddons(context, addons, path.resolve(functionPath))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
name: 'hello-world',
priority: 1,
description: 'Basic function that shows async/await usage, and response formatting',
}
12 changes: 12 additions & 0 deletions src/functions-templates/ts/hello-world/hello-world.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Handler } from '@netlify/functions'

export const handler: Handler = async (event, context) => {
const { name = 'stranger' } = event.queryStringParameters

return {
statusCode: 200,
body: JSON.stringify({
message: `Hello, ${name}!`,
}),
}
}
75 changes: 75 additions & 0 deletions src/functions-templates/ts/hello-world/package-lock.json

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

Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
"author": "Netlify",
"license": "MIT",
"dependencies": {
"node-fetch": "^2.6.1",
"@netlify/functions": "^0.7.2",
"@types/node": "^14.0.0",
"typescript": "^4.0.0",
"@types/aws-lambda": "^8.10.64"
"typescript": "^4.0.0"
}
}
18 changes: 0 additions & 18 deletions src/functions-templates/unused_go/hello-world/hello-world.go

This file was deleted.

21 changes: 0 additions & 21 deletions src/functions-templates/unused_ts/hello-world/hello-world.ts

This file was deleted.

26 changes: 0 additions & 26 deletions src/functions-templates/unused_ts/node-fetch/node-fetch.ts

This file was deleted.

23 changes: 0 additions & 23 deletions src/functions-templates/unused_ts/node-fetch/package.json

This file was deleted.

Loading

1 comment on commit 86bcad1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Benchmark results

Package size: 331 MB

Please sign in to comment.