From 35778f9422a0cadfecbe3a0c3c4441c3a7066a9c Mon Sep 17 00:00:00 2001 From: Alexandre Sauner Date: Tue, 24 Sep 2024 22:29:43 +0200 Subject: [PATCH] feat: add custom esbuild entrypoint in function definitions (#546) * feat: add custom esbuild entrypoint in function definitions * docs: change quotes --- README.md | 10 ++++++++++ src/helper.ts | 12 +++++++----- src/tests/helper.test.ts | 21 +++++++++++++++++++++ src/types.ts | 1 + 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 867b811..8aa261b 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,16 @@ module.exports = (serverless) => { As long as the plugin is properly installed, all regular Serverless operations `sls package`, `sls deploy`, `sls deploy function`, `sls invoke local`, `sls offline` will automatically compile using `serverless-esbuild`. +### Specify a custom entrypoint for a function + +You can specify a custom entrypoint for ESBuild by specifying the `esbuildEntrypoint` field in your function definition. +```typescript +export const myLambdaFunction = { + handler: '/opt/nodejs/node_modules/my_custom_extension/handler.handler', + esbuildEntrypoint: './handler.main', +}; +``` + ### Serverless Offline The plugin integrates very well with [serverless-offline](https://github.com/dherault/serverless-offline) to diff --git a/src/helper.ts b/src/helper.ts index ae5ffdc..83bd7aa 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -62,14 +62,16 @@ export function extractFunctionEntries( return !(functions[functionAlias] as EsbuildFunctionDefinitionHandler).skipEsbuild; }) .map((functionAlias) => { - const func = functions[functionAlias]; + const func = functions[functionAlias] as EsbuildFunctionDefinitionHandler; assert(func, `${functionAlias} not found in functions`); - const { handler } = func; - const fnName = path.extname(handler); - const fnNameLastAppearanceIndex = handler.lastIndexOf(fnName); + const { handler, esbuildEntrypoint } = func; + const entrypoint = esbuildEntrypoint || handler; + + const fnName = path.extname(entrypoint); + const fnNameLastAppearanceIndex = entrypoint.lastIndexOf(fnName); // replace only last instance to allow the same name for file and handler - const fileName = handler.substring(0, fnNameLastAppearanceIndex); + const fileName = entrypoint.substring(0, fnNameLastAppearanceIndex); const extensions = resolveExtensions ?? DEFAULT_EXTENSIONS; diff --git a/src/tests/helper.test.ts b/src/tests/helper.test.ts index 9709d46..ccfa2e8 100644 --- a/src/tests/helper.test.ts +++ b/src/tests/helper.test.ts @@ -211,6 +211,27 @@ describe('extractFunctionEntries', () => { ]); }); + it('should use esbuildEntrypoint in priority', () => { + jest.mocked(fs.existsSync).mockReturnValue(true); + const functionDefinitions = { + function1: { + events: [], + handler: '/opt/extension/my_custom_handler', + esbuildEntrypoint: 'file1.handler', + }, + }; + + const fileNames = extractFunctionEntries(cwd, 'aws', functionDefinitions); + + expect(fileNames).toStrictEqual([ + { + entry: 'file1.ts', + func: functionDefinitions.function1, + functionAlias: 'function1', + }, + ]); + }); + it('should throw an error if the handlers reference a file which does not exist', () => { jest.mocked(fs.existsSync).mockReturnValue(false); const functionDefinitions = { diff --git a/src/types.ts b/src/types.ts index 9ba69a5..0c85fe7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -55,6 +55,7 @@ export interface Configuration extends EsbuildOptions { export interface EsbuildFunctionDefinitionHandler extends Serverless.FunctionDefinitionHandler { disposeContext?: boolean; skipEsbuild: boolean; + esbuildEntrypoint?: string; } export interface FunctionEntry {