@navarrotech/servefunctions
Version:
If you're used to writing serverless functions in individual files, but need to write them on an express.js app, this package makes that easy.
122 lines (98 loc) • 3.3 kB
JavaScript
const path = require("path")
const fs = require("fs")
const fileEndingRegex = new RegExp(/(t|j)s$/)
module.exports = function (app, options={}) {
let {
path: directoryPath='./functions',
defaultMethod='post',
middleware=[],
autoTryCatch=false,
auto500Message="Something went wrong, please try again later.",
verbose=false
} = (options || {})
defaultMethod = defaultMethod.toLowerCase();
// Statistics
let directoriesCount = 0,
filesCount = 0;
function scanDirectory(pathname, history = []) {
fs
.readdirSync(pathname)
.forEach((fileName) => {
let filePath = path.join(pathname, fileName)
let file = fs.lstatSync(filePath)
// If it's a directory, keep scanning!
if (file.isDirectory()) {
directoriesCount += 1;
return scanDirectory(filePath, [...history, fileName])
}
filesCount += 1;
// If it's a file, initialize it!
let url = "/" + path.join(history.join("/"), fileName),
fn = require(path.join(filePath))
if(!fn || typeof fn !== 'function')
return console.warn(`Unable to init function as the file's return method is not a function:\n${url}`)
let preMiddlewares = [],
postMiddlewares = [],
method = null
middleware
.filter(mid => mid?.match.test(url))
.forEach(mid => {
if(!method && mid?.method)
method = mid.method?.toLowerCase()
if(mid?.pre && mid.pre.length)
preMiddlewares.push(...mid.pre)
if(mid?.post && mid.post.length)
postMiddlewares.push(...mid.post)
})
// Url options
if (fileEndingRegex.test(url)) {
url = url.slice(0, -3)
fileName = url.slice(0, -3)
}
if (fileName === 'index') {
url = url.slice(0, -5)
}
let proxy = null;
if(autoTryCatch){
proxy = function(req, res, ...props) {
try {
fn(req, res, props)
} catch(exception) {
console.error(exception)
if(!res.headersSent) {
if(auto500Message){
res.status(500).send(auto500Message)
} else {
res.sendStatus(500)
}
}
}
}
}
app[method || defaultMethod](url, ...preMiddlewares, (proxy || fn), ...postMiddlewares)
})
}
scanDirectory(
resolvePath(directoryPath), []
)
if(verbose){
if(!!directoriesCount)
console.log(`Scanned ${directoriesCount} sub-directories and created ${filesCount} routes!`)
else if(!!filesCount)
console.log(`Created ${filesCount} routes!`)
}
}
function resolvePath(directoryPath){
// First try a relative import
let startPath = path.resolve(directoryPath)
// Then try an import from the project root
if(!fs.existsSync(startPath)){
throw new Error(`Functions directory does not exist in: \n >> ${startPath}\n In ${directoryPath}\n`)
}
// Must be a directory
let pathStats = fs.lstatSync(startPath)
if(!pathStats.isDirectory()){
throw new Error(`Functions path is not a directory!`)
}
return startPath
}