lambda-live-debugger
Version:
Debug Lambda functions locally like it is running in the cloud
136 lines (135 loc) • 5.4 kB
JavaScript
import * as fs from 'fs/promises';
import * as path from 'path';
import { constants } from 'fs';
import toml from 'toml';
import * as yaml from 'yaml';
import { findPackageJson } from '../utils/findPackageJson.mjs';
import { CloudFormation } from '../cloudFormation.mjs';
import { Logger } from '../logger.mjs';
/**
* Support for AWS SAM framework
*/
export class SamFramework {
samConfigFile = 'samconfig.toml';
samTemplateFile = 'template.yaml';
/**
* Framework name
*/
get name() {
return 'sam';
}
/**
* Can this class handle the current project
* @returns
*/
async canHandle() {
try {
await fs.access(path.resolve(this.samConfigFile), constants.F_OK);
}
catch {
Logger.verbose(`[SAM] This is not a SAM framework project. ${path.resolve(this.samConfigFile)} not found.`);
return false;
}
try {
await fs.access(path.resolve(this.samTemplateFile), constants.F_OK);
}
catch {
Logger.verbose(`[SAM] This is not a SAM framework project. ${path.resolve(this.samTemplateFile)} not found.`);
return false;
}
return true;
}
/**
* Get Lambda functions
* @param config Configuration
* @returns Lambda functions
*/
async getLambdas(config) {
const awsConfiguration = {
region: config.region,
profile: config.profile,
role: config.role,
};
const environment = config.configEnv ?? 'default';
const samConfigContent = await fs.readFile(path.resolve(this.samConfigFile), 'utf-8');
const samConfig = toml.parse(samConfigContent);
const stackName = samConfig[environment]?.global?.parameters?.stack_name;
if (!stackName) {
throw new Error(`Stack name not found in ${path.resolve(this.samConfigFile)}`);
}
const samTemplateContent = await fs.readFile(path.resolve(this.samTemplateFile), 'utf-8');
const template = yaml.parse(samTemplateContent);
const lambdas = [];
// get all resources of type AWS::Serverless::Function
for (const resourceName in template.Resources) {
const resource = template.Resources[resourceName];
if (resource.Type === 'AWS::Serverless::Function') {
lambdas.push({
Name: resourceName,
...resource,
});
}
}
const lambdasDiscovered = [];
Logger.verbose(`[SAM] Found Lambdas`, JSON.stringify(lambdas, null, 2));
const lambdasInStack = await CloudFormation.getLambdasInStack(stackName, awsConfiguration);
Logger.verbose(`[SAM] Found Lambdas in stack ${stackName}:`, JSON.stringify(lambdasInStack, null, 2));
// get tags for each Lambda
for (const func of lambdas) {
const handlerFull = path.join(func.Properties.CodeUri ?? '', func.Properties.Handler);
const handlerParts = handlerFull.split('.');
const handler = handlerParts[1];
const functionName = lambdasInStack.find((lambda) => lambda.logicalId === func.Name)?.lambdaName;
if (!functionName) {
throw new Error(`Function name not found for function: ${func.Name}`);
}
let esBuildOptions = undefined;
let codePath;
if (func.Metadata?.BuildMethod?.toLowerCase() === 'esbuild') {
if (func.Metadata?.BuildProperties?.EntryPoints?.length > 0) {
codePath = path.join(func.Properties.CodeUri ?? '', func.Metadata?.BuildProperties?.EntryPoints[0]);
}
esBuildOptions = {
external: func.Metadata?.BuildProperties?.External,
minify: func.Metadata?.BuildProperties?.Minify,
format: func.Metadata?.BuildProperties?.Format,
target: func.Metadata?.BuildProperties?.Target,
};
}
if (!codePath) {
const fileWithExtension = handlerParts[0];
const possibleCodePaths = [
`${fileWithExtension}.ts`,
`${fileWithExtension}.js`,
`${fileWithExtension}.cjs`,
`${fileWithExtension}.mjs`,
];
for (const cp of possibleCodePaths) {
try {
await fs.access(cp, constants.F_OK);
codePath = cp;
break;
}
catch {
// ignore, file not found
}
}
}
if (!codePath) {
throw new Error(`Code path not found for function: ${func.Name}`);
}
const packageJsonPath = await findPackageJson(codePath);
Logger.verbose(`[SAM] package.json path: ${packageJsonPath}`);
const lambdaResource = {
functionName,
codePath,
handler,
packageJsonPath,
esBuildOptions,
};
lambdasDiscovered.push(lambdaResource);
}
return lambdasDiscovered;
}
}
export const samFramework = new SamFramework();