Skip to content

Commit

Permalink
refactor: use zip-it-and-ship-it to list Functions (#1478)
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky authored Nov 4, 2020
1 parent ee056ff commit b2de7ea
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 114 deletions.
9 changes: 7 additions & 2 deletions src/commands/functions/invoke.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class FunctionsInvokeCommand extends Command {
)
const port = flags.port || DEFAULT_PORT

const functions = getFunctions(functionsDir)
const functions = await getFunctions(functionsDir)
const functionToTrigger = await getNameFromArgs(functions, args, flags)

let headers = {}
Expand Down Expand Up @@ -180,7 +180,7 @@ const processPayloadFromFlag = function (payloadString) {
// also used in functions:create
const getNameFromArgs = async function (functions, args, flags) {
const functionToTrigger = getFunctionToTrigger(args, flags)
const functionNames = Object.keys(functions)
const functionNames = functions.map(getFunctionName)

if (functionToTrigger) {
if (functionNames.includes(functionToTrigger)) {
Expand All @@ -193,6 +193,7 @@ const getNameFromArgs = async function (functions, args, flags) {
)} supplied but no matching function found in your functions folder, forcing you to pick a valid one...`,
)
}

const { trigger } = await inquirer.prompt([
{
type: 'list',
Expand All @@ -217,6 +218,10 @@ const getFunctionToTrigger = function (args, flags) {
return args.name
}

const getFunctionName = function ({ name }) {
return name
}

FunctionsInvokeCommand.description = `Trigger a function while in netlify dev with simulated data, good for testing function calls including Netlify's Event Triggered Functions`
FunctionsInvokeCommand.aliases = ['function:trigger']

Expand Down
29 changes: 12 additions & 17 deletions src/commands/functions/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,40 +58,35 @@ class FunctionsListCommand extends Command {
process.exit(1)
}

const functions = getFunctions(functionsDir)
const functionData = Object.entries(functions)
const functions = await getFunctions(functionsDir)
const normalizedFunctions = functions.map(normalizeFunction.bind(null, deployedFunctions))

if (functionData.length === 0) {
if (normalizedFunctions.length === 0) {
this.log(`No functions found in ${functionsDir}`)
this.exit()
}

if (flags.json) {
const jsonData = functionData.map(([functionName, { moduleDir }]) => {
const isDeployed = deployedFunctions.map((deployedFunction) => deployedFunction.n).includes(functionName)
return {
name: functionName,
url: `/.netlify/functions/${functionName}`,
moduleDir,
isDeployed,
}
})
this.logJson(jsonData)
this.logJson(normalizedFunctions)
this.exit()
}

// Make table
this.log(`Based on local functions folder ${functionsDir}, these are the functions detected`)
const table = new AsciiTable(`Netlify Functions (in local functions folder)`)
table.setHeading('Name', 'Url', 'moduleDir', 'deployed')
functionData.forEach(([functionName, { moduleDir }]) => {
const isDeployed = deployedFunctions.map((deployedFunction) => deployedFunction.n).includes(functionName)
table.addRow(functionName, `/.netlify/functions/${functionName}`, moduleDir, isDeployed ? 'yes' : 'no')
table.setHeading('Name', 'URL', 'deployed')
normalizedFunctions.forEach(({ name, url, isDeployed }) => {
table.addRow(name, url, isDeployed ? 'yes' : 'no')
})
this.log(table.toString())
}
}

const normalizeFunction = function (deployedFunctions, { name, urlPath: url }) {
const isDeployed = deployedFunctions.some((deployedFunction) => deployedFunction.n === name)
return { name, url, isDeployed }
}

FunctionsListCommand.description = `List functions that exist locally
Helpful for making sure that you have formatted your functions correctly
Expand Down
32 changes: 0 additions & 32 deletions src/utils/finders.js

This file was deleted.

58 changes: 22 additions & 36 deletions src/utils/get-functions.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,29 @@
const fs = require('fs')
const path = require('path')
const { listFunctions } = require('@netlify/zip-it-and-ship-it')

const { findModuleDir, findHandler } = require('./finders')
const { fileExistsAsync } = require('../lib/fs')

const getUrlPath = (functionName) => {
return `/.netlify/functions/${functionName}`
}

const BACKGROUND = '-background'

const isBackground = (functionPath) => {
const filename = path.basename(functionPath, path.extname(functionPath))
return filename.endsWith(BACKGROUND)
const addFunctionProps = ({ mainFile, name, runtime }) => {
const urlPath = getUrlPath(name)
const isBackground = name.endsWith(BACKGROUND)
return { mainFile, name, runtime, urlPath, isBackground }
}

module.exports = {
getFunctions(dir) {
const functions = {}
if (fs.existsSync(dir)) {
fs.readdirSync(dir).forEach((file) => {
if (dir === 'node_modules') {
return
}
const functionPath = path.resolve(path.join(dir, file))
const handlerPath = findHandler(functionPath)
if (!handlerPath) {
return
}
if (path.extname(functionPath) === '.js') {
functions[file.replace(/\.js$/, '')] = {
functionPath,
moduleDir: findModuleDir(functionPath),
isBackground: isBackground(functionPath),
}
} else if (fs.lstatSync(functionPath).isDirectory()) {
functions[file] = {
functionPath: handlerPath,
moduleDir: findModuleDir(functionPath),
isBackground: isBackground(handlerPath),
}
}
})
}
return functions
},
const JS = 'js'

const getFunctions = async (functionsSrcDir) => {
if (!(await fileExistsAsync(functionsSrcDir))) {
return []
}

const functions = await listFunctions(functionsSrcDir)
const functionsWithProps = functions.filter(({ runtime }) => runtime === JS).map((func) => addFunctionProps(func))
return functionsWithProps
}

module.exports = { getFunctions }
47 changes: 26 additions & 21 deletions src/utils/get-functions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ const test = require('ava')

const { withSiteBuilder } = require('../../tests/utils/site-builder')

const { findModuleDir } = require('./finders')
const { getFunctions } = require('./get-functions.js')

test('should return empty object when an empty string is provided', (t) => {
const funcs = getFunctions('')
t.deepEqual(funcs, {})
test('should return empty object when an empty string is provided', async (t) => {
const funcs = await getFunctions('')
t.deepEqual(funcs, [])
})

test('should return an empty object for a directory with no js files', async (t) => {
await withSiteBuilder('site-without-functions', async (builder) => {
await builder.buildAsync()

const funcs = getFunctions(builder.directory)
t.deepEqual(funcs, {})
const funcs = await getFunctions(builder.directory)
t.deepEqual(funcs, [])
})
})

Expand All @@ -30,14 +29,16 @@ test('should return object with function details for a directory with js files',
await builder.buildAsync()

const functions = path.join(builder.directory, 'functions')
const funcs = getFunctions(functions)
t.deepEqual(funcs, {
index: {
functionPath: path.join(functions, 'index.js'),
moduleDir: findModuleDir(functions),
const funcs = await getFunctions(functions)
t.deepEqual(funcs, [
{
name: 'index',
mainFile: path.join(builder.directory, 'functions', 'index.js'),
isBackground: false,
runtime: 'js',
urlPath: '/.netlify/functions/index',
},
})
])
})
})

Expand All @@ -55,18 +56,22 @@ test('should mark background functions based on filenames', async (t) => {
await builder.buildAsync()

const functions = path.join(builder.directory, 'functions')
const funcs = getFunctions(functions)
t.deepEqual(funcs, {
'foo-background': {
functionPath: path.join(functions, 'foo-background.js'),
moduleDir: findModuleDir(functions),
const funcs = await getFunctions(functions)
t.deepEqual(funcs, [
{
name: 'bar-background',
mainFile: path.join(builder.directory, 'functions', 'bar-background', 'bar-background.js'),
isBackground: true,
runtime: 'js',
urlPath: '/.netlify/functions/bar-background',
},
'bar-background': {
functionPath: path.join(functions, 'bar-background/bar-background.js'),
moduleDir: findModuleDir(functions),
{
name: 'foo-background',
mainFile: path.join(builder.directory, 'functions', 'foo-background.js'),
isBackground: true,
runtime: 'js',
urlPath: '/.netlify/functions/foo-background',
},
})
])
})
})
13 changes: 7 additions & 6 deletions src/utils/serve-functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ const shouldBase64Encode = function (contentType) {

const BASE_64_MIME_REGEXP = /image|audio|video|application\/pdf|application\/zip|applicaton\/octet-stream/i

const createHandler = function (dir) {
const functions = getFunctions(dir)
const createHandler = async function (dir) {
const functions = await getFunctions(dir)

const watcher = chokidar.watch(dir, { ignored: /node_modules/ })
watcher.on('change', clearCache('modified')).on('unlink', clearCache('deleted'))
Expand All @@ -176,12 +176,13 @@ const createHandler = function (dir) {
const cleanPath = request.path.replace(/^\/.netlify\/functions/, '')

const functionName = cleanPath.split('/').find(Boolean)
if (!functions[functionName]) {
const func = functions.find(({ name }) => name === functionName)
if (func === undefined) {
response.statusCode = 404
response.end('Function not found...')
return
}
const { functionPath: lambdaPath, isBackground } = functions[functionName]
const { mainFile: lambdaPath, isBackground } = func

const isBase64Encoded = shouldBase64Encode(request.headers['content-type'])
const body = request.get('content-length') ? request.body.toString(isBase64Encoded ? 'base64' : 'utf8') : undefined
Expand Down Expand Up @@ -345,7 +346,7 @@ const createFormSubmissionHandler = function (siteInfo) {
}
}

const serveFunctions = function (dir, siteInfo = {}) {
const serveFunctions = async function (dir, siteInfo = {}) {
const app = express()
app.set('query parser', 'simple')

Expand All @@ -367,7 +368,7 @@ const serveFunctions = function (dir, siteInfo = {}) {
res.status(204).end()
})

app.all('*', createHandler(dir))
app.all('*', await createHandler(dir))

return app
}
Expand Down

0 comments on commit b2de7ea

Please sign in to comment.