UNPKG

lambda-live-debugger

Version:

Debug Lambda functions locally like it is running in the cloud

198 lines (197 loc) 7.22 kB
import * as fs from 'fs/promises'; import * as path from 'path'; import { constants } from 'fs'; import { findPackageJson } from '../utils/findPackageJson.mjs'; import { Logger } from '../logger.mjs'; /** * Support for Serverless Framework */ export class SlsFramework { /** * Framework name */ get name() { return 'sls'; } /** * Can this class handle the current project * @returns */ async canHandle() { const serverlessFiles = [ path.resolve('serverless.yml'), path.resolve('serverless.yaml'), path.resolve('serverless.js'), path.resolve('serverless.ts'), path.resolve('serverless.json'), ]; for (const file of serverlessFiles) { try { await fs.access(file, constants.F_OK); return true; } catch { continue; } } Logger.verbose(`[SLS] This is not a Serverless framework project. None of the files found: ${serverlessFiles.join(', ')}`); return false; } /** * Get Lambda functions * @param config Configuration * @returns Lambda functions */ async getLambdas(config) { // LLD arguments might conflict with serverless arguments process.argv = []; let resolveConfigurationPath; let readConfiguration; let Serverless; try { // lazy load modules resolveConfigurationPath = (await import( //@ts-ignore 'serverless/lib/cli/resolve-configuration-path.js')).default; readConfiguration = (await import( //@ts-ignore 'serverless/lib/configuration/read.js')).default; Serverless = (await import('serverless')).default; } catch (error) { Logger.error('Error loading serverless modules', error); Logger.log('If you are running Lambda Live Debugger from a global installation, install Serverless Framework globally as well.'); throw new Error(`Error loading serverless modules. ${error.message}`, { cause: error, }); } const configurationPath = await resolveConfigurationPath(); Logger.verbose(`[SLS] Configuration path: ${path.resolve(configurationPath)}`); const configuration = await readConfiguration(configurationPath); Logger.verbose(`[SLS] Configuration:`, JSON.stringify(configuration, null, 2)); const serviceDir = process.cwd(); const configurationFilename = configuration && configurationPath.slice(serviceDir.length + 1); Logger.verbose(`[SLS] Configuration filename: ${path.resolve(configurationFilename)}`); const commands = []; const options = {}; if (config.stage) { options.stage = config.stage; } if (config.region) { options.region = config.region; } if (config.profile) { options.profile = config.profile; } const variablesMeta = {}; let serverless; try { serverless = new Serverless({ configuration, serviceDir, configurationFilename, commands, options, variablesMeta, }); } catch (error) { throw new Error(`Error creating Serverless instance. ${error.message}`, { cause: error, }); } try { await serverless.init(); } catch (error) { throw new Error(`Error initializing Serverless. ${error.message}`, { cause: error, }); } try { await serverless.run(); } catch (error) { throw new Error(`Error running Serverless. ${error.message}`, { cause: error, }); } const lambdasDiscovered = []; const esBuildOptions = this.getEsBuildOptions(serverless); const lambdas = serverless.service.functions; Logger.verbose(`[SLS] Found Lambdas:`, JSON.stringify(lambdas, null, 2)); for (const func in lambdas) { const lambda = lambdas[func]; const handlerFull = lambda.handler; const handlerParts = handlerFull.split('.'); const handler = handlerParts[1]; const possibleCodePaths = [ `${handlerParts[0]}.ts`, `${handlerParts[0]}.js`, `${handlerParts[0]}.cjs`, `${handlerParts[0]}.mjs`, ]; let codePath; 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 handler: ${handlerFull}`); } const functionName = lambda.name; if (!functionName) { throw new Error(`Function name not found for handler: ${handlerFull}`); } const packageJsonPath = await findPackageJson(codePath); Logger.verbose(`[SLS] package.json path: ${packageJsonPath}`); const lambdaResource = { functionName, codePath, handler, packageJsonPath, esBuildOptions, }; lambdasDiscovered.push(lambdaResource); } return lambdasDiscovered; } getEsBuildOptions(serverless) { // 1) Get from from LLD specific options in custom.lldEsBuild let esBuildOptions = serverless.service.custom?.lldEsBuild; // 2) Get from serverless-esbuild plugin const esBuildPlugin = serverless.service.plugins?.find((p) => p === 'serverless-esbuild'); if (esBuildPlugin) { Logger.verbose('[SLS] serverless-esbuild plugin detected'); const settings = serverless.service.custom?.esbuild; if (settings) { esBuildOptions = { minify: settings.minify, target: settings.target, external: settings.external, }; } } else { // 3) Get from serverless-plugin-typescript plugin const typeScriptPlugin = serverless.service.plugins?.find((p) => p === 'serverless-plugin-typescript'); if (typeScriptPlugin) { Logger.verbose('[SLS] serverless-plugin-typescript plugin detected'); const settings = serverless.service.custom?.serverlessPluginTypescript; if (settings) { esBuildOptions = { tsconfig: settings.tsConfigFileLocation, }; } } } return esBuildOptions; } } export const slsFramework = new SlsFramework();